目录
代码讲解
初始化函数
1.定时器部分的配置步骤
第一步,对TMOD的赋值
第二步,给TF0赋值
第三步,给TR0赋值开启定时器
第四步,给TL0和TH0赋初值
2.中断系统部分的配置步骤
第一步,给ET0赋值
第二步,给EA赋值
第三步,给PT0赋值
中断函数
定时器计算器生成代码工具
按键控制流水灯代码演示
时钟代码演示
看这一节之前一定要先依次看完这前三节:
单片机学习笔记---中断系统(含外部中断)-CSDN博客
单片机学习笔记---定时器/计数器(简述版!)-CSDN博客
单片机学习笔记---定时器和中断系统如何连起来工作-CSDN博客
这一节开始代码演示。
本节代码主要演示的是这个工作路线:
新创建一个工程,写上框架:
现在来写一个配置定时器和中断系统的函数
首先是对TMOD的赋值,TMOD的寄存器,我们前面已经说了,它是用来确定我们定时器的工作方式,像里面包含了T0和T1这两个定时器的工作方式,那你使用的是T0或者是T1,那就对这个TMOD的高4位或者是低4位进行赋值。
我们给TMOD赋值0x01,高四位它是0,说明我们现在使用的是低4位,它低四位是用于定时器0的管控,定时机1是高四位来控制的。
我们先不管定时器1,先看定时器0,所以高四位赋值0,低四位赋值1,也就是T1=0,T0=1的意思
那定时器0低4位是0001,对应的寄存器的值是0001
最后八位赋值成0000 0001
以上GATE设置为0,说明定时器的启动是通过TR0来控制的;
然后这个C/T设置为0,说明它的工作与T模式,也就是定时器模式;
然后,M1和M0=01,01的话是工作方式1,也就是16位定时计数器。
于是写TMOD=0x01
所以,我们通过这种设置可以知道我们定时器它处于定时器0,并且是16位,方式1,而且GATE是0可以直接通过TCON中的TR0来进行启动。
这样我们就完成了工作流程图中的这一部分的配置:
根据流程图,我们现在需要给TF0赋值:
先补充一个概念知识:
不可位寻址VS可位寻址:
不可位寻址的寄存器只能整体赋值;
可位寻址的寄存器可以对它其中的每一位单独赋值。
由于TCON是可位寻址的,所以我们可以单独赋值。
上图中TF1和TR1是定时器1的,我们不管。
我们可以单独看定时器0中的TF0和TR0。
TF0是定时器T0溢出中断标志,我们给它赋值0,因为一旦等于1之后,它就产生中断。我们先给TF0清零是为了防止还没有配置好就产生中断。这一步没有的话也不会产生特别大的影响。
根据流程图,我们要配置TR0
由于GATE位等于0了,所以开启定时器主要取决于TR0,所以就写TR0=1;如果不知道为什么TR0=1的话请看上一节解释。
之前第一步的时候我们只是选择了方式1,即选择16个位的寄存器存放初值,现在我们要来给这16个位的寄存器赋初值。
我们怎么让它每隔1毫秒产生中断呢?怎么定时呢?
我们前几节提到过,这个计数器的计数范围是0~65535,每隔一微秒(前面定时器那节已经说过12Mhz的晶振机器周期是1微秒),计数加1,总共定时时间是65535微秒,那我们怎样让它计1秒呢?
每隔1毫秒,让它产生中断,每一次中断之后,我们再来计数,中断1000次的时候,就是1s。
时间换算:
1s【秒】 = 1000ms【毫秒】
1ms【毫秒】 = 1000μs【微秒】
1μs【微秒】 = 1000ns【纳秒】
1ns 【纳秒】= 1000ps【皮秒】
首先我们先给它定一毫秒产生中断。
如果一开始给它赋值64535,那64535离计数器溢出差值1000(微秒),所以计时时间为1ms。
那么我们想要64535这个初值给T0定时器,首先将64535换算成二进制给到T0的高八位TH0和低八位TL0(TH0和TL0都是八个位的寄存器,只能存放256)
所以我们可以这样赋值:
为什么这样写就可以把64535的高八位和低八位取出来呢?
因为64535在寄存器当中的数据是十六位的,
我们可以类比一下十进制,十进制的取值范围是0~99,正好100个数,比如说123这个数,我们想要取出它的最高位和它的余数,那么我们就可以123/100=1,123%100=23。
同理,八个位的寄存器的存放数值是256,想要取出64535十六进制的高八位和低八位和就可以写成64535/256=高八位,64535%256=低八位。
现在整个定时器部分就配置好了。
ET0=1就打开开关
把总中断打开EA=1
PT0默认等于0,但我们要写上
以上这样就配置完了,
这个子函数也就完成了,我们来调用一下。
到这里这个定时器就可以工作了,那定时器溢出之后,也就是闹钟响了之后,我们要干嘛呢?
我们还要写一个子函数,让闹钟响了之后,要跳转到这个子函数来,执行中断任务。
为什么要写一个中断函数呢?
也就是定时器初始化之后,
过了一个毫秒while(1){},中断一过来,程序就会从while(1){}跳转到中断函数里面来,等中断函数执行完之后,程序再回到while(1)里面去。
其实我们是根据这个程序流程图的逻辑来写程序的:
主程序在执行的过程中来了一个中断,主程序就跳转到中断函数,等执行完中断再跳转会主函数中。
这个中断函数的形式是这样的:
前面定时器那一节讲过这个子函数的书写规则了,不懂的话可以去看看。
这里我们需要记住对应的中断号,芯片手册上也列出了中断号:
现在我们把这整个程序下载到单片机里面来验证一下。
现在看还是什么现象都没有的
怎么证明它的确是在执行这个函数了呢?
可以在中断函数里点亮一个灯看看
可以发现,到目前为止我们的主程序中还没有调用这个函数的。
如果中断真的过来了,那P2_0=0;这句代码肯定执行,它一执行,开发板上的这个灯就会点亮,所以我们先这样验证主程序是不是过来了,稍后再验证时间。
我们看到D1的确亮了。
这个中断函数根本没有执行,只是跟了一个尾巴(interrupt 1)定时器过来之后,它就点亮了,就说明程序真的跳转到这个地方来执行了。
这里我们的代码还没有结束,先介绍一个非常好的方法可以省去我们的敲代码的时间!
在STC上找到定时器计算器。
可以看到这里有已经写的框架,我们只需要选择参数即可
根据我们选择的工作模式和开发板的资源设置正确的参数:
为什么选择定时器时钟是12T呢?
这里就是我们前面几节说的这里,我们这节主要选择的工作流程是12T。
怎么让它拨到6T呢?
只要在这里打勾就是6T模式,没有打勾就是12T模式,但是我们一般是用12T模式。
最后把设置好的代码复制下来
改造一下:
因为89系列没有AUXR这个寄存器,所以删掉这一行
然后这两句代码是因为TMOD=0x01这种写法只适合用TMOD的高四位或者低四位时。
当高四位和低四位同时应用的时候,因为TMOD是不可位寻址,最好是优化成:
TMOD&=0xF0;//表示把TMOD的低四位清零,高四位保持不变,TMOD&1111 0000
TMOD|=0x01;//表示把TMOD最低位置1,其他位保持不变
然后这两句写的和我们自己写的这两句不一样
我们自己写是这样:
其实我们写成的这样会有一微秒的差别。
可以验证一下它生成的这种写法是不是跟我们写的结果一样,
用计算机算一下
取整是252,转换成16进制就是FC
说明我们的写的和它生成的结果是一样的
再验证一下低四位:
余23
23换成16进制是17
说明我们配置的低四位比它生成的低四位少了1微秒
所以我们可以把自己写的这句代码改成+1这样:
可以直接用它生成的形式也可以!
然后他还缺少这三句,我们把自己写的加上
最后我们把代码优化成这样(分模块化来写),每一行代码几乎都有注释了,这里就不再赘述:
main.c文件:
Timer0.c文件:
Timer0.h文件:
下载后的效果:
D1在以1秒为间隔在闪烁
说明我们的定时器程序已经没有错误了。
接下来开始演示按键控制流水灯的程序:
同样分文件来写,创建Key.c和Key.h文件
然后把之前矩阵键盘那一节写好的Delay.c和Delay.h文件复制到本节的程序文件中
然后再添加进来
然后main.c文件改成:
Timer0.h文件没变:
Timer0.c文件也没变:
Key.h文件
Key.c文件
运行后的效果:
LED正在以500ms位间隔向右移动,随便按下一个独立按键后,移动方向变为向左移动。
最后开始演示时钟的程序:
新创建一个工程
写上框架
然后把LCD1602调试工具那一节的Delay.c,Delay.h,LCD1602.c,LCD1602.h文件复制到刚刚新创建的工程文件里面来,然后点击添加。
添加进来
同样操作,再把本节前面按键控制流水灯的Timer.c和Timer.h文件添加进来
编译后无错误,但是有9个警告
嫌警告碍眼可以点击这里忽略警告
再次编译,无错误,无警告
然后怎么调用这些函数的话都在之前相关章节讲过了,不懂的伙伴去翻我的单片机专栏里的文章看看。这里就不再赘述。
添加进来的文件不用修改什么,直接看本节主程序main.c调用写好的代码,每一行代码基本都有注释。这里就不再赘述。
看看效果:
观察23:59:59再计一秒后开始进位,全部变成00:00:00
然后从1开始计:
以上就是本节全部的代码演示。
本节全部的源码都放在评论区了,自取!
有问题的欢迎评论区留言或者私信。