单片机学习笔记---定时器计数器(含寄存器)工作原理介绍(详解篇2)

目录

T1工作在方式2时

T0工作在方式3时

四种工作方式的总结

定时计数器对输入信号的要求

定时计数器对的编程的一个要求

关于初值计算的问题

4种工作方式的最大定时时间的大小

关于编程方式的问题

实例分析

实例1

实例2


T1工作在方式2时

51单片机,有两个16位的定时计数器,是T0和T1。上面我们介绍了的方式0和方式1的两种工作方式,分别对应的是13位定时计数器方式和16位定时计数器方式。 我们开始介绍方式2。 方式2,又叫做常数自动重装入。在工作的一个等效的图是这样的一种形式,以定时计数器T1为例。

我们大家可以看到基本控制位跟刚才我们所介绍的方式1和方式0是一样的,C/T这一位用来控制是定时器还是计数器方式。gate位是门控位。TR1是这个定时计数器的启动位。

跟方式0和方式1的不同之处就是在这儿:

这里参与计数的是一个八位的单元,对于T1来说是把这个T1这个16位的定时计数器放初值单元分成了两个,一个是TL1,一个是TH1,其中参与计数的是TL1这个单元,所能记录的数据的大小就是从0到FF这样的256数。然后,当记录的数值超过FF就会发生溢出,发生溢出的时候会指位TF1。

在跟方式0和方式1的特点不同之处,就是在TL1发生溢出的同时,会把T1自动的把TH1的数放入到TL1当中去,这个又叫做常数自动重转入,也就是说当一个定时计数器工作在方式2,工作在这种常数自动重装入方式时,相当于是一个八位的定时计数器,这个八位定时器有一个特点,就是每当溢出之后,计数初值是由硬件自动的把TH当中的内容给写入到TL当中去。计数初值是由硬件自动来写入的,这是的一个特点。

下面就是一个方式2正常在工作的时候的一个流程图,就是正常有脉冲技术脉冲经过的时候,TL的值加1。 如果要是说当TL值在记录的过程当中没有超过255这个数,就相当于是没有溢出,始终是循环,有计数值就要加1,有计数值加1,当产生溢出的时候,是把1赋值给TF位,就是产生这个溢出标志位,同时,是把TH的内容再写入到TL当中去,这样的话,在T当中,就得到了一个初始值。

在这里,我们要强调一点,对于方式0和方式1来说,在正常工作的时候,记录的数据达到溢出,比如说从0000开始记,当我记到FFFF,然后又有一个数来的时候,那是从FFFF变成0000,这叫溢出,一旦溢出之后,在定时计数器的初值寄存器当中,放的就是0,也就是说,第一次计数可以在你所设定的那个初值的基础上开始计数。 当第一次计数溢出之后,第二次计数将是从0000这个初始值开始计数。 对于我们来说,有的很多时候,这样做是不可以的,比如说你要产生一个周期非常稳定的一个方波,希望这个周期的高低电平都保持一个固定的时间,换句话说,产生这个方波的定时计数器的初值都是一个固定的值,这样的话,记录时间长度才能够保持一致。 如果要是你使用的是13位或者是16位的定时计数器的话,每一次在产生溢出之后都需要重新的写入的初值,有这样的两条指令,重新写初值的指令,而对于方式2的话,就不需要,每一次溢出的时候是由硬件自动的把TH中的内容给写入到TL当中,你只要把TH当中存放的就是你所需要产生的那段定时时间的常数,只要这样做,就可以保证产生一个周期比较稳定的这样的一个信号。

这种工作方式(方式2),可以省去用户软件中重新装入常数的这个程序,当定时计数器的这个初值寄存器产生溢出之后,对于方式2来说,就不需要你再手工的去写初值,就硬件自动的赋予初值,而对方式0方式1,需要手工写初值,这样的话,如果你采用方式2的话,就省去了重新写入初值的这样的程序,会使得这个定时时间更加精确,可以产生比较稳定的周期,比较稳定的这样的一个方波信号,产生一个就是定时时间非常精度比较高的这样的一个时间信号。

比如说,我们在波特律发生器当中,就要采用方式2,让这个定时器产生一个稳定的一个波特率。 波特率,是在串行通讯当中所需要使用到的一个定义,主要是指的是串行通讯的时候每秒钟所发送的位数,一般,我们要求串行通讯的时候每秒钟发送的这个位数是有一个比较稳定的,非常固定的这样的一个速率发送的话,对于发送方和接收方才能够可靠的实现双机通讯,所以要求这个时间是精度比较高,一般,我们是让定时器工作在方式2作为波特律发射器来使用。

T0工作在方式3时

下面,再看一下工作于方式3。

只有定时计数器T0才有工作方式3,当T0工作于方式3的时候,是把内部分成了两个8位的定时计数器TL0和TH0,就是TL0在方式3下,自己自动分解成两个8位的定时计数器,其中,TL0仍然使用T0的状态控制位,C/T位,GATE位,TR0位和INT0位,这些仍然使用原先T0的这些资源。而TH0被固定的作为一个8位的定时器来使用,并且使用定时器T1的两个控制位TR1和TF1,同时占用定时计数器T1的中断源,也就是说,当T0如果工作在方式3的话,相当于是把T0分解成了两个8位的定时计数器,其中TL0(低8位)占用了原先T0的所有资源,而TH0这个8位定时计数器是只能工作在定时器方式下,同时占用了原先T1的那些启动位和的中断标志位。 这就是T0工作的方式三,而T1是不能工作在方式三的,如果在设置这个命令字的时候,让T1进入到了方式三,等效就是T1是停止工作,不会工作了,在T0工作在方式三的时候,我们可以设置让T1进入到方式0,方式1,方式,2,这些都可以。

大家看一下,这就是工作在方式3的T0的等效的一个逻辑图。

大家可以看到在T0工作在方式3的时候,是分解成了一个低8位的一个定时计数器,占用了原先的T0的所有的资源,所有的资源包括的这个门控位,包括的启动位,以及包括的中断源等等这些都被所占用了。

TH这个8位的定时计数器就是只能作为定时器使用,只能对机器周期计数。 同时,使用到了T1的两个资源,一个是启动位,另一个就是中断源,这就是工作在方式3的T0。

我们再看,当T0工作在方式3的时候,T1可以工作在什么样的一个状态?可以工作在方式0,是一个13位的定时计数器,也可以工作在方式1,作为一个16位的定时计数器来使用,也可以工作在方式2,作为一个8位的定时计数器来使用。

大家要注意到,当T0工作在方式3的时候,T1不论是工作在方式0还是方式1方式2,都缺失了两个控制位,一个是TR位,另一个就是TF位,也就是说,什么时候启动,这个是无法控制的,这无法控制的启停,另一个就是无法知道什么时候溢出,一般,我们在这时候,都是让这个T1工作在自动的这种工作方式下,这时候T1就是自动处于一个工作方式,周而复始的会产生这种溢出信号。即一般当T0工作在方式3的时候,我们一般让T1工作在方式2这种八位常数自动重装入方式下,作为波特律发生器来使用,所以下面接着是串行口。

其实其它的都可以作为波特利发生器使用,但是方式2是最为灵活的,最为准确的一种方式。刚才我们已经介绍过了,8倍长数自动重装入所产生的定时时间精度是最高的。

四种工作方式的总结

下面就对这个定时计数器的四种工作方式做一个简单的总结。

方式0,是一个13位的定时计数器,大家要注意到的,是TL就是T1或者是T0当中的低8位只有5位参与计数,高8位同时参与计数,所以是构成一个13位的定时计数器。

方式1,是一个16位的定时计数器,使用和方式0是完全相同的,只不过是参与计数的T0这个寄存器当中参与计数的位数不同,一个是13位,一个是16位。 方式2,是一个常数自动重装入的八位定时计数器,特点就是可以把计数常数/计数初值,是由硬件自动的装入进来的,每一次溢出之后,硬件自动的把TH中的内容装入到TL当中,使得这个八位定时计数器能够在一个固定的初值的基础上参与计数,那这样的话,能够产生一个时间比较准确的一个定时。

方式3,是T0分成两个独立的八位计数器,其中,TL0是作为定时计数器来使用,TH0是只能作为定时器来使用。TL0的控制使用了原先T0, TH0占用了原先T1的那些控制位,TR1和TF1,同时还占用了定时器T1的中断源。此时,T0工作在方式3的时候,T1可以工作在方式0~方式2,溢出的时候,这种溢出信号,自动的送到了串行口,经常是作为串行口的波特律发生器来使用,在T1作为波特率发生器使用的时候,是可以工作在方式0,方式一,方式二都可以,但是最好的一种工作方式是让T1工作在方式2,这时候的波特率是最为稳定的。

定时计数器对输入信号的要求

下面我们来看一个定时计数器对输入信号的要求。 作为一个定时计数器来说,一方面可以对内部的一个机器周期计数,另一方面,可以对一个管角上所来的一个脉冲信号进行计数。

我们首先看一下,当定时计数器工作于定时器方式的时候,因为是对内部的时钟信号,对内部的机器周期计数,每一个机器周期,计数值加1,比如说我如果采用一个12兆的晶振的话,机器周期就是一微秒,也就一味着,每一微秒的这个计数值会加1。 由于定时的精度是取决于输入脉冲的周期的,所以要想提高这种定时期的分辨率,就必须选用频率比较高的晶振才可以,对于51来说,最高的晶振就是12兆,除非是你选用一些特殊的型号才可以。 这一点我们在学数字电路的时候有过印象,如果要想记录的时间更加准确的话,产生一个时间非常准的这样的一个信号的话,就要求时钟源频率越高,时间的分辨率才越高,比如说如果对一个两秒钟这样的一个信号进行计数的话,那时间分辨率就是两秒,就是2秒之内就无法区分出来。定时时间的精度是与时钟源的精度有关的。 对于51单片机来说,定时器的精度就取决于了这个外界的这个晶振的频率,外界晶振的频率是每个机器周期加1的。

下面我们看,如果要是工作于计数器方式的话,是怎样的一个工作过程。

当定数计数器作为计数器使用的时候,计数脉冲是来自外部的输入引脚,T0或者是T1,当输入信号产生从1~0的这种下调沿的时候,计数值会增1,也就是说,计数器是对外部引脚的一个脉冲信号计数,所记录的是这个引脚上所出现的下调沿的个数。 这个51单片机是如何判断这个引脚上出现了一个下调沿?是每个机器周期对这个引脚的电平信号进行一次采样,每个机器周期对这个电平信号采样一次,当这个机器周期采样得到的电平是高电平,而下一个机器周期样得到电平是低电平的话,它认为在这个过程当中引脚上出现了一个下调沿,就会使得内部的计数器的值加1。

由此,大家可以看到,51工作在计数器方式的时候,对这个外部输入引脚的电平是有要求的,第一个要求,就是必须要高低电平的持续时间必须大于一个机器周期,因为只有大于一个机器周期的话,51单片机才能够可靠的识别出这个引脚的电平状态,因为每一个机周期只采样一次,只有当这个电频信号持续的时间大于一个机器周期的话,才能够被51单片机所识别出来。 正是因为有了这样的一个要求,如果我要是选用的是一个6兆频率的这样的一个晶体的话,允许输入脉冲的频率是多少?是250K赫兹,如果选用的是12兆频率晶体的话,允许输入的最高的外部脉冲就是500K赫兹,为什么是这样?

大家看一下,如果你选用的是一个12兆晶振的话,一个机器周期就是1微秒,对外部的技术脉冲的要求是什么?高电平持续时间要大于1微秒,低电平持续时间也要大于1微秒,这样的话,这个脉冲的频率是多少?高电平1微秒,低电平1微秒,这个脉冲的周期,就是2微秒。 周期是2微秒,对应的频率是多少?就是500K赫兹,也就是说,对于当你单片机所使用的晶振是12MHz的话,作为计数器使用的时候,所能记录的最高的这个脉冲的频率是500K赫兹。

这个就是我们所画的一个简单的一个示意图,在这个图当中,大家可以看到作为计数器使用的时候,要求这个高低电平的持续的时间必须是大于一个机器周期,这样的话,这个高低电平信号才能够被51单片机可靠的识别出来,从而才能够判断出什么时候出现了这个下降沿,才能保证计数器能够可靠的工作,不会发生定脉冲的事件。

定时计数器对的编程的一个要求

下面我们看一下定时计数器对的编程的一个要求,如何使用这个定时计数器。

首先,要对进行初始化,根据要求给方式寄存器TMOD送一个方式控制字,以设定定时器的工作方式。 你让这个定时计数器工作在什么方式,是8位的,16位的还是13位的?就是定工作方式

  • 根据需要给TH和TL当中送,就是作为一个定时器使用的话,你需要定时时间是多长? 就需要从一个初值开始记录,能够产生这个定时时间,就是要写有一个初值的问题。
  • 根据需要给中断允许寄存器IE送中断控制字,以开放相应的中断也可以用查询的方式来响应这个定时器。 第三步是什么?你已经写完初值了,然后你就可以让它工作了,紧接着,你要知道这个定时计数器工作之后,你如何来判断这个定时时间到了,就是记录的数据到达你所设的个数,如何来判断的问题。一方面,作为这种定时器的响应,可以用中断来作为的一个响应。

中断系统,我们在后面要说要讲到这个中断系统,在这里,只是给大家简单介绍一下。  专门有一个中断源是定时计数器所使用的这样一个中断源,如果你用一个中断处理子程序来处理这个定时计数器的这个溢出这个事件的话,就要求你首先要开放这个中断源,要允许中断。其次,要把你的中断子程序写入到固定的中断入口那个位置上去,就这两个要求,希望大家在这里就是要有一个印象,就是如果你要是用一个中断来作为这种定时器溢出事件的响应的话,你怎么样来编写这个中断子程序?就中断子程序有对中断子程序有什么样的要求?第一个要求,就是说你要开放中断,要允许这个中断,第二个,就是说要把这个中断处理子程序,放入到固定的位置上去,就这两个要求。 或者是你可以通过查询的方法来作为这个定时计数器溢出的一个响应事件,所谓的查询方式就是可以查询这个TF标志位,因为在前面的介绍当中,大家都知道说对于一个定时计数器,一旦开始启动之后开始计数,当发生溢出的时候,TF位会有一个标志会被置1,只要我在编程当中,可以判断这个TF位的状态,如果TF位是高电平,那就表示这个定时计数器已经到了,到溢出已经记录的时间到了,那可以调用我的这个处理子程序,处理程序去处理,去响应这个定时计数器溢出的时间。

最后,就是给TCON这个命令寄存器当中送启动位,让它启动,就是TR位给置1,就开始启动,就开始计数了。

这就是定时计数器使用编程的一个步骤,就首先,要确定的工作方式,是8位,16位还是13位,第二个,是根据你的定时时间的长度,确定的初值,第三个是编写一段程序,这段程序是作为定时计数器溢出事件的一个响应的一个程序,第四个是启动的定时计数器,让开始计数。

关于初值计算的问题

下面,我们看一下关于初值计算的问题,在前面给大家说过,工作在51的定时计数器是一个加计数,加计数每一次当发生溢出的时候,才会有一个明确的标志,明确的标志就是TF未被置1,这样的话就存在一个问题,就是我需要记录的时间长度如何换算成的计数初值的问题,是从一定初值开始记,才能够得到你的那个计数的时间长度。

我们看一下有这样的一个计算公式,希望大家能够给记录下来,以后在使用当中可以直接写这个公式。

如果作为一个计数器来使用的话,计数器的初值应该是如何的?

设计数的模值为M,这个M就是指的是8位计数器,16位计数器还是13位计数器,如果是8位计数器的话,模值就是2的8次方,16位就是2的16次方,以此类推。

所需要的计数值是C,我需要记C个数的话,计数初值是多少?我们一般应该写的计数初值是,如果是T/C的话,应该等于是M-C。

什么意思?你比如说我让这个定时计数器工作在八位方式,我现在去需要记录250个数,你写初值的时候,可以把250这个数直接写入到的初值计数器当中吗?就是写入到这个TH当中去吗?很显然是不行的,因为你如果要是把250写入到了这个初值计数器当中,记录多少个数将会卖溢出?记录6个250,在250的基础上开始记251、 252,当记录到255再来一个脉冲的时候,变成00,255对应的是FF,再来一个又变成00,会产生溢出,也就是说,如果你把你需要的计数值写入到初始计数器当中的话,你得不到你所需要的那个时间长度,因为是一个加计数。 在这里,通过这个公式计算,大家都知道,如果你需要记录250个数据的话,250个脉冲的话,记录250个脉冲的话,所写的初值,T/C这个位置应该放多少?应该是模值是2的八次方,就是让定时计数器工作在八位方之下的话,模值是2的八次方减掉你所需要记录的这250个数,T/C应该等于是6,也就是说,你应该在这个初值寄存器当中,就是T0,T1在的这个存放初值这个寄存器里,放的是6这个数,而不能说是放250这个数,这就是你需要记录的计数值和你写入到初值计算器当中的计数值是不一样的,那不一样有一个换算,就通过这个公式,可以直接的换算出来,根据你的定时计数器的位数,根据你所需要的计数值,可以换算出的数值来,就是采用这样一个公式。

同样的看一下,如果要是让定时计数器工作在定时器方式的话,初值应该如何计算?

设定时计数器的模值为M,需要的定时时间为T,定时器的初值T等于什么?T/C等于的是M减去大T除小T,大T是定时时间,小T是你的机器周期,要除以一下这个机器周期,这个换算的过程大家可以对照计数器的数值,可以看一下它们两个有相同之处。

大家在使用的时候,,就是完全可以根据这两个公式来直接计算出的初值,那就不需要再手工的去其采用其方法去换算就不用了,这个公式用起来还是比较方便的,这是关于初值的计算的问题,给了大家两个公式,可以直接,根据你的定时计数器工作的位数,根据你所需要的定时和计数的这个长度,然后,计算出的这个初值来。  

4种工作方式的最大定时时间的大小

下面我们再看一下几种工作方式的最大定时时间的大小是多少?

如果要是定时器的初值为0的时候,这时候的定时时间是最大的,因为将是从0开始,一直记录到满为止这个计数时间,定时和计数时间是最长的。 如果要是外接的晶振是12MHz的话,几种方式下最大的定时间是多少?

如果要是对应的是一个方式0的话,所能够产生的最大定时时间,是8.192ms,方式0是一个13位的,这样的一个接定时计数器。

方式一的话,实际上工作在16位方式下,最多得到的时间长度可以达到65.536ms。 方式2方式3都是一个八位的定时计数器方式,所能够达到的最大时间是0.256ms。

把这个告诉大家主要的一个目的就是根据你的定时时间长度,可以确定我让这个定时计数器工作在什么方式下,你比如说我现在如果产想产生一个30ms的这样的一个是定时时间的话,很显然你只能让定时计数器工作在方式1;只能工作在方式1。 如果你要是让这个定时计数器产生一个0.1ms这样的一个定时时间的话,那你既可以让工作方式1,也可以工作方式0,也可以工作方式二、方式三都可以,这个就是说是你确定工作方式的一个依据。 这几个数据就是在外界晶振为12MHz的时候,所能够达到的最大时间,实际上计算是比较简单的,外界晶振12兆表示一个机器周期是1微秒,对于方式一来说,定时计数器的位数是16位的,最大记录时间就是2的16次方乘以1微秒,2的16次方对应的就是65536,然后再乘以1微秒,对应的时间就变成了这个65.36ms。就是这么计算出来的,其的都是以此类推。

下面我们再看一个初始计算的例子,比如说现在外界晶振是12MHz,我现在需要产生的这个定时时间是2ms,希望大家,来计算一下我这个定时计数器的初值是多少?

对于方式二和方式三来说,就是这个八位的定时计数器,所能够产生的最大时间,刚才领大家算了一下,看了一下是0.256ms,很显然,这个是不满足要求的,我要产生一个2ms的信号,你这个工作在8位方式,最多才是0.256ms,这是不能够使用的。

再看一下方式0的时候,这是可以的,最大时间是大于2ms,最大是8ms多,所以的计数初值是多少?是2的13次方,方式0是一个13位的定时计数期,所以的模值是2的13次方,然后减去定时时间长度是2ms除以机器周期,机器周期是1微秒,外接晶振为12兆的时候,机器周期是1微秒,所以,初值的公式就可以写成2的13次方减去2微秒除以1微秒,最后,等于的是6192。换算16进制数是1830H,写成初值的形式,写的是C1H和10H,高八位放的是C1,低8位放的是10。

这块就是提醒大家注意的一点,当工作在13位方式下,是低八位寄存器当中只有5位参与基数。高8位当中是8位都参与计数,所以你得到的这个16进制数在放的时候你要会放。把低五位放完之后,低八位当中的高三位是不管的,接着往高八位当中放数据,最后,把这个1830给换算过来才可以,比如说在这里这个1830,这对应的是0001,8是1000,3是0011,然后最后一个0是0000。

这是1830这个16进制数,我们放取的时候,是低五位取过来就变成10000,然后,在剩下的8位1100 0001给写入到高八位这个寄存器当中,对应的正好是这样的这八位正好是C1,然后低5位是10000写入到寄存器当中后高三位是无关位,我们给写成0。最后,对应的这个初值就是C110。

注意就是在工作的13位方式的时候,大家计算所得到的这样的一个16位的初值,在往这个数值寄存器当中写的时候,不能把这个16位的数值直接写进去,还要再进行一下变换,是写成低5位和高8位的形式,然后给写进去才行。

如果要是工作在16位方式下,就比较简单了。计数初值是2的16次方减去2ms除以1微秒,最后等于6336。转换成16进制就是F830,写初值的时候,就可以直接写高8位写的是F8,低8位写的是30H,这就可以了。

由此可见,大家应该明白,就是定时计数器工作的时候,实际上方式一,应该是时间比较长的时候,让定时计数器直接工作在16位方式下是比较经济比较合算的,因为这时候,无论是你的初值的计算,还是你初值的这个直接的书写,往定寄存器当中写这个初值都是比较方便的,比较简单的,而13位相对比较麻烦一下,计算出来的值和往初值寄存器当中所写的是还要再进行一次变换,这一点要注意,就是16位的和8位的使用起来是最灵活的,13位是一个过渡阶段,这个13位的定时计数器,主要是51单片机为了和48系列单片机相兼容所保留的这样的一种工作方式。

这就是的关于初值计算的一些问题。

关于编程方式的问题

下面再看一下关于编程方式的问题。

一种编程方式是可以采用查询的方式,程序一直检测这个TF位,因为定时计数器一旦工作之后,我们唯一能够判断定时计数器工作状态,就是通过TF位,这是一个唯一的一个窗口,TF位所指示的就是是否溢出这样的一个信息,所以我们在编程序的时候,就可以在程序当中指令当中就专门就有这样的一条指令,就是查询TF位。如果TF位为1的话,表示定时时间已经到了,在如果你要是采用这种查询方式,就是我靠我指令去查询TF位,从而判断定时计数器是否溢出。

如果用这种方法的话,要注意的是在编程当中要及时的对TF位进行清零,因为TF位是定时计数器如果溢出是由硬件置1的,而如果要是用你人为的用编写的一段程序,就是非中断方式去响应的话,去作为的一个处理程序的话,需要你在程序当中有清除TF位的指令,这种方法又叫做硬件置1软件清零。在后面我们再讲标志位的时候,有一些标志位就是有这样的一个要求,就是有是硬件置1的,但是需要你的响应程序通过一条指令,就是通过软件去清理。

第二种方式,就是说采用中断的方式来作为的响应,这个是相对比较简单,你只要把这个定时计数器的中断给开放了,允许响应这个中断事件就可以了,是不是开放的中断,然后把你的处理这个定时计数器事件的这段程序,放入规定位置,这个规定位置就是指的是的中断入口这个位置,CPU在运行的时候,当如果要是定时计数器真的溢出了,会自动的调用这段中断子程序去处理这个事件,去响应这个事件。如果要是用采用这种中断的方法,作为定时计数器溢出的响应的处理程序的话,这个TF位,就不再需要你用指令去对清零了,的特性就是硬件置1硬件清零,在进入中断的时候,是由硬件自动的清除TF位,所以我们应该说,就是用中断作为这个定时计数器的一个响应,应该是相对比较简单的,无论使用还是它的编程都是比较简单的。

这是的两种编程方式,无论是查询还是中断,我们在后面的例子当中,都要给大家有所介绍。

实例分析

实例1

下面我们看一个第一个例子,就是一个方式0的一个应用。

现在要求选用T1让工作在方式0,产生一个500微秒的定时,在P1.1输出周期为1ms的方波,p1.1是P1口的第1引脚,我要求在p1.1这个引脚上输出一个周期是1ms的方波,高电平500微秒,低电平500微秒这样的一个方波。这个单片机的晶振是6MHz。

当使用到T1的时候,有一点就是提醒大家注意的,这时候,我们没有用到T0,大家给T0在写这个命令寄存器的时候,你不要任意的去写,不要让T0,比如说一个误操作,让T0工作在方式3了,在前面所说的就是方式寄存器,如果要是控制设成工作方式3的话,T0是分解成两个8位的定时计数器,某些资源T1就无法用了,我们说了T1的启动位停止位,启动位和溢出标志位被T0所使用了所以,如果在没有使用到T0的时候,千万不要让T0进入到方式3,这是一个原则。 禁止进入方式3,一般给设成方式0,就是如果我使用到的是T1这个定时计数器的话,大家对T0如何处理,一定要让T0设一个默认值是方式0,而不要让写成方式3。

下面看一下初值的计算,在这里,你看我们的题目的要求,告诉你的工作方式了,那剩下的就是看一下的技术初值应该是多少。 计数初值应该等于的是2的13次方,方式0嘛,对应的是一个13位的定时计数期,然后减掉计数的定时时间是500每秒,再除以机器周期6MHz晶振下,其周期是2微秒,最后得到的计数的常数是计数的初值是7942。7942对应的16进制数是1F06。 然后把这个1F06这个16位的数给写成低5位和高8位的形式,就把低五位摘出来放到一个寄存器当中,然后再取前八位取出来放到高8位寄存器当中,所对应的一个16位的寄存器的初值是F806。

这点要注意就是到13位这块,大家计算得到的初值和实际写入到初值寄存器当中的值,中间是需要变换一下,这一点大家要注意!对于一个初学者,建议大家直接使用16位的定时计数器,就不要使用这个13位的

下面看一下如何对进行编程。 这是它的主程序:

PS:以下是用汇编语言作为一个讲解示例,学C语言的伙伴只需了解它的程序逻辑即可,不用纠结某些符号所代表的意思。

对于我们在前面的介绍当中说过了,首先要设工作方式,现在让T1是工作在方式0。

我们看一下工作方式寄存器TMOD。

在写的时候,大家需要记录测量外部脉冲的宽度吗?不需要,所以门控位为0。 门控位一定是在需要测量外部脉冲宽度的时候使用门控位,其它的情况下都不使用门控位,所以这位为0。

然后再看C/T这一位,T1是工作在定时器方式,所以这一位为0,如果1的话就表示计数器方式.

然后看工作方式M1、M0,因为我现在就要求工作的方式0,所以这两位写的是0 0。

对于T0,没有使用到T0这个定时计数器可以都写成0,在前面说过了,对于没有使用到的定时计数器,把的所有位数都写成0就可以了。

这样的话,大家可以判断一下,这个TMOD这个寄存的命令字是什么?是0000 0000,所以我们直接写的是MOV TMOD,#0,这是的命令字。

在这里,才是我们所学的这个51单片机当中,就给SFR写命令字,这是第一个,第一个命令字是TMOD。 第二个看就是我要写初值,刚才我们的计算已经计算清楚了(拉上去看看我们算出来的),是F806,所以要写的是MOV TL1,低8位放的是什么?是06H,高八位当中放的是什么?MOV TH1放的是0F8H,这个0是由于是书写16位的一个规范。#号后面,如果这个立即数的首位是介于A到F之间的话,要求前面再最加一个0,再追加一个0,所以是0F8H。

这是写完初值了,下面该干什么了?

确定的工作方式,确定的初值应该是启动让它开始工作了。

启动定时计数器,直接使用的是SETB,因为TR1如果置1的话,就意味着启动这个定时计数器T1,TR0是T0的启动控制位。在后面,如果我们用到T0的话,应该是SET TR0,就是启动T0在这里。因为使用到的是T1,所以执行的是SET TR1这样的一个指令。

下面再看,启动之后,我们在前面介绍功能部件的时候说过,就是由这个SFR自动的去控制管理这个定时计数器的工作了,而CPU根本就不需要理会,CUP继续执行你的指令就可以了。在这里,我们在这个编程当中是采用一种查询的方法来判断定时计数器是否溢出,用查询的方法来判断的。所以这块,CPU在执行的时候,采用了这样一条指令,JBC TF1, PF0。 JBC指令是为操作指令当中的一个,它的指令的含义就是把后面这一位的信息拿过来判断一下是否为1。等于1吗?如果要是等于1的话,则转移,同时,把这一位清0。

在介绍JBC指令的时候说过,是非常有用的一条指令,主要功能就是把后面的一位取出来,判断是否为1,如果是为1的话,则转移,同时把这一位的信息清零。如果要是定时计数器时间到了,TF1将被置1。

现在我把TF1拿过来之后,开始判断是否为1,很显然刚开始程序运行的时候,没有到这500每秒时间,所以应该是为零的,如果是为零的话,将顺序执行进行下来,执行的是SJMP Loop跳转指令,又跳转回来了,接着又执行JBC TF1, PF0,又取出PF0,接着来判断的话,这个这两条指令的一个含义就是什么?让这个程序循环,在等待,始终是取TF1这一位来判断,如果是0的话,表明定时时间没到,就回过头来再取TF1,再判断,然后再取,再判断,这样直到TF1这一位为1为止。TF1这位为1意味着什么?意味着定时时间到了,500位秒到了。

如果要是说这时候这个定时时间到了的话,TF1位被置1,发生长期跳转,跳转到PF这个位置来,所以就是跳转到了这条指令这跳转到这条指令

这在这里你看是如何做的,是MOV TL1, #06H,接着MOV TH1 #0F8H,这两条指令是干什么用的?叫重新写初值。 刚才我们说过了,如果要是让这个定时计数器产生一个周期的这样的一个信号的话,那就存在着我再次对写入初值的问题。除非是你让这个定时计数器工作在8位常数自动重装入方式下,每一次溢出这个计数初值是自动写入的,由硬件自动写入的,工作在方式0和方式1都需要你在指令当中给写入,再次写入数值,在这里,就是当发生溢出的时候,通过JBC指令及时的把这个TF位的信息给清零了,同时要把这个计数初值再重新的写入到这个TL1和TH1当中去,写入的仍然是F806,就是还让再产生一个500微秒这样的一个时间长度。

然后接着是CPL P1.1,CPL是一个取反指令,对P1.1取反,这块,就是我们在前面的硬件结构当中介绍的时候说过,对于P1口来说,上电复位之后,初始状态是1,都是高电平。也就是说P1.1上电复位之后,就是高电平。当程序这个定时计数器启动之后,执行到这一条的时候,就到500每秒的时候,对它取反,从高电平变成了低电平,变成低电平之后,AJMP loop循环回来,又循环回来了,循环回来之后接着取TF1这一位,JBC只能接着取T1这位,判断是否为1。

很显然,在刚开始第一次循环回来的时候,这个TF位肯定是为0,没到500微秒,当到500微秒的时候,怎么样?程序又执行下来,再把的初值重新写一遍,然后又取反,取反的结果就是让又变成了高电平,这样的话,这个低电平持续时间是多长时间?是500微秒。 然后,变成高电平之后,再过500微秒之后,又变成了低电平,然后这个时间,也是500微秒,从而得到了一个周期是1ms这样的一个方波信号。

在这里,大家应该注意到这个我在这个定时计数器的应用当中,我是采用查询的方法。 始终在主程序当中有一个判断指令,取出这个TF位,然后进行判断,也就是说,通过指令取这个TF位来判断是否为1,这个叫做查询的方法来作为定时计数器溢出的一个处理程序。

如果要是采用这种方式的话,要注意及时的对TF位进行清零,置1是发生在定时计数器的溢出时刻,只有在软件当中及时的清零,才能够捕捉到下一次的溢出时刻,这是要注意的,就是如果采用的是查询的方法来作为定时计数器溢出的响应的处理程序的话,那要及时的对TF位清零,在这里,是通过JBC指令,既判断什么时候为1了,然后又可以及时的清零,采用的是这个指令来完成的。

第二个大家要注意的是,如果要是定时计数器工作在非方式2的工作方式下,也就是说工作在不是8位乘数自动冲撞入这种方式下的时候,方式0,方式1等等工作在这些方式下,大家要注意及时进行及时的写初值。

在第一次启动之前,你一定是给写入初值,然后启动了,当溢出之后,如果你还想产生刚才所设定的长的定时时间的话,那就要求在第一次溢出之后,再次的把那个计数数值,通过指令给写进来,这样的话才能够产生一个你所需要的那个定时时间,就是关于初值的再次写入的问题,这是需要注意的,只要你不是工作在这个方式2的话,都存在着一个初值重新写入的问题。

实例2

下面再看再看一个例子,在这里,是让T0是工作在计数器方式,然后T1工作在定时器方式,同时,都是工作在方式二,方式二是我们在定时计数器使用当中,经常使用到的一种工作方式,就是一个是方式一,一个是方式二,这两个是工经常使用的,我们看一下工作在方式二的情况。

T0工作在计数器方式,然后T1工作在定时器方式。我们再看一下这个题目,要求从P3.4引脚,P3.4是T0引脚,输入低频脉冲(作为一个计数器使用嘛,那就要求输入一个低频脉冲),要求该脉冲,每发生一次跳变,由p1.0输出一个500微秒的负脉冲。 然后与此同时,p1.1输出一个宽度为1ms的正脉冲,晶振6MHz。什么意思?

就是T0是工作在一个计数器方式T0外接的引脚如果有出现了一个计数脉冲的话,就要求p1.0输出一个500微秒的一个负脉冲。然后P1.1这个引脚输出一个一毫秒的一个正脉

就是每当我T0这个引脚,就是P3.4这个引脚输入一个脉冲的话,那就在p1.0和P1.1这两个引脚上就分别出现一个正脉冲和一个负脉冲,这是我们题目的要求。 我们设的时候,就是要求是T0工作在计数器方式嘛,所以就设T0为计数器方式,而且工作在方式二,初值我们给设成FF,设成FF有一个好处,只要有一个脉冲的话就会溢出。

我就可以判断出来什么时候溢出,因为你如果要是初值给设成的是F0的话,T0这个引脚来一个脉冲,你根本无法判断出来什么时候来一个脉冲,F0变成F1,溢出标志位不会被置1,只有发生溢出的时候,这个溢出标志位才会被注销。 所以,我现在想每当这个引脚来一个脉冲,我就能够识别出来,能够判断出来,如果要达到这个目的的话,就要求你让这个计数的初值是FF,这样的话,每当有一个脉冲,就是加1就变成00嘛,就要产生溢出嘛。 然后,当外部输入一个脉冲的时候,计数器加1产生溢出,使得TF0=1,引起CPU的T0中断。

用中断来作为这个计数器的一个响应,然后中断服务程序内是对定时器T1设初值,用T1工作在这个方式2,给设一个初值,让T1产生这个规定的这个500微秒这样的一个脉冲,产生这个500微秒的定时,从而在这个P1.1和P1.0这个引脚上得到这个正负脉冲。

本节先讲到这儿,下节继续!

如有问题课可评论区留言或者私信,谢谢!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/357559.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

python3.8 安装缺少ssl、_ctypes模块解决办法

问题 安装pyhton3.8安装默认不依赖ssl 运行Flask项目时报错&#xff1a; Traceback (most recent call last):File "/usr/local/python3/bin/flask", line 8, in <module>sys.exit(main())File "/usr/local/python3/lib/python3.8/site-packages/flask…

C语言递归篇章+系统讲解分析+深入理解递归+根源进行讲解+进制转换+操作环境+实例剖析+万字+百张图片精细化讲解

递归的讲解系统分析 什么是递归 本质上就是一种算法 最简单递归 栈溢出 没有限制条件 导致无穷尽的调用自己 从而溢出 最后变成死递归 _________________________________________________________________________________________________________________________________…

SAP MM 采购发票输入税额,模拟时候发现没有税科目记账税额!

原因&#xff1a;行项目税额和抬头不一样导致&#xff0c;以下是调整过的截图&#xff0c;原来底下是J2

去中心化世界的奇迹:深度解析Web3

随着科技的飞速发展&#xff0c;我们正逐渐进入一个新的数字时代&#xff0c;而Web3技术正是这个时代的奇迹之一。本文将深入解析Web3&#xff0c;揭示它在构建去中心化世界方面的深远影响以及给我们带来的可能性。 什么是Web3&#xff1f; Web3是互联网的第三个时代&#xff…

低代码数字孪生平台

老子云平台 老子云平台专注于3D资源服务与应用在Web页面上的极致展示&#xff08;渲染与交互&#xff09; 老子云平台架构 核心优势-AMRT标准格式 2022年6月&#xff0c;在国家科技部科技成果鉴定中心的成果评定中&#xff0c;AMRT标准被评为国际先进成果&#xff0c;AMRT格式…

TCP常用端口号

知识改变命运&#xff0c;技术就是要分享&#xff0c;有问题随时联系&#xff0c;免费答疑&#xff0c;欢迎联系&#xff01; 厦门微思网络​​​​​​https://www.xmws.cn 华为认证\华为HCIA-Datacom\华为HCIP-Datacom\华为HCIE-Datacom 思科认证\CCNA\CCNP\CCIE Linux\RHCE\…

Git安装,Git镜像,Git已安装但无法使用解决经验

git下载地址&#xff1a; Git - 下载 (git-scm.com) <-git官方资源 Git for Windows (github.com) <-github资源 CNPM Binaries Mirror (npmmirror.com) <-阿里镜像&#xff08;推荐&#xff0c;镜…

shell脚本——函数与数组

目录 一、函数 1、什么是函数&#xff1f; 2、函数的定义与调用 2.1 函数的格式 2.2 函数的调用方法 3 、查看与删除函数 3.1 查看函数 3.2 删除函数 4、函数的返回值 5、函数的传参数 6、函数的作用范围 7、函数的递归 二、数组 1、什么是数组&#xff1f; 2、数…

【数据结构】(一)从绪论到各种线性表

目录 一、绪论Introduction 1、数据结构 2、逻辑结构&#xff08;数据元素之间的相互关系&#xff09; 3、物理结构&#xff08;数据逻辑结构在计算机中的存储形式&#xff09; 4、数据类型&#xff08;一组性质相同的值的集合及定义在此集合上的一些操作的总称&#xff09…

mysql之基本查询

基本查询 一、SELECT 查询语句 一、SELECT 查询语句 查询所有列 1 SELECT *FORM emp;查询指定字段 SELECT empno,ename,job FROM emp;给字段取别名 SELECT empno 员工编号 FROM emp; SELECT empno 员工编号,ename 姓名,job 岗位 FROM emp; SELECT empno AS 员工编号,ename …

数据结构-数组(详细讲解)

文章目录 数组数组的概述数组的图示一维数组二维数组 数组的定义一维数组的定义二维数组的定义 数组的取值赋值一维数组二维数组 数组的操作一维数组的操作索引实现指针实现 二位数组的操作矩阵转三元组矩阵的乘法 数组 数组的概述 概述&#xff1a;数组是一种线性数据结构&a…

【机器学习300问】21、什么是激活函数?常见激活函数都有哪些?

在我写的上一篇文章中介绍了感知机&#xff08;单个神经元&#xff09;的构成&#xff0c;其中就谈到了神经元会计算传送过来的信号的总和&#xff0c;只有当这个总和超过了某个界限值时&#xff0c;才会输出值。这也称为“神经元被激活”。如果想对神经网络是什么有更多了解的…

网络防御保护——课程笔记

一.防火墙 防火墙的主要职责&#xff1a;控制和防护 --- 安全策略 --- 防火墙可以根据安全策略来抓取流量之后做出对应的动作。 防火墙的分类 防火墙的发展进程 防火墙的控制 带内管理 --- 通过网络环境对设备进行控制 --- telnet&#xff0c;ssh&#xff0c;web --- 登录设备…

【Go 快速入门】包及依赖管理 | Go 第三方包发布 | 接口 | 反射

文章目录 包和依赖管理依赖管理go modgo get go.mod 文件go.sum 文件Go Modules 发布包 接口空接口接口值类型断言 反射reflect.TypeOfreflect.ValueOf结构体反射 项目代码地址&#xff1a;04-PackageInterfaceReflection 包和依赖管理 Go 使用包来支持代码模块化和代码复用&…

【Django开发】前后端分离美多商城项目:项目准备和搭建(附代码,文档)

本系列文章md笔记&#xff08;已分享&#xff09;主要讨论django商城项目开发相关知识。本项目利用Django框架开发一套前后端不分离的商城项目&#xff08;4.0版本&#xff09;含代码和文档。功能包括前后端不分离&#xff0c;方便SEO。采用Django Jinja2模板引擎 Vue.js实现…

分表过多引起的问题/Apache ShardingSphere元数据加载慢

目录 环境 背景 探寻 元数据的加载策略 如何解决 升级版本到5.x 调大max.connections.size.per.query max.connections.size.per.query分析 服务启动阶段相关源码 服务运行阶段相关源码 受到的影响 注意事项&#xff08;重要&#xff09; 其他 环境 Spring Boot 2…

数据结构3、基于栈的后缀算术表达式求值

1 题目描述 图1 中缀表达式转化为后缀表达式题目描述 图2 基于栈的后缀算术表达式求值题目描述 2 题目解读 借助一个运算符栈&#xff0c;可将中缀表达式转化为后缀表达式&#xff1b;借助一个运算数栈&#xff0c;可对后缀表达式求值。借助一个运算符栈和一个运算数栈&#xf…

MongoDB安装以及卸载

查询id&#xff1a; docker ps [rootlocalhost ~]# docker stop c7a8c4ac9346 c7a8c4ac9346 [rootlocalhost ~]# docker rm c7a8c4ac9346 c7a8c4ac9346 [rootlocalhost ~]# docker rmi mongo sudo docker pull mongo:4.4 sudo docker images 卸载旧的 sudo docker stop mong…

Win10无法完成更新正在撤销更改的解决方法

在Win10电脑操作过程中&#xff0c;用户看到了“无法完成更新正在撤销更改”的错误提示&#xff0c;这样系统就不能成功完成更新&#xff0c;不知道如何操作才能解决此问题&#xff1f;以下小编分享最简单的解决方法&#xff0c;帮助大家轻松解决Win10电脑无法完成更新正在撤销…

BIO、NIO编程与直接内存、零拷贝

一、网络通信 1、什么是socket&#xff1f; Socket 是应用层与 TCP/IP 协议族通信的中间软件抽象层&#xff0c;它是一组接口&#xff0c;一般由操作 系统提供。客户端连接上一个服务端&#xff0c;就会在客户端中产生一个 socket 接口实例&#xff0c;服务端每接受 一个客户端…