找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 259|回复: 0
打印 上一主题 下一主题
收起左侧

带详细注释的单片机交通信号灯简易代码

[复制链接]
跳转到指定楼层
楼主
ID:1165182 发表于 2025-12-17 21:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
#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();
        }
    }
}

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏1 分享淘帖 顶 踩
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表