×
单片机 > 单片机程序设计 > 详情

51单片机的定时器与计数器

发布时间:2022-09-29 发布时间:
|

1.0

1.0.1定时器

(1)51单片机的定时器是一个内部外设。

(2)定时器相当于CPU的一个“闹钟”。

(3)定时器是用计数器来实现的。


1.0.2计数器

(1)计数器可以计数外部脉冲的个数.

(2)脉冲:(个人理解)单片机中一个低电平跳变成高电平在回到低电平的这么一个过程就称为一个脉冲。


1.0.3定时器是如何工作的

(1)第一步:先设置好定时器的时钟源(AT89C51单片机的时钟源只有一个不需要设置)

(2)第二步:初始化时钟相关寄存器

(3)第三步:设置定时时间(计数个数)

(4)第四步:设置中断处理程序(定时器总是与中断相互配合使用)

(5)第五步:打开定时器

(6)第六步:定时器计数到后产生中断,然后执行中断处理程序。


1.1寄存器

1.1.0什么是寄存器

(1)register。

(2)寄存器,寄存,内容可变,一般按位定义。

(3)寄存器使用地址访问,编程上像内存一样。


1.1.1寄存器的工作原理

(1)寄存器和硬件之间有双向影响。

(2)软件可以读写寄存器。

(3)总结:寄存器是软件能够控制硬件的关键。


1.1.2单片机学习的关键就是各种寄存器

(1)单片机的学习主要包括2个:CPU和各种内部外设。

(2)各种内部外设的编程接口就是寄存器。

(3)熟悉一款单片机其实就是熟悉他的寄存器。

(4)寄存器会随着单片机的复杂化而变复杂。

(5)学会用C语言操作寄存器的技巧。


1.2AT89C51定时器介绍

(1)外部12MHz晶振,单片机工作在12T模式下,则内部时钟频率是1MHz,则时钟脉冲宽度为1us(1/1MHz = 1us)。

(2)单片机工作在6T模式下,则内部时钟频率是2MHz,则时钟脉冲宽度为0.5us(1/2MHz = 0.5us)。


1.3定时器的主要寄存器介绍

TCON

(1)8个位,但是有4个名字:TF、TR、IE、IT,每个名字的符号都有2个,后面分别带0和1,对应T0和T1.


(2)TF: timer flag,定时器(溢出)标志位,是只读(软件只是通过读取TF1来知道硬件的状态,而不用去写这一位来设置硬件的状态)的。timer定时时间到了后会做2件事情:第一个是把TF标志改为1,第二个是产生中断让CPU去中断处理;TF是硬件清零的(由1变0是自动的,不需要软件来干预。)有一些CPU的设计是需要软件去清零的,这时候用户的程序就一定要记得给标志位清零,不然就不能重复进入中断或者反复不停的重复进入中断。


(3)TR就是timer run,就是定时器的启动计数的开关。当我们把整个定时器初始化好了之后,我们给TR位写1就可以开启计数了。

TR位和GATE位有一定关联性。


(4)GATE是TMOD寄存器中的,也有2个分别对应T0和T1。GATE位中文名叫门控位,工作方式是:当GATE=0时(相当于门是打开的,此时GATE位是可以忽略的),此时定时器开关就只受TR位影响。具体就是TR=1开启计数,TR=0结束计数。当timer处于定时器工作模式时GATE就要等于0;GATE一般是在timer处于计数器模式时用的。当timer用来计数时,很关键的就是什么条件下计数,什么条件下不计数。当GATE=0时计数条件只有TR1一个(TR1=1就计数,TR1=0就不计数),当GATE=1时是否计数不仅取决于TR1还取决于INT1引脚(P3.3),实际规则是:当TR1=1并且INT1引脚也为高电平时才会计数。


(5) TH0 TL0中写入的值计算:

假如要写入1000.那么1000所对应的16进制为:0x3E8高八位为:0x03 低八位为:0XE8

也可以是TL0 = 1000 %256 TH0 = 1000 / 256;

1000 = 0x3E8 = 高0x3 低0xE8 => TL0 = 0xE8 TH0 = 0x3

要写入8888也一样。

8888 = 0x22B8 = 高0x22 低0xB8 => TL0 = 0xB8 TH0 = 0x22


(6)我们开发板的定时器最多能订多长时间?

内部时钟频率是1MHz,时钟周期是1us。最多能定65535(16位定时器),也就是说最大定时时间为65535*1us=65535us=65.535ms。

如果要定比较长的时间(譬如2s),定时器直接是不能满足的,解决办法是多次定时后加起来构成一个长时间。


1.4加法定时器与减法定时器

加法定时器和减法定时器

(1)定时器的原理就是计数器,加法定时器计数方法是从我们给定的值开始计数,直到溢出(譬如16位定时器最大值为0xffff,也就是65535,计数值到达这个值就溢出了)。减法定时器是从我们给定的值开始减1,减到0就溢出了。

(2)实例来看,譬如16位定时器。我们设置的计数值为1000,则如果是减法定时器那么计数个数就是1000,如果是加法定时器则计数个数就是65535-1000=64535.

(3)51单片机就是典型的加法定时器

(4)现代的单片机或者嵌入式soc,一般常用的都是减法定时器了。虽然加法定时器和减法定时器都能实现功能,但是明显减法定时器更加直观。


#include


//利用定时器实现数码管显示0-F依次显示的同时 LED灯每1s闪烁一次。

//定时器选用定时器0:


/*

接线:

矩阵键盘:P1端口

数码管: P0端口

*/

#define DIG P0


sbit LED = P3^0;


unsigned char count = 20; //20次,对应1s。

void delay(unsigned char t);


//独立数码管的显示0-F

unsigned char varry[16] =

{0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,

0x80,0x90,0x88,0x83,0xc6,0xA1,0x86,0x8e};


void Timer0_Isr() interrupt 1 using 1 // interrupt 1

{

TL0 = (65535 - 50000) / 256;

TH0 = (65535 - 50000) % 256;

if(count-- == 0)

{

count = 20;

LED = !LED;

}

}

void main(void)

{

unsigned char i = 0;


TMOD = 0x01; //定时器模式寄存器:0x01表示16位定时器,TL0 TH0全用


//51单片机的定时器是加法定时器,要注意初值的计算。

TL0 = (65535 - 50000) / 256;

TH0 = (65535 - 50000) % 256;

TR0 = 1; //定时器0的运行控制位,该位由软件置位或清0. TR=1表示允许开始记数

ET0 = 1; //T0中断溢出允许位,也就是打开中断

EA = 1; //打开中断总开关

count = 20;


while(1)

{

for(i=0; i<16;i++)

{

DIG = varry[i];

delay(200); //大概延时,能看出变化就好。

}

}

}


void delay(unsigned char t)

{

unsigned char i,j;

for(i=0;i

for(j=0; j


}


逻辑取反与按位取反?

在51单片机中实现LED的翻转,得以LED闪烁。

sbit LED = P3^0;

LED = !LED;

LED = ~ LED;竟然都可以达到效果!!!


using 关键字的使用


(1)目前还没有完全搞懂用法:但是我在以上的程序中实验了一下:0-3(4及其以上是不存在的,编译器报错)

using 0 的情况下数码管的显示切换速度变快了,而且快了很多。

using 1 2 3 这三个没有区别。(就是简单的实验现象上没有区别)

去掉using 0 的数码管显示与using 1 2 3 速度一样。(肉眼观察)


以下是网友总结的:原文链接:/zixunimg/eeworldimg/www.51hei.com/mcu/766.html


using关键字的作用就是指定某个函数在执行时切换寄存器组的

51中有四个寄存器组,每个组有R0-R7这8个寄存器,用于CPU的数据处理,一般主函数main()默认使用第0组,因为PSW寄存器的初始值的第3、4位是00嘛,即默认指定了第0组。using 0就是指定在执行函数时切换为使用第0组,不加using关键字的话,一般都默认使用第0组,但这样的话在调用其他函数(包括中断服务函数)的时候,就会加入一些压栈指令以保护原来的R0-R7寄存器的(这样的话,程序执行会效率会低一点,因为会产生很多个指令是用来压栈出栈的),照这样说,只是加了using 0,使用的也是第0组又不是其它的组,程序就不应该有问题了吧,但是就是因为加了using 0,编译了一次,才发现在定时器中断函数t0()的入口中并没有发现把R0-R7的代码压入栈呀,就是说没有保护好R7呀,那当然就是在执行完之后回来R7不能回复原来的值啦,接下来的事情。。。。我就不说啦,这就是问题的根源,去掉using 0就可以了,编译器就自动帮你将R0-R7压栈,手动加了using 0,就是让编译器以为之前用的并不是第0组,而现在执行这个中断函数时就切换到第0组,而省去了将R0-R7这8个寄存器压入栈的指令了,这样虽然看起来是快了,然而对于这个程序来说却是致命的问题!!!因为根本没有保护好R0-R7,而没有保护R7并不是我预期发生的!!!这不是编译器的问题,是自己没有了解好、用好using的问题啊!还有,其实也可以在编译器选项里有选项来硬性规定编译器统一不直接使用寄存器而是使用间接寻址的办法来改变循环变量分配的地址的,或者使用。


『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
51单片机CO2检测显示程序解析