目录
0 引言
1 定时的原理
1.1 生活中的定时
1.2单片机中的定时器
2 C语言程序设计
2.1 定时器的内部功能
2.2 设置寄存器
2.3 实验程序
3 有两个小问题(算是延伸吧)
3.1 只能0.05s闪一次吗?
3.2 每次都要在草稿纸手算初始值好麻烦
4 最后
0 引言
其实,这个是可以通过软件延时计算器来生成自己需要的延时程序,而且效果非常好,但是既然要学习单片机,我们的目的就是要搞清楚硬件系统的知识,所以必须要学会如何用软件(程序)去控制硬件(处理器)。
1 定时的原理 1.1 生活中的定时我们每个人都用过“定时”这个功能。比如说闹钟,我们需要学习1小时,然后定个1小时的闹钟,然后闹钟从59:59:59开始倒计时,直到00:00:00结束,闹钟响铃,学习结束,这个从我们的角度看来它是倒计时。单片机里的定时器原理也是这个,但是用的是正计时哈。
1.2单片机中的定时器单片机的定时器,它是从某个值(你可以设定)加到某个值后停止。
这里要提到两个概念:①时钟周期;②机器周期。(下面用人话讲一下,可能不太严谨)
①时钟周期T:单片机上面有一个晶振,通电后它会产生一个工作频率f(我用的C51是11.0592MHz,板子不同频率可能不同,可以用放大镜看一下,他的参数刻在晶振的上面了),这个评率的倒数就是你用的这个板子的时钟周期,单位:s。
T = 1/f
②机器周期:一个机器周期 = n×时间周期;不同的板子,n的取值不同,C51系列的n是12(那我就以12说明了),单位也是:s。
说完这两个,开头说单片机的定时器的工作就是不停地加,而且是加1(计一个数),而每次+1,就要消耗一个机器周期(所有这就是我为啥要先解释①②),或者讲每隔一个机器周期就寄存器就会+1。
知道这个有啥用呢?因为我们要算出来,我们打算让LED隔多长时间闪一次啊,这就是目的。
现在我们打算让灯泡每隔0.05秒闪一次,即:
0.05 (s) = m × n/f (s)
n和f刚才提到了,我用我的板子的参数代进去举个例子吧。
0.05 (s) = m × 12/11059200(s)
m = 46080(次)
也就是说,寄存器加了四万多个1,所用时长大约0.05s。
闹钟是从你设定的某个值倒计时至00:00后停止计时,单片机中寄存计时是你设置某个值后不断+1,直到加到某个值后停止,那加到哪个数值才能停止呢?
51系列标准的单片机内部是有两个寄定时器T0和T1,每一个定时器(也就是寄存器)有16bit,最大可以存储2^16bit的数据,换成16进制就是可以储存0x00到0xFF,超过这个数值,就会发生溢出(overflow),从0xFF进位后再次变成0x00,所以如果我们可以检测到有进位的情况,说明我们设置的初值经过不断+1,到了最大值(是不是很像闹钟计时到了终值00:00呢),此时LED闪一次。
这就是所需的原理部分。下面讲一下如何将原理通过c程序的方式实现目标。
2 C语言程序设计 2.1 定时器的内部功能注意:这块很繁琐,也牵涉很多东西,但是不要强记,先了解就好,数据手册里面的内容是用来查的,遇到需要的或者不会的翻翻就行了!而且这一块内容我暂时只讲用的着的哈,见谅,以后的blog会遇到一个讲一个,慢慢来,哈哈。
C51单片机内部的定时器有两个,即T0、T1。这两个定时器有基本相同功能的寄存器(大部分功能都是相同的),下面我用大白话讲方便快速理解,且我只讲暂时用的到的,见谅哈)。
- 定时器控制寄存器(TCON):
可位寻址就是说你可以直接赋值到某一位,举个栗子:比如我想把TCON的TR0置1。
像TMOD这种不可位寻址的,如我想把TMOD的定时器0.3置1,怎么写到?TR0 = 1;//可位寻址
GATE = 1;//错 TMOD = 00001000;//只能这样写,不能具体地给某一位赋值。 TMOD = 0x04;//这样也行。
- 定时器模式寄存器(TMOD):
模式寄存器TMOD是用来选择模式的,每个定时器都有4个模式(模式0、1、2、3),除了模式3功能不同,其他的是一样。
功能大家可以自查手册,我们在这次只用模式1(为啥只用模式1?)∵模式1的功能是我们想要的。
- 定时值储存寄存器(TL、TH):用来存放我们初始值的,你把初始值放着后,开始慢慢地+1计数。
定时器0的值储存寄存器是16bit的,分两块:TL0(低8bit)、TH0(高8bit)
上图,是定时器模式1示意图。
分析:OSC是时钟频率源,也就是你板子上的晶振,d*就是之前讲的n,一般是12,Tn是外接振荡源,一般用计数器才接Tn,图上写的很清楚,当C/~T = 0时,我们接的是OSC,这也是我们要的。我们看后面的控制框,如果想闭合,即下面的组件需要输入1,TRn就是TCON中的一位,我们用的是T0的,所以就是TR0,功能见下图。
只有TR0 = 1时或者TR0 = 0且~INT0输入高电平时,哪一个情况都行,T0才开始工作(计数), 那我直接把TR0 = 1不就好了,不用考虑另一个了,多好。因为是与门,所以下面的~INT0和GATE通过并门后必须得出1,那只有GATE = 0,经过反相器后出1,此时不用考虑~INT0的情况了(因为或门的性质)啦。
看一下后面的TF0。
当从初始值经过不断+1后,到达最大值(也就是16bit值储存器的最大容量0xFF)后 ,会产生进位(溢出),会被TF0检测到,一旦检测到,TF0就会自动置1。
整理一下:
也就是说,我需要设置一下参数
- TMOD = 0x01//使定时器工作在模式1状态下。
- TH0 =0x4B ;
- TL0 = 0xFF;
- TR0 = 1//置1使定时器计数。
我们之前打算设置0.05s闪烁,通过计算需要加46080次1可以实现,用65535(0xFFFF)-46080=19455(这个就是初值)→0x4BFF,把0x4B、0xFF分别放入高8位,低8bit,也就是TH0 =0x4B、TL0 = 0xFF。
2.3 实验程序#include
sbit LED = P2^7;//select LED you want to flash.
void main()
{
TMOD = 0X01;//select mode1 of Timer0
TH0 = 0X4B;//input start.
TL0 = 0XFF;
TR0 = 1;//make T0 start to count.
while(1)
{
if(TF0==1)//examine overflow of value
{
TF0 = 0;
TH0 = 0X4B;
TL0 = 0XFF;
LED =! LED;
}
}
}
3 有两个小问题(算是延伸吧)
3.1 只能0.05s闪一次吗?
0.05s闪一次是可以看出来的,但是我想延时的时间长一点怎么办?
有个小问题,就是我们把初值放在TL0、TH0这个存储器里,最大也就只能放0xFFFF(65535)个数据,换句话说,我就是把初值设为0x0000,让他+1,直到0xFFFF,但是折合过来也只是有71ms(0.071s),那我想设置每隔1s(1000ms)闪一次咋办?那就用下面这个程序,稍微加一点东西。
#include
sbit LED = P2^7;//select LED you want to flash.
void main()
{
unsigned int i;//define a counter.
TMOD = 0X01;//select mode1 of Timer0
TH0 = 0X4B;//input start.
TL0 = 0XFF;
TR0 = 1;//make T0 start to count.
while(1)
{
if(TF0==1)//examine overflow of value
{
TF0 = 0;
TH0 = 0X4B;
TL0 = 0XFF;
i++;
if(i>=20)//examine if it is 20 enough.
{
LED =! LED;
i = 0;//counter is back 0.
}
}
}
}
因为从我们设定的初值0x4BFF到0xFFFF溢出是0.05s,我们希望变成1s,那么我们可以设置到溢出时并不让LED取反(闪一次),而是攒够20次才执行。
20×0.05s = 1s
当攒够20次(我们用一个if语句判断),我们才让他取反一次,并且同时让次数清零,反复这样搞,就实现了隔1s闪一次。如果是想两秒闪一次,就是40×0.05s = 2s,可以先算好在设置参数。
3.2 每次都要在草稿纸手算初始值好麻烦我不知道这个blog发布之后还可不可以改,如果可以改,我在补充,要不让就另写一篇补一下这个没解决的点。
4 最后单片机确实好难啊,要涉猎的知识要很多,要不然到处都是洞,要有恒心学下去。文章内如有错误,请猛烈地抨击我吧~跪蟹各位看官老爷们了。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)