51单片机公交车智能报站系统设计与Proteus仿真实现
2026/9/8 9:24:05 网站建设 项目流程

简介:这是一份面向单片机课程设计与毕业设计场景的公交车智能报站系统完整方案,基于51单片机为主控,使用Proteus仿真、Keil编程,覆盖了智能报站常见功能,适合借助仿真熟悉开发流程的初学者参考。系统通过按键切换自动与手动两种报站模式:自动模式定时切换站点,手动模式通过上一站/下一站按键触发;上下行由LED指示灯区分,报站状态通过LED与LCD1602液晶屏实时显示,功能链路完整。资源包共34个文件、约53.25MB,主要包含Proteus仿真图(dsn/pdsprj)、Keil工程源码(c/h/uvproj/hex)、讲解视频(avi)及备份与辅助文件(bak/txt),结构完整,目录划分适合按需查阅。目前已有148人学习下载,仿真图可直接打开运行验证逻辑,源码模块化程度较高,配合讲解视频可快速理解报站流程与按键切换原理,便于二次开发和移植到其他单片机平台,也可作为课程设计答辩与项目演示的参考。 公交车智能报站系统,是我大学时代课程设计里的一道经典题目。最近整理网盘,翻出当时做的一套完整资料——Proteus仿真图、C语言源代码、还有一份自己录的演示讲解视频,正好拿来给正在做单片机课设或者毕业设计的同学做个参考。这套东西做完大概花了三天,时间主要耗费在调试语音模块的时序上,仿真图反而半天就搭完了。我的方案用的是51单片机(AT89C51)+LCD1602显示+独立按键模拟上下行和进站触发,在Proteus 8环境下跑通,编译环境是Keil C51。标题里提到的仿真图、源代码和讲解视频,我这里都有,但更重要的是把设计和实现思路讲清楚,毕竟直接把代码拷过去交差,很容易被老师问住。这篇博文就把完整的设计过程拆开揉碎讲一遍。

1. 项目需求与功能规划

1.1 公交车报站系统到底要解决什么问题

公交车上的报站器,核心功能是“到了哪一站,就告诉乘客下一站是哪”。听起来简单,但实际里有两个关键点:一是司机不可能每次进站都手动按几十次键盘,所以需要自动化或半自动化触发;二是报站内容要分两种情况,进站前报“下一站xx到了,请准备下车”,出站后报“欢迎乘坐xx路公交车,下一站是xx”。此外,公交车还有上行和下行之分,方向变了,站序就反了。我的设计用状态机把这两个逻辑理顺。

很多人觉得这个题目太简单,就用几个按键加上一个蜂鸣器,按一下“嘀”一声就算报站,答辩时根本拿不出手。真正像样的设计至少要包含四个模块:站序控制模块(上下行切换、站号增减)、显示模块(当前站和下一站)、语音播报模块(或仿真中用等效的LED串口提示)、手动自动切换模块(方便司机操作)。我的系统把自动报站做成了按键触发式——仿真里没有GPS模块,就用“进站按键”代替位置传感器,这是仿真环境下最贴近真实场景的折中方案。

1.2 功能拆解与器件选型对比

功能上,我设计了六条公交线路,但代码里通过数组配置很容易扩展,题板用的是“上行6站+下行6站”。核心参数和功能如下:

  • 上行/下行切换:用一个拨动开关或者独立按键控制方向标志位;
  • 站数显示:LCD1602两行分别显示“CUR: xxx”和“NEXT: xxx”;
  • 报站触发:独立按键K1模拟“出站/进站”,每按一次,站号递增/递减;
  • 开关门提示:仿真中用一个LED闪烁模拟动力开关门动作;
  • 语音播报:正式项目通常用ISD1760或SYN6288语音合成芯片,但Proteus没有直接模型,我就用串口输出的方式向虚拟终端打印播报文本,并在讲解视频中同步说明真实硬件接法。

选型上,控制核心优先用AT89C51,因为Proteus自带这个模型,Keil里选Atmel AT89C51直接编译烧写,不需要额外配置。如果换成STC89C52,也能兼容,但仿真里要专门选STC型号。LCD1602是五块钱就能买到的字符液晶,用来显示站名足够;如果学校要求高,可以上用12684中文液晶或者OLED,但代码复杂度会上升。语音芯片方面,ISD1700系列是模拟录音芯片,录的是真实人声,适合“欢迎乘坐xx路公交车”这种固定语句;SYN6288是TTS芯片,可以动态合成任意站名,但接线和串口字协议复杂。仿真阶段,虚拟终端是最省事的选择。

下表是我对比过的几种方案:

方案显示模块语音播报仿真难度答辩亮点
基础版LED数码管蜂鸣器
推荐版(我的方案)LCD1602虚拟终端+串口,实物可接ISD1760有语音逻辑
进阶版12864中文屏SYN6288 TTS全自动动态播报

我的建议是,别一上来就追求复杂,先把基础的站序逻辑和显示跑通,再考虑语音,这样时间分配更合理。

2. 系统硬件设计与仿真搭建

2.1 核心电路设计:单片机最小系统

单片机要工作,三个条件缺一不可:电源、时钟、复位。Proteus里放置AT89C51后,必须给它补上晶振电路和复位电路,否则仿真一跑程序就卡在初始状态或者直接不运行。晶振我用的12MHz,两个30pF电容接地;复位电路用10uF电解电容和10k电阻组成上电自动复位,这个和51手册里的典型电路完全一致。

需要注意的一个细节是,Proteus里AT89C51的RST引脚不是默认低电平有效的,是高电平复位。很多新手习惯性忽略复位电路,导致程序能编译、仿真图能画,但运行后P1口电平怎么都不对。我第一次做的时候就是漏了这个,折腾了半个小时才反应过来。所以,画完图别急着写代码,先往P1口接8个LED,随便写一个端口电平翻转程序,看LED有没有反应,这是最快验证最小系统是否能跑通的方法。

电源部分,Proteus仿真不需要额外接VCC/GND,元件属性里默认5V,但实物制作时必须在40脚接+5V,20脚接GND,还要并接104电容做电源去耦。我见过很多同学在仿真里一切正常,焊板子后程序乱跑,就是供电滤波没做好。实际做PCB时,建议单片机电源引脚旁边放一个10uF和一个0.1uF电容并联,离引脚越近越好。

2.2 按键与显示模块接线要点

按键模块我用的是P1口的P1.0-P1.3,分别代表“进站/出站”“上行/下行切换”“自动/手动模式”“模拟GPS信号(随机触发)”。51单片机的P1口内部有上拉电阻,硬件上可以直接把按键一端接P1引脚,另一端接地,按下为低电平,程序通过判断端口是否为0来检测动作。很多人问“要不要加上拉电阻”,P1口内部已经有上拉,不需要外部加电阻,只有P0口作为输入时才需要,这地方是高频考点,答辩时老师很喜欢问。

显示模块用LCD1602,数据线接P0口,控制线RS、RW、EN分别接P2.5、P2.6、P2.7。这里有个关键点:P0口是开漏输出,必须接上拉电阻排(10k排阻),否则LCD1602显示乱码或者不显示。我在Proteus里用了一个RESPACK-8排阻,一端接P0,另一端接VCC,显示效果立刻正常。如果实物调试,用4个10k电阻也可以,但排阻接线更整洁,而且避免飞线过多干扰信号。

LCD1602的软件驱动并不复杂,用標準库函数控制使能端时序即可。我习惯把LCD驱动单独成文件,底层函数包括lcd_init()、lcd_write_cmd()、lcd_write_data()、lcd_set_cursor()。这里给出初始化代码的关键部分,可以直接拿去用:

void lcd_init(void) { lcd_write_cmd(0x38); // 8位数据,双行显示,5x7点阵 lcd_write_cmd(0x0C); // 显示开,光标关 lcd_write_cmd(0x06); // 写入数据后光标右移 lcd_write_cmd(0x01); // 清屏 }

注意,写命令前要判断忙标志(读BF位),或者干脆用延时等待,Proteus仿真中延时3ms左右足够。如果实物用12MHz晶振,建议写一个毫秒级延时函数,因为不同LCD模块的响应速度有差异,时序太紧会导致花屏。

2.3 语音播报模块的仿真替代方案

语音播报是报站器的灵魂。仿真里没有真实的语音芯片,我做了一个“串口语音输出”模块:将AT89C51的TXD和RXD连接到Proteus的VIRTUAL TERMINAL(虚拟终端),单片机通过串口发送字符串,虚拟终端显示文本,用来模拟语音播报内容。同时在讲解视频里演示,如果换成真实硬件,串口应当连接ISD1760的SPI接口或SYN6288的UART接口。

这个方案的巧妙之处在于,程序里保留了完整的播报语句数组和发送函数,只是把硬件发声替换成了文本输出。比如播报“下一站:人民广场,请准备下车”,代码里就是:

void station_broadcast(uint8_t dir, uint8_t index) { char buf[32] = {0}; sprintf(buf, "Next station: %s", station_names[index]); uart_send_string(buf); }

虚拟终端上看到文本,副作用是调试极其方便——代码运行到哪一步、站号对不对、方向切换有没有成功,一目了然。等真正做实物时,把uart_send_string替换成语音芯片的播放指令即可,业务逻辑完全不用动。这也是仿真设计的精髓:逻辑功能优先,硬件关联最后适配。

3. 软件代码框架与核心算法实现

3.1 主程序状态机设计

报站系统的控制流程可以用有限状态机来描述,我设计了三个状态:行车状态(RUN)、靠站状态(STOP)、切换方向状态(TURN)。主循环不断检测输入事件,根据当前状态决定要做什么。

状态机的好处是让代码结构清晰,不至于一团乱麻。比如,行车状态下按“进站按键”,系统进入STOP,站号+1,显示“Arrive: xxx”,同时触发语音“xxx站到了”;再按一次“出站按键”,系统回到RUN,显示“Next: yyy”,语音播放“欢迎乘车”。如果按下“方向切换”,则站号清零,序列反向。

主循环的伪代码如下:

void main(void) { sys_init(); while(1) { key_scan(); if(flag_stop == 1) { handle_stop_state(); } else if(flag_turn == 1) { handle_turn_state(); } else { handle_run_state(); } } }

很多同学的代码喜欢把全部逻辑塞在一个中断里,或者用一个超大switch-case,这样调试很痛苦。我的经验是,把所有按键扫描、状态更新、显示更新分离成独立函数,每个函数只干一件事。这样即使代码有bug,也可以通过串口输出直接定位到具体模块。

3.2 上下行切换与站号递增判定逻辑

这是整个项目最容易出错的地方。公交车可以上行也可以下行,上行从始发站到终点站,站号递增;下行反过来,站号递减。所以我定义了一个方向标志位dir,1表示上行,0表示下行。

进出站的逻辑分两种情况:

  • 上行时,每进站一次,站号+1,当站号等于总站数N时,自动切到下行,站号变为N-1;
  • 下行时,每进站一次,站号-1,当站号等于0时,自动切到上行,站号变为1。

这里有个难点:为什么下行时始发站是N-1而不是N?因为公交车在终点站发车时,如果没有“当前站”为“终点站”的报站需求,应该自动播报“欢迎乘坐xx路,下一站xx”,所以始发站的站号定位到第N-1站,即终点的前一站。有的设计是用一个专门的“start_flag”来避免歧义,但更简洁的方式是让站号代表“当前站下标”,在进出站瞬间再更新“下一站下标”,这样计算不会乱。

我提供的源程序里用了一个station_id变量,初始化为0,表示当前还没到站。进站判定时,根据dir决定station_id自增还是自减,然后通过station_names[station_id]取站名。站名数组如下:

code char station_names[MAX_STATIONS][12] = { "Zhongshan Road", "People Square", "Railway Station", "City Hall", "Park", "Airport" };

答辩时老师问“为什么用code关键字”,答案是为了把数组放到程序存储区而不是RAM里,因为51单片机的RAM很小(128字节),站名很多的时候RAM根本放不下。这也是嵌入式开发的基础考点。

3.3 语音播报与显示驱动的代码片段

显示部分,我做了四个页面:初始欢迎页、当前站页、下一站页、方向切换页。LCD1602两行最多显示16个字符,为了美观,站名不能超过16字节,建议用英文或拼音。有的同学用中文LCD模块,那需要额外做字库映射,Proteus自带字符型LCD不支持汉字,所以我的仿真中全部用英文或者拼音站名,这也是一个务实的取舍。

串口发送函数,这里是标准的51串口初始化:

void uart_init(void) { TMOD = 0x20; // 定时器1工作在方式2 TH1 = 0xFD; // 波特率9600 TL1 = 0xFD; TR1 = 1; SCON = 0x50; // 串口方式1,允许接收 }

虚拟终端波特率要设置成9600,数据位8,无校验,1停止位,否则接收到的是一堆乱码。我见过有同学在Proteus里用了默认的2400波特率,虚拟终端显示外星文,以为是代码问题,其实是两边参数不一致。

4. 仿真调试、常见问题与排查技巧实录

4.1 Proteus仿真时的常见坑

第一个大坑是电源没接好。Proteus原理图里如果不给单片机添加电源网络,仿真时会直接报错“Invalid opcode”或者界面闪退。解决办法是,AT89C51默认是有VCC和GND的,但如果你用到了其他元件(如运放),它们可能需要显式接电源;对于单片机,最简单的办法是直接放置一个VCC和GND的终端符号,连接到对应引脚。

第二个坑是晶振不起振。Proteus中默认晶振频率12MHz,但如果你从元件库里选的晶体模型过于简化,仿真可能不运行。我的建议是不要纠结晶振模型,直接把XTAL1和XTAL2引脚对接上“simulation clock source”,或者在晶振设置里选择“Digital model”而不是“Analog model”,否则会出现振荡器未能起振的警告。

第三个坑是用P0口直连LED不亮。P0口是开漏,驱动LED必须外接上拉电阻,否则高电平输出能力几乎为零。我推荐的排阻值在1k到10k之间,选4.7k或10k都可以。如果是驱动数码管或LCD的数据线,排阻必须接,否则显示亮度不均匀,甚至完全没反应。

第四个坑出现在虚拟终端上:单片机通过串口发送数据,虚拟终端却一个字符都显示不出来。排查顺序是:先检查波特率设置、再检查TXD引脚是否接到了虚拟终端的RXD(交叉连接)、最后检查发送函数是否真的被调用。多数情况是引脚接错,因为虚拟终端有两个输入引脚RXD和TXD,容易搞混,单片机TXD要接到虚拟终端的RXD。

4.2 代码编译和烧录的雷区

Keil C51编译时,最经典的问题是忘记勾选“Create HEX File”。很多人在Keil里写完代码,不知道编译后没有输出HEX文件,就说单片机烧不进程序。具体操作是:Project窗口右边找到Target1,右击选“Options for Target”,在Output标签页勾上“Create HEX File”,再重新编译,这样项目目录下就会生成.hex文件。

另一个常见雷区是“C51编译器内存模式设置”,如果你用了大数组,比如站名表,没有加code关键字,编译时经常报“DATA segment too large”错误。解决办法是把固定查表数据都放到code区(ROM),只把变量放到data或xdata。51内核ROM大RAM小,这一点要刻在脑子里。

烧录仿真时,Proteus双击AT89C51芯片,加载HEX文件,点击开始仿真。如果程序运行不对,先在Keil里用软件仿真(Debug模式)看变量值,或者用Proteus的调试工具(比如虚拟示波器、电压探针)观察引脚电平。我一般是在关键变量处加断点,或者用串口print某些状态值,比一点一点单步要高效得多。

4.3 从仿真到实物的移植注意事项

仿真跑通后,很多人觉得万事大吉,直接焊板子,结果各种翻车。我这里列几个实物移植时容易忽略的点:

  • 晶振两端要接22-30pF负载电容,不要直接裸接晶振,否则可能起振不稳定;
  • 单片机复位电路必须有,否则一上电程序乱跑,我建议用典型RC复位电路(10uF+10k);
  • 按键一定要做软件消抖,因为机械按键按下瞬间会产生几十毫秒的抖动,Proteus仿真里往往没有这个物理现象,不处理的话站号可能一次跳两个。消抖方案是延时20ms再读一次电平,或者用定时器扫描去抖;
  • LCD1602的对比度调节电位器(V0引脚)要接滑动变阻器,在实物中这个电位器非常关键,调不好就是满屏方块,仿真图里一般省略这步,很多人忽略了;
  • 语音芯片的供电电流比较大,建议用独立稳压芯片。如果直接用单片机的5V引脚带ISD1760,音质会变得很柴,而且单片机可能复位。

从我的经验来看,仿真和实物最大的差异就在这些“看不见的物理世界”:上拉电阻、负载电容、电源纹波、按键抖动。把实物当成一个更严苛的仿真调试环境,出现问题时先对照原理图检查硬件,再怀疑软件逻辑,这样排查效率最高。

最后再分享一个小技巧:如果你想在答辩时让老师眼前一亮,可以在仿真里加一个红外传感器或霍尔传感器模型,模拟公交车进站时的真实触发,这样报站系统就从“手动按键”升级成了“自动检测”,技术含量瞬间不一样。我的资料里保留了这套扩展思路的说明,也有对应的参考代码,感兴趣的话可以在仿真图上继续深化。做课程设计也好,毕业设计也罢,不要只满足于功能“跑通”,多问自己一句“为什么这么设计”“还有没有更好的方案”,收获会大很多。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询