单片机--第六章中断系统--例6-3学习
同6-2图,要求:使用定时器T0中断实现流水灯操作,流水频率为每0.5s更替一次(假设单片机外接11.0592MHZ的晶振)。
这个代码实现的是流水灯的效果,即将多个 LED 灯依次点亮并在一定时间内熄灭。下面是整个程序的实现过程:
在 main 函数中,首先设置了定时器 TMOD 的值为 0x01,表示使用定时器 0 并采用工作方式 1,即 16 位定时器。
然后初始化了定时器 0 的高位和低位,使其每 0.05 秒产生一个中断,并且开启了总中断和定时器 0 的中断。
while(1) 表示程序在这里无限循环,等待定时器产生中断。
当定时器产生中断后,会进入中断处理程序 T0_int()。在该中断处理程序中,首先 cnt 自增 1,当其等于10时,也就是经过了 10 次中断之后,也就是 0.05 * 10s = 0.5s 这段时间到了,就需要让流水灯更新,此时流水灯 led 向左移位一位,并将一个二进制的 1 加到最右边。继续判断 led 是否等于 0xFF,如果等于说明所有的 LED 灯都亮起来了,需要重新开始,此时将 led 设置成 0xFE,也就是最右边的灯灭了,其余灯亮起来了。
最后将更新后的流水灯 led 显示在 P0 引脚上,P0 是 8051 芯片的一个 I/O 引脚,通过程序控制其输出高低电平,就可以控制连接在该引脚上的 LED 灯的亮灭。重新设置 TH0 和 TL0 寄存器,使定时器继续计数,从而达到流水灯效果。
代码:
#include<reg51.h>
unsigned char cnt=0;//用于中断次数计数
unsigned char led=0xfe; //初始化流水灯,即1111 1110 只有一个灯是点亮的
int main(void){
TMOD=0x01; //16位定时方式,即方式1
TH0=(65536-46083)/256; //初始化T0的高8位
TL0=(65536-46083)%256; //初始化T0的低8位
EA=1;
ET0=1; //开中断
TR0=1; //启动T0工作
while(1); //等待中断
}
void T0_int(void) interrupt 1{
cnt++;
if(cnt==10) //0.5s时间到了
{
cnt=0;//次数清除统计
led=(led<<1)|1;//更新流水灯数据
if(led==0xff)
{
led=0xfe;
}
P0=led; //显示流水灯
}
TH0=(65536-46083)/256; //初始化T0的高8位
TL0=(65536-46083)%256; //初始化T0的低8位
}
Ai改进:
这个代码已经是一个很简单的流水灯程序,但是还可以进一步改进。以下是一些可能的改进点:
减少中断次数的判断:在这个程序中,每次中断都会增加 cnt 的值,并且在 cnt 达到 10 的时候才会更新流水灯。我们可以将 cnt 的范围缩小一些,比如设置 cnt 的最大值为 4,这样可以每 0.2s 更新一次流水灯,也就是 LED 灯闪动的频率更快。而且,在更新流水灯时,我们可以使用位运算来代替除法和取模运算,这样可以减少 CPU 的负担。
显示流水灯的方式:在这个程序中,我们每次都是将流水灯的值赋给 P0 引脚,然后通过 P0 引脚控制 LED 灯的亮灭。但是这种方法有一个问题:如果 P0 引脚上有其他设备连接,可能会影响其它设备的工作,而且如果需要控制多个 LED 灯组成的矩阵,这种方法就不可行了。因此,我们可以考虑采用移位寄存器来控制 LED 灯的亮灭,这样不仅能够实现流水灯效果,而且可以有效地控制 LED 灯的数量和排列方式。
优化定时器的配置:在这个程序中,我们使用的是方式 1,即 16 位定时器。但是,在实际应用中,我们可以根据需要选择不同的定时器方式来适应不同的应用场景。比如,如果我们只需要 8 位定时器,那么可以使用方式 0;如果需要同时控制多个定时器,那么可以使用方式 2;如果需要高精度计时器,那么可以使用方式 3。
下面是改进后的代码:
#include <reg51.h>
sbit LED = P2 ^ 0; // 用 P2.0 控制 LED
unsigned char led_group = 0x7f; // 定义一个 8 位的 LED 灯组
unsigned char shift_register = 0x01; // 初始化移位寄存器
void delay(unsigned int t)
{
unsigned int i, j;
for (i = 0; i < t; i++)
{
for (j = 0; j < 125; j++); // 延时函数,每次循环大约延时 1ms
}
}
void main()
{
TMOD = 0x01; // 设置定时器 0 的工作方式为 16 位定时器
TH0 = 0xB1; // 装载定时器初值,计算公式:65536 - (50000 / 12) = 44536,即 0xB1D0
TL0 = 0xD0;
ET0 = 1; // 打开定时器 0 中断
EA = 1; // 打开总中断
TR0 = 1; // 启动定时器 0
while (1)
{
shift_register <<= 1; // 使移位寄存器向左移动一位
if (shift_register == 0x00) // 如果移位寄存器的值变成了 0,则将其重新设为 0x01
{
shift_register = 0x01;
}
led_group = ~shift_register; // 根据移位寄存器的值,计算出 LED 灯组需要显示的值(取反)
delay(5); // 停留一段时间,防止 LED 灯过快闪烁,造成视觉疲劳
}
}
void timer0() interrupt 1
{
TH0 = 0xB1; // 重新设置定时器初值
TL0 = 0xD0;
LED = ~LED; // 每次定时器中断时将 LED 灯取反,达到闪烁的效果
}
改进后的程序采用移位寄存器控制 LED 灯的亮灭,而不是直接控制 I/O 引脚。程序还加入了一个延时函数,使 LED 灯的闪烁频率更慢,避免用户视觉疲劳。程序使用了定时器 0 产生中断,每次中断时将 LED 灯的状态取反,以达到闪烁效果。由于程序只需要控制一个 LED 灯,因此将其连接到了 P2.0 引脚上,而不是 P0 引脚上,这样可以避免影响其他设备的工作。
仿真:
仿真视频;
第六章例6-3