定时器


按视频中的去理解,up主的板子晶振为12MHz,如果分频接了÷12的线路,那么传给计数器的就会是1µs计数一次(计算:1/(12MHz/12)=1/1*10^9s=1µs)
CT选择开关:那里给1是counter计数器 给0是timer定时器
GATE那里:三角是异门(1—>0 0—->1),第二个是或门(只有00情况给0,其余给1),第三个是与门跟C的异或与的与意思差不多

实操
1.TMOD(TIME_MODE)

由于TMOD不可寻址,所以要整体表示
(对于(不)可寻址的理解,可寻址像之前的点LED灯,直接P2=0x01<==>P2_1=1然后P2_2~8=0,不可寻址就是只能一坨地表示,就像P2=0x00这样)
目的:我们要实现定时器0运行且进入模式1
①M0&M1

所以M0—>1,M1—>0
②C/T

因为是定时器 C/T—>0
③GATE

要让TR0参与控制,所以GATE—>0
附GATE的运行模式

因此
TMOD=0X01;//0000 0001
TMOD的优化
TMOD &= 0xF0; //设置定时器模式 0xF0-->1111 0000
TMOD |= 0x01; // 0x01-->0000 0001
由于TMOD是同时控制定时器0和定时器1,我们希望在控制定时器0的时候,不影响到可能正在工作的定时器1,于是用到了以下操作
e.g.
原TMOD=1010 0011—-我们希望把它转换为—->1010 0001
1st———-1010 0011 & 1111 0000 =1010 0000
理解:n & 1 时,n为几就返回几(此时不影响定时器1);n & 0 时,全为0(有点初始化定时器0的感觉)
2nd———1010 0000 | 0000 0001 =1010 0001
理解:n | 0 时,n为几就返回几(还是不影响定时器1);0 | n 时,n为几也返回几(n是人工决定的,这时可以自行操控定时器0了)
2.TCON

①TR0

省流:GATE=0 && TR0=1时允许T0计数,开始工作
TR0=1;
②TF0

省流:TF0=1时就产生中断,所以要=0,防止刚配置好就产生中断
TF0=0;
③TH0&TL0

来自弹幕大佬:两个寄存器TH0、TL0为二进制八位(2^8),单独可计256次,低八位计满256次后高八位进1,所以除以256可得高八位得次数,取余就是低八位的次数,合并在一起就是所赋的初始值
来自UP主:123要放到两个容量为100的盒子里,高位次的盒子存储—-123/100=1,低位次的盒子存储—-123%100=23,合并之后就是123(初始值)
对应项目,就是两个容量为256的小盒子要存储64535这个庞然大物
TH0=64535/256;//high,拿出高八位
TL0=64535%256;//low,拿出低八位
④ET0&EA&PT0

把通道打通
ET0=1;
EA=1;
PT0=0;//虽然默认PT0为0,但还是说明一下比较好
3.Time0.c
#include <REGX52.H>
/**
* @brief 定时器0初始化,1ms@11.0592MH
* @param 无
* @retval 无
*/
void Timer0Init(void) //1毫秒@11.0592MHz
{
TMOD &= 0xF0; //设置定时器模式 0xF0-->1111 0000
TMOD |= 0x01; // 0x0x-->0000 0001
TL0 = 0x66; //设置定时初值
TH0 = 0xFC; //设置定时初值
/**
64535/256=252
252--10进制转16进制-->FC
64535%256=23
23--10进制转16进制-->17
但是我们这个有0.04%的偏差,所以...问题...应该不大吧...
*/
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0=1; //允许中断
EA=1;
PT0=0;
}
/*
定时器中断函数模板
void Timer0_Routine() interrupt 1
{
static unsigned int T0Count;//为了不丢失这个数字
TL0 = 0x66; //设置定时初值
TH0 = 0xFC; //设置定时初值
T0Count++;
if(T0Count>=1000)
{
//这里要写具体实现什么
T0Count=0;
}
}
*/
对定时器中断函数模板的理解:
首先,中断程序本身可视为一个while循环,会一直执行这个函数
其次,对于秒数,这个计时器(TH0 TL0)最高可达到65535µs,我们设置定时初值时,用的是64535,它距离65535还有1000才即将溢出归零,所以我们让计时器计时1000µs(=1ms),每过1ms,T0Count++,当它加了1000次,此时已经过去1000ms(=1s)
最后,进入if执行具体实现,然后把T0Count归零,进入下一次中断程序(循环)
4.新学两个函数
要用到#include
crol函数&cror函数(..头尾都要_..
循环移位
unsigned char a=0x80;
a=_crol_(a,1);
//a为0x01,循环回去开头了
//如果是<<的话,移到边界就溢出越界
5.最终呈现
main.c
#include <REGX52.H>
#include "Timer0.h"
#include "Key.h"
#include <INTRINS.H>
unsigned char KeyNum,LEDMode;
void main()
{
P2=0xFE;//P2是LED模块噢,一端接了VCC,那么只有给P2_n赋值为0的时候才亮,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 = 0x66; //设置定时初值
TH0 = 0xFC; //设置定时初值
T0Count++;
if(T0Count>=500)//0.5s亮一次
{
T0Count=0;
if(LEDMode==0)
P2=_crol_(P2,1);
if(LEDMode==1)
P2=_cror_(P2,1);
}
}
针对流水效果,我们添加了这些
if(T0Count>=500)
{
T0Count=0;
if(LEDMode==0)
P2=_crol_(P2,1);//往左流
if(LEDMode==1)
P2=_cror_(P2,1);//往右流
}
配合主函数食用
unsigned char KeyNum,LEDMode; //一开始初始化了KeyNum和LEDMode,这两个玩意初始值都为0
那么,当开关被按下的时候,LEDMode==0,开始往左流,对应下面这句
if(LEDMode==0)
P2=_crol_(P2,1);//往左流
然后接收KeyNum,当我不按P3_1时,LEDMode就一直为0,一直往左流
我按下P3_1时,配合Key.c食用,此时返回KeyNumber=1
Key.c
#include <REGX52.H>
#include "Delay.h"
/**
* @brief 获取独立按键键码
* @param 无
* @retval 按下按键的键码,范围:0~4,无按键按下时返回值为0
*/
unsigned char Key()
{
unsigned char 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==2);Delay(20);KeyNumber=3;}
if(P3_3==0){Delay(20);while(P3_3==3);Delay(20);KeyNumber=4;}
return KeyNumber;
}
KeyNum==1后,执行
if(KeyNum==1)
{
LEDMode++;
if(LEDMode>=2)LEDMode=0;
}
这时候LEDMode++,LEDMode==1
if(LEDMode==1)
P2=_cror_(P2,1);//往右流
再次按下P3_1时,LEDMode++,LEDMode==2,然后执行if让LEDMode归零
定时器时钟
main.c
#include <REGX52.H>
#include "Delay.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,1," : :");
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 = 0x66; //设置定时初值
TH0 = 0xFC; //设置定时初值
T0Count++;
if(T0Count>=1000)
{
T0Count=0;
Sec++;
if(Sec>=60)
{
Sec=0;
Min++;
if(Min>=60)
{
Min =0;
Hour++;
if(Hour>=24)
{
Hour=0;
}
}
}
}
}
这个,不难理解,dddd