一、51单片机基础知识点
1. 最小系统
单片机最小系统是单片机能独立运行的最简电路,包含:电源电路、复位电路、晶振电路、单片机芯片本体。没有最小系统,单片机无法启动工作。
2. 单片机内部构成
- GPIO外设:通用IO口,用来输出高低电平,控制三极管、LED、数码管
- 定时器:内部硬件计时器,精准产生定时,不需要占用CPU一直循环延时
- UART串口:异步串行通信,用于和电脑、外设收发数据
- RAM:随机存储器,存放临时变量(程序运行时的数据,断电丢失)
- Flash:程序存储器,存放我们烧录的代码,断电数据保留
3. 中断处理流程(考试背诵)
中断事件产生 → 中断标志位置1 → 向CPU请求处理中断 → CPU根据中断向量表调用中断处理函数 → 处理完成后CPU将中断标志位清0
通俗理解:CPU正在做显示任务,定时器计时到了,触发中断,CPU暂停当前任务,跑去执行中断服务函数,执行完再回到原来代码继续运行。
3.1 中断标志位与中断向量表
- 中断标志位 :每个中断源都有对应的标志位,例如定时器0溢出时
TF0自动置1。标志位置1后,CPU在每条指令结束后查询是否收到中断请求。 - 中断向量表 :51单片机在程序存储器的固定地址存放各中断服务函数的入口地址。定时器0中断对应地址
0x000B,Keil C51中通过interrupt 1关键字自动把函数挂到该入口。 - 硬件自动清0 :定时器0的中断标志
TF0在CPU响应中断后由硬件自动清0,无需软件干预;串口等部分中断标志则需要软件手动清0。
3.2 中断响应过程(结合本实验)
- 定时器0计数溢出,硬件将
TF0置1。 - CPU执行完当前指令后检测到
TF0=1,且总中断EA=1、定时器0中断ET0=1均开启。 - CPU暂停主循环中的数码管扫描,自动跳转到中断向量表
0x000B,进入Timer0Handler中断服务函数。 - 中断服务函数内重装初值、累加
cnt,满20次后num++。 - 函数执行完毕,CPU自动回到主循环断点处,继续执行
Display()扫描数码管。
3.3 中断优先级与嵌套(了解)
51单片机默认有两个优先级:高优先级和低优先级。本实验只使用定时器0一个中断源,不存在抢占问题。若后续加入串口或外部中断,可通过IP寄存器设置优先级,高优先级中断可以打断低优先级中断服务函数。
4. 定时器操作流程(5步固定流程,必背)
- 设置定时器工作模式(本例使用16位定时器,模式1)
- 设置定时器初值,确定单次定时时间
- 开启定时器中断总开关:
EA全局中断 +ET0定时器0中断使能 - 编写中断处理函数(中断里面放定时累加逻辑)
- 开启定时器,
TR0=1启动定时器开始计数
4.1 定时器工作模式详解
- 模式0:13位定时器,兼容老型号,实际开发很少使用。
- 模式1:16位定时器,计数范围0~65535,最常用。本实验采用模式1。
- 模式2:8位自动重装定时器,初值自动恢复,适合需要固定周期且不想在中断里手动重装的场景。
- 模式3:定时器0拆分为两个独立8位定时器,定时器1此时停止工作,特殊场景才用。
4.2 定时器初值计算(重点)
本实验晶振为11.0592MHz,机器周期为12个时钟周期,因此定时器每计数一次耗时:
c
机器周期 = 12 / 11.0592MHz ≈ 1.085us
要定时50ms,需要计数次数为:
c
计数次数 = 50ms / 1.085us ≈ 46080
16位定时器最大计数65536,因此初值为:
c
初值 = 65536 - 46080 = 19456 = 0x4C00
TH0 = 0x4C; //高8位
TL0 = 0x00; //低8位
单次定时50ms,中断20次正好1s,实现秒计数。
4.3 定时器相关寄存器
- TMOD :工作模式寄存器,高4位控制定时器1,低4位控制定时器0。本实验
TMOD |= 0x01把定时器0设为模式1。 - TH0/TL0:定时器0初值寄存器,高8位和低8位。
- TCON :控制寄存器,其中
TR0是定时器0运行控制位,TF0是溢出标志位。 - IE :中断使能寄存器,
EA是总开关,ET0是定时器0中断使能位。
4.4 为什么必须手动重装初值
模式1是16位非自动重装模式,定时器溢出后初值不会自动恢复,而是从0重新计数。如果不手动重装TH0和TL0,下一次定时时间会变成65536个机器周期,导致计数节奏错乱。因此必须在中断服务函数开头重新写入初值。
二、实验需求
- 编写中断代码、定时器代码
- 定义数字变量,初始值为0
- 定时器定时1s,数字自动+1
- 将数字实时显示在4位共阴极数码管上(S8050三极管驱动位选)
三、硬件说明
- P0口:段选,共阴极数码管a~dp段,串联100Ω限流电阻
- P1.0~P1.3:位选,经过1k电阻驱动S8050 NPN三极管
P1.x=1→ 三极管导通,COM接地,点亮当前数码管P1.x=0→ 三极管截止,断开COM,熄灭数码管(消隐,消除鬼影)
- 晶振:11.0592MHz
四、完整Keil C51代码
c
#include <reg52.h>
#include <intrins.h>
//共阴极数码管段码 0~9,无小数点
unsigned char code seg[]= {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
//volatile:中断修改全局变量,禁止编译器优化
volatile unsigned char cnt = 0;
volatile unsigned int num = 0;
//位选引脚定义,P1.0~P1.3接S8050三极管
sbit led1 = P1^0; //个位
sbit led2 = P1^1; //十位
sbit led3 = P1^2; //百位
sbit led4 = P1^3; //千位
//STC-ISP生成 11.0592MHz,1ms软件延时,用于数码管动态扫描
void Delay1ms(void)
{
unsigned char data i, j;
_nop_();
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
//定时器0中断服务函数 interrupt 1
void Timer0Handler(void) interrupt 1
{
TH0 = 0x4C; //16位模式,必须手动重装初值
TL0 = 0x00;
cnt++;
if(cnt == 20) //单次50ms,20次=1s
{
cnt = 0;
num++;
if(num >= 10000)
{
num = 0; //计数到9999,归零循环
}
}
}
//数码管动态扫描函数
void Display(void)
{
unsigned char ge,shi,bai,qian;
qian = num /1000; //千位
bai = num /100 % 10; //百位
shi = num /10 %10; //十位
ge = num % 10; //个位
//个位
P0 = seg[ge];
led1 = 1;
Delay1ms();
led1 = 0; //消隐,防止鬼影
//十位
P0 = seg[shi];
led2 = 1;
Delay1ms();
led2 = 0;
//百位
P0 = seg[bai];
led3 = 1;
Delay1ms();
led3 = 0;
//千位
P0 = seg[qian];
led4 = 1;
Delay1ms();
led4 = 0;
}
void main(void)
{
//1.设置定时器工作模式:模式1(16位定时器)
TMOD &= ~(0x03);
TMOD |= (0x01);
//2.设置定时器初值,定时50ms
TH0 = 0x4C;
TL0 = 0x00;
//3.开启中断开关
ET0 = 1; //定时器0中断使能
EA = 1; //开启总中断
//5.开启定时器
TR0 = 1;
while(1)
{
Display(); //主循环持续扫描数码管
}
return ;
}
五、代码对应知识点解析
- 中断逻辑:定时器溢出触发中断,在中断里面做秒计数,不占用主循环。主循环只负责数码管动态扫描。
- 动态扫描+消隐:轮流点亮四位数码管,点亮完成后置低位选,关闭三极管,消除鬼影。
- 变量
volatile:中断函数修改全局变量,不加volatile编译器会优化,导致计数异常。
六、常见故障排查
- 数码管不亮:核对sbit引脚,硬件三极管接P1口,不能写P2。
- 有鬼影:缺少
ledx=0消隐语句。 - 计数不正常:定时器初值错误,或者没有加volatile。
- 画面闪烁:单段延时超过2ms,推荐固定1ms。
七、总结
本篇覆盖单片机基础概念、中断原理、定时器配置流程,结合S8050三极管驱动四位共阴极数码管,完成0~9999秒计数器,是51单片机中断+动态数码管经典综合实验。