目录
(4)为什么计算机要进行调制与解调? 计算机通讯方式有哪些(优缺点)?通信数据校验方法?
一、简答题
(1)常用编程语言有哪些及其特点?
1.机械语言
核心特点:计算机唯一能直接识别的语言
缺点:由二进制代码组成,阅读和编写难度极大
2.汇编语言
核心特点:能利用单片机所有特性,可直接控制硬件;基于 CPU 指令系统和寻址方式,占用空间小、执行速度快
缺点:编写效率低,开发周期长
3.高级语言(C 语言)
核心特点:需配合单片机 C 编译器使用,代码效率高,语法简单易读,开发效率高
适用场景:单片机开发主流语言,兼顾性能与开发效率
(2)定时的方法有哪些?
定时方法按实现层级可分为软件定时、硬件定时、操作系统级定时三类,具体特点如下:
1.软件定时 / 计数
实现方式:通过执行一段循环程序实现时间延迟
优点:不需要外加硬件电路,成本低
缺点:定时时间有限(不宜过长);占用 CPU 资源,增加 CPU 负担,影响其他任务执行
2.硬件 / 数字电路定时
实现方式:利用硬件定时器 / 计数器,通过配置时钟源、设定计数初值实现精准定时
优点:定时 / 计数功能完全由硬件完成,定时精度高(可达微秒级),不占用 CPU 资源
缺点:使用灵活性差,修改定时时间需调整硬件电路参数
3.可编程定时 / 计数
实现方式:通过对系统时钟脉冲进行计数实现定时,可通过程序修改计数值
优点:使用灵活,无需改动硬件,通过软件配置即可改变定时时间
缺点:依赖操作系统环境,实时性略低于纯硬件定时
(3)中断系统有哪些应用、功能、概念、服务程序、中断源?
1. 核心概念
中断:CPU 暂停当前主程序,优先处理紧急事件(如硬件触发),处理完毕后返回原程序继续执行的机制
中断服务程序:中断触发后 CPU 执行的专用程序,代码需简洁高效,必须清除中断标志以避免重复触发
中断源:引起中断的具体来源,常见类型包括:
输入 / 输出设备(如键盘、串口)
实时时钟或计数信号
故障源(如电源异常、溢出错误)
2. 中断技术的应用场景
实现分时操作:使 CPU 与外设并行工作,提高系统利用率
实时处理:快速响应外部紧急事件(如传感器触发信号)
故障处理:及时处理系统异常(如过载、错误指令)
3. 中断系统的核心功能
实现中断及返回:确保 CPU 能正确暂停主程序并在中断处理后恢复执行
实现优先权排队:当多个中断同时触发时,按优先级顺序处理
实现中断嵌套:高优先级中断可打断低优先级中断的处理过程
(4)为什么计算机要进行调制与解调? 计算机通讯方式有哪些(优缺点)?通信数据校验方法?
1. 调制与解调的作用
计算机处理的是数字信号,而远距离传输通常依赖模拟信道(如电话线、射频信道)。调制是将数字信号转换为模拟信号以适配传输信道,解调是将接收的模拟信号还原为数字信号,从而实现远距离稳定传输。
2. 计算机通讯方式及优缺点
通讯方式
核心特点
优点
缺点
异步通讯
收发设备使用各自独立的时钟控制数据传输
无需严格同步时钟,硬件要求简单
每个字符需加起始位、停止位,帧间有间隔,传输效率较低
同步通讯
发送方时钟直接控制接收方时钟,实现完全同步
以特定位组合 “01111110” 作为帧标志,数据位长度灵活,传输效率高
硬件设备复杂,时钟同步要求严格
3. 通信数据校验方法
- 奇偶校验:发送数据时,在数据末尾添加 1 位校验位(1 或 0),使整个数据块中 1 的个数为奇数或偶数
- 代码和校验:发送方对数据块求和(或各字节 “异或” 运算),生成 1 字节校验字符附加到数据块末尾
- 循环冗余校验(CRC):通过数学运算建立有效信息与校验位的循环关系,校验精度高,抗干扰能力强
(5)仿真涉及的软件名称及作用
Protues仿真软件:支持单片机原理图设计、虚拟仿真与软硬件联调,助力学习开发,降低硬件成本与调试难度。
二、程序填空
(1)流水灯程序
#include<reg52.h> //52系列单片机头文件
unsigned int i,j; //生成无符号整型变量i,j
void main(){ //主函数
while(1){ //大循环
P1=0xfe; //点亮发光二极管D1
for(i=100;i>0;i--); //延时
for(j=110;j>0;j--);
P1=0xfd; //点亮发光二极管D2
for(i=100;i>0;i--); //延时
for(j=110;j>0;j--);
P1=0xfb; //点亮发光二极管D3
for(i=100;i>0;i--); //延时
for(j=110;j>0;j--);
P1=0xf7; //点亮发光二极管D4
for(i=100;i>0;i--); //延时
for(j=110;j>0;j--);
P1=0xef; //点亮发光二极管D5
for(i=100;i>0;i--); //延时
for(j=110;j>0;j--);
P1=0xdf; //点亮发光二极管D6
for(i=100;i>0;i--); //延时
for(j=110;j>0;j--);
P1=0xbf; //点亮发光二极管D7
for(i=100;i>0;i--); //延时
for(j=110;j>0;j--);
P1=0x7f; //点亮发光二极管D8
for(i=100;i>0;i--); //延时
for(j=110;j>0;j--);}
(2)流水灯程序
#include<reg52.h> //包含单片机寄存器的头文件
#include<intrins.h> //包含单片机寄存器的头文件
void delay(void){ //延时子程序
unsigned char i, j;
for(i=0;i<250;i++);
for(j=0;j<250;j++);}
void main(void){ //主程序
P1=Oxfe; //第一个灯亮
while(1){
P1=_crol_(P1,1); //将P1口的二进制位循环左移一位,再赋给P1
delay(); } //调用延时函数
}
三、程序设计——数码管编程
(1)共阴极段码必背:
| 字符 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| 段码 | 3FH | 06H | 5BH | 4FH | 66H | 6DH | 7DH | 07H | 7FH |
| 字符 | 9 | A | B | C | D | E | F | 灭 | — |
| 段码 | 6FH | 77H | 7CH | 39H | 5EH | 79H | 71H | 00H | 40H |
(2)数码管的段选和位选的作用
- 段选:控制数码管a-g、dp段的亮灭,决定显示的数字/字符(如段选输出0x3F显示数字0);
- 位选:选中要点亮的数码管,控制多数码管中哪一个显示内容。
(3)静态显示与动态显示的区别
静态显示与动态显示
类型 核心特点 优缺点 静态显示 每个数码管独立段选+位选,持续通电 显示稳定无闪烁,占用I/O口多 动态显示 分时选通各数码管,利用视觉暂留 占用I/O少,需定时刷新,易闪烁
(4)例题及代码
设计AT89C51单片机与一个4位效码相连的Protues仿真显示电路,并用C语言编程使数码管从左到右显示1~4。
#include <reg51.h> // 定义端口 sbit dula = P0; // 段选端口(P0) sbit wela1 = P2^0; // 第1位数码管位选 sbit wela2 = P2^1; // 第2位数码管位选 sbit wela3 = P2^2; // 第3位数码管位选 sbit wela4 = P2^3; // 第4位数码管位选 // 共阴极数码管段码表(0-9) unsigned char code seg_table[] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F}; // 延时函数(视觉暂留,约1ms) void delay(unsigned int t) { unsigned int i,j; for(i=t;i>0;i--) for(j=110;j>0;j--); } // 动态显示函数:依次显示1、2、3、4 void display() { // 第1位显示1 wela1 = 1; dula = seg_table[1]; delay(1); wela1 = 0; // 第2位显示2 wela2 = 1; dula = seg_table[2]; delay(1); wela2 = 0; // 第3位显示3 wela3 = 1; dula = seg_table[3]; delay(1); wela3 = 0; // 第4位显示4 wela4 = 1; dula = seg_table[4]; delay(1); wela4 = 0; } void main() { while(1) { display(); // 循环动态显示 } }
四、程序设计——外部中断编程

(1)中断源数量:
51单片机中断源数量 经典8051核心的单片机(如AT89C51)默认有5个基础中断源
1. 外部中断0(‘INT0’):对应引脚P3.2;
2. 外部中断1(’INT1‘):对应引脚P3.3;
3.定时器/计数器0(T0)溢出中断;
4. 定时器/计数器1(T1)溢出中断;
5. 串行口中断(UART):包含发送完成、接收完成中断。
(2)中断触发方式:
中断触发方式分为内部触发(由单片机内部事件触发)和外部触发(由外部引脚电平 / 边沿变化触发)两类,具体如下:
中断源 触发类型 具体触发方式 外部中断 0(‘INT0’) 外部触发 可通过 TCON 寄存器的 IT0 位配置:
IT0=0:低电平触发,IE0置1,申请中断;(电平触发)
IT0=1:下降沿触发,IE0置1,申请中断;(边沿触发)
IT0外部中断0的触发方式选择位,
IE0外部中断‘INT0’的中断请求标志位
外部中断 1(‘INT1’) 外部触发 可通过 TCON 寄存器的 IT1 位配置:
IT1=0:低电平触发,IE1置1,申请中断;(电平触发)
IT1=1:下降沿触发,IE1置1,申请中断;(边沿触发)
IT1外部中断1的触发方式选择位,
IE1外部中断‘INT1’的中断请求标志位
定时器 / 计数器 0(T0) 内部触发 定时器 / 计数器计数溢出(计数值从最大值(如 0xFF)溢出到 0),触发溢出中断(TF0 标志置 1); 定时器 / 计数器 1(T1) 内部触发 定时器 / 计数器计数溢出(TF1 标志置 1); 串行口中断(UART) 内部触发 串行口收发完成触发:
发送完成(TI 标志置 1)、
接收完成(RI 标志置 1),
TI/RI 共用该中断源,需手动清 0;
(3)中断优先级选择
上电时,中断优先级寄存器IP被清零,每个中断源都处于同一个优先级,这时如果其中几个中断同时产生中断请求,则CPU按照片内硬件优先级链路的顺序即自然优先级响应中断,硬件优先级有高到低的顺序如下表所示:
(4)例题及代码:
例题1:单片机外中断‘INT0’应用
关于单片机外中断‘INT0’应用的Proteus仿真电路如图所示,要求单片机主程
序控制P2口所接的8段共阳极数码管各段依次循环点亮,当外部中断‘INT0’输入出现从高到
低的负跳变时,数码管开始亮灭闪烁显示“8”,闪烁显示8次后,8段数码管的各段继续依
次循环点亮。试编写程序,实现上述功能。
#include<reg51.h> // 引入51单片机寄存器定义头文件,用于操作中断、I/O口等寄存器 #include <intrins.h> // 引入内置函数头文件,包含_crol_(循环左移)等操作函数 #define uint unsigned int // 宏定义:简化无符号整型变量声明(uint替代unsigned int) #define uchar unsigned char // 宏定义:简化无符号字符型变量声明(uchar替代unsigned char) uchar i, aa; // 全局变量:i用于中断服务函数的循环计数;aa用于主循环流水灯控制 void delay(uint z); // 延时函数声明:提前声明避免主函数调用时编译器报错 void main() // 主函数:程序入口,执行核心逻辑 { EA=1; // 置1:开启总中断(51单片机所有中断的总开关,必须开启才会响应中断) EX0=1; // 置1:开启外部中断0(对应P3.2引脚,允许该中断请求被响应) IT0=1; // 置1:设置外部中断0为下降沿触发(IT0=0为低电平触发,1为下降沿) aa =0xfe; // 初始化aa为0xfe(二进制11111110),用于P2口流水灯初始状态 P3=0xff; // P3口全部置高电平(外部中断0引脚P3.2默认上拉,避免误触发) while(1) // 死循环:主程序持续执行流水灯逻辑 { P2=aa; // 将aa的值输出到P2口,控制P2口外接设备(如LED/数码管)状态 delay(1000); // 调用延时函数,保持当前P2口状态1000个延时单位(约1ms) aa=_crol_(aa, 1); // 调用循环左移函数:将aa的二进制位左移1位(如0xfe→0xfd→0xfb…),实现流水灯 } } // 软件延时函数:通过双层for循环实现延时,参数z控制延时时长(z越大,延时越久) void delay(uint z) { uint x, y; // 局部变量:用于循环计数 // 外层循环z次,内层固定循环110次(12MHz晶振下,单次内层循环约10us,整体delay(1000)≈1ms) for(x=z; x>0; x--) for(y=110; y>0; y--); } // 外部中断0服务函数:interrupt 0表示该函数对应中断号0(外部中断0),中断触发时执行 void exter0()interrupt 0 { // 循环8次:实现数码管8次“亮-灭”闪烁 for(i=8; i>0; i--) { P2=0x00; // P2口全部置低电平(若接共阴极数码管,此段码并非数字8,注释存在偏差;共阴8的段码是0x7f) delay(500); // 延时500个单位(约0.5s),保持数码管点亮状态 P2=0x7f; // P2口输出0x7f(二进制01111111),尝试熄灭数码管(但段码逻辑仍有问题) delay(500); // 延时0.5s,保持数码管熄灭状态 } }
例题2:单片机中断优先级应用
单片机中断优先级应用的仿真电路如图所示,要求用单片机主程序控制
p1口流水灯循环显示;外部中断‘INT0’引脚出现负跳变时,P1口全部发光二极管亮5s,外部
断‘INT1’引脚出现负跳变时,P2口所接的共阳极数码管显示1,保持时间为5s。外部中新
‘INT1’为高优先级,外部中断‘INT0’为低优先级。试编写程序,实现上述功能。
#include<reg51.h> // 51单片机寄存器定义头文件 #include <intrins.h> // 包含循环移位等内置函数(如_crol_) #define uint unsigned int // 宏定义:将uint替换为unsigned int,简化代码 #define uchar unsigned char // 宏定义:将uchar替换为unsigned char uchar aa; // 全局变量:用于控制P1口输出的段选数据(初始0xfe,对应单个LED亮) uchar K1; // 全局标志位:标记外部中断0是否触发(0=触发,1=未触发) void delay(uint z); // 延时函数声明:避免函数调用时未定义报错 void main() // 主函数:程序入口 { EA=1; // 开启总中断(51单片机所有中断的总开关,EA=1才允许中断响应) EX0=1; // 开启外部中断0(INT0,对应P3.2引脚) EX1=1; // 开启外部中断1(INT1,对应P3.3引脚) IT0=1; // 设置外部中断0为下降沿触发(IT0=1:下降沿;IT0=0:低电平) IT1=1; // 设置外部中断1为下降沿触发 PX0=0; // 设置外部中断0为低优先级(PX0=0:低优先级;PX0=1:高优先级) PX1=1; // 设置外部中断1为高优先级(高优先级中断可打断低优先级) aa =0xfe; // 初始化aa为0xfe(二进制11111110),初始让P1.0引脚输出低电平 P2=0xff; // P2口全部置高(默认关闭P2口外接设备,如数码管位选) K1=1; // 初始化K1标志位为1(表示外部中断0未触发) P3=0xff; // P3口全部置高(外部中断引脚默认上拉,避免误触发) while(1) // 死循环:程序主逻辑持续运行 { P1=aa; // 将aa的值输出到P1口,控制P1口外接的LED/数码管等外设 delay(1000); // 调用延时函数,持续显示当前aa对应的状态 aa=_crol_(aa, 1); // 将aa循环左移1位(如0xfe→0xfd→0xfb…),实现P1口LED流水灯 } } // 延时函数:软件延时,参数z控制延时时长(z越大,延时越久) void delay(uint z) { uint x, y; // 双层for循环实现延时,x从z递减到0,内层y固定循环110次 for(x=z; x>0; x--) for(y=110; y>0; y--); } // 外部中断0服务函数:interrupt 0表示中断号0(对应INT0),优先级由PX0决定 void exter0()interrupt 0 { K1=0; // 置位K1为0,标记外部中断0已触发 P1=0xff; // P1口全部置高(先关闭所有LED/外设) P1=0x00; // P1口全部置低(点亮P1口所有LED) delay(5000); // 延时5000个单位,保持全亮状态 P1=0xff; // 恢复P1口为全高(熄灭所有LED) K1=1; // 恢复K1为1,标记外部中断0处理完成 } // 外部中断1服务函数:interrupt 2表示中断号2(对应INT1),优先级由PX1决定 void exter1()interrupt 2 { P1=0xff; // 先关闭P1口所有外设 P2=0xf9; // P2口输出0xf9(二进制11111001),控制P2口外接设备(如数码管显示数字1) delay(5000); // 延时保持该状态 P2=0xff; // 恢复P2口为全高(关闭P2口外设) if(K1==0) P1=0x00; // 若K1=0(外部中断0刚触发过),则将P1口置低(点亮所有LED) }
例题3:
电路如图所示,要求单片机主程序控制P0口数码管循环显示0-9;外部中断‘INT0’发生时,控制p2口数码管显示0-9,外部中断‘INT1’发生时,控制p1口数码管显示0~9,外部中断‘INT1’为高优先级,外部中断‘INT0’为低优先级,都采用边沿触发方式,数码管为共阴极数码管。试编写程序。
#include <reg51.h> #define uint unsigned int #define uchar unsigned char // 共阴极数码管段码表(0-9):段码对应a-g+dp,共阴极低电平点亮 uchar code seg_table[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; uchar num = 0; // 显示数字索引(0-9循环) bit flag_int0 = 0; // INT0中断触发标志(0=未触发,1=触发) bit flag_int1 = 0; // INT1中断触发标志(0=未触发,1=触发) // 软件延时函数(稳定数码管显示,晶振12MHz时,delay(1000)≈1ms) void delay(uint z) { uint x, y; for(x = z; x > 0; x--) for(y = 110; y > 0; y--); } // 外部中断0服务函数(INT0/P3.2,低优先级) void int0_service() interrupt 0 { flag_int0 = 1; // 置位INT0触发标志,主循环处理显示 } // 外部中断1服务函数(INT1/P3.3,高优先级) void int1_service() interrupt 2 { flag_int1 = 1; // 置位INT1触发标志,主循环处理显示 } void main() { // 中断初始化 EA = 1; // 开启总中断(核心,所有中断必须开总中断) EX0 = 1; // 使能外部中断0 EX1 = 1; // 使能外部中断1 IT0 = 1; // 设置INT0为下降沿触发(边沿触发) IT1 = 1; // 设置INT1为下降沿触发(边沿触发) PX0 = 0; // 设置INT0为低优先级 PX1 = 1; // 设置INT1为高优先级 while(1) { // 主循环:持续处理显示逻辑 if(flag_int1) { // 优先处理高优先级INT1触发 P1 = seg_table[num]; // P1口显示当前数字 delay(1000); num++; if(num > 9) num = 0; // 数字循环0-9 } else if(flag_int0) { // 处理INT0触发(INT1未触发时) P2 = seg_table[num]; // P2口显示当前数字 delay(1000); num++; if(num > 9) num = 0; } else { // 无中断时,主程序逻辑 P0 = seg_table[num]; // P0口显示当前数字 delay(1000); num++; if(num > 9) num = 0; } } }
五、程序设计——定时器编程
(1)寄存器
1.寄存器介绍
TMOD用于设置其工作方式;TCON用于控制其启动和中断申请。
工作方式寄存器:TMOD,确定工作方式和功能;
控制寄存器:TCON,控制T0、T1的启动和停止及设置溢出标志。
2.寄存器使用原理:
工作方式寄存器TMOD
控制寄存器TCON
【1】GATE门控位:
【2】C/T:定时器/计数器工作模式选择位
C/T=0: 定时器工作
C/T=1:计数器工作
【3】M1、M0:工作方式选择位:
注意:TMOD控制两个定时器T1,T2,所以有两个M0,M1。一对M0,M1控制一个定时器。
【4】TF1/0: 定时器T1/0溢出中断请求标志位
【5】TR1/0: 定时器T1/0运行控制位
【6】IT1/0: 外部中断1/0的触发方式选择位
【7】IE1/0: 外部中断‘INT1’的中断请求标志位
(2)定时器/计数器工作方式及定时器初值计算
模式 位数 核心结构 计数/定时范围: 重装方式 核心特点 & 适用场景 模式 0 13 位 TL0 低 5 位 + TH0高8 位 (1~8192)Tcy
手动 早期兼容 8048,冗余(不如模式 1 灵活),极少使用 模式 1 16 位 TLx8 位 + THx8 位 (1~65536)Tcy 手动 定时范围最大(11.0592MHz 下≈71ms),灵活通用 模式 2 8 位自动重装 TLx 计数,THx 存重装值 (1~256)Tcy 自动 精度最高(无软件重装误差),适合短定时 / 串口波特率发生器 模式 3 双 8 位(仅 T0) T0 拆为 TL0(独立定时器)+ TH0(借用 T1 的 TR1/TF1) (1~256)Tcy 手动 需两个独立 8 位定时器场景,T1 若设为模式 3 则停止工作 公式:
1.方式0:(13位)
2.方式1:(16位)
3.方式2:(8位)
4.方式3:(双8位仅T0)
(3)定时器计数器初值计算例题:
(4)例题及代码:
例题1:定时器设计秒表
利用单片机的定时器设计一个秒表,使它从0~59s计数,晶振频率f=12MHz,设计秒表的Proteus仿真电路,并编写程序。
解:
#include<reg51.h> // 引入51单片机寄存器定义头文件,用于操作定时器、中断、I/O口 #define uint unsigned int // 宏定义:简化无符号整型声明(uint替代unsigned int) #define uchar unsigned char// 宏定义:简化无符号字符型声明(uchar替代unsigned char) uchar temp, aa, shi, ge; // 全局变量: // temp:计时累计值(0-59循环,对应秒数);aa:定时器中断计数(累计20次=1秒) // shi:十位数字(temp/10);ge:个位数字(temp%10) // 共阴极数码管段码表(0-9):段码对应a-g+dp,低电平点亮,dp默认熄灭 uchar code table[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; void display(uchar shi, uchar ge); // 显示子程序声明(十位、个位数字) void delay(uint z); // 延时子程序声明 void init(); // 初始化子程序声明 void main() // 主函数:程序入口 { init(); // 调用初始化函数(定时器、中断、变量初始化) while(1) // 死循环:持续处理计时和显示逻辑 { if(aa == 20) // 定时器0中断累计20次(20×50ms=1秒) { aa = 0; // 重置中断计数,重新累计1秒 temp++; // 秒数+1 if(temp == 60) // 秒数到60,重置为0(0-59循环) { temp = 0; } } shi = temp / 10; // 计算十位数字(如temp=12→shi=1) ge = temp % 10; // 计算个位数字(如temp=12→ge=2) display(shi, ge); // 调用显示函数,刷新数码管显示 } } // 软件延时函数:参数z控制延时时长(12MHz晶振下,delay(5)≈5ms) void delay(uint z) { uint x,y; // 局部循环计数变量 for(x=z; x>0; x--) // 外层循环z次 for(y=110; y>0; y--); // 内层固定循环110次,单次约10us } // 数码管显示子程序:显示十位+个位数字(动态扫描,消影处理) void display(uchar shi, uchar ge) { P2 = 0xbf; // 送入十位数码管位选信号(0xbf=10111111,假设P2.6控制十位选通) P0 = table[shi]; // 送入十位数字的段选信号(如shi=1→table[1]=0x06) delay(5); // 短暂延时,确保段码稳定(消影) P2 = 0x7f; // 送入个位数码管位选信号(0x7f=01111111,假设P2.7控制个位选通) P0 = table[ge]; // 送入个位数字的段选信号(如ge=2→table[2]=0x5b) delay(5); // 主延时,保持显示状态 } // 初始化子程序:定时器0、中断、变量初始化 void init() { temp = 0; // 初始化秒数为0 TMOD = 0x01; // 设置定时器0为“定时器模式+工作方式1”(16位定时器,最大计数65536) // 定时器0初值设置:定时50ms(晶振12MHz,机器周期1us,50000us=50ms) TH0 = (65536 - 50000) / 256; // 高8位初值:(65536-50000)=15536 → 15536/256=60 TL0 = (65536 - 50000) % 256; // 低8位初值:15536%256=176 EA = 1; // 开启总中断(所有中断的总开关,必须置1) ET0 = 1; // 允许定时器0中断(开启定时器0的中断请求) TR0 = 1; // 启动定时器0(定时器开始计数) } // 定时器0中断服务函数:interrupt 1表示中断号1(定时器0中断) void timer0() interrupt 1 { // 重装定时器0初值(方式1无自动重装,需手动赋值,保证定时精度) TH0 = (65536 - 50000) / 256; TL0 = (65536 - 50000) % 256; aa++; // 中断计数+1(每50ms触发1次中断,aa=20时累计1秒) }
例题2:交通信号灯
利用单片机的定时器设计交通信号灯控制电路,用Proteus仿真软件验证。
#include <reg52.h> // 引入STC89C52寄存器定义头文件(兼容51,扩展了更多资源) #define uint unsigned int // 宏定义:简化无符号整型声明 #define uchar unsigned char// 宏定义:简化无符号字符型声明 // 引脚定义:A组=东西向指示灯,B组=南北向指示灯(共阴极/共阳极需匹配硬件,1/0对应亮灭) sbit RED_A = P0^0; // 东西向红灯 → P0.0引脚 sbit YELLOW_A = P0^1; // 东西向黄灯 → P0.1引脚 sbit GREEN_A = P0^2; // 东西向绿灯 → P0.2引脚 sbit RED_B = P0^3; // 南北向红灯 → P0.3引脚 sbit YELLOW_B = P0^4; // 南北向黄灯 → P0.4引脚 sbit GREEN_B = P0^5; // 南北向绿灯 → P0.5引脚 // 全局变量: uchar Count = 0; // 基础计数器(配合定时器,累计50ms中断次数) uchar Flash_Count = 0; // 闪烁计数器(统计黄灯闪烁次数) uchar Operation_Type = 1; // 交通灯工作模式(1-4循环) // 定时器0中断服务函数:interrupt 1 → 中断号1(定时器0中断),每50ms触发1次 void T0_INT() interrupt 1 { // 重装定时器0初值(16位方式1,无自动重装,需手动赋值) // 晶振12MHz,机器周期1us,定时50ms:初值=65536-50000=15536 TH0 = (65536 - 50000) / 256; // 高8位初值:15536/256=60 TL0 = (65536 - 50000) % 256; // 低8位初值:15536%256=176 // 根据工作模式执行不同逻辑(switch分支) switch(Operation_Type) { case 1: // 模式1:东西向绿灯亮、南北向红灯亮(持续5秒) RED_A = 0; YELLOW_A = 0; GREEN_A = 1; // 东西向:红灭、黄灭、绿亮 RED_B = 1; YELLOW_B = 0; GREEN_B = 0; // 南北向:红亮、黄灭、绿灭 if(++Count != 100) return; // 累计100次中断=100×50ms=5秒,未到则直接返回 Count = 0; // 5秒到,基础计数器清零 Operation_Type = 2; // 切换到模式2(东西向黄灯闪烁) break; // 退出当前分支 case 2: // 模式2:东西向绿灯灭、黄灯闪烁(闪烁5次,总计5秒) if(++Count != 8) return; // 累计8次中断=8×50ms=400ms(单次闪烁的亮/灭时长) Count = 0; // 400ms到,基础计数器清零 YELLOW_A = !YELLOW_A; // 东西向黄灯状态翻转(亮→灭/灭→亮) GREEN_A = 0; // 确保东西向绿灯熄灭 if(++Flash_Count != 10) return; // 闪烁10次翻转=5次完整闪烁(亮+灭=1次) Flash_Count = 0; // 闪烁次数清零 Operation_Type = 3; // 切换到模式3(南北向绿灯亮) break; // 退出当前分支 case 3: // 模式3:东西向红灯亮、南北向绿灯亮(持续5秒) RED_A = 1; YELLOW_A = 0; GREEN_A = 0; // 东西向:红亮、黄灭、绿灭 RED_B = 0; YELLOW_B = 0; GREEN_B = 1; // 南北向:红灭、黄灭、绿亮 if(++Count != 100) return; // 累计100次中断=5秒,未到则返回 Count = 0; // 5秒到,基础计数器清零 Operation_Type = 4; // 切换到模式4(南北向黄灯闪烁) break; // 退出当前分支 case 4: // 模式4:南北向绿灯灭、黄灯闪烁(闪烁5次,总计5秒) if(++Count != 8) return; // 累计8次中断=400ms(单次闪烁时长) Count = 0; // 400ms到,基础计数器清零 YELLOW_B = !YELLOW_B; // 南北向黄灯状态翻转 GREEN_B = 0; // 确保南北向绿灯熄灭 if(++Flash_Count != 10) return; // 10次翻转=5次完整闪烁 Flash_Count = 0; // 闪烁次数清零 Operation_Type = 1; // 切换回模式1,循环工作 break; // 退出当前分支 } } void main() // 主函数:程序入口,仅做初始化,逻辑由定时器中断驱动 { TMOD = 0x01; // 设置定时器0为“定时器模式+工作方式1”(16位定时器,最大计数65536) // 初始化定时器0初值(定时50ms) TH0 = (65536 - 50000) / 256; TL0 = (65536 - 50000) % 256; IE = 0x82; // 开启总中断(EA=1)+ 允许定时器0中断(ET0=1),0x82=10000010B TR0 = 1; // 启动定时器0(定时器开始计数,溢出触发中断) while(1); // 死循环:主程序无其他逻辑,所有交通灯控制由定时器中断完成 }

静态显示与动态显示

























375

被折叠的 条评论
为什么被折叠?



