❤️ 专栏简介:本专栏记录了从零学习单片机的过程,其中包括51单片机和STM32单片机两部分;建议先学习51单片机,其是STM32等高级单片机的基础;这样再学习STM32时才能融会贯通。
☀️ 专栏适用人群 :适用于想要从零基础开始学习入门单片机,且有一定C语言基础的的童鞋。
🌙专栏目标:实现从零基础入门51单片机和STM32单片机,力求在玩好单片机的同时,能够了解一些计算机的基本概念,了解电路及其元器件的基本理论等。⭐️ 专栏主要内容: 主要学习51单片机的功能、各个模块、单片机的外设、驱动等,最终玩好单片机和单片机的外设,全程手敲代码,实现我们所要实现的功能。
🌴 专栏说明 :如果文章知识点有错误的地方,欢迎大家随时在文章下面评论,我会第一时间改正。让我们一起学习,一起进步。
💑专栏主页:http://t.csdn.cn/HCD8v
本学习过程参考:https://space.bilibili.com/383400717
单片机安装软件、各种资料以及源码的路径:
https://pan.baidu.com/s/1vDTN2o8ffvczzNQGfyjHng
提取码:gdzf
本节主要介绍学习定时器的相关功能,包括定时器简介、定时器原理、计数器、时钟、中断系统等基础知识;并利用两个小实验来进行练习,分别是使用定时器实现按键控制流水灯模式以及使用定时器实现定时器时钟。
文章目录
- 一、定时器介绍和本节目标
- 1.1 定时器简介和定时器原理
- 1.1.1 定时器简介
- 1.1.2 定时器原理
- 1.1.2.1 计数器
- 1.1.2.2 时钟
- 1.1.2.3 中断系统
- 1.1.2.4 定时器和中断系统
- 1.1.2.5 定时器相关寄存器
- 1.2 本节目标
- 二、按键控制LED流水灯模式
- 三、定时器时钟
一、定时器介绍和本节目标
1.1 定时器简介和定时器原理
1.1.1 定时器简介
STC89C52的定时器资源,如下图所示,具体的可以 参考手册:\51单片机入门教程资料\课件及程序源码\相关资料\STC89C52.pdf 第七章 定时器/计数器
1.1.2 定时器原理
下面会学习什么是中断系统。
定时器的工作模式如下:
本文我们只学习模式1:16位定时器/计数器;其是最常用的;其他几个模式基本不怎么常用。
模式1的框图如下图所示:
可以将其分为三部分,时钟部分,计数器部分和中断部分,如下图所示:
1.1.2.1 计数器
计数器是如何进行计数的呢?上图中我们可以看到计数器部分有TL0和TH0,每个都存放8字节数据,总共可以存的数字为从0-65535;由左边的时钟给TL0和TL1提供脉冲,每提供一个脉冲,这16为计数器里面的值就会加1;当值加到最大值65535时,下一个脉冲再过来,就会造成计数器的溢出,溢出时计数器就会给到一个标志位给TF0,收到标志位,TF0就会给中断系统一个中断信号申请中断;
图中的TR0是控制定时器是否启动;
1.1.2.2 时钟
从图中可以看出,时钟的来源有两个,一个是SYSclk,即系统时钟;另一个是T0Pin,T0Pin是单片机外部的接口;如下图所示
在关键图中可以看出,14引脚的位置就是T0Pin;
所以时钟既可以由系统时钟(SYSclk)来提供,也可以通过外部引脚来提供;当由外部引脚来提供时钟的时候,我们的定时器就是一个计数器;因为外部来一个脉冲计数器就加一,所以就相当于一个脉冲计数器;计数器名字的来源也是来于此。
当然最常用的还是由系统时钟来提供脉冲。系统时钟也是晶振周期。晶振在开发部上的位置如下图所示:
本开发板上的晶振为12MHz,所以该开发板上的系统时钟也就是12MHz。
系统时钟输入的12MHz;12T模式会将输入的12MHz的系统时钟12分频,分频后输出的频率就是1MHz;所以定时器的周期就是1微秒(Hz代表每秒周期震动1次, 60Hz代表每秒周期震动60次 1MHZ=1000000HZ。所以1MHz为一秒震动一百万次)。即在图中画的红色线路(C/T=0)的情况下,定时器的计数单元每隔一微秒就计数一次。
我们本节定时器时钟的选择配置为:分频模式选择12分频,然后选择C/T=0,即定时器功能(当C/T=1时,是连接下面,即计数器功能); 所以整体流程为:系统时钟(SYSclk)->12分频(12T模式)->定时器模式(C/T=0),如下图所示:
1.1.2.3 中断系统
中断系统也是51单片机一个很重要的知识点,本节的定时器需要中断系统,后面要学习到的串口也需要中断系统;i/o口也需要中断系统,等等,许多设备都需要中断系统。所以中断系统会和许多外设打交道。
中断系统的介绍如下图所示(参考\51单片机入门教程资料\课件及程序源码\相关资料\STC89C52.pdf 第6章 中断系统
)
总结几个重点:
1、中断是可以使CPU拥有对外界紧急事件的实时处理能力
2、高优先级的中断可以打断中优先级的中断
中断的处理流程
中断的处理流程如下图所示:
当主程序正在运行时,如果突然来了一个中断请求,则CPU会响应中断请求
,进而去处理中断处理程序,当中断处理程序执行完之后,再返回主程序
执行主程序的任务。
中断资源
中断资源也是和单片机的型号关联在一起的,即不同种类的单片机可能拥有不同的中断资源,例如中断源个数不同,优先级个数不同等。
下图是STC89C5的中断资源:
1.1.2.4 定时器和中断系统
定时器和中断系统具体的关系图如下图所示(以传统51单片机为例):
整体的流程如下图红线部分所示:,由时钟到定时器再到中断系统;关于整个流程的具体代码实现下面会学习;
1.1.2.5 定时器相关寄存器
前面我们学过,单片机通过配置寄存器来控制内部线路的连接。通过内部线路的不同连接方式来实现不同的功能。
下图列出了和定时器有关的寄存器已经和中断有关的寄存器:
寄存器具体由什么作用呢?
前面我们讲到的具体模式的配置,也就是上图中的各个开关,具体拨到哪个位置上, 就是由寄存器来控制的。如下图所示:
具体的介绍请参考手册(\51单片机入门教程资料\课件及程序源码\相关资料\STC89C52.pdf 第7.1.1节
)
视频介绍请参考(https://www.bilibili.com/video/BV1Mb411e7re/?p=17&spm_id_from=pageDriver&vd_source=e2638d12685eef84cda913d9d67be0a9的第48分钟后)
总结如下:
1、寄存器是连接软硬件的媒介
2、在单片机中寄存器就是一段特殊的RAM存储器一方面,寄存器可以存储和读取数据,另一方面,每一个寄存器背后都连接了一根导线,控制着电路的连接方式
3、寄存器相当于一个复杂机器的“操作按钮
所以只要我们在程序中将相应的寄存器配置好,单片机的电路就会按照我们审定的模式去工作。
1.2 本节目标
目标1:按键控制LED流水灯模式
流水灯的效果在前面学习LED灯的时候已经演示过了,本节要做的就是通过定时器,将按键和流水灯结合在一起;效果还是流水灯的效果。
目标2:使用定时器实现时钟
二、按键控制LED流水灯模式
代码路径:51单片机入门教程资料\课件及程序源码\程序源码\KeilProject\7-1 按键控制LED流水灯模式
代码的编写过程,请参考https://www.bilibili.com/video/BV1Mb411e7re/?p=18&spm_id_from=pageDriver&vd_source=e2638d12685eef84cda913d9d67be0a9
具体代码:
#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) //如果K1按键按下
{
LEDMode++; //模式切换
if(LEDMode>=2)LEDMode=0;
}
}
}
}
//定时器中断函数
void Timer0_Routine() interrupt 1
{
static unsigned int T0Count;
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
T0Count++; //T0Count计次,对中断频率进行分频
if(T0Count>=500)//分频500次,500ms
{
T0Count=0;
if(LEDMode==0) //模式判断
P2=_crol_(P2,1); //LED输出
if(LEDMode==1)
P2=_cror_(P2,1);
}
}
其中,Timer0.c如下:
#include <REGX52.H>
/**
* @brief 定时器0初始化,1毫秒@12.000MHz
* @param 无
* @retval 无
*/
void Timer0Init(void)
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0=1;
EA=1;
PT0=0;
}
/*定时器中断函数模板
void Timer0_Routine() interrupt 1
{
static unsigned int T0Count;
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
T0Count++;
if(T0Count>=1000)
{
T0Count=0;
}
}
*/
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==0);Delay(20);KeyNumber=3;}
if(P3_3==0){Delay(20);while(P3_3==0);Delay(20);KeyNumber=4;}
return KeyNumber;
}
代码解释,这里主要解释一下Timer0.c
里的Timer0Init函数,
/**
* @brief 定时器0初始化,1毫秒@12.000MHz
* @param 无
* @retval 无
*/
void Timer0Init(void)
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0=1;
EA=1;
PT0=0;
}
首先是配置定时器模式TMOD:
TMOD &= 0xF0
表示将TMOD的低四位清零,高四位保持不变;
TMOD |= 0x01
表示将TMOD的最低位置1,其他位不变;
这两步操作,其实跟TMOD=0x01;
效果是一样的,好处是防止同时操作多个寄存器时串位的情况,能够在实现配置高四位的同时,不影响低四位。
TL0 = 0x18; TH0 = 0xFC;
表示设置定时初值,TL0为低位,TH0为高位。
TF0=0
清除TF0标志;
TR0 = 1;
如上图中,当TR0为1时,与门的输出结果就全取决于TR0下面那部分了。
ET0=1
,打开ET0的开关;
EA=1
,打开EA的开关;
PT0=0
,设置为低优先级。
其实对定时器的设置不一定要我们手敲代码,简单方法是利用STC-ISP软件里的功能生成,步骤如下:
假设我们要设置一个1ms的定时器:
按照下图进行配置,点击生成C代码即可生成一个1ms的定时器代码,
但是AUXR是相对于最新版本的,我们的单片机并不需要,直接删掉就行。可以发现,跟我们自己上面写的代码是一致的。但是少了中断的配置,我们将中断的配置加上即可。
配置中断:
ET0=1
,打开ET0的开关;
EA=1
,打开EA的开关;
PT0=0
,设置为低优先级。
所以后面我们在写定时器的代码时,直接用STC-ISP软件里生成代码,然后在后面加上以上配置中断的代码即可
。
前面讲了定时器的配置,那定时器达到定时时间后,需要触发中断,转而去执行中断处理程序,中断处理程序如下所示,其中在函数名定义后面加上 interrupt 1
,就表示这是中断处理程序,当定时器达到时间后,就会自动触发中断去执行该中断处理程序。
void Timer0_Routine() interrupt 1
{
static unsigned int T0Count;
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
T0Count++; //T0Count计次,对中断频率进行分频
if(T0Count>=500)//分频500次,500ms
{
T0Count=0;
if(LEDMode==0) //模式判断
P2=_crol_(P2,1); //LED输出
if(LEDMode==1)
P2=_cror_(P2,1);
}
}
每次执行中断处理程序,都要给TL0和TH0赋初值,具体设置为多少,参考上面STC-ISP生成的代码里的值即可。
T0Count
用来控制时间,比如我们想要达到0.5秒间隔再去处理某件事,但是定时器每次触发的频率是1ms,所以就定义T0Count
,每次触发中断处理函数,T0Count
就加1,代表过了1ms了,当T0Count
加到500时,就表示过了500ms,也就是0.5s了。
再后面的代码(中断控制函数里的代码)就是控制独立按键了。
_crol_
和_cror_
函数包含在INTRINS.H
函数库中,含义分别是循环左移和循环右移
执行完中断处理函数后,继续执行主程序的代码。
最终的效果就是目标1了,即用按键控制流水灯模式。
三、定时器时钟
代码路径:51单片机入门教程资料\课件及程序源码\程序源码\KeilProject\7-2 定时器时钟
具体代码:
#include <REGX52.H>
#include "Delay.h"
#include "LCD1602.h"
#include "Timer0.h"
unsigned char Sec=55,Min=59,Hour=23;
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 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
T0Count++;
if(T0Count>=1000) //定时器分频,1s
{
T0Count=0;
Sec++; //1秒到,Sec自增
if(Sec>=60)
{
Sec=0; //60秒到,Sec清0,Min自增
Min++;
if(Min>=60)
{
Min=0; //60分钟到,Min清0,Hour自增
Hour++;
if(Hour>=24)
{
Hour=0; //24小时到,Hour清0
}
}
}
}
}