51单片机学习笔记|最小系统、中断、定时器+数码管动态扫描实现0~9999计数器

一、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 中断响应过程(结合本实验)
  1. 定时器0计数溢出,硬件将TF0置1。
  2. CPU执行完当前指令后检测到TF0=1,且总中断EA=1、定时器0中断ET0=1均开启。
  3. CPU暂停主循环中的数码管扫描,自动跳转到中断向量表0x000B,进入Timer0Handler中断服务函数。
  4. 中断服务函数内重装初值、累加cnt,满20次后num++
  5. 函数执行完毕,CPU自动回到主循环断点处,继续执行Display()扫描数码管。
3.3 中断优先级与嵌套(了解)

51单片机默认有两个优先级:高优先级和低优先级。本实验只使用定时器0一个中断源,不存在抢占问题。若后续加入串口或外部中断,可通过IP寄存器设置优先级,高优先级中断可以打断低优先级中断服务函数。

4. 定时器操作流程(5步固定流程,必背)

  1. 设置定时器工作模式(本例使用16位定时器,模式1)
  2. 设置定时器初值,确定单次定时时间
  3. 开启定时器中断总开关:EA全局中断 + ET0定时器0中断使能
  4. 编写中断处理函数(中断里面放定时累加逻辑)
  5. 开启定时器,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重新计数。如果不手动重装TH0TL0,下一次定时时间会变成65536个机器周期,导致计数节奏错乱。因此必须在中断服务函数开头重新写入初值。

二、实验需求

  1. 编写中断代码、定时器代码
  2. 定义数字变量,初始值为0
  3. 定时器定时1s,数字自动+1
  4. 将数字实时显示在4位共阴极数码管上(S8050三极管驱动位选)

三、硬件说明

  1. P0口:段选,共阴极数码管a~dp段,串联100Ω限流电阻
  2. P1.0~P1.3:位选,经过1k电阻驱动S8050 NPN三极管
    • P1.x=1 → 三极管导通,COM接地,点亮当前数码管
    • P1.x=0 → 三极管截止,断开COM,熄灭数码管(消隐,消除鬼影)
  3. 晶振: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 ;
}

五、代码对应知识点解析

  1. 中断逻辑:定时器溢出触发中断,在中断里面做秒计数,不占用主循环。主循环只负责数码管动态扫描。
  2. 动态扫描+消隐:轮流点亮四位数码管,点亮完成后置低位选,关闭三极管,消除鬼影。
  3. 变量volatile:中断函数修改全局变量,不加volatile编译器会优化,导致计数异常。

六、常见故障排查

  1. 数码管不亮:核对sbit引脚,硬件三极管接P1口,不能写P2。
  2. 有鬼影:缺少ledx=0消隐语句。
  3. 计数不正常:定时器初值错误,或者没有加volatile。
  4. 画面闪烁:单段延时超过2ms,推荐固定1ms。

七、总结

本篇覆盖单片机基础概念、中断原理、定时器配置流程,结合S8050三极管驱动四位共阴极数码管,完成0~9999秒计数器,是51单片机中断+动态数码管经典综合实验。

相关推荐
平头哥AI1 小时前
Day 21 _ error 是个普通值_errors.New 与 fmt.Errorf 造出来,沿调用栈抛到 main 接住
后端·学习·golang·go
snow@li1 小时前
服务器运维: k3s 安装及配置的完整操作笔记
笔记
ECT-OS-JiuHuaShan1 小时前
哲学是迭代学,数学是拓扑学
开发语言·人工智能·学习·算法·机器学习·php·拓扑学
leo_messi941 小时前
Mysql学习(十二) -- SQL执行到底做了什么事?
sql·学习·mysql
影视飓风TIM1 小时前
C++异常与RAII智能指针笔记
开发语言·c++·笔记
敲代码的嘎仔1 小时前
从零实现视频续播 + 学习进度统计:前端心跳、条件更新、GROUP BY 统计全链路拆解
java·前端·数据库·学习·面试·职场和发展·音视频
xcLeigh2 小时前
AI 编程学习路线图:一份覆盖前端、后端、全栈的系统学习计划
前端·人工智能·学习
YHHLAI2 小时前
基于 Next.js 16 + Redis 的全栈笔记博客系统
javascript·redis·笔记
fanfan_hongyun3 小时前
UE5.1 学习记录-玩家控制器
学习·ue5