目录
一、配置TMOD寄存器工作模式
我们选择16位定时器(0000 0001)
注:TMOD为不可位寻址,即不能位赋值,不能像P2_1=0;
二、配置TCON控制寄存器
可以位寻址:我们单独赋值
TF0=0;
三、配置TH0,TH1
配置我们想要定时一毫秒(TH0,TL0高低位赋值)
十进制取值:123/100=1 高位 123%100=23 低位
底下的十六进制类似
TH0=64535/256;
TH1=64535%256;
配置中断:
ET0=1;
EA=1;
PT0=0;
中断子函数
这是定时器到了要跳转执行的函数(主函数不用调用自动跳转)
参考:
四、测试一下中断:
我让寄存器计数每过2S,P2_1亮灭一次
#include <REGX52.H>
void Timer0_Init()
{
TMOD=0x01;
//TCON
TF0=0; //先清零,防止一开始就产生中断
TR0=1;
//TH0,TL0
TH0=64535/256;
TL0=64535%256;
//中断
ET0=1;
EA=1;
PT0=0;
}
void main()
{
Timer0_Init();
while(1)
{
}
}
unsigned int T0Count;
//中断子函数
void Timer0_Routine() interrupt 1
{
TH0=64535/256;//移到这边防止第二次计数的时候从0开始
TL0=64535%256;
T0Count++;
if(T0Count>=1000)//1000ms
{
T0Count=0;
P2_0=~P2_0;
}
}
效果:
7_2计时器中断测试
注意:
其实在TMOD寄存器赋值是有问题的:因为他是不可位寻址的,如果我们的定时器1被赋值后,我们再用
TMOD=0x01;
就把定时器清零了。
解决:我们用与或赋值法
TMOD=TMOD&0xF0; // 把TMOD的低四位清零,高四位保持不变
TMOD=TMOD|0x01; // 把TMOD的最低位 置1,高四位不变
举例:比如TMOD原为1010 0011
1010 0011 & 1111 0000 ==>1010 0000
1010 0000 | 0000 0001 ==> 1010 0001
这样就配置定时器0就不会影响到定时器1了
然后其实我们在TH0,TF0的配置中会有一微秒的差距(具体解释见UP主视频)
解决:我们用STC-ISP软件生成,但他不能配置中断,我们得在复制来的代码加上中断的配置;
void Timer_Init(void) //1毫秒@12.000MHz
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x18; //设置定时初始值
TH0 = 0xFC; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
//中断
ET0=1;
EA=1;
PT0=0;
}
然后我们就可以将上方(就是定时器0的初始函数)模块化了:
这是定时器一秒的模板:需要可以直接复制修改
//中断子函数
void Timer0_Routine() interrupt 1
{
static unsigned int T0Count;
TL0 = 0x18; //设置定时初始值
TH0 = 0xFC; //设置定时初始值
T0Count++;
if(T0Count>=1000)//1000ms
{
T0Count=0;
P2_0=~P2_0;
}
}
五、另一种流水灯的实现(K1可改变流水方向):
我们需要引入一个软件自带库
#include <INTRINS.H>,他的_crol_和_cror函数可以左移右移,且不会溢出。
代码部分
我们还是采用模块化编程;
之前封装过的直接复制就不说了
Key.c
#include <REGX52.H>
#include "Delay.h"
/**
* @brief 获取独立按键键码
* @param 无
* @retval 按下按键的键码,范围:0-4;无按键按下返回0
*/
unsigned char Key()
{
unsigned char KeyNumber=0;
// Delay消抖
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 "Timer0.h"
#include "Key.h"
#include <INTRINS.H>
unsigned char KeyNum,LEDMode;
void main()
{
P2=0xFE;
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);
}
}
仿真
遇到的问题:
仿真部分我又踩了大坑,无论怎么试就是只有第一个灯(P2_0)亮灭一下。代码检查了N遍都没发现问题。因为上方中断小测试的仿真都是有用的,我一直以为是库函数的问题,百度了一堆也没解决。然后最后终于看到说可能LED灯(二极管)他的电压太大承受不住,我就在P2每个灯加一个100R的电阻,结果真的可以了,简直是浪费了我好多时间。
效果:
一开始P2值为1111 1110 仅最低位(P2_0)亮,移位==>1111 1101,在我的仿真图中就是向下移动。要是我按了K1,P2右移==>1111 1110 在我的仿真图中就是向上移动。再按一下就又向下移动……
7-2流水灯
六、定时器时钟
代码:
LCD第一行显示clock,第二行第三个和第六个显示“:”
while循环内不断更新时间;
定时器中每隔一秒(1000ms) Sec+1
Sec==>60==> Min+1
Min==>60==>Hour+1
Hour==>24==>Hour=0
#include <REGX52.H>
#include "LCD1602.h"
#include "Timer0.h"
unsigned char Sec,Min,Hour;
void main()
{
LCD_Init();
Timer0Init();
LCD_ShowString(1,1,"clock:");
LCD_ShowString(2,3,": :");
while(1)
{
LCD_ShowNum(2,1,Hour,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)//1000ms
{
T0Count=0;
Sec++;
if(Sec>=60)
{
Sec=0;
Min++;
if(Min>=60)
{
Min=0;
Hour++;
if(Hour>=24)
{
Hour=0;
}
}
}
}
}
效果:
为了方便演示我为时分秒赋初始值
Sec=55,Min=59;Hour=23;
7-2定时器时钟