从单片机基础到程序框架(全集 2019pdf版).pdf - 第362页
第八十五节: 定时中断的寄存器配置。 【85.1 寄存器配置的本质。 】 单片机内部 可供我们选择 的资源非常丰 富,有 定时器,有串 口,有外部中 断,等等。这 些丰富 的资源, 就像你进入 一家超市, 你只需选择你所需 要的东 西就可以了, 所以配置寄存器的 关键在于选 择, 所谓选择就 是往 寄存器 里面 做填空 题, 单片 机系统 内部 再根据 你的“ 选择 清单” ,去 启动对 应的 资源。 那么 我们 怎么知 道某 个型号 …
断函数”仿佛是隔离的毫无“物理连接”的,为什么单片机还能在“main 函数”与“中断函数”两者中切换
自如?没错,确实,“main 函数”与“中断函数”在书写上是隔离的毫无关联的,但是它们之间之所以能相
互切换,是因为背后有一只无形的手在自动操控这一切,这只手就是单片机硬件自身,这是一种特殊机制,
也可以理解成一种特殊的游戏规则,我们只要遵守就好了,除了普通函数,其它凡是中断函数的,都不用跟
main 函数发生软件上的关联调用,它们之间的切换都是硬件自动完成的,这就是 main 函数与中断函数的特
殊跳转机制(或者称为游戏规则也可以)。
【84.2 常用的中断函数有哪三类?】
单片机的中断有很多,但常用在项目上的有三类:
第一类是定时中断。配置中断后,使其每隔一段时间就产生一次中断,比如“1ms 一次的定时中断”几
乎是所有的系统里的标配,因为它对程序框架起到一个时间节拍的作用。
第二类是通讯中断。比如串口每接收完一个字节就会产生一个中断通知我们去处理。
第三类是电平变化的中断。下降沿或者上升沿的中断,常常用在采集高速的脉冲信号。
【84.3 我们如何操控中断?】
刚才 84.1 提到“单片机硬件自动”这个概念,但是说它“硬件自动”并不意味着它不可控。单片机本
身出厂的时候内部就携带了很多种类的中断,这些中断是否开启取决于你的“配置中断”代码,你要开启或
者关闭某类中断,只需编写对应的“配置中断”代码就可以,而“配置中断”的代码本质就是填写某些寄存
器数值。

第八十五节: 定时中断的寄存器配置。
【85.1 寄存器配置的本质。】
单片机内部可供我们选择的资源非常丰富,有定时器,有串口,有外部中断,等等。这些丰富的资源,
就像你进入一家超市,你只需选择你所需要的东西就可以了,所以配置寄存器的关键在于选择,所谓选择就
是往寄存器里面做填空题,单片机系统内部再根据你的“选择清单”,去启动对应的资源。那么我们怎么知
道某个型号的单片机内部有哪些资源呢?看该型号“单片机的说明书”呀,“单片机的说明书”就是我们通
常所说的“芯片的 datasheet”,或者说是“芯片的数据手册”,这些资料单片机厂家会提供的。
跟单片机打交道,其实跟人打交道没什么区别,你要让单片机按照你的“意愿”走,你首先要把你的“意
愿”表达清楚,这个“意愿”就是信息,信息要具备准确性和唯一性,不能模凌两可。比如,现在要让单片
机“每 1ms 产生一次中断”,你想想你可能需要给单片机提供哪些信息?
(1)51 单片机有 2 个定时器,一个是 0 号定时器,一个是 1 号定时器,我们要面临“二选一”的选择,
本例子中用的是“0 号定时器”。
(2)0 号定时器内部又有 4 种工作方式:方式 0,方式 1,方式 2,方式 3,本例子中用的是“方式 1”。
(3)定时器到底多长时间中断一次,这就涉及到填充与中断时间有关的寄存器的数值,该数值是跟时
间成比例关系,本例子中配置的是 1ms 中断,就要填充对应的数值。
(4)默认状态下,定时器是不会被开启的,如果要开启,这里就是涉及到定时器的“开关”,本例子要
开启此开关。
(5)定时器时间到了就要产生中断,中断也有“总开关”和“定时器的局部开关”,这两个开关都必须
同时打开,中断才会有效。
要配置定时器“每 1ms 产生一次中断”,大概就上述这些信息,根据这些信息提示,下面开始讲解一下
寄存器的具体内容。
【85.2 定时器/计数器的模式控制寄存器 TMOD。】
寄存器 TMOD 是一个 8 位的特殊变量,里面每一位都代表了不同的功能选择。根据芯片的说明书,TMOD
的 8 位从左到右依次对应从 D7 到 D0(左高位,右低位),定义如下:
GATE C/T M1 M0 GATE C/T M1 M0
仔细观察,发现左 4 位与右 4 位是对称的,分别都是“GATE,C/T , M1 , M0”,左 4 位控制的是“定
时器 1”,右 4 位控制的是“定时器 0”,因为本例子用的是“定时器 0”,因此“定时器 1”的左 4 位都设置
为 0 的默认数值,我们只需重点关注右 4 位的“定时器 0”即可。
GATE:定时器是否受“其它外部开关”的影响的标志位。定时器的开启或者停止,受到两个开关的影响,
第一个开关是“自身原配开关”,第二个开关是“其它外部开关”。GATE 取 1 代表定时器受“其它外部开关”
的影响,取 0 代表定时器不受“其它外部开关”的影响。本例子中,定时器只受到“自身原配开关”的影响,
而不受到“其它外部开关”的影响,因此,GATE 取 0。
C/T:定时器有两种模式,该位取 1 代表“计数器模式”,取 0 代表“定时器模式”。本例子是“定时器
模式”,因此,C/T 取 0。
M1 与 M0:工作方式的选择。M1 与 M0 这两位的 01 搭配,可以有 4 种组合(00,01,10,11),每一种组合
就代表一种工作方式。本例子选用“方式 1”,因此 M1 与 M0 取“01”的组合。
综上所述,TMOD 的配置代码是:TMOD=0x01;

【85.3 决定时间长度的寄存器 TH0 与 TL0。】
TH0 与 TL0,T 代表定时器英文单词 TIME 的 T,H 代表高位,L 代表低位,0 代表定时器 0。
TH0 是一个 8 位宽度的寄存器,TL0 也是一个 8 位宽度的寄存器,两者合并起来成为一个整体,实际上
就是一个 16 位宽度的寄存器,TH0 是高 8 位,TL0 是低 8 位,它们合并后的数值范围是:0 到 65535。该 16
位寄存器取值越大,定时中断一次的时间反倒越小,为什么?TH0 与 TL0 的初始值,就像一个水桶里装的水。
如果这个桶是空桶(取值为 0),“雨水”想把这个桶“滴满溢出”所需要的时间就很大。如果里面已经装了
大半的水(取值为大于 32767),“雨水”想把这个桶“滴满溢出”所需要的时间就很小。这里的关键词“滴
满溢出”的“滴”与“满溢出”,“滴”的速度是由单片机晶振决定的,而“满溢出”一次就代表产生一次中
断,执行完中断函数在即将返回主函数之前,我们重新装入特定容量的水(重装初值),为下一次的“滴满
溢出”做准备,依次循环,从而连续不断地产生间歇的定时中断。
配置中断时间的大小是需要经验的,因为,每次定时中断的时间太长,就意味着时间的可分度太粗,而
如果每次定时中断的时间太短,则会产生很频繁的中断,势必会影响主函数 main()的执行效率,而且累记中
断次数的时间误差也会增大。因此,配置中断时间是需要经验的,根据经验,定时中断取 1ms 一次,是几乎
所有单片机项目的最佳选择,按我的理解,“1ms 定时中断一次”已经是单片机界公认的一种“标配”。
要配置 1ms 定时中断,TH0 与 TL0 如何取值?刚才提到一个形象的例子“桶,滴,满溢出”。TH0 与 TL0
的最大取值范围是 65535,可以理解成为最大 65535“滴”,如果超过 65535“滴”(比如加 1“滴”后变成 65536
“滴”)就会“满溢出”,从而产生一次中断(65536 是中断发生的临界值)。而“滴一次的时间”就刚好是单
片机执行“一次单指令的时间”,“一次单指令的时间”等于 12 个晶振周期,比如 12MHz 的晶振,晶振周期
是(1/12000000)秒,而“一次单指令的时间”就等于 12 乘以(1/12000000)秒,等于 0.000001 秒,也就
是 1us。1us“滴”一次,要产生 1ms 的时间就需要“滴”1000 次。“满溢出”的前提条件是“桶里”一共需
要装入 65536 滴才溢出,因此,在 12MHz 的晶振下要产生 1ms 的定时中断,TH0 与 TL0 的初值应该是 64536
(65536 减去 1000 等于 64536),而 64536 变成十六进制 0xfc17,再分解到高 8 位 TH0 为 0xfc,低 8 位 TL0
为 0x17。
刚才的例子是假如晶振在 12MHz 的情况下所计算出来的结果,而本教程所用的晶振是 11.0592MHz,根据
11.0592MHz 产生 1ms 的定时中断,TH0 与 TL0 应该取值多少?根据刚才的计算方式:
初值=[溢出值]-([0.001 秒]/([晶振周期的 12 个]*([1 秒]/[晶振频率])))
初值=65536-(0.001/(12*(1/11059200)))
初值=65536-922 (注:922 是 921.6 的四舍五入)
初值=64614
初值=0xfc66
初值 TH0=0xfc
初值 TL0=0x66
【85.4 中断的总开关 EA 与局部开关 ET0。】
EA:中断的总开关。宽度是 1 位的位变量。此开关如果取 0,就会强行屏蔽所有的中断,因此,只要用
到中断,此开关必须取 1。
ET0:专门针对定时器 0 中断的局部开关。宽度是 1 位的位变量。此开关如果取 0,则会屏蔽定时器 0
的中断,如果取 1 则允许定时器 0 中断。如果要定时器 0 能产生中断,那么总开关 EA 与 ET0 必须同时都打
开(都取 1),两者缺一不可。