1.10.1.定时器(timer)介绍
1.10.1.1、什么是定时器
(1)SoC(片上系统,可以理解成就是单片机)的一种内部外设
(2)定时器就是CPU的“闹钟”
定时器相对于SoC来说,就好像闹钟相对于人来说意义一样。单核的CPU是单线程的,只能干一件事情,干完这件事情完去干另一件事情需要定时器来提醒。
1.10.1.2、什么是计数器
计数器和定时器其实是一回事
(1)计数器也是soc当中的一个内部外设
(2)计数器顾名思义是用来计数的
例如秒表(指针表)实际上就是一个计数器,每隔一个单位走一个格(就是计一个数),因为秒表的计数时间周期是固定的,因此到了一定时间只要用计数值*计数时间周期,就能得到一个时间段,这个时间段就是我们定的时间(这就是定时器了)。
(3)计数器可以计算外部脉冲个数(脉冲其实就是电平信号通过引脚流进单片机)
每隔多长时间会有一个脉冲,通过计数器得到脉冲个数,就知道时间了!
也就是说:对内部脉冲计数,就叫作定时器;对外部脉冲计数,就叫作计数器。
内部脉冲,是稳定的,每个脉冲相隔时间固定,所以可以定时;
外部脉冲,是很不稳定的,无法得知两个脉冲之间的像个时间,只能计数!
(4)是定时器还是计数器,是通过寄存器来控制的
1.10.1.3、定时器如何工作
(1)第1步:先设置好定时器的时钟源(如果需要的话)
时钟源:石英晶体振荡器(晶振)
(2)第2步:初始化时钟相关寄存器
(3)第3步:设置定时时间(计数脉冲个数+每个脉冲时间)
(4)第4步:设置中断处理程序(外部输入优先级高于定时)
例如:主任务看电视,定时器中断程序是烧水!
(5)第5步:打开定时器
(6)运行时:定时器计数到后产生中断,然后执行中断isr
后面看代码会更明白!
1.10.2.软件控制硬件的关键-寄存器
有些硬件怎么被软件控制的呢?靠的就是寄存器!
1.10.2.1、什么是寄存器
(1)register [reg]
(2)寄存器
寄存,内容可变,一般按位定义
就相当于一个变量,只不过这个变量在固定的地址,有一个特殊的名称。
寄存器分为通用寄存器(P0...)和特殊功能寄存器(IE...)
(3)寄存器使用地址访问,编程上像内存一样
reg51.h定义的那样!
1.10.2.2、寄存器的工作原理
(1)寄存器和硬件之间有双向影响
说白了,就是寄存器也有IO,和硬件属于输入/输出设备有关!
(2)软件可以读写寄存器
程序可以把值写进寄存器,也可以读取寄存器的值,就比如我们往GPIO的引脚的值,其实我们读的是引脚里面的寄存器。
(3)总结:寄存器是软件能够控制硬件的关键
1.10.2.3、单片机学习的关键就是各种寄存器
寄存器之于单片机就像单词之于英语学习!
(1)单片机的学习主要包括2个:CPU和各种内部外设
(2)各种内部外设的编程接口就是寄存器
(3)熟悉一款单片机其实就是熟悉他的寄存器
(4)寄存器会随着单片机的复杂化而变复杂(STM32和单片机的虽然有差异,但是你去学习的时候,会发现很简单!)
(5)学会用C语言操作寄存器的技巧--关键点
要学会C语言操纵单片机的技巧!
1.10.3.51单片机的定时器简介
参考数据手册P136 定时器/计数器
如果单片机工作在12T模式下!
外部12MHz晶振,则内部时钟频率是1MHz,则时钟脉冲宽度为1us(1/1MHz = 1us)。
如果单片机工作在6T模式下
外部12MHz晶振,则内部时钟频率是2MHz,则时钟脉冲宽度为0.5us(1/2MHz = 0.5us)。
51的模式和工作方式和繁琐,我们不用记住,过一遍就行,等到学嵌入式STM32的时候,会简单很多!
后几小节,我们重点通过手册来讲一下定时器的主要寄存器
1.10.4.定时器的主要寄存器介绍
MSB大端,LSB小端 P136
1.10.4.1、TCON(Timer Control) 定时器/计数器控制寄存器
0b11000011就是从B7-B0
(1)8个位,但是有4个名字:TF、TR、IE、IT,每个名字的符号都有2个,后面分别带0和1,对应T0和T1.
(2)TF: timer flag,定时器(溢出)标志位
是只读(软件只是通过读取TF1来知道硬件的状态,而不用去写这一位来设置硬件的状态)的。
timer定时时间到了后会做2件事情:第一个是把TF标志改为1,第二个是产生中断让CPU去中断处理;当CPU响应之后,TF是硬件清零的(由1变0是自动的,不需要软件来干预。)
有一些CPU的设计是需要软件去清零的(主流单片机),这时候用户的程序就一定要记得给标志位清零,不然反复不停的重复进入中断。
=1 溢出,=0 溢出清零
(3)TR:timer run,就是定时器的启动计数的开关。
当我们把整个定时器初始化好了之后,我们给TR位写1就可以开启计数了。但是呢!
TR位和GATE位有一定关联性。GATE是TMOD寄存器中的寄存器位,先看一下!
(4)IE: interrupt enable ,外部中断(INTx)请求源标志位
作用就是用来展示硬件的状态改变的。
当INT1发生中断时,硬件自动IE1=1
当CPU处理了INT1中断时硬件会自动给IE1=0(硬件自动清零)。
(5)IT:interrput trigger,外部中断触发方式控制位
是用来设置外部中断的中断触发方式的。
所谓中断的触发方式,就是指硬件在某种条件下才会被判定为要产生中断,所以其实就是中断产生的条件。
中断触发方式一般就是:边沿触发和电平触发2种。边沿触发又分为:上升沿触发、下降沿触发、双边沿触发;电平触发方式分为:高电平触发、低电平触发2种。
=0 外部中断1为低电平触发方式,当INTx输入低电平时,IE=1
=1 外部中断( INTx )端口由“1”→“0”下降沿跳变时,IE=1
1.10.4.2、TMOD(Timer Mode) 定时器 / 计数器工作模式寄存器
(1)GATE:中文名叫门控位
是TMOD寄存器中的,也有2个分别对应T0和T1。
工作方式:
当GATE=0时 timer处于定时器工作模式。此时定时器开关就只受TR位影响。具体就是TR=1开启计数,TR=0结束计数。
当GATE=1时,timer处于计数器工作模式。当timer用来计数时,很关键的就是什么条件下计数,什么条件下不计数。所以当GATE=1时是否计数不仅取决于TR1还取决于INT1引脚(P3.3),实际规则是:当TR1=1并且INT1引脚也为高电平时才会计数。
(2)C/T位
设置T0/T1工作在定时器模式还是计数器模式。
=1 表示计数器模式
=0 表示定时器模式。
(3)M1 + M0
2个位一起来表示T0/T1处于哪种工作模式下,一般有4种:13位、16位、8位自动重载、双8位。
1.10.4.3、TLx、THx
TL0+TH0:定时器0的16位寄存器
TL1+TH1:定时器1的16为寄存器
假如说,我们的脉冲宽度是0.5us,我们要定定时0.5ms和4.444ms,则计数分别为:
1000 = 0x3E8 = 高0x3 低0xE8 => TL0 = 0xE8 TH0 = 0x3
8888 = 0x22B8 = 高0x22 低0xB8 => TL0 = 0xB8 TH0 = 0x22
纠正之前的一个错误,溢出就是指的超出定时器的最大位数!
1.10.5.定时器编程实践1
1.10.5.1、实验任务
原来实现闪烁时中间的延迟是用delay函数实现的,在delay的过程中CPU要一直耗在这里不能去做别的事情。这是之前的缺点
中断+定时:
CPU可以去做主任务(静态数码管显示0-F),定时器定时500ms,到了定时的时间就产生中断,在中断处理程序isr中让LED闪烁!!
1.10.5.2、如何编程
1.思路:
(1)定时(timer初始化)
对TMOD赋值,以确定T0的工作模式。
计算定时器计数初值,并将其写入TH0、TL0
开放定时器中断,对ET0和EA赋值
使TR0置位,启动定时器定时。
(2)主程序该干嘛干嘛
(3)中断处理程序
可以在上节课按键中断程序中直接修改!【单片机学习】第九课:按键
2.问题思考:0.5s如何计算出来呢?
机器周期也就是CPU完成一个基本操作所需要的时间。机器周期=1/单片机的时钟频率。
咱们的单片机外部晶振是12MHz、工作在12T模式下,所以内部时钟频率是1MHz,机器周期是1us。最多能定65535(16位定时器工作模式下)个脉冲,也就是说最大定时时间为65535*1us=65535us=65.535ms。
如果要定比较长的时间(譬如500ms),定时器直接是不能满足的,解决办法是多次定时后加起来构成一个长时间。
定时办法:先用定时器定50ms,然后*10
而我们定时50ms的初值是多少呢?
12T模式,外部晶振12MHz,内部时钟频率1MHz,内部机器周期:1us
定时个数就是:50ms/1us=50000
初值:要从65535-50000+1(因为实际上计数器计数到65536才溢出)=15536 =0x3CB0开始计数,初值等于0x3CB0
TL0 = 0xB0, TH0 = 0x3C (也可以这样理解:TH0 = 15536/256,TL0 = 15536%256)
大家一块看这两个图来分析我们需要如何编程!
MCU工作在12T,没得选
咱们选用定时器0
工作模式配置
因为是内部时钟计数,相当于定时器模式,GATE = 0
因为是定时器模式,所以C/T = 0
因为选用16位定时器模式,M1 = 0、M0 = 1
综合以上三点:TMOD = 0x01
设置定时器初值(注意看手册,16位定时器模式,TH0和TL0需要重装载)
TH0 = 0x3C
TL0 = 0xB0
中断使能
ET0 = 1
EA = 1
定时器/计数器控制配置
允许计数,TR0 = 1
不涉及外部中断,IT、IE不用管
IF在计数溢出时,会自动变化,不用管
定时器中断处理程序 参考手册P141
最后程序如下
#include <reg51.h>
/**************代码说明**************************
本程序采用定时器中断方式,实现了静态数码管显示0-F并且每隔0.5s时,LED1闪一次
P0--静态数码管
P1.0 LED1
*************************************************/
sbit led = P1^0;//LED接在P1.0
unsigned char CNT = 10; //每次计数50ms,重复10次
unsigned char count;
// 独立数码管的段码表
unsigned char val[16] =
{
0xc0, 0xf9, 0xa4, 0xb0,
0x99, 0x92, 0x82, 0xf8,
0x80, 0x90, 0x88, 0x83,
0xc6, 0xa1, 0x86, 0x8e
};
/*************延时函数*************/
void delay()
{
unsigned char i = 200,j = 100;
while(i--)
while(j--);
}
/******************************定时器中断服务程序*************************/
void tm0_isr() interrupt 1 using 1
{
TH0 = 0x3C;
TL0 = 0xB0; //手工重载
if (count--== 0)
{
// 说明已经中断了10次了,0.5s已经到了
led = !led; // LED取反
count = CNT;
}
}
/***************************主函数*********************************/
void main(void)
{
unsigned char i = 0;
/************定时器初始化*************/
TMOD = 0x01;
TH0 = 0x3C;
TL0 = 0xB0;
ET0 = 1;
EA = 1;
TR0 = 1;
led = 1;
count = CNT;
while (1)
{
//主线任务
for (i=0; i<16; i++)
{
P0 = val;
delay();
}
}
}
AI写代码
1.10.6.3、51定时器小工具使用
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/qq_27148893/article/details/109690947
|