proteus仿真51单片机通过定时器控制红绿灯

模块一:定时器

如果是零基础的话可以先看视频

[7-1] 定时器_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1Mb411e7re?spm_id_from=333.788.videopod.episodes&vd_source=9bbb8d94b8b74a9b12630b585230dc13&p=17

  1. 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;

}

逐行拆解代码

  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 的配置)。
  1. TH0 = (65535-50000)/256; ------ 定时器 0 高 8 位初值

  2. 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 来说几乎无影响)。

  1. EA = 1; ------ 开启总中断允许

EA 是「总中断允许位」(寄存器 IE 的 bit7),51 单片机的所有中断都需要先开总中断,否则即使开启外设中断,也无法响应。

  • EA=1:开启总中断;

  • EA=0:关闭所有中断(默认)。

  1. ET0 = 1; ------ 开启定时器 0 中断允许

ET0 是「定时器 0 中断允许位」(寄存器 IE 的 bit1),专门控制 T0 的溢出中断:

  • ET0=1:允许 T0 溢出后触发中断;

  • ET0=0:禁止 T0 中断(默认)。

  1. 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)

东西方向放行,南北方向禁行

  1. 倒计时 15 → 4 (共 12 秒)
    • 东西:绿灯亮
    • 南北:红灯亮
  2. 倒计时 3 → 1 (最后 3 秒)
    • 东西:黄灯闪烁
    • 南北:红灯亮

模式 1(g_seg_mode = 1)

南北方向放行,东西方向禁行

  1. 倒计时 15 → 4 (共 12 秒)
    • 南北:绿灯亮
    • 东西:红灯亮
  2. 倒计时 3 → 1 (最后 3 秒)
    • 南北:黄灯闪烁
    • 东西:红灯亮

二、自动切换逻辑(完全不用人管)

  1. 定时器每 50ms 中断一次
  2. 累计 20 次 = 1 秒g_timer_red_cnt 减 1
  3. 倒计时从 15 → 0
  4. 归 0 后立刻:
    • 重置倒计时为 15
    • 模式 0 ↔ 1 自动翻转→ 实现:东西绿 → 南北绿 → 东西绿...... 无限循环

三、黄灯闪烁逻辑(最后 3 秒)

只要倒计时 ≤3

  • 500ms 黄灯引脚翻转一次(亮→灭→亮→灭)
  • 对应方向黄灯开始闪烁
  • 另外一方向一直保持红灯

四、按键强制切换逻辑(外部中断)

你有两个按键,分别控制:

  1. INT0 中断(按键 0)

    • 强制切到 模式 0(东西绿、南北红)
    • 倒计时重置为 15 秒
  2. 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;		
		}
  }
}

好了

这就本次的内容

如果你喜欢,可以给我一个免费的赞和收藏,非常感谢

相关推荐
鲨辣椒100863 小时前
51单片机初相识
单片机·嵌入式硬件·51单片机
独处东汉3 小时前
freertos开发空气检测仪之完结_Air_check_App 工程概览与使用说明
stm32·单片机·嵌入式硬件·freertos
爱喝纯牛奶的柠檬5 小时前
基于STM32的4*4矩阵软键盘驱动
stm32·嵌入式硬件·矩阵
电子工程师成长日记-C515 小时前
51单片机低频信号发生器
单片机·嵌入式硬件·51单片机
鲨辣椒100866 小时前
UART通用异步收发器与其中断
51单片机
逐步前行6 小时前
STM32_DMA_寄存器操作
stm32·单片机·嵌入式硬件
Funing77 小时前
无法打开 源 文件 “esp_err.h“
嵌入式硬件·esp32
Hello World . .8 小时前
51单片机基础外设:中断、定时器/计数器(PWM控制蜂鸣器、电机)
单片机·嵌入式硬件·51单片机
LCG元9 小时前
基于STM32CubeMX的HAL库串口通信与DMA传输深度优化
stm32·单片机·嵌入式硬件