
模块一:定时器
如果是零基础的话可以先看视频

- AT89C51 核心参数
-
定时器数量 :2 个通用定时器(T0、T1),均支持「定时器模式」(计时)和「计数器模式」(计数外部脉冲),交通灯 / 数码管场景用定时器模式。
-
常用工作模式:模式 1(16 位定时器 / 计数器),最灵活、最常用。
-
晶振与机器周期:AT89C51 默认机器周期 = 12 / 晶振频率(例:11.0592MHz 晶振 → 机器周期≈1.085μs)。
-
定时原理:定时器从「预装初值」开始加 1,直到溢出(0xFFFF→0x0000),触发中断或标志位,实现精准定时。
以50ms为一个计数周期
void Timer0_Init(void)
{
TMOD |= 0x01;
TH0 = (65535-50000)/256; //50ms
TL0 = (65535-50000)%256;
EA = 1;
ET0 = 1;
TR0 = 1;
}
逐行拆解代码
TMOD |= 0x01;------ 配置定时器 0 为 16 位模式(模式 1)
TMOD是「定时器 / 计数器模式控制寄存器」,字节地址 0x89,低 4 位控制定时器 0(T0),高 4 位控制定时器 1(T1),位结构如下
TMOD 位 D7 D6 D5 D4 D3 D2 D1 D0 含义 GATE C/T M1 M0 GATE C/T M1 M0 归属 定时器 1(T1) 定时器 0(T0)
M1、M0:定时器模式选择位,决定 T0 的工作模式
M1 M0 模式 描述 0 0 0 13 位定时器 / 计数器 0 1 1 16 位定时器 / 计数器(最常用) 1 0 2 8 位自动重装模式 1 1 3 双 8 位定时器 / 计数器(仅 T0)
TMOD |= 0x01的含义:TMOD 初始值默认是 0x00(全 0),|= 0x01等价于TMOD = TMOD | 0x01,即把 T0 的 M0 位设为 1、M1 位保持 0 → T0 工作在模式 1(16 位定时器) 。(如果直接写TMOD = 0x01也可以,|=是更安全的写法,避免覆盖 T1 的配置)。
-
TH0 = (65535-50000)/256;------ 定时器 0 高 8 位初值 -
TL0 = (65535-50000)%256;------ 定时器 0 低 8 位初值
这两行是定时初值的拆分 ,核心逻辑是:定时器从「初值」开始加 1,直到加到 65535(0xFFFF)后溢出,触发中断。因此:初值 = 65536 - 计数次数(注:有的教材用 65535,本质是计数次数差 1,误差可忽略)
第一步:计算需要的初值
要定时 50ms(50000μs),需要计数 50000 次,因此:
-
正确初值 = 65536 - 50000 = 15536(十进制);
-
你代码中用了
65535-50000=15535,和 15536 仅差 1,对应定时误差 1μs(50ms vs 49999μs),新手无需纠结,重点学拆分方法。
第二步:把 16 位初值拆分为高 8 位(TH0)和低 8 位(TL0)
51 单片机的 16 位定时器 0 由两个 8 位寄存器组成:
-
TH0:Timer0 High(定时器 0 高 8 位);
-
TL0:Timer0 Low(定时器 0 低 8 位);16 位数值 = TH0 × 256 + TL0(因为 2^8=256,高 8 位每 1 位对应低 8 位的 256)。
因此拆分规则:
-
高 8 位(TH0)= 初值 ÷ 256(取整数部分);
-
低 8 位(TL0)= 初值 % 256(取余数部分)。
实际计算(以正确初值 15536 为例):
-
TH0 = 15536 ÷ 256 = 60(十进制)= 0x3C(十六进制);
-
TL0 = 15536 % 256 = 176(十进制)= 0xB0(十六进制)。
你代码中用 65535-50000=15535 的计算:
-
TH0 = 15535 ÷ 256 = 59(0x3B);
-
TL0 = 15535 % 256 = 255(0xFF);(两种初值的定时误差仅 1μs,对 50ms 来说几乎无影响)。
EA = 1;------ 开启总中断允许
EA 是「总中断允许位」(寄存器 IE 的 bit7),51 单片机的所有中断都需要先开总中断,否则即使开启外设中断,也无法响应。
-
EA=1:开启总中断;
-
EA=0:关闭所有中断(默认)。
ET0 = 1;------ 开启定时器 0 中断允许
ET0 是「定时器 0 中断允许位」(寄存器 IE 的 bit1),专门控制 T0 的溢出中断:
-
ET0=1:允许 T0 溢出后触发中断;
-
ET0=0:禁止 T0 中断(默认)。
TR0 = 1;------ 启动定时器 0 计数
TR0 是「定时器 0 运行控制位」(寄存器 TCON 的 bit4):
-
TR0=1:启动 T0,从初值开始加 1 计数;
-
TR0=0:停止 T0 计数(默认)。
三、补充:为什么代码里用 65535 而不是 65536?
这是新手最容易困惑的点,本质是「计数次数的理解差异」:
-
用 65536:定时器从初值加到 65535(溢出),计数次数 = 65536 - 初值(比如初值 15536,计数 50000 次);
-
用 65535:计数次数 = 65535 - 初值 + 1(比如初值 15535,计数 = 65535-15535+1=50001 次);
模块二 :数码管

用一个端口控制4个数码管
其原理和上一期讲的矩阵按键差不多
proteus仿真51单片机通过矩阵按键和数码管制作简单计算器-CSDN博客
https://blog.csdn.net/a2026_2_14/article/details/159322193
数码管的逻辑是:
输出段码 → 使能位选 → 延时 → 关闭位选 → 切换下一位
矩阵按键的逻辑完全对应:
拉低当前行 → 读列电平 → 拉高当前行 → 切换下一行
模块3:红绿灯逻辑
这是一个 东西 ↔ 南北 双向交替通行 的红绿灯:
- 总倒计时:15 秒
- 规则:前 12 秒亮绿灯,最后 3 秒闪黄灯
- 支持:自动循环切换 + 按键强制切换模式
- 同时:4 个数码管实时显示倒计时
一、两种固定工作模式
你的程序只有 2 种模式,来回切换:
模式 0(g_seg_mode = 0)
东西方向放行,南北方向禁行
- 倒计时 15 → 4 (共 12 秒)
- 东西:绿灯亮
- 南北:红灯亮
- 倒计时 3 → 1 (最后 3 秒)
- 东西:黄灯闪烁
- 南北:红灯亮
模式 1(g_seg_mode = 1)
南北方向放行,东西方向禁行
- 倒计时 15 → 4 (共 12 秒)
- 南北:绿灯亮
- 东西:红灯亮
- 倒计时 3 → 1 (最后 3 秒)
- 南北:黄灯闪烁
- 东西:红灯亮
二、自动切换逻辑(完全不用人管)
- 定时器每 50ms 中断一次
- 累计 20 次 = 1 秒 ,
g_timer_red_cnt减 1 - 倒计时从 15 → 0
- 归 0 后立刻:
- 重置倒计时为 15
- 模式 0 ↔ 1 自动翻转→ 实现:东西绿 → 南北绿 → 东西绿...... 无限循环
三、黄灯闪烁逻辑(最后 3 秒)
只要倒计时 ≤3:
- 每 500ms 黄灯引脚翻转一次(亮→灭→亮→灭)
- 对应方向黄灯开始闪烁
- 另外一方向一直保持红灯
四、按键强制切换逻辑(外部中断)
你有两个按键,分别控制:
-
INT0 中断(按键 0)
- 强制切到 模式 0(东西绿、南北红)
- 倒计时重置为 15 秒
-
INT1 中断(按键 1)
- 强制切到 模式 1(南北绿、东西红)
- 倒计时重置为 15 秒
五、数码管显示逻辑
- 东西数码管:显示东西方向剩余时间
- 南北数码管:显示南北方向剩余时间
- 绿灯阶段:显示 倒计时 - 3(因为要扣掉后面 3 秒黄灯)
- 黄灯阶段:直接显示 当前倒计时(3、2、1)
模块四 :代码实现
cpp
// 头文件保护宏:防止该头文件被重复包含,避免重复定义错误
#ifndef _HEADFILE_H__
#define _HEADFILE_H__
// 包含51单片机寄存器定义头文件,提供P0/P1/TMOD等硬件寄存器的定义
#include <REGX51.H>
// 包含自定义功能函数头文件(如延时、数据转换等通用功能)
#include "fun.h"
// 包含事件处理头文件(如红绿灯状态切换、按键事件处理等逻辑)
#include "handler.h"
// 包含按键相关头文件(如按键扫描、按键状态判断等)
#include "key.h"
// 定时器初始化初值宏定义:大概率是定时器0/1的重装初值(对应15)
#define TIMER_INIT 15
// 黄灯计时3秒的计数阈值宏:结合定时器中断(如1秒进1次中断),累计3次即为3秒
#define YELLOW_TIMER_3S 3
// 状态宏:表示"开启/导通"(如LED亮、继电器吸合)
#define ON 1
// 状态宏:表示"关闭/断开"(如LED灭、继电器断开)
#define OFF 0
// 数码管位置宏:东西方向数码管的个位(对应硬件引脚/位选端编号1)
#define EW_SEG_UNITS 1
// 数码管位置宏:东西方向数码管的十位(对应硬件引脚/位选端编号0)
#define EW_SEG_TENS 0
// 数码管位置宏:南北方向数码管的个位(对应硬件引脚/位选端编号3)
#define NS_SEG_UNITS 3
// 数码管位置宏:南北方向数码管的十位(对应硬件引脚/位选端编号2)
#define NS_SEG_TENS 2
// 外部变量声明:红灯计时计数器(无符号整型),定义在其他.c文件(如main.c)
extern unsigned int g_timer_red_cnt;
// 外部变量声明:数码管显示模式(无符号字符型),控制数码管显示逻辑(如倒计时/固定数值)
extern unsigned char g_seg_mode;
// 外部变量声明:黄灯状态标志位(无符号字符型),标记黄灯是否需要点亮/熄灭
extern unsigned char g_yellow_flag;
// 函数声明:定时器0初始化函数(配置定时器模式、初值、开启中断等)
void Timer0_Init(void);
// 函数声明:数码管显示函数(参数1=要显示的数字,参数2=数码管位置)
void Show_Seg(unsigned char num,unsigned char loaction);
// 函数声明:数码管显示逻辑总函数(整合显示数字、切换位置等逻辑)
void Seg_Show(void);
#endif // 头文件保护宏结束(与开头#ifndef配对)
// 头文件保护宏:防止key.h被重复包含,避免函数/宏重复定义错误
#ifndef _KEY_H__
#define _KEY_H__
// 函数声明:外部中断初始化函数(Exti = External Interrupt,针对按键的外部中断配置)
// 功能:配置51单片机的外部中断(如INT0/INT1),用于按键的中断触发(代替轮询扫描,降低CPU占用)
void Exti_Init(void);
#endif // 头文件保护宏结束(与开头#ifndef _KEY_H__配对)
// -------------------------- fun.h 头文件保护区域 --------------------------
// 头文件保护宏:检查_fun_h__是否未定义,防止fun.h被重复包含(避免重复定义错误)
#ifndef _FUN_H__
// 定义_fun_h__宏,标记fun.h已被包含,后续重复包含时会被#ifndef拦截
#define _FUN_H__
// 此处预留空行:用于存放fun.h的核心内容(如通用功能函数声明、通用宏定义等)
// 示例可添加:延时函数声明、数据转换宏、硬件初始化辅助宏等
// 头文件保护宏结束:与开头#ifndef _FUN_H__配对,闭合fun.h的保护区域
#endif
// -------------------------- handler.h 头文件保护区域 --------------------------
// 头文件保护宏:检查_handler_h__是否未定义,防止handler.h被重复包含
#ifndef _HANDLER_H__
// 定义_handler_h__宏,标记handler.h已被包含
#define _HANDLER_H__
// 此处预留空行:用于存放handler.h的核心内容(如业务逻辑处理函数声明、状态机宏等)
// 示例可添加:红绿灯状态处理函数、按键事件响应函数、数码管显示逻辑声明等
// 头文件保护宏结束:与开头#ifndef _HANDLER_H__配对,闭合handler.h的保护区域
#endif
// 包含项目通用头文件(整合了寄存器、宏定义、函数声明等)
#include "headfile.h"
// 主函数:51单片机程序的入口,程序从这里开始执行
int main()
{
// 调用定时器0初始化函数:配置定时器0的工作模式、初值、开启定时器/中断
// 用途:大概率用于红绿灯倒计时(如1秒触发1次中断,更新倒计时数值)
Timer0_Init();
// 调用外部中断初始化函数:配置按键对应的外部中断(如INT0/INT1)
// 用途:按键采用中断触发方式(代替轮询),按下按键时触发中断处理(如切换红绿灯模式)
Exti_Init() ;
// 死循环:单片机主程序的核心循环,程序会一直执行循环内的代码,不会退出
while(1)
{
// 调用数码管显示总函数:刷新数码管显示内容(如红绿灯倒计时数值)
// 用途:持续更新东西/南北方向的数码管显示,保证倒计时数值实时可见
Seg_Show();
}
}
// 包含项目通用头文件(整合宏定义、函数声明、寄存器定义)
#include "headfile.h"
// 共阳数码管段码表:索引0~9对应数字0~9的段码(0xc0=0,0xf9=1,0xa4=2...0x90=9)
// 共阳数码管特性:段脚低电平点亮,高电平熄灭
unsigned char seg_table[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
// 定义红绿灯引脚(P0口):东西方向红灯引脚 P0^0
sbit g_eastwest_red_pin = P0^0;
// 东西方向黄灯引脚 P0^1
sbit g_eastwest_yellow_pin = P0^1;
// 东西方向绿灯引脚 P0^2
sbit g_eastwest_green_pin = P0^2;
// 南北方向红灯引脚 P0^3
sbit g_northsouth_red_pin = P0^3;
// 南北方向黄灯引脚 P0^4
sbit g_northsouth_yellow_pin = P0^4;
// 南北方向绿灯引脚 P0^5
sbit g_northsouth_green_pin = P0^5;
// 全局变量:红灯倒计时计数器,初始值为TIMER_INIT(15),用于控制红绿灯时长
unsigned int g_timer_red_cnt = TIMER_INIT;
// 毫秒级延时函数(适配12MHz晶振)
// 参数nms:要延时的毫秒数
void Delaynms(unsigned char nms) //@12.000MHz
{
// 定义局部变量(data修饰:放在51单片机内部RAM,访问更快)
unsigned char data i, j;
// 外层循环:实现nms毫秒的总延时(每次循环约1ms)
while(nms--)
{
// 内层循环初始化:12MHz晶振下,该数值组合对应约1ms延时
i = 2;
j = 239;
// 嵌套循环:空指令消耗CPU时钟,实现精准延时
do
{
while (--j); // j自减到0,消耗时钟
} while (--i); // i自减到0,完成一次内层循环
}
}
// 红绿灯状态设置函数:东西绿灯亮,南北红灯亮
void Set_Traffic_Light_EWGreen_NSRed(void)
{
// 东西红灯灭
g_eastwest_red_pin = OFF;
// 东西黄灯灭
g_eastwest_yellow_pin = OFF;
// 东西绿灯亮
g_eastwest_green_pin = ON;
// 南北红灯亮
g_northsouth_red_pin = ON;
// 南北黄灯灭
g_northsouth_yellow_pin = OFF;
// 南北绿灯灭
g_northsouth_green_pin = OFF;
}
// 红绿灯状态设置函数:东西黄灯亮(闪烁),南北红灯亮
void Set_Traffic_Light_EWYellow_NSRed(void)
{
// 东西红灯灭
g_eastwest_red_pin = OFF;
// 东西黄灯状态由g_yellow_flag控制(0灭/1亮,实现闪烁)
g_eastwest_yellow_pin = g_yellow_flag;
// 东西绿灯灭
g_eastwest_green_pin = OFF;
// 南北红灯亮
g_northsouth_red_pin = ON;
// 南北黄灯灭
g_northsouth_yellow_pin = OFF;
// 南北绿灯灭
g_northsouth_green_pin = OFF;
}
// 红绿灯状态设置函数:东西红灯亮,南北绿灯亮
void Set_Traffic_Light_EWRed_NSGreen(void)
{
// 东西红灯亮
g_eastwest_red_pin = ON;
// 东西黄灯灭
g_eastwest_yellow_pin = OFF;
// 东西绿灯灭
g_eastwest_green_pin = OFF;
// 南北红灯灭
g_northsouth_red_pin = OFF;
// 南北黄灯灭
g_northsouth_yellow_pin = OFF;
// 南北绿灯亮
g_northsouth_green_pin = ON;
}
// 红绿灯状态设置函数:东西红灯亮,南北黄灯亮(闪烁)
void Set_Traffic_Light_EWRed_NSYellow(void)
{
// 东西红灯亮
g_eastwest_red_pin = ON;
// 东西黄灯灭
g_eastwest_yellow_pin = OFF;
// 东西绿灯灭
g_eastwest_green_pin = OFF;
// 南北红灯灭
g_northsouth_red_pin = OFF;
// 南北黄灯状态由g_yellow_flag控制(实现闪烁)
g_northsouth_yellow_pin = g_yellow_flag;
// 南北绿灯灭
g_northsouth_green_pin = OFF;
}
// 数码管显示总函数:根据当前模式控制红绿灯状态+刷新数码管显示
void Seg_Show()
{
// 根据显示模式g_seg_mode切换逻辑(0=东西绿灯/南北红灯;1=东西红灯/南北绿灯)
switch(g_seg_mode)
{
// 模式0:东西绿灯、南北红灯(倒计时>3秒)→ 东西黄灯、南北红灯(倒计时≤3秒)
case 0:
// 倒计时>3秒:处于绿灯阶段
if(g_timer_red_cnt>YELLOW_TIMER_3S)
{
// 设置红绿灯:东西绿灯亮,南北红灯亮
Set_Traffic_Light_EWGreen_NSRed();
// 显示东西方向倒计时十位:(总时长-3)/10(绿灯时长=总时长-3秒黄灯)
Show_Seg(seg_table[(g_timer_red_cnt-3)/10],EW_SEG_TENS);
// 显示东西方向倒计时个位:(总时长-3)%10
Show_Seg(seg_table[(g_timer_red_cnt-3)%10],EW_SEG_UNITS);
// 显示南北方向倒计时十位:总时长/10(南北红灯时长=总时长)
Show_Seg(seg_table[g_timer_red_cnt/10],NS_SEG_TENS);
// 显示南北方向倒计时个位:总时长%10
Show_Seg(seg_table[g_timer_red_cnt%10],NS_SEG_UNITS);
}
// 倒计时≤3秒:处于黄灯阶段
if(g_timer_red_cnt<=YELLOW_TIMER_3S)
{
// 设置红绿灯:东西黄灯闪烁,南北红灯亮
Set_Traffic_Light_EWYellow_NSRed();
// 显示东西方向倒计时十位:当前时长/10(黄灯3秒倒计时)
Show_Seg(seg_table[g_timer_red_cnt/10],EW_SEG_TENS);
// 显示东西方向倒计时个位:当前时长%10
Show_Seg(seg_table[g_timer_red_cnt%10],EW_SEG_UNITS);
// 显示南北方向倒计时十位:当前时长/10
Show_Seg(seg_table[g_timer_red_cnt/10],NS_SEG_TENS);
// 显示南北方向倒计时个位:当前时长%10
Show_Seg(seg_table[g_timer_red_cnt%10],NS_SEG_UNITS);
}
// 退出模式0
break;
// 模式1:东西红灯亮,南北绿灯亮(倒计时>3秒)→ 东西红灯亮,南北黄灯闪(倒计时≤3秒)
case 1://东西红灯,南北绿灯
// 倒计时>3秒:南北绿灯阶段
if(g_timer_red_cnt>YELLOW_TIMER_3S)
{
// 设置红绿灯:东西红灯亮,南北绿灯亮
Set_Traffic_Light_EWRed_NSGreen();
// 显示东西方向倒计时十位:总时长/10(东西红灯时长=总时长)
Show_Seg(seg_table[g_timer_red_cnt/10],EW_SEG_TENS);
// 显示东西方向倒计时个位:总时长%10
Show_Seg(seg_table[g_timer_red_cnt%10],EW_SEG_UNITS);
// 显示南北方向倒计时十位:(总时长-3)/10(南北绿灯时长=总时长-3秒黄灯)
Show_Seg(seg_table[(g_timer_red_cnt-3)/10],NS_SEG_TENS);
// 显示南北方向倒计时个位:(总时长-3)%10
Show_Seg(seg_table[(g_timer_red_cnt-3)%10],NS_SEG_UNITS);
}
// 倒计时≤3秒:南北黄灯阶段
if(g_timer_red_cnt<=YELLOW_TIMER_3S)
{
// 设置红绿灯:东西红灯亮,南北黄灯闪烁
Set_Traffic_Light_EWRed_NSYellow();
// 显示东西方向倒计时十位:当前时长/10
Show_Seg(seg_table[(g_timer_red_cnt)/10],EW_SEG_TENS);
// 显示东西方向倒计时个位:当前时长%10
Show_Seg(seg_table[(g_timer_red_cnt)%10],EW_SEG_UNITS);
// 显示南北方向倒计时十位:当前时长/10
Show_Seg(seg_table[g_timer_red_cnt/10],NS_SEG_TENS);
// 显示南北方向倒计时个位:当前时长%10
Show_Seg(seg_table[g_timer_red_cnt%10],NS_SEG_UNITS);
}
// 退出模式1
break;
// 默认情况:无操作
default:
break;
}
}
// 数码管动态显示函数:控制单个数码管显示指定数字
// 参数1 num:要显示数字的段码(来自seg_table)
// 参数2 location:数码管位置(EW_SEG_TENS/EW_SEG_UNITS等)
void Show_Seg(unsigned char num,unsigned char location)
{
// P1口置高:关闭所有数码管位选(防止重影)
P1 = 0xff;
// P2口清零:准备输出段码
P2 = 0x00;
// P2口输出段码:控制数码管显示的数字
P2 = num;
// 延时2ms:保持段码稳定,避免闪烁
Delaynms(2);
// P1口输出位选:点亮指定位置的数码管(~取反:0x01<<location → 对应位低电平点亮)
P1 = ~(0x01<<location);
// 延时10ms:保持数码管点亮状态,实现视觉暂留(动态扫描核心)
Delaynms(10);
}
// 外部中断初始化函数:配置INT0/INT1中断(用于按键触发)
#include "headfile.h"
void Exti_Init(void)
{
// 配置TCON寄存器:INT0触发方式为低电平/下降沿(0x01=IT0=0,低电平触发)
TCON |= 0x01;
// 配置TCON寄存器:INT1触发方式为低电平/下降沿(0x04=IT1=0,低电平触发)
TCON |= 0x04;
// 开启总中断(EA=1:51单片机所有中断的总开关)
EA = 1;
// 开启外部中断0(EX0=1:允许INT0中断)
EX0 = 1;
// 开启外部中断1(EX1=1:允许INT1中断)
EX1 = 1;
}
// 外部中断0服务函数(interrupt 0:INT0的中断号)
// 功能:按键触发INT0,切换到模式0(东西绿灯/南北红灯)
void Int0_ISR(void) interrupt 0
{
// 设置显示模式为0
g_seg_mode = 0;
// 重置倒计时计数器为初始值(15)
g_timer_red_cnt = TIMER_INIT;
}
// 外部中断1服务函数(interrupt 2:INT1的中断号)
// 功能:按键触发INT1,切换到模式1(东西红灯/南北绿灯)
void Int1_ISR(void) interrupt 2
{
// 设置显示模式为1
g_seg_mode = 1;
// 重置倒计时计数器为初始值(15)
g_timer_red_cnt = TIMER_INIT;
}
// 定时器相关全局变量:1秒计时标志(累计20次50ms中断=1秒)
#include "headfile.h"
unsigned int g_timer_1s_flag;
// 500毫秒计时标志(累计10次50ms中断=500ms,用于黄灯闪烁)
unsigned int g_timer_500ms_flag;
// 黄灯闪烁标志(0/1切换,控制黄灯亮灭)
unsigned char g_yellow_flag;
// 数码管显示模式(0=东西绿灯/南北红灯;1=东西红灯/南北绿灯)
unsigned char g_seg_mode;
// 按键标志(未实际使用,预留)
unsigned char g_key_flag;
// 定时器0初始化函数:配置16位定时器模式,50ms中断一次(12MHz晶振)
void Timer0_Init(void)
{
// 配置TMOD寄存器:Timer0为16位定时器模式(0x01=GATE=0, C/T=0, M1=0, M0=1)
TMOD |= 0x01;
// 设置Timer0初值:12MHz晶振下,50ms中断的初值计算
// 50ms = 50000us,12MHz下机器周期1us,65535-50000=15535 → 高8位=15535/256=60,低8位=15535%256=135
// 注:代码中写为(65535-50000)/256 和 %256,更通用
TH0 = (65535-50000)/256; //50ms中断:高8位初值
TL0 = (65535-50000)%256; //低8位初值
// 开启总中断
EA = 1;
// 开启Timer0中断(ET0=1:允许Timer0溢出中断)
ET0 = 1;
// 启动Timer0(TR0=1:定时器开始计数)
TR0 = 1;
}
// Timer0中断服务函数(interrupt 1:Timer0的中断号),每50ms进入一次
void Timer0_Isr(void) interrupt 1 //50ms中断一次
{
// 重装Timer0初值:16位定时器溢出后需手动重装,保证下次中断仍为50ms
TH0 = (65535-50000)/256;
TL0 = (65535-50000)%256;
// 1秒计时标志+1(每50ms+1,20次=1秒)
g_timer_1s_flag++;
// 按键标志置1(预留:用于按键消抖/检测)
g_key_flag = 1;
// 累计20次50ms → 达到1秒
if(g_timer_1s_flag>=20)
{
// 倒计时计数器-1(每1秒减1)
g_timer_red_cnt--;
// 重置1秒标志,重新计时
g_timer_1s_flag=0;
// 倒计时到0:重置计数器+切换模式
if(g_timer_red_cnt<=0)
{
// 重置倒计时为初始值(15)
g_timer_red_cnt = TIMER_INIT;
// 显示模式+1(0→1,1→0)
g_seg_mode++;
// 模式取模2:保证仅在0/1之间切换
g_seg_mode%=2;
}
}
// 倒计时≤3秒:进入黄灯阶段,控制黄灯闪烁
if(g_timer_red_cnt<=3)
{
// 500ms计时标志+1(每50ms+1,10次=500ms)
g_timer_500ms_flag++;
// 累计10次50ms → 达到500ms
if(g_timer_500ms_flag>=10)
{
// 黄灯标志取反(0→1,1→0),实现500ms闪烁一次
g_yellow_flag=!g_yellow_flag;
// 重置500ms标志,重新计时
g_timer_500ms_flag=0;
}
}
}
好了
这就本次的内容
如果你喜欢,可以给我一个免费的赞和收藏,非常感谢