资讯详情

基于51单片机的公交车报站系统设计与实现

📅 2026/10/9 4:35:10 | 华诺云谱 👁 阅读
基于51单片机的公交车报站系统设计与实现
1. 公交车报站系统到底在做什么公交车报站系统这个东西说白了就是公交车上的电子嗓门——它要知道车现在开到哪一站了然后通过语音和显示屏告诉乘客下一站是XX。听起来简单但真做起来涉及的东西一点都不少站点检测、语音播报、LCD显示、按键控制、状态切换每一个环节都有坑。我这次做的是一个基于51单片机的公交车报站系统主控用的是STC89C52RC配套有仿真文件、完整代码、原理图、硬件框图、软件流程图、元器件清单和参考文档。整个项目从硬件设计到软件调试走了一遍中间踩了不少坑也积累了一些经验这里完整地分享出来。这套系统适合谁看如果你是电子信息、自动化、计算机相关专业的学生正在做单片机课程设计或者毕业设计这个项目可以直接拿来参考。如果你是想入门51单片机的爱好者这个项目涉及了GPIO控制、定时器、中断、串口通信、LCD驱动、语音模块控制等核心知识点是一个很好的练手项目。如果你已经有一定基础想看看别人是怎么做系统级设计的这里的设计思路和避坑经验也能给你一些启发。整个系统能实现的功能包括手动/自动报站切换、站点加减、语音播报、LCD显示当前站和下一站信息、按键控制等。下面我从设计思路开始一步步拆解整个项目。2. 系统整体设计与方案选型2.1 为什么选51单片机作为主控很多人可能会问现在STM32这么便宜ESP32功能这么强为什么还要用51单片机这个问题我在做方案选型的时候也想过。实际考虑下来有几个原因第一教学和入门价值。51单片机的架构简单寄存器少没有复杂的时钟树和中断向量表初学者能很快理解单片机的基本工作原理。你搞懂了51的定时器、中断、串口再去看STM32会发现很多概念是相通的。第二成本极低。STC89C52RC这片芯片零售价大概两三块钱批量更便宜。对于公交车报站这种功能需求不复杂的场景51完全够用没必要上更贵的平台。第三资料丰富生态成熟。51单片机的开发工具Keil C51非常成熟网上能找到的例程和教程浩如烟海遇到问题很容易找到解决方案。第四仿真支持好。Proteus对51单片机的仿真支持非常完善可以在没有硬件的情况下先验证逻辑这对于学生做课设来说太重要了——不用等PCB打样回来就能调代码。当然51也有明显的短板RAM只有128字节STC89C52RC是256字节ROM 8KBIO口驱动能力弱没有硬件浮点运算。但公交车报站系统不需要复杂的运算也不需要大容量存储这些短板在这个项目里影响不大。2.2 整体架构是怎么搭的整个系统按功能划分为几个模块主控模块STC89C52RC最小系统包括晶振电路11.0592MHz、复位电路、电源电路显示模块LCD1602液晶屏显示当前站和下一站信息语音模块WT588D语音芯片或者ISD1820录放音模块负责语音播报按键模块独立按键用于手动控制站点加减、模式切换电源模块5V供电可以用USB供电或者7805稳压各模块之间通过IO口连接主控负责协调各个模块的工作。整个系统的硬件框图大致是这样的按键输入→单片机处理→LCD显示语音播报。这里说一下为什么选LCD1602而不是数码管。数码管只能显示数字没法显示站名而报站系统需要显示中文站名或者至少是拼音/英文所以LCD1602是更合适的选择。虽然LCD1602只能显示两行各16个字符但对于显示当前站和下一站这两个信息来说刚好够用。2.3 语音方案的取舍语音播报是报站系统的核心功能之一方案选择上我考虑了几种方案优点缺点适用场景ISD1820录放音模块电路简单可以直接录音音质一般存储时间短不能灵活更换语音简单演示WT588D语音芯片音质好可通过串口更换语音支持多段语音需要专用下载器和上位机软件正式项目SYN6288语音合成任意文字转语音不需要预先录音成本较高控制复杂需要动态语音DFPlayer Mini支持MP3文件音质好价格便宜需要SD卡控制协议稍复杂推荐方案最终我选择了WT588D方案因为它在仿真中比较容易模拟而且实际硬件上也方便更换语音内容。如果你只是做仿真演示其实用蜂鸣器模拟嘀一声也能凑合但效果就差很多了。3. 硬件设计核心细节3.1 单片机最小系统最小系统是整个硬件的基础包括三个部分晶振电路用11.0592MHz晶振两个30pF瓷片电容。为什么选11.0592MHz而不是12MHz因为11.0592MHz能让串口波特率更精确。比如要得到9600bps的波特率用11.0592MHz晶振时定时器初值算出来是整数误差为0而用12MHz的话会有误差可能导致串口通信不稳定。虽然这个项目不一定用串口但养成好习惯没坏处。复位电路经典的上电复位按键复位电路。10uF电解电容接VCC10K电阻接地复位按键并联在电容两端。上电时电容充电RST引脚维持高电平一段时间单片机复位。按下按键时电容放电RST再次拉高手动复位。电源电路如果用USB供电直接取5V就行。如果用电池或者适配器需要加一个7805稳压电路。注意7805的输入输出都要加滤波电容输入端加100uF和0.1uF输出端加10uF和0.1uF这样能有效抑制电源纹波。注意STC89C52RC的IO口是准双向口输出高电平时驱动能力很弱大概只有几十微安输出低电平时能吸收20mA左右的电流。所以驱动LED时要用低电平驱动也就是LED正极接VCC负极接IO口。这个坑我在第一次做硬件的时候踩过LED死活不亮后来才发现是驱动方式搞反了。3.2 LCD1602显示电路LCD1602有16个引脚实际用到的就那几个引脚1VSS接地引脚2VDD接5V引脚3V0对比度调节接一个10K电位器的中间脚引脚4RS寄存器选择接单片机IO口引脚5RW读写选择一般接地只写模式引脚6E使能信号接单片机IO口引脚7-14D0-D7数据线接单片机P0口需要加上拉电阻引脚15A背光正极接5V串一个限流电阻引脚16K背光负极接地这里有个细节P0口作为普通IO使用时需要外接上拉电阻一般用10K排阻。因为P0口内部没有上拉电阻是开漏输出。如果你直接把LCD的数据线接到P0口而不加上拉电阻LCD可能不显示或者显示乱码。这个坑很经典很多初学者都会遇到。对比度调节那个10K电位器也很关键。调不好的话要么屏幕一片白要么一片黑什么都看不见。调节方法上电后慢慢旋转电位器直到字符清晰显示为止。3.3 按键电路设计按键电路很简单就是独立按键加消抖电容。每个按键一端接地另一端接单片机IO口同时通过一个10K电阻上拉到VCC。按下按键时IO口被拉低单片机检测到低电平就知道按键按下了。硬件消抖用电容并联在按键两端一般用0.1uF。但实际调试下来硬件消抖效果有限软件消抖更可靠。软件消抖就是检测到按键按下后延时10-20ms再检测一次如果还是按下状态就确认按键有效。我用了4个按键模式切换、站点加、站点减、确认/播报。按键布局要考虑操作方便性实际安装时最好把按键放在司机容易够到的位置。3.4 语音模块接口WT588D语音芯片通过串口或者一线串口协议与单片机通信。一线串口模式只需要一根数据线控制简单。单片机发送一个字节的命令语音芯片就播放对应的语音段。接线方式WT588D的DATA脚接单片机的一个IO口BUSY脚可以接另一个IO口用来检测是否在播放。供电是3.3V如果单片机是5V系统需要在数据线上加电平转换电路或者串一个1K电阻限流。语音内容需要提前用上位机软件烧录到WT588D的SPI Flash中。每段语音分配一个地址单片机发送地址就能播放对应语音。比如地址0x00是欢迎乘坐XX路公交车0x01是下一站XX站等等。4. 软件设计思路与代码实现4.1 软件整体流程软件采用前后台架构主循环负责按键扫描和状态更新定时器中断负责定时任务比如语音播报的延时控制。整体流程是系统初始化LCD初始化、定时器初始化、串口初始化如果用了、变量初始化进入主循环扫描按键根据当前模式手动/自动处理按键更新LCD显示如果需要播报触发语音模块定时器中断每50ms触发一次用于按键消抖计时和自动报站计时这个架构简单可靠适合51单片机的资源限制。不需要上RTOS裸机跑就够了。4.2 LCD1602驱动代码LCD1602的驱动是基础但容易出错的部分。关键点在于时序写命令和写数据都要遵循一定的时序特别是使能信号E的脉冲宽度不能太短。#include reg52.h #define LCD_DATA P0 sbit LCD_RS P2^0; sbit LCD_RW P2^1; sbit LCD_EN P2^2; void delay_ms(unsigned int ms) { unsigned int i, j; for(i 0; i ms; i) for(j 0; j 110; j); } void lcd_write_cmd(unsigned char cmd) { LCD_RS 0; // 命令模式 LCD_RW 0; // 写模式 LCD_DATA cmd; delay_ms(1); LCD_EN 1; delay_ms(1); LCD_EN 0; delay_ms(2); } void lcd_write_data(unsigned char dat) { LCD_RS 1; // 数据模式 LCD_RW 0; LCD_DATA dat; delay_ms(1); LCD_EN 1; delay_ms(1); LCD_EN 0; delay_ms(2); } void lcd_init(void) { lcd_write_cmd(0x38); // 8位数据两行显示5x7点阵 lcd_write_cmd(0x0C); // 显示开光标关 lcd_write_cmd(0x06); // 写入后地址自动加1 lcd_write_cmd(0x01); // 清屏 delay_ms(5); }这段代码里delay_ms(1)的延时时间需要根据晶振频率调整。11.0592MHz下大概110个空循环是1ms。如果延时不够LCD可能不响应延时太长刷新速度慢。实际调试时可以先用较长的延时确保能工作再逐步缩短优化。4.3 按键扫描与状态机按键处理我用了一个简单的状态机。每个按键有三个状态空闲、消抖、确认。状态转移在定时器中断里进行每10ms检查一次。typedef enum { KEY_IDLE, KEY_DEBOUNCE, KEY_PRESSED, KEY_RELEASE } KeyState; KeyState key_state[4] {KEY_IDLE, KEY_IDLE, KEY_IDLE, KEY_IDLE}; unsigned char key_pressed_flag[4] {0, 0, 0, 0}; void timer0_isr(void) interrupt 1 { unsigned char i; TH0 (65536 - 10000) / 256; // 10ms定时 TL0 (65536 - 10000) % 256; for(i 0; i 4; i) { switch(key_state[i]) { case KEY_IDLE: if(key_input[i] 0) { key_state[i] KEY_DEBOUNCE; } break; case KEY_DEBOUNCE: if(key_input[i] 0) { key_state[i] KEY_PRESSED; key_pressed_flag[i] 1; } else { key_state[i] KEY_IDLE; } break; case KEY_PRESSED: if(key_input[i] 1) { key_state[i] KEY_RELEASE; } break; case KEY_RELEASE: key_state[i] KEY_IDLE; break; } } }这个状态机的好处是不会漏掉按键也不会因为抖动误触发。key_pressed_flag在主循环里检查如果为1就执行对应操作然后清零。4.4 报站逻辑实现报站逻辑是整个系统的核心。我用一个数组存储所有站名用一个变量记录当前站序号。报站时显示当前站和下一站同时触发语音播报。#define MAX_STATIONS 20 char *station_names[MAX_STATIONS] { Station 1, Station 2, // ... 更多站名 }; unsigned char current_station 0; unsigned char total_stations 10; unsigned char auto_mode 0; // 0手动1自动 void report_station(void) { // 显示当前站 lcd_set_cursor(0, 0); lcd_print(Current:); lcd_print(station_names[current_station]); // 显示下一站 lcd_set_cursor(1, 0); lcd_print(Next:); if(current_station 1 total_stations) { lcd_print(station_names[current_station 1]); } else { lcd_print(Terminal); } // 触发语音播报 voice_play(current_station); } void next_station(void) { if(current_station 1 total_stations) { current_station; report_station(); } } void prev_station(void) { if(current_station 0) { current_station--; report_station(); } }自动模式下用一个定时器计数每30秒自动切换到下一站。这个时间可以根据实际线路调整城市公交一般站间行驶时间在2-5分钟但仿真演示时30秒比较合适。4.5 语音模块控制WT588D的一线串口协议比较简单拉低数据线5ms然后发送8位数据先发低位。每位数据用不同的脉宽表示0和1。sbit VOICE_DATA P3^5; void voice_play(unsigned char addr) { unsigned char i; VOICE_DATA 0; delay_ms(5); // 起始信号 VOICE_DATA 1; delay_ms(1); for(i 0; i 8; i) { VOICE_DATA 0; if(addr 0x01) { delay_ms(3); // 数据1长低电平 } else { delay_ms(1); // 数据0短低电平 } VOICE_DATA 1; delay_ms(1); addr 1; } VOICE_DATA 1; // 恢复空闲 }实际调试时脉宽需要根据WT588D的数据手册微调。如果语音不播放或者播放错误大概率是脉宽不对。用示波器看波形是最直接的调试方法。5. Proteus仿真与调试5.1 仿真电路搭建Proteus仿真最大的好处是不用焊电路就能验证代码逻辑。搭建仿真电路的步骤在Proteus中新建工程选择单片机型号STC89C52RC添加LCD1602元件连接到P0口和P2口添加按键元件连接到P3口添加扬声器或者蜂鸣器模拟语音输出加载编译好的HEX文件运行仿真仿真中LCD1602的对比度引脚V0需要接一个电位器否则可能不显示。这个细节在仿真里也适用。5.2 常见仿真问题排查问题现象可能原因解决方法LCD不显示对比度没调好调整V0引脚电位器LCD显示乱码数据线接错或时序不对检查P0口连接确认上拉电阻按键无反应按键连接错误或消抖参数不对检查按键接线调整消抖时间语音不播放脉宽不对或地址错误用示波器看波形核对地址程序跑飞中断冲突或堆栈溢出检查中断优先级减少局部变量提示Proteus仿真和实际硬件有差异仿真通过不代表硬件一定没问题。特别是时序敏感的部分如LCD驱动、语音控制实际硬件上可能需要微调延时参数。5.3 仿真与实物的差异仿真中很多问题不会暴露比如电源纹波仿真中电源是理想的实际中电源纹波可能导致单片机复位信号完整性仿真中导线是理想的实际中长导线可能引入干扰驱动能力仿真中IO口可以驱动任意负载实际中驱动能力有限晶振起振仿真中晶振总是起振的实际中可能因为负载电容不匹配而起振困难所以仿真验证逻辑实物验证可靠性两者缺一不可。6. 元器件清单与选型建议6.1 核心元器件清单元件型号数量备注单片机STC89C52RC1也可以用AT89S52晶振11.0592MHz1串口通信更精确电容30pF2晶振负载电容电解电容10uF1复位电路电阻10K1复位电路电阻排10K1P0口上拉按键轻触开关46x6mmLCDLCD16021带背光电位器10K1对比度调节语音芯片WT588D1含SPI Flash扬声器8欧姆 0.5W1语音输出电源5V1USB供电或78056.2 选型经验单片机选STC89C52RC而不是AT89C51STC的芯片支持串口直接下载不需要专用编程器而且价格更便宜。AT89C51现在基本停产了不容易买到。LCD1602选带背光的不带背光的LCD在光线暗的环境下看不清带背光的贵不了几毛钱体验好很多。语音芯片选WT588D而不是ISD1820ISD1820只能录一段语音而且音质差。WT588D可以存储多段语音通过串口切换更适合报站系统。晶振选11.0592MHz而不是12MHz前面说过了串口波特率更精确。虽然这个项目不一定用串口但万一以后要加串口通信功能不用换晶振。7. 调试中踩过的坑与解决技巧7.1 LCD1602不显示的三种情况第一种屏幕亮但无字符。这通常是对比度问题调节V0引脚的电位器就能解决。如果调到极限还是不显示检查V0是否接对了有时候电位器接错线也会导致这个问题。第二种屏幕亮但显示乱码。这通常是数据线接触不良或者时序不对。检查P0口的连接确认上拉电阻是否接好。如果用的是杜邦线连接接触不良很常见建议焊接或者用排线。第三种屏幕完全不亮。检查背光引脚是否接对背光正极串的限流电阻是否合适。一般背光LED的正向电压是3V左右电流10-20mA5V供电的话串一个150欧姆到200欧姆的电阻比较合适。7.2 按键误触发的解决方法按键误触发是常见问题原因可能有几个消抖时间不够一般10-20ms的消抖时间就够了但如果按键质量差可能需要更长按键电路没有上拉电阻如果IO口内部上拉不够强需要外接上拉电源干扰电机等大功率设备工作时会引入干扰需要在电源端加滤波电容我的经验是软件消抖比硬件消抖更可靠。硬件消抖用电容但电容值不好选太小没效果太大影响响应速度。软件消抖灵活调整参数就行。7.3 语音模块调试技巧WT588D调试最大的问题是不知道语音有没有播放。我的做法是先用示波器看DATA线的波形确认脉宽是否正确如果波形正确但不播放检查语音文件是否烧录成功如果烧录成功但不播放检查扬声器是否接好如果都正常可能是地址不对尝试不同的地址还有一个技巧WT588D的BUSY引脚在播放时会输出低电平可以用单片机检测这个引脚来判断是否在播放。这样可以避免在播放时发送新的播放命令导致冲突。7.4 程序跑飞的排查思路程序跑飞是单片机开发中最头疼的问题。排查思路检查中断函数中断函数里不要做太多事情尽量简短检查堆栈51单片机的堆栈空间有限局部变量不要太多检查指针数组越界是常见原因特别是字符串操作加看门狗如果程序跑飞看门狗能自动复位我在调试时遇到过一次程序跑飞最后发现是数组越界。站名数组只有10个元素但代码里访问了第11个导致覆盖了其他变量。这种问题在仿真中可能不会暴露但实际硬件上就会出问题。8. 资料包内容与使用建议8.1 资料包包含什么这套资料包包含仿真文件Proteus工程文件可以直接打开运行代码完整的Keil C51工程包含所有源文件原理图Altium Designer格式的原理图也可以导出PDF硬件框图系统整体架构图软件流程图程序执行流程图元器件清单BOM表包含型号、数量、备注参考文档相关芯片的数据手册和应用笔记8.2 怎么用这套资料如果你是做课设建议按这个顺序先看硬件框图理解系统整体架构再看原理图理解每个模块的电路连接然后看软件流程图理解程序逻辑接着看代码对照流程图理解每一段代码的作用最后在Proteus中运行仿真观察实际效果如果有硬件把代码烧录到单片机中实际调试如果你是做产品开发这套资料可以作为参考但需要注意51单片机的资源有限如果功能复杂建议换STM32语音模块的选型要根据实际需求WT588D适合固定语音如果需要动态语音考虑SYN6288电源设计要考虑实际工作环境公交车上的电源波动很大需要加保护电路8.3 后续扩展方向这个系统还可以扩展很多功能GPS定位加一个GPS模块自动检测到站不需要手动按键无线通信加一个无线模块与调度中心通信实现远程监控语音合成用SYN6288替代WT588D实现任意文字转语音OLED显示用OLED替代LCD1602显示更多信息效果更好数据存储加一个EEPROM或者Flash存储线路信息方便更换线路我个人觉得GPS自动报站是最实用的扩展方向。手动报站需要司机操作容易忘记或者出错。GPS自动报站可以检测车辆位置自动触发报站减轻司机负担。9. 个人经验总结做这个项目最大的体会是硬件设计要考虑软件实现的便利性软件设计要了解硬件的能力边界。比如按键电路如果硬件上加了消抖电容软件上就可以简化消抖逻辑如果硬件上没有消抖软件上就必须做消抖处理。另一个体会是仿真和实物调试是两回事。仿真通过只是第一步实物调试才是真正的考验。我在仿真中跑得好好的代码烧到实物上就出现了LCD显示不稳定的问题最后发现是电源纹波太大加了一个100uF的电解电容才解决。最后分享一个小技巧调试LCD1602时如果怀疑是时序问题可以先把所有延时加长一倍确认能显示后再逐步缩短。这样能快速定位是时序问题还是其他问题。这个方法我在调试其他外设时也经常用很有效。这个项目后续还可以这样扩展把站点信息存储在EEPROM中通过按键或者串口修改站名这样就不需要重新编译代码就能更换线路。对于实际应用来说这个功能很实用。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑