目录
前言
你是否好奇过电子时钟的实现机理?其实在本质上与机械时钟原理是一致的,都需要一个频率极度稳定且精确的振荡装置,用于精确计数。由于振荡频率已知,从而实现计时。
在单片机中,定时器是非常重要的片内资源,依靠晶振实现精确计时。本章,我们来谈一谈定时器。
本节完整工程文件已上传GitHub,仓库地址,欢迎下载交流!
晶振概述
晶振,即石英晶体振荡器,誉为单片机的心脏。
从一块石英晶体上按一定方位角切下薄片,并在两端施加电场,晶体会产生机械变形。反之,若在晶片的两侧施加机械压力,则在晶片相应的方向上将产生电场,这种物理现象称为压电效应。
晶振正是利用压电效应制成的,对晶振两端施加直流电压(多种频率的叠加),晶振会和其固有频率一致的电波产生共振(类似于选频器),这个输出频率十分的稳定。
晶振一般分为石英晶体谐振器(Quartz Crystal, XTAL
)和石英晶体振荡器(Crystal Oscillator, XO
)两种。
- 谐振器即无源晶振,需要外部接振荡电路
- 振荡器即有源晶振,已内置振荡电路
晶振的主要参数
- 标称频率:即晶振的理想输出频率。常用的有8MHZ、11.0592MHZ、12MHZ、16MHZ等等。
- 调整频差(
Frequency Tolerance
):在25℃下晶振的输出频率与标称频率的偏差。一般用单位ppm(parts per million
, 1 0 − 6 10^{-6} 10−6)、ppb(parts per billion
, 1 0 − 9 10^{-9} 10−9)表示。 - 负载电容(
Load Capacitance
):晶体的频率会根据串联的电容而改变。用于设计振荡电路。
时序概述
- 振荡周期:又称时钟周期,即晶振振荡一次的时间。
T = 1 f o s c T= \frac{1}{f_{osc}} T=fosc1 - 状态周期:由振荡周期二分频得到。
T = 1 f o s c 2 = 2 f o s c T= \frac{1}{\frac{f_{osc}}{2}}=\frac{2}{f_{osc}} T=2fosc1=fosc2 - 机器周期:即执行基本操作所需最短时间。对于
12T
单片机(STC89C52为12T
单片机),机器周期为振荡周期的12分频;对于1T
单片机,机器周期等于振荡周期,不分频。因此1T
单片机运行速度理论上为12T
单片机的12倍。
T = 1 f o s c 12 = 12 f o s c T= \frac{1}{\frac{f_{osc}}{12}}=\frac{12}{f_{osc}} T=12fosc1=fosc12 - 指令周期:执行一条指令所需的时间,通常由若干个机器周期组成。51单片机采用的是51指令集(Intel 8031指令集),显然它属于复杂指令集运算(
Complex Instruction Set Computing
,CISC),特点是每个指令执行时间不一。
T = n × 12 f o s c T=n\times \frac{12}{f_{osc}} T=n×fosc12
对于1T
单片机,已经可以实现单指令周期,即执行一条指令只需一个机器周期。
定时器概述
定时器一般指定时计数器,本质是一个计数器,即每接收一个机器周期脉冲,计数器内部加1,直至溢出归0(向上计数),此时会向CPU申请溢出中断。由于晶振频率一定,也可以实现计时。
以51单片机为例,如果外接晶振12MHz,一个机器周期由12个振荡周期组成(12分频),故一个机器周期 T T T 为
T = 1 12 × 1 0 6 × 12 = 1 μ s T=\dfrac{1}{12\times 10^6}\times 12 = 1\mu s T=12×1061×12=1μs
那么由计数个数 × \times ×机器周期,即可得到时长。
关于定时器,需要强调两点:
- 定时器和中断是两个不同的概念,不存在包含与被包含的关系。我们完全可以只使用定时器而不启用中断。只不过在实际开发中,定时器与中断结合使用比较多。
- 定时器是独立于CPU的外设,只要上电就不停工作。因此在定时器中断程序的内容并不影响定时器的计时精度,延迟函数也只是使CPU保持空转。但对于程序本身而言,我们若希望准确记录定时器的每次溢出(不丢数),应当保证程序的畅通,否则从结果来看,计时是 " 不准的 "。
工作方式寄存器(TMOD)
一个定时器一共有16位,分为低8位(TL
)和高8位(TH
)。我们通过工作方式寄存器(TMOD
)来配置其工作方式。
寄存器 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
TMOD | GATE | C/T | M1 | M0 | GATE | C/T | M1 | M0 |
- 工作方式寄存器(TMOD):低四位配置T0,高四位配置T1。字节地址89H。
-
GATE:门控位。决定定时器的启动是否受外部中断的影响
- 置0:TR0 =1 ,TR1 = 1,定时器启动(常用)
- 置1:TR0 =1 ,TR1 = 1,INT0/INT1 = 1, 定时器启动
-
C/T:定时计数器选择位。0为定时器,1为计数器。
-
M1/M0:工作方式设置。
M1/M0 定时器工作方式 00 13位定时器/计数器 01 16位定时器/计数器(常用于定时器) 10 8位自动重装定时器/计数器 (常用于串口) 11 T0分为两个独立的8位定时器/计数器;T1停止计数
-
定时器配置流程
- 对工作方式寄存器(
TMOD
)赋值,确定定时器 T0 或 T1 的工作方式。TMOD |= 0X01;
- 如果使用中断,则计算初值(即计数目标次数后刚好发生溢出中断),并写入TH0和TL0、TH1和TL1,共16位。
TH0 = (65536-9216)/256; TL0 = (65536-9216)%256;
- 如果使用中断,则打开关总中断允许位(EA=1),使能定时器中断(ET0=1/ET1=1)。
ET0 = 1; EA = 1;
- 使TR0或TR1置1,启动定时计数器。
TR0 = 1;
初值的简便算法
已知定时器一共16位,故最大计数值为 2 16 − 1 = 65535 2^{16}-1=65535 216−1=65535,对于任意初值 x x x,如何计算它的高八位和低八位呢?
例如,希望计时1ms,对于12MHz晶振,需要计数1000次后溢出,则反推出定时器初值为 65536 − 1000 = 64536 65536-1000=64536 65536−1000=64536,则
- 高八位: T H 0 = 64536 / 256 = 252 TH0=64536/256=252 TH0=64536/256=252
- 低八位: T L 0 = 64536 % 256 = 24 TL0=64536\%256=24 TL0=64536%256=24
因为低八位最大值为 2 8 − 1 = 255 2^8-1=255 28−1=255,故存在非负整数 k = T H 0 < 256 k=TH0<256 k=TH0<256 和非负整数 b = T L 0 < 256 b=TL0<256 b=TL0<256,使 x = 256 × k + b x=256\times k+b x=256×k+b,因此以256整除即可得高八位(十进制),取余即可得低八位(十进制)。
注:这里要避免一个误区,给寄存器赋值,可以用任意进制,实际存储时都会转化成二进制。但在书写时,为了简洁,往往不会直接写二进制,而是用十六进制代替。
微秒级定时中断的注意事项
51单片机理论上单条指令运行周期为1微秒左右,在对定时器进行微秒级别的定时中断时,中断服务函数中定时器初值表达式的形式将大大影响定时中断的正常执行。
对于STC89C52芯片,开启毫秒级别