单片机交通灯课程设计如何实现?

99ANYc3cd6 课程介绍 1

下面我将为你提供一个完整、详细、可执行的交通灯课程设计方案,包括设计要求、方案论证、硬件设计、软件设计(含C51代码)、系统调试和扩展思考


基于51单片机的十字路口交通灯控制系统

设计要求

  1. 基本功能:

    单片机交通灯课程设计如何实现?-第1张图片-指南针培训网
    • 模拟一个十字路口的交通灯控制。
    • 东西方向和南北方向交替放行。
    • 每个方向的灯有红、黄、绿三种状态。
    • 东西方向绿灯亮,南北方向红灯亮。
    • 东西方向黄灯亮,南北方向红灯亮。
    • 南北方向绿灯亮,东西方向红灯亮。
    • 南北方向黄灯亮,东西方向红灯亮。
    • 如此循环。
  2. 时间要求:

    • 东西绿灯亮 30 秒。
    • 东西黄灯亮 3 秒。
    • 南北绿灯亮 30 秒。
    • 南北黄灯亮 3 秒。
    • 总周期为 66 秒。
  3. 显示要求:

    • 使用LED灯模拟红、黄、绿灯。
    • 使用数码管或LCD显示屏实时显示当前方向的剩余时间(倒计时)。
  4. 扩展功能(可选,用于拿高分):

    • 紧急模式: 当按下某个按键(如K1)时,所有方向都变为红灯,用于紧急情况(如救护车通过)。
    • 夜间模式: 当按下另一个按键(如K2)时,所有方向黄灯闪烁,表示提醒车辆慢行。
    • 车流量检测: 模拟通过红外对管检测车流量,根据车流量动态调整绿灯时间(东西方向车多,则延长其绿灯时间)。

方案论证

  1. 核心控制器选型:

    单片机交通灯课程设计如何实现?-第2张图片-指南针培训网
    • 方案A:51系列单片机(如AT89C51/52或STC89C52)
      • 优点: 经典入门级单片机,资料丰富,价格低廉,I/O口足够,定时器/计数器资源满足需求,非常适合课程设计。
      • 缺点: 性能相对较弱,处理复杂逻辑能力有限。
    • 方案B:STM32系列单片机
      • 优点: 性能强大,资源丰富,适合更复杂的控制和算法。
      • 缺点: 学习曲线较陡,对于本项目来说“杀鸡用牛刀”,成本也稍高。
    • 本课程设计选用方案A,STC89C52单片机,它性价比最高,足以完美实现所有要求。
  2. 显示方案选型:

    • 方案A:LED数码管
      • 优点: 亮度高,视角大,驱动简单,成本低。
      • 缺点: 显示内容有限,只能显示数字。
    • 方案B:LCD1602液晶显示屏
      • 优点: 显示信息丰富,可以显示文字和数字,界面友好。
      • 缺点: 驱动相对复杂,需要占用更多I/O口(通常需要用到数据总线和控制线)。
    • 为简化设计,同时保证清晰显示,本设计选用方案A,共阴极或共阳极数码管,倒计时显示可以共用一组数码管,通过切换显示内容来节省I/O口。
  3. 时间方案选型:

    • 方案A:软件延时
      • 优点: 编程简单,无需额外硬件。
      • 缺点: CPU被占用,无法执行其他任务(如按键检测),精度低。
    • 方案B:单片机定时器中断
      • 优点: 精度高,不占用CPU主循环,CPU可以在中断间隙处理其他任务(如刷新显示、检测按键),这是专业和高效的实现方式。
    • 本设计采用方案B,使用STC89C52的定时器0(Timer0)来产生精确的1秒基准时间。

硬件设计

  1. 系统硬件框图

  2. 主要元器件清单

    单片机交通灯课程设计如何实现?-第3张图片-指南针培训网
    • 主控芯片:STC89C52RC (或AT89C52) 1片
    • 晶振:12MHz 1个
    • 电容:30pF (晶振匹配) 2个,10uF (电源滤波) 1个,22pF (按键去抖) 若干
    • 电阻:1kΩ (LED限流) 12个,10kΩ (上拉电阻) 1个,200Ω (数码管限流/位选) 4个
    • 数码管:4位一体共阴极数码管 1个
    • LED灯:红、黄、绿各4个 (东西南北各一组)
    • 按键:轻触开关 2个 (用于紧急模式和夜间模式)
    • 电源:5V直流电源
    • 面包板或洞洞板、杜邦线若干
  3. 电路连接说明

    • 交通灯连接:
      • 东西绿灯 -> P1.0
      • 东西黄灯 -> P1.1
      • 东西红灯 -> P1.2
      • 南北绿灯 -> P1.3
      • 南北黄灯 -> P1.4
      • 南北红灯 -> P1.5
      • 注意:每个LED串联一个1kΩ电阻到GND
    • 数码管连接(以共阴极为例):
      • 段选 a, b, c, d, e, f, g, dp -> P0口 (P0.0-P0.7)。P0口需要外接10kΩ上拉电阻。
      • 位选 (从左到右) -> P2.0, P2.1, P2.2, P2.3,每个位选引脚串联一个200Ω电阻。
    • 按键连接:
      • 紧急模式按键 K1 -> P3.2 (外部中断0)
      • 夜间模式按键 K2 -> P3.3 (外部中断1)
      • 按键另一端接地,引脚通过10kΩ电阻上拉到VCC。
    • 晶振电路: 12MHz晶振接XTAL1和XTAL2,两端分别接30pF电容到地。
    • 复位电路: RST引脚接一个10uF电容到VCC,一个10kΩ电阻到GND(上电复位)。

软件设计

  1. 软件流程图

  2. 核心思想:状态机

    • 将交通灯的运行过程看作4个状态:
      1. S0: 东西绿灯,南北红灯 (30s)
      2. S1: 东西黄灯,南北红灯 (3s)
      3. S2: 南北绿灯,东西红灯 (30s)
      4. S3: 南北黄灯,东西红灯 (3s)
    • 程序通过一个状态变量(如 unsigned char state;)来记录当前处于哪个状态,定时器每1秒中断一次,更新倒计时,并在倒计时为0时,切换到下一个状态。
  3. C51代码实现

#include <reg52.h>
#include <intrins.h>
// 定义交通灯引脚
sbit EW_GREEN = P1^0;  // 东西绿灯
sbit EW_YELLOW = P1^1; // 东西黄灯
sbit EW_RED = P1^2;    // 东西红灯
sbit SN_GREEN = P1^3;  // 南北绿灯
sbit SN_YELLOW = P1^4; // 南北黄灯
sbit SN_RED = P1^5;    // 南北红灯
// 定义按键引脚
sbit KEY_EMERGENCY = P3^2; // 紧急模式按键
sbit KEY_NIGHT = P3^3;     // 夜间模式按键
// 定义数码管段选码 (共阴极, 0-9)
unsigned char code SegCode[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F};
// 定义数码管位选码 (从左到右)
unsigned char code BitCode[] = {0xFE, 0xFD, 0xFB, 0xF7};
// 全局变量
unsigned char state = 0;        // 状态机变量: 0,1,2,3
unsigned int counter = 30;      // 倒计时计数器
unsigned char flag_1s = 0;      // 1秒到标志位
unsigned char display_buf[4];   // 数码管显示缓冲区
// 函数声明
void Timer0_Init();
void Key_Scan();
void State_Set();
void Display();
void Emergency_Mode();
void Night_Mode();
void main() {
    // 初始化
    Timer0_Init();
    EA = 1; // 开启总中断
    while (1) {
        // 主循环中处理按键和显示
        Key_Scan();
        // 根据状态设置交通灯
        State_Set();
        // 更新显示缓冲区
        display_buf[0] = counter / 10;
        display_buf[1] = counter % 10;
        display_buf[2] = 0x10; // 显示横杆 '-'
        display_buf[3] = 0x10; // 显示横杆 '-'
        // 显示
        Display();
    }
}
// 定时器0中断服务函数
void Timer0_ISR() interrupt 1 {
    static unsigned int count_50ms = 0;
    TH0 = (65536 - 50000) / 256; // 重新装载初值 (50ms)
    TL0 = (65536 - 50000) % 256;
    count_50ms++;
    if (count_50ms >= 20) { // 50ms * 20 = 1000ms = 1s
        count_50ms = 0;
        flag_1s = 1; // 置1秒标志位
    }
}
// 状态设置函数
void State_Set() {
    if (flag_1s) {
        flag_1s = 0; // 清除标志位
        if (counter > 0) {
            counter--;
        } else {
            // 时间到,切换到下一个状态
            state++;
            if (state > 3) {
                state = 0;
            }
            // 根据新状态设置倒计时
            if (state == 0 || state == 2) {
                counter = 30; // 绿灯30秒
            } else {
                counter = 3;  // 黄灯3秒
            }
        }
    }
    // 根据当前状态设置交通灯
    switch (state) {
        case 0: // 东西绿灯,南北红灯
            EW_GREEN = 1; EW_YELLOW = 0; EW_RED = 0;
            SN_GREEN = 0; SN_YELLOW = 0; SN_RED = 1;
            break;
        case 1: // 东西黄灯,南北红灯
            EW_GREEN = 0; EW_YELLOW = 1; EW_RED = 0;
            SN_GREEN = 0; SN_YELLOW = 0; SN_RED = 1;
            break;
        case 2: // 南北绿灯,东西红灯
            EW_GREEN = 0; EW_YELLOW = 0; EW_RED = 1;
            SN_GREEN = 1; SN_YELLOW = 0; SN_RED = 0;
            break;
        case 3: // 南北黄灯,东西红灯
            EW_GREEN = 0; EW_YELLOW = 0; EW_RED = 1;
            SN_GREEN = 0; SN_YELLOW = 1; SN_RED = 0;
            break;
    }
}
// 数码管动态扫描显示函数
void Display() {
    static unsigned char index = 0;
    P2 = 0xFF; // 消隐,关闭所有位选
    P0 = SegCode[display_buf[index]]; // 送段选码
    P2 = BitCode[index]; // 送位选码
    index++;
    if (index >= 4) {
        index = 0;
    }
}
// 按键扫描函数 (此处简化,实际应用中可用外部中断)
void Key_Scan() {
    // 紧急模式
    if (KEY_EMERGENCY == 0) {
        Delay_ms(10); // 消抖
        if (KEY_EMERGENCY == 0) {
            Emergency_Mode();
            while (!KEY_EMERGENCY); // 等待按键释放
        }
    }
    // 夜间模式
    if (KEY_NIGHT == 0) {
        Delay_ms(10);
        if (KEY_NIGHT == 0) {
            Night_Mode();
            while (!KEY_NIGHT);
        }
    }
}
// 紧急模式函数
void Emergency_Mode() {
    // 所有灯亮
    EW_GREEN = 1; EW_YELLOW = 1; EW_RED = 1;
    SN_GREEN = 1; SN_YELLOW = 1; SN_RED = 1;
}
// 夜间模式函数
void Night_Mode() {
    // 所有黄灯闪烁
    while(1) {
        EW_YELLOW = !EW_YELLOW;
        SN_YELLOW = !SN_YELLOW;
        Delay_ms(500);
        // 可以加入按键检测来退出此模式
    }
}
// 简单延时函数 (ms级,不精确)
void Delay_ms(unsigned int ms) {
    unsigned int i, j;
    for (i = ms; i > 0; i--)
        for (j = 110; j > 0; j--);
}

系统调试与问题解决

  1. 硬件调试:

    • 电源检查: 用万用表测量VCC和GND之间是否为5V,确保电路没有短路。
    • LED检查: 逐个检查LED的连接和极性是否正确,上电后是否能被点亮。
    • 数码管检查: 检查段选和位选的连接,特别是P0口的上拉电阻,可以先写一个简单的程序,让数码管显示"8888",检查每个段和位是否正常。
    • 按键检查: 按下按键时,用万用表测量对应引脚是否被拉低。
  2. 软件调试:

    • 下载与运行: 将编译好的.hex文件烧录到单片机中,观察交通灯是否按预期规律变化。
    • 逻辑错误: 如果灯的顺序不对,检查State_Set()函数中的switch-case逻辑。
    • 时间不准: 如果倒计时不准,检查定时器的初值计算是否正确,晶振频率是否与程序中设定的12MHz一致。
    • 显示问题: 如果数码管显示乱码或闪烁,检查动态扫描的频率是否太慢(Delay_ms函数的参数)或太快,以及段选、位选码是否正确。

扩展思考与优化

  1. 车流量检测:

    • 硬件: 在每个方向的红绿灯前安装一对红外对管(发射和接收)。
    • 软件: 将红外接收管的输出接到单片机的某个I/O口(如P3.4),平时,有车挡住时,I/O口为高电平;无车时为低电平,在程序中,可以统计一个绿灯周期内被触发的次数,从而判断车流量,动态调整下一个绿灯周期的时长。
  2. 实现人行横道灯:

    在东西和南北方向各增加一组红绿灯(人行道),连接到单片机的剩余I/O口,在主干道绿灯亮时,人行道红灯亮;主干道红灯亮时,人行道绿灯亮,这需要更精细的状态控制。

  3. 使用LCD1602替代数码管:

    可以更直观地显示"东西方向:30秒"这样的文字信息,提升用户体验。

  4. 代码优化:

    • 使用结构体来封装交通灯的状态,使代码更清晰。
    • 将所有延时函数替换为基于定时器的精确延时,提高系统效率。
    • 将按键检测改为外部中断方式,响应更迅速,不占用主循环时间。

这个方案为你提供了一个从理论到实践的完整路径,你可以根据自己的实际情况(如手头的元件、老师的具体要求)进行调整,祝你课程设计顺利成功!

标签: 状态机 定时器 硬件驱动

上一篇新课程同步练习册如何有效辅助课堂学习?

下一篇当前分类已是最新一篇

抱歉,评论功能暂时关闭!