配置定时器有关的寄存器(定时器初始化)
TMOD
TMOD = 0x01; //0000 0001
TCON
TF0 = 0; TR0 = 1;
赋初值
我们来计算一下我们从开始计数到产生中断的时间
定时器每隔1us产生一次脉,计一次数。计数上限是65535。那么总共定时时间就是65536us,即65ms。如果我们需要让它每隔1ms来记一次数,我们可以给计数器一个初值.
我们设置定时器初始值为64535,离溢出差值1000us,所以计数时间为1ms。
代码是这样写的:
TH0 = 64535/256;//252,即二进制的1111 1100 TL0 = 64535%256;//23,即0001 01111
怎么解释呢,假如我们有一个数123,我们想把它分离到两个寄存器里,那么对123除以100可以得到最高位的1,对123用100取余,可以得到后两位的23,这样它的高低位就分离了。同理,我们的寄存器保存的是二进制数,8位二进制数的值就是256,除以256就可以得到高八位,对256取余可以得到低八位。
中断寄存器
ET0 = 1; EA = 1; PT0 = 0;
void Timer0_Init() { TMOD = 0x01; //0000 0001 TF0 = 0; TR0 = 1; TH0 = 64535/256;//252,即二进制的1111 1100 TL0 = 64535%256;//23,即0001 01111 ET0 = 1; EA = 1; PT0 = 0; }
中断函数内容
void Timer0_Routine() interrupt 1 { //这里写中断函数的内容 }
举个例子
#include <REGX52.H> void Timer0_Init() { TMOD = 0x01; //0000 0001 TF0 = 0; TR0 = 1; TH0 = 64535/256;//252,即二进制的1111 1100 TL0 = 64535%256;//23,即0001 01111 ET0 = 1; EA = 1; PT0 = 0; } void main() { Timer0_Init(); while(1) { } } void Timer0_Routine() interrupt 1 { P2_0 = 0; }
我们可以写这样一个代码,主程序只有定时器初始化, 烧录程序会发现,LED0的确会亮,虽然我们主程序并没有点灯的语句。
现在升级一下代码,试试看效果。我们在中断函数里面加入T0Count变量,让他自增到1000来延长时间。同时在中断执行后,将定时器的初始值设置为64535,不然定时器执行中断后会置零,那我们之前计算的定时周期执行一次就无效了。
#include <REGX52.H> void Timer0_Init() { TMOD = 0x01; //0000 0001 TF0 = 0; TR0 = 1; TH0 = 64535/256;//252,即二进制的1111 1100 TL0 = 64535%256;//23,即0001 01111 ET0 = 1; EA = 1; PT0 = 0; } void main() { Timer0_Init(); while(1) { } } unsigned int T0Count; void Timer0_Routine() interrupt 1 { TH0 = 64535/256;//252,即二进制的1111 1100 TL0 = 64535%256;//23,即0001 01111 T0Count++; if(T0Count>=1000) { T0Count = 0; P2_0 = ~P2_0; } }
用逻辑语言升级以下代码
我们之前配置寄存器时的操作方式,是直接用等号赋值。比如配置TMOD寄存器,它是不可位寻址的,所以我们直接给TMOD一个0x01,让它的寄存器值为0000 0001,来配置T0。但是这样的操作有个非常明显的缺点,假如我们增加配置定时器T1,就需要对高四位进行操作,赋值0x10,即0001 0000。这样的配置会把低四位之前配置好的定时器T0给覆盖掉。
为了解决这样的缺点,我们提出一种新的配置寄存器方法。姑且叫做“与或赋值法”,“与”用来清零,“或”用来赋值。
学习过数电大家应该知道,&(与)符号和|(或)符号的含义。没学过也没关系,我来解释一下,两个数相与,全1结果就是1,否则结果就是0;两个数相或,只要有1结果就是1,否则结果就是0。
观察以下代码
TMOD = TMOD & 0xF0;//与操作用来清零。和1111相与的结果都是它本身,任何数和0000相与结果都是0.这样低四位就被清零,而高四位不会变化。 TMOD = TMOD | 0x01;//或操作用来赋值。任何数和1相或结果都是1,和0相或结果都是它本身。所以或操作可以用来赋值。高四位保持不变,低四位赋值为0001.
TMOD &= 0xF0;//低四位清零,高四位不变 TMOD |= 0x01;//低四位赋值0001,高四位不变
如果大家看过一些高级工程师的代码,就会发现这种方法使用率特别高,因为在工程量比较大的时候,我们会记不住之前寄存器的配置的方式。但是采用这样的方法,不会对以前寄存器的状态进行修改,还能对我们想要的位进行赋值,效果非常好。
用STC-ISP来生成初始化代码
刚才我们配置定时器的方法是查手册,配寄存器。实际上STC-ISP这个软件可以帮助我们生成我们需要的初始化函数。只需要在软件中对相关参数进行调配。
void Timer0Init(void) //1毫秒@12.000MHz { //AUXR &= 0x7F; //定时器时钟12T模式 TMOD &= 0xF0; //设置定时器模式 TMOD |= 0x01; //设置定时器模式 TL0 = 0x18; //设置定时初值 TH0 = 0xFC; //设置定时初值 TF0 = 0; //清除TF0标志 TR0 = 1; //定时器0开始计时 }
void Timer0Init(void) //1毫秒@12.000MHz { TMOD &= 0xF0; //设置定时器模式 TMOD |= 0x01; //设置定时器模式 TL0 = 0x18; //设置定时初值 TH0 = 0xFC; //设置定时初值 TF0 = 0; //清除TF0标志 TR0 = 1; //定时器0开始计时 ET0 = 1; EA = 1; PT0 = 0; }
定时器模块化
//Timer0.h #ifndef ___TIMER0_H__ #define ___TIMER0_H__ void Timer0Init(void); #endif
//Timer0.c #include <REGX52.H> #include "Timer0.h" /** * @brief 定时器0初始化,1毫秒@12.000MHz * @param 无 * @retval 无 */ void Timer0Init(void) //1毫秒@12.000MHz { TMOD &= 0xF0; //设置定时器模式 TMOD |= 0x01; //设置定时器模式 TL0 = 0x18; //设置定时初值 TH0 = 0xFC; //设置定时初值 TF0 = 0; //清除TF0标志 TR0 = 1; //定时器0开始计时 ET0 = 1; EA = 1; PT0 = 0; } /** * @brief 定时器中断函数模板 * @param 无 * @retval 无 */ /* void Timer0_Routine() interrupt 1 { static unsigned int T0Count; ;//这里将它设置为静态变量。否则函数在主程序中引用完该变量会被销毁。不设置成全局变量的目的是为了节省内存 TL0 = 0x18; //设置定时初值 TH0 = 0xFC; //设置定时初值 T0Count++; if(T0Count>=1000) { T0Count = 0; P2_0 = ~P2_0; } } */
模块化函数之前讲过,这里不详细说了。建立好定时器模块后,别忘了在主函数头文件#include一下哦。
定时器和独立按键实现流水灯
这里介绍一个函数 _crol_,_cror_。它们在头文件 "INTRNINS.H"里。功能是实现数值移位,cror代表右移,crol代表左移。第一个参数是需要移位的数值,第二个参数是移位的位数。
完整工程
//main.c #include <REGX52.H> #include <INTRINS.H> #include "Key.H" #include "Delay.H" #include "Timer0.H" unsigned char KeyNum,LEDMode; void main() { P2=0xFE;//1111 1110 Timer0Init(); while(1) { KeyNum = Key(); if(KeyNum) { if(KeyNum==1) { LEDMode++; if(LEDMode>=2) LEDMode = 0; } } } } void Timer0_Routine() interrupt 1 { static unsigned int T0Count; TL0 = 0x18; //设置定时初值 TH0 = 0xFC; //设置定时初值 T0Count++; if(T0Count>=500) { T0Count = 0; if(LEDMode == 0) P2 = _crol_(P2,1); if(LEDMode == 1) P2 = _cror_(P2,1); } }
//key.c #include <REGX52.H> #include "Delay.h" /** * @brief 识别独立按键按下的位置 * @param 无 * @retval 返回按下独立按键的位置 */ unsigned char Key() { unsigned int KeyNumber=0; if(P3_1 == 0) {Delay(20);while(P3_1==0);Delay(20);KeyNumber=1;} if(P3_0 == 0) {Delay(20);while(P3_0==0);Delay(20);KeyNumber=2;} if(P3_2 == 0) {Delay(20);while(P3_2==0);Delay(20);KeyNumber=3;} if(P3_3 == 0) {Delay(20);while(P3_3==0);Delay(20);KeyNumber=4;} return KeyNumber; }
//Key.h #ifndef __Key_H__ #define __Key_H__ unsigned char Key(); #endif
所有模块
定时器实现一个计时钟
完整工程
//main.c #include <REGX52.H> #include "Delay.h" #include "LCD1602.h" #include "Timer0.h" unsigned char Sec,Min,Hou; void main() { Timer0Init(); LCD_Init(); LCD_ShowString(1,1,"Clock:"); LCD_ShowString(2,1," : : "); while(1) { LCD_ShowNum(2,1,Hou,2); LCD_ShowNum(2,4,Min,2); LCD_ShowNum(2,7,Sec,2); } } void Timer0_Routine() interrupt 1 { static unsigned int T0Count; TL0 = 0x18; //设置定时初值 TH0 = 0xFC; //设置定时初值 T0Count++; if(T0Count>=1000) { T0Count = 0; Sec++; if(Sec>=60) { Sec = 0; Min++; if(Min>=60) { Min = 0; Hou++; if(Hou>=24) Hou = 0; } } } }
所有模块
转自:https://blog.csdn.net/Destiny_Di/article/details/127121966