#include <reg51.h>
#include <intrins.h>
// ------------------- 端口定义 -------------------
// 东西交通灯(低电平亮)
sbit E_GREEN = P0^2; // 东西绿灯
sbit E_YELLOW = P0^0; // 东西黄灯
sbit E_RED = P0^1; // 东西红灯
// 南北交通灯(低电平亮)
sbit S_GREEN = P0^5; // 南北绿灯
sbit S_YELLOW = P0^3; // 南北黄灯
sbit S_RED = P0^4; // 南北红灯
// 按键定义(低电平有效,需外接上拉电阻)
sbit KEY_RST = P1^0; // 复位键
sbit KEY_NIGHT = P1^1; // 夜间模式键
sbit KEY_EMER = P1^2; // 紧急模式键
sbit KEY_SET = P1^3; // 时间设定键
sbit KEY_ADD = P1^4; // 时间加键
sbit KEY_SUB = P1^5; // 时间减键
// 数码管位选(高电平亮)
sbit BIT_E_TEN = P3^0; // 东西十位
sbit BIT_E_ONE = P3^1; // 东西个位
sbit BIT_S_TEN = P3^2; // 南北十位
sbit BIT_S_ONE = P3^3; // 南北个位
// ------------------- 全局变量 -------------------
// 共阳数码管段码表(0-9,高电平灭、低电平亮)
unsigned char code SEG_TABLE[] = {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90};
unsigned char flag_night = 0; // 夜间模式标志(0-退出,1-进入)
unsigned char set_state = 0; // 设定状态标志(0-退出,1-统一设定)
unsigned char flag_emer = 0; // 紧急状态标志(0-退出,1-进入)
unsigned int time_e = 30; // 东西通行时长(绿灯+黄灯,30s)
unsigned int time_s = 30; // 南北通行时长(绿灯+黄灯,30s)
unsigned int disp_common = 30; // 数码管计时(两边显示相同数值)
unsigned char seg_index = 0; // 数码管刷新索引
unsigned char phase = 1; // 阶段标志:1-东西通行,2-南北通行
// 定时器/按键状态机相关
volatile unsigned int ms_cnt = 0; // 毫秒计数(volatile避免编译优化)
unsigned char key_value = 0; // 按键状态机输出值(0-无按键)
// 按键状态机枚举(三状态)
typedef enum {
KEY_IDLE, // 空闲状态
KEY_PRESS_DEBOUNCE, // 按下消抖
KEY_RELEASE_DEBOUNCE // 释放消抖
} Key_State;
Key_State key_state = KEY_IDLE; // 按键状态机当前状态
unsigned char key_temp = 0; // 按键临时缓存
// ------------------- 定时器初始化 -------------------
// 定时器0初始化:200μs中断(数码管刷新)
void timer0_init(void) {
TMOD |= 0x01; // 定时器0模式1(16位)
TH0 = 0xFF; // 200μs初值(11.0592MHz)
TL0 = 0x45;
ET0 = 1; // 开启定时器0中断
TR0 = 1; // 启动定时器0
}
// 定时器1初始化:10ms中断(按键状态机+毫秒计数)
void timer1_init(void) {
TMOD |= 0x10; // 定时器1模式1(16位)
// 10ms初值(11.0592MHz:65536-9210=56326=0xDC06)
TH1 = 0xDC;
TL1 = 0x06;
ET1 = 1; // 开启定时器1中断
TR1 = 1; // 启动定时器1
}
// ------------------- 非阻塞延时函数(基于定时器1,单位:ms) -------------------
void delay_ms(unsigned int ms) {
ms_cnt = 0; // 重置毫秒计数器
while(ms_cnt < ms); // 等待计数达标
}
// ------------------- 定时器0中断服务函数(数码管刷新) -------------------
void timer0_isr(void) interrupt 1 {
TH0 = 0xFF; // 重装200μs初值
TL0 = 0x45;
// 取消所有位选(消影)
BIT_E_TEN = 0;
BIT_E_ONE = 0;
BIT_S_TEN = 0;
BIT_S_ONE = 0;
if(flag_night) {
return; // 夜间模式下不刷新数码管
}
// 按索引刷新对应数码管位
switch(seg_index) {
case 0: // 东西十位
P2 = (disp_common < 99) ? SEG_TABLE[disp_common / 10] : 0xFF;
BIT_E_TEN = 1;
break;
case 1: // 东西个位
P2 = (disp_common < 99) ? SEG_TABLE[disp_common % 10] : 0xFF;
BIT_E_ONE = 1;
break;
case 2: // 南北十位
P2 = (disp_common < 99) ? SEG_TABLE[disp_common / 10] : 0xFF;
BIT_S_TEN = 1;
break;
case 3: // 南北个位
P2 = (disp_common < 99) ? SEG_TABLE[disp_common % 10] : 0xFF;
BIT_S_ONE = 1;
break;
}
seg_index = (seg_index + 1) % 4; // 索引循环
}
// ------------------- 定时器1中断服务函数(10ms中断:按键状态机+毫秒计数) -------------------
void timer1_isr(void) interrupt 3 {
unsigned char cur_key = 0;
TH1 = 0xDC; // 重装10ms初值
TL1 = 0x06;
// 1. 毫秒计数(供delay_ms函数)
ms_cnt += 10; // 10ms中断,计数器+10
// 2. 按键状态机处理
// 读取当前按键状态(低电平有效)
if(KEY_RST == 0) cur_key = 1;
else if(KEY_NIGHT == 0) cur_key = 2;
else if(KEY_EMER == 0) cur_key = 3;
else if(KEY_SET == 0) cur_key = 4;
else if(KEY_ADD == 0) cur_key = 5;
else if(KEY_SUB == 0) cur_key = 6;
else cur_key = 0;
// 状态机逻辑
switch(key_state) {
case KEY_IDLE: // 空闲状态:检测到按键按下,进入消抖
if(cur_key != 0) {
key_temp = cur_key;
key_state = KEY_PRESS_DEBOUNCE;
}
break;
case KEY_PRESS_DEBOUNCE: // 按下消抖:连续两次检测到同一按键,确认按下
if(cur_key == key_temp) {
key_value = key_temp; // 输出按键值
key_state = KEY_RELEASE_DEBOUNCE;
} else {
key_state = KEY_IDLE; // 消抖失败,回到空闲
}
break;
case KEY_RELEASE_DEBOUNCE: // 释放消抖:按键释放后回到空闲
if(cur_key == 0) {
key_state = KEY_IDLE;
key_temp = 0;
}
break;
}
}
// ------------------- 按键读取函数(仅读取状态机结果,无阻塞) -------------------
unsigned char key_scan(void) {
unsigned char ret = 0;
if(key_value != 0) {
ret = key_value; // 读取按键值
key_value = 0; // 清空缓存,避免重复触发
}
return ret;
}
// ------------------- 夜间模式处理(500ms精准闪烁) -------------------
void night_mode(void) {
// 基础灯全灭
E_GREEN = 0; E_RED = 0;
S_GREEN = 0; S_RED = 0;
// 黄灯亮500ms
E_YELLOW = 1;
S_YELLOW = 1;
delay_ms(500);
// 黄灯灭500ms
E_YELLOW = 0;
S_YELLOW = 0;
delay_ms(500);
}
// ------------------- 紧急模式处理 -------------------
void emer_mode(void) {
// 全亮红灯,其他灯灭
E_RED = 1; S_RED = 1;
E_GREEN = 0; E_YELLOW = 0;
S_GREEN = 0; S_YELLOW = 0;
}
// ------------------- 正常交通逻辑(核心:两边计时一致+红绿灯切换) -------------------
void normal_mode(void) {
// 1. 阶段1:东西通行(绿灯/黄灯),南北红灯
if(phase == 1) {
// 东西绿灯(disp_common > 3:绿灯亮,黄灯灭)
if(disp_common > 3) {
E_GREEN = 1; E_YELLOW = 0; E_RED = 0;
S_GREEN = 0; S_YELLOW = 0; S_RED = 1;
}
// 东西黄灯(disp_common <=3:黄灯闪烁,绿灯灭)
else if(disp_common > 0) {
E_GREEN = 0; E_YELLOW = !E_YELLOW; E_RED = 0;
S_GREEN = 0; S_YELLOW = 0; S_RED = 1;
}
// 东西通行结束,切换到南北通行阶段
else {
phase = 2; // 切换阶段
disp_common = time_s; // 重置计时为南北时长
E_YELLOW = 0; // 黄灯灭
return;
}
}
// 2. 阶段2:南北通行(绿灯/黄灯),东西红灯
else if(phase == 2) {
// 南北绿灯(disp_common > 3:绿灯亮,黄灯灭)
if(disp_common > 3) {
E_GREEN = 0; E_YELLOW = 0; E_RED = 1;
S_GREEN = 1; S_YELLOW = 0; S_RED = 0;
}
// 南北黄灯(disp_common <=3:黄灯闪烁,绿灯灭)
else if(disp_common > 0) {
E_GREEN = 0; E_YELLOW = 0; E_RED = 1;
S_GREEN = 0; S_YELLOW = !S_YELLOW; S_RED = 0;
}
// 南北通行结束,切换到东西通行阶段
else {
phase = 1; // 切换阶段
disp_common = time_e; // 重置计时为东西时长
S_YELLOW = 0; // 黄灯灭
return;
}
}
// 3. 倒计时每秒递减(两边显示相同数值,同步递减)
delay_ms(1000);
disp_common--;
}
// ------------------- 设定时间模式处理 -------------------
void set_mode(unsigned char key) {
// 切换设定状态:0(退出)→1(统一设定)→0(退出)
if(key == 4) {
set_state = !set_state; // 仅0/1切换,按一次进入设定,再按退出
if(set_state == 1) {
disp_common = time_e; // 进入设定,显示当前统一时长
} else {
phase = 1; // 退出设定,回到东西通行
disp_common = time_e; // 重置计时为设定后的时长
}
}
// 时间加/减(边界保护:最小4s=绿灯1s+黄灯3s)- 同步修改time_e和time_s
if(set_state == 1) {
// 加键:同步增加东西、南北时长,保证两边一致
if(key == 5 && time_e < 99) {
time_e++;
time_s = time_e; // 南北同步等于东西,只需设置一次
disp_common = time_e;
}
// 减键:同步减少东西、南北时长,保证两边一致
if(key == 6 && time_e > 4) {
time_e--;
time_s = time_e; // 南北同步等于东西,只需设置一次
disp_common = time_e;
}
}
}
// ------------------- 主函数 -------------------
void main(void) {
unsigned char key_val;
// 初始化:全灯灭+双定时器启动+总中断开启
P0 = 0x00;
timer0_init(); // 数码管刷新定时器
timer1_init(); // 按键状态机+毫秒计数定时器
EA = 1; // 开启总中断
while(1) {
key_val = key_scan(); // 仅读取状态机结果,无阻塞
// 模式优先级:复位 > 紧急 > 夜间 > 设定 > 正常
if(key_val == 1) { // 复位
flag_night = 0;
flag_emer = 0;
set_state = 0;
time_e = 30;
time_s = 30;
disp_common = 30;
P0 = 0x00;
}
else if(key_val == 3) { // 紧急模式切换
flag_emer = !flag_emer;
flag_night = 0;
set_state = 0;
}
else if(flag_emer) { // 执行紧急模式
emer_mode();
}
else if(key_val == 2) { // 夜间模式切换
flag_night = !flag_night;
set_state = 0;
}
else if(flag_night) { // 执行夜间模式
night_mode();
}
else if(key_val == 4 || set_state != 0) { // 设定模式
set_mode(key_val);
}
else { // 正常模式
normal_mode();
}
}
}
|