简介:基于51单片机与Proteus仿真的无线叫号系统设计资源,面向单片机课程设计、电子竞赛及医院/银行等需要排队取号与叫号联动的场景,适合基础学习者动手实践。系统包含主机与从机两个终端:主机侧通过独立按键完成叫号,数码管清晰显示当前被叫号码,蜂鸣器同步发声提醒顾客;从机侧基于串行通信完成自助取号功能,并实时显示队列前方等待人数,从而实现双机协同的无线叫号流程。压缩包共61个文件,主要涵盖Proteus仿真图、AD原理图、Keil工程源码(C语言与汇编启动代码)、hex烧录文件、原理图PDF预览及设计文档等,整体约1.82MB,目录结构清晰,便于按需查阅。目前已有69人学习,资料中附有文件清单与说明文本,可辅助理解各模块功能和引脚连接。整套资源不仅覆盖多机通信、数码管动态显示、按键处理、蜂鸣器驱动等核心知识点,还提供可直接运行与修改的工程,方便二次开发,是一套完整且易上手的课程设计参考方案。
1. 无线叫号机仿真,51单片机项目到底在仿什么
你按一下取号按钮,柜台再按一次“呼叫下一号”,显示屏上就多一位号码,这就是叫号系统的全部业务闭环。现实中它由射频模块、高低频震荡电路和数码管显示共同完成,但落到“基于51单片机protues仿真的无线叫号机”这种课程设计上,最容易被绕进去的地方是:Protues仿真里根本没有空气,也没有电磁波。所谓“无线”在仿真图中的真实形态,是一根把发射端I/O和接收端I/O直接连起来的导线,或者干脆只是一个同名网络标号。
因此这个项目真正要仿的是链路协议,不是载波调制。你要处理的只有三件事:发射端按键怎么把号码编成脉冲串,接收端怎么把脉冲串解出来,以及这段数据如何在51单片机的定时器和外部中断之间流转。把这三件事拆开,再配上数码管显示、蜂鸣器提示和AD原理图重绘,就是一个可以在Proteus里反复运行、也能拿去答辩的完整设计。文章写给正在做51单片机课程设计的人,也适合已经能点亮LED、想再走一个带通信协议的项目练手的同行。
2. 51单片机无线叫号系统架构、选型与链路分工
2.1 叫号链路先拆成四块,再决定用什么器件
一套完整的无线叫号机,从功能上可以拆成四个独立块:按键取号与呼叫输入、编码发射、解码接收、号码显示。实体产品里这四块有各自的电路,而在51单片机protues仿真里,最常见的做法是两块AT89C51芯片分别承担发射和接收,中间用一对PT2262/PT2272编解码芯片充当无线链路的数据载体。
功能块与器件选型可以按下表来定:
| 功能块 | 常用器件 | 仿真中的作用 |
|---|---|---|
| 发射端主控 | AT89C51 / STC89C52 | 读取按键状态,生成叫号数据帧 |
| 编码发射 | PT2262 或 I/O 模拟 | 把地址和号码打成串行脉冲 |
| 接收端主控 | AT89C51 | 外部中断接收脉冲,软件解码 |
| 解码接收 | PT2272 或 中断+定时器 | 识别同步码和数据位 |
| 号码显示 | 两位共阳数码管 / LCD1602 | 显示当前排队号码 |
| 提示单元 | 蜂鸣器、LED | 取号成功、呼叫提示 |
这里先说一个原则:仿真时如果Protues元件库有PT2262和PT2272,可以直接放置并连接它们的Dout与Din引脚;如果库版本里没有这两个芯片,不要硬等,用51的I/O口模拟2262的发射时序即可。“51单片机模拟pt2262工作及发射”本身就是很多课程设计的标准退路,它绕开了芯片库缺失问题,也让时序变成可调试的源代码。你在答辩时还能多讲一句“载波调制不是本设计重点”,反而把项目核心聚焦到叫号逻辑上。
2.2 无线链路参数:同步码、数据位“1”和“0”的宽度比
PT2262的编码格式是串行脉冲,典型参数为:同步码由4T高电平和124T低电平组成,数据位“1”在一个码元宽T内先高电平后低电平,比例约2:1;数据位“0”则反之,比例约1:2。这里的T由编码芯片的振荡电阻决定,典型值振荡电阻选1.2MΩ,解码端PT2272选200kΩ左右。
关键参数整理成表:
| 参数项 | 典型值 | 说明 |
|---|---|---|
| 振荡电阻(发射端) | 1.2 MΩ | 决定编码时钟T |
| 振荡电阻(接收端) | 200 kΩ | 需要与发射端匹配 |
| 同步码 | 4T 高 + 124T 低 | 帧起始标志 |
| 数据位 1 | 高2/3 T + 低1/3 T | 用脉宽比例表示 |
| 数据位 0 | 高1/3 T + 低2/3 T | 与电平绝对值无关 |
| 地址位 | A0~A7 三态 | 可设高、低、悬空 |
在仿真环境中,我不会真的去按比例还原射频载波,而是用一组方波代替:同步码发送一段较长的低电平,数据位用不同的高电平持续时间区分“1”和“0”。这样接收端只需要一个定时器测量脉冲宽度,就能完成解码。下面两行定义就是仿真里替代天线和射频模块的最小连接:
sbit RF_TX = P1^0; // 发射端输出,替代PT2262的Dout引脚 sbit RF_IN = P3^2; // 接收端外部中断输入,替代PT2272的Din引脚在Protues里把RF_TX和RF_IN直接用导线连起来,或给两端取同一个网络名,就完成了无线链路的仿真等效。载波、天线增益、接收灵敏度这些参数在这个层级上不需要考虑,因为51单片机只能识别高低电平,而编码数据也是靠高低电平持续时间传递的。
2.3 51单片机引脚资源分配,避免显示和通信打架
一块AT89C51的I/O资源做这种项目够用,但要注意P0口是开漏结构,驱动共阳数码管时需要在每一位加10kΩ上拉电阻;P2口驱动位选时也要注意电流限制。发射端和接收端的引脚分配建议如下:
| 功能 | 发射端引脚 | 接收端引脚 |
|---|---|---|
| 取号按键 | P3.2 | - |
| 呼叫下一号按键 | P3.3 | - |
| 无线数据输出/输入 | P1.0 | P3.2 /INT0 |
| 数码管段码 | P0 | P0 |
| 数码管位选 | P2.0~P2.1 | P2.0~P2.1 |
| 蜂鸣器 | - | P1.7 |
接收端选择P3.2作为无线数据输入,是因为它同时也是外部中断INT0引脚,下降沿可以自动触发中断,配合定时器测脉宽,比在主循环里轮询电平更稳定。发射端不需要中断,按键扫描放在主循环即可。这样分配之后,代码里不会出现两个功能抢同一个引脚的问题,后续画AD原理图时网络关系也清晰。
3. 在Protues里搭建无线叫号系统仿真图
3.1 放置51芯片与最小系统,Protues入门常错的三个点
打开Protues后,第一步是通过左侧Component Mode点“P”进入元件库。搜索AT89C51添加主控芯片;再搜索RES、BUTTON、CAP-ELEC、7SEG-COM-ANODE添加电阻、按键、电解电容和共阳数码管。具体操作可以按下面顺序执行:
1. 点击左侧工具栏的 Component Mode(元件模式) 2. 点击对话框左下角 P(Pick Devices)打开库浏览器 3. Keywords 输入 AT89C51,双击添加到设计 4. 继续搜索 RES、BUTTON、CAP-ELEC、7SEG-COM-ANODE 5. 放置元件后,右键旋转元件,按已有图纸布局Protues入门最容易错的有三个地方:第一,晶振不用像画AD原理图那样非要放一个XTAL,只要双击单片机在“Clock Frequency”属性里填12MHz即可;第二,复位电路不是必须加,仿真里RST脚悬空也能跑,但如果要画AD原理图,就必须补上10kΩ复位电阻和10μF电容;第三,放置电阻后要改阻值,用鼠标双击电阻元件直接改,而不是重新搜索。
3.2 无线链路直连与网络标号,通信引脚这样接
发射端RF_TX和接收端RF_IN的连线,可以采用两种方式:布局在同一张图纸时直接放置Wire连接;如果为了整洁把发射和接收分成两个区域,则用“LBL”工具放置网络标号。给P1.0所在的导线命名RF_TX,给接收端P3.2所在导线也命名RF_TX,两者就默认电气连接,不需要拉一根横穿整张图的线。
这里涉及一个常见问题“protues怎么添加引脚”:如果你需要的芯片在库里找不到现成符号,可以用左侧Terminal Mode里的DEFAULT引脚,放置后用导线连接并改名。但大多数情况下,直接用51引脚替代编解码芯片更高效。仿真的重点是协议,是发送端和接收端之间这根线上跑的时序,不是引脚封装本身。
连接完成的检查点有三个:发射端P1.0是否接到网络RF_TX,接收端P3.2是否也接入RF_TX,两边的GND是否统一接入同一地网络。Protues仿真中即使不画GND线,芯片的GND引脚默认连接也会参与仿真,但养成接地的习惯对后面AD原理图重绘有好处。
3.3 数码管接线、电源设置与HEX文件装载
叫号号码用两位数字显示就足够,一位显示十位,一位显示个位。段码通过共阳数码管的a~g、dp引脚接51的P0口,位选接P2.0和P2.1。如果代码里段码表按共阳方式编码,这里就不能接共阴数码管,否则显示会反相,表现为数字乱跳或相同段全亮。
装载编译好的HEX文件是让仿真跑起来的最后一步,操作如下:
1. 双击AT89C51芯片,打开 Edit Component 对话框 2. Program File 一栏选择 Keil 输出的“叫号发射端.hex” 3. Clock Frequency 保持 12MHz,与Keil工程设置一致 4. 同样方式给接收端单元格载入“叫号接收端.hex” 5. 点击左下角运行按钮Program File是编译后的目标文件,Protues通过它模拟单片机执行程序,因此程序逻辑错误只能在源码层面修改后重新编译。Clock Frequency必须和Keil中选用的晶振一致,否则软件延时或定时器超时的时间基准就会整体偏移,表现为发射端发送过快、接收端解码失败。仿真报错时优先查看仿真控制台最后一行的错误信息,最常见的两类错误是HEX路径为空和元件引脚重复连接。
4. Keil C源代码实现叫号:编码、解码与显示逻辑
4.1 发射端源代码:取号、呼叫下一号与脉冲帧生成
发射端负责两件事:按取号键时把当前排队号加一,按呼叫键时发送最新号码。数据帧结构我固定为“同步码 + 1字节地址 + 1字节号码”,地址固定为0x5A,号码范围1~99。下面是发射端最核心的三个函数:
/* 发射端主程序:取号、呼叫、发送一帧数据 */ #include <reg51.h> #define ADDR_CODE 0x5A // 发射端与接收端约定的地址 sbit KEY_TAKE = P3^2; // 取号按键 sbit KEY_CALL = P3^3; // 呼叫下一号按键 sbit RF_TX = P1^0; // 无线发射引脚 unsigned char current_no = 1; void delay_us(unsigned int n) { while (n--); } void delay_ms(unsigned int n) { unsigned int i, j; for (i = 0; i < n; i++) for (j = 0; j < 120; j++); } void send_frame_head(void) { RF_TX = 1; delay_us(400); // 同步码高电平 RF_TX = 0; delay_us(1200); // 同步码低电平 } void send_byte(unsigned char dat) { unsigned char i; for (i = 0; i < 8; i++) { if (dat & 0x80) { // 数据位“1”:宽高电平 + 窄低电平 RF_TX = 1; delay_us(600); RF_TX = 0; delay_us(300); } else { // 数据位“0”:窄高电平 + 宽低电平 RF_TX = 1; delay_us(300); RF_TX = 0; delay_us(600); } dat <<= 1; } } void send_frame(unsigned char no) { send_frame_head(); send_byte(ADDR_CODE); send_byte(no); delay_ms(10); } void main(void) { while (1) { if (KEY_TAKE == 0) { while (KEY_TAKE == 0); // 松手消抖 send_frame(current_no); // 取号并发送当前号 } if (KEY_CALL == 0) { while (KEY_CALL == 0); if (current_no < 99) current_no++; send_frame(current_no); // 呼叫下一号 } } }这段代码的关键在于把“无线发射”理解成一组不同宽度的方波。同步码的400μs高电平加1200μs低电平,是接收端判断帧起始的边界;数据位“1”和“0”的脉宽比例是2:1,只要接收端能识别这个比例就能解码,不需要关心绝对时间精度。地址码0x5A放在每帧第二段,目的是过滤相同环境里可能存在的其他干扰帧。注意这里使用了源码中的软件延时,在Keil默认优化级别O0下时序基本稳定,如果开启高优化,必须改成定时器延时。
4.2 接收端源代码:外部中断配合定时器做脉冲宽度解码
接收端如果用轮询方式读RF_IN,51单片机单周期指令会漏掉短脉冲,因此正确做法是把数据线接到INT0,利用下降沿触发中断,同时用Timer0记录两次下降沿之间的时间。宽度大于设定的经验阈值时判为“1”,小于阈值判为“0”,超长的低电平则判定为同步码并复位当前字节。
/* 接收端中断解码:INT0下降沿 + T0测脉冲宽度 */ #include <reg51.h> #define ADDR_CODE 0x5A #define WIDTH_1 450 // 经验阈值,单位是定时器计数 sbit BUZZER = P1^7; volatile unsigned char rx_byte = 0; volatile unsigned char bit_cnt = 0; volatile unsigned char state = 0; volatile unsigned char recv_no = 0; volatile unsigned char frame_ready = 0; void timer0_init(void) { TMOD = 0x01; // 定时器0,16位模式 TH0 = 0; TL0 = 0; ET0 = 0; TR0 = 1; } void int0_isr(void) interrupt 0 { unsigned int t; t = (TH0 << 8) | TL0; // 读取上次清零到这次的计数 TH0 = 0; TL0 = 0; if (t > 1200) { // 同步码对应的超长低电平 state = 0; bit_cnt = 0; rx_byte = 0; return; } if (t > WIDTH_1) rx_byte |= 0x80 >> bit_cnt; // 宽电平判为“1” if (++bit_cnt >= 8) { bit_cnt = 0; if (state == 0 && rx_byte == ADDR_CODE) { state = 1; // 地址接收成功,准备号码 rx_byte = 0; } else if (state == 1) { recv_no = rx_byte; // 号码字节 frame_ready = 1; state = 0; rx_byte = 0; } else { state = 0; rx_byte = 0; } } } void main(void) { IT0 = 1; // INT0下降沿触发 EX0 = 1; EA = 1; timer0_init(); while (1) { if (frame_ready) { frame_ready = 0; BUZZER = 0; // 低电平驱动蜂鸣器 // 此处调用数码管显示 recv_no BUZZER = 1; } } }接收解码最核心的判定动作发生在中断里:读TH0和TL0拼接出两次下降沿之间的时间t,然后把t和阈值WIDTH_1比较。阈值450的取值依据是12MHz晶振下,350μs对应的定时器计数约为420,600μs约为720,取中间值450能稳定区分。同步码因为低电平持续1200μs,计数超过1200,可以放心作为帧复位信号。使用外部中断的好处是CPU不参与电平轮询,主循环可以同时处理数码管动态扫描和蜂鸣器控制,不会因为显示刷新丢数据。
4.3 叫号逻辑的边界:防抖、重复帧和号码上限
叫号系统看似简单,实际跑仿真最容易出的问题有两个:一次按键发出多帧,以及号码超过99后溢出。前面的代码已经用while等待松手处理了按键抖动,但如果你用的是实体按键,最好在while里加一段5ms延时再判断IO电平,否则机械抖动可能导致一次按下被当成两次。发射帧重复发送问题可以通过在接收端加一个“上次号码缓存”解决,连续两帧号码相同且值不为0时,只刷新显示不触发蜂鸣器。
| 现象 | 常见原因 | 对策 |
|---|---|---|
| 按一次取号键跳两个号 | 按键消抖不足 | 主循环加松手检测,必要时加5ms延时 |
| 接收端显示乱码 | 地址字节或号码字节顺序错 | 检查帧结构,确认先发地址再发号码 |
| 号码超过99后退回0 | unsigned char溢出 | 发送前判断current_no是否小于99 |
| 仿真运行时死循环 | 定时器未清零或中断标志未清 | 在中断入口重新赋TH0、TL0 |
这段逻辑做完,发射端和接收端的源代码就已经能配合Protues仿真图完整跑通。下一步要做的是把同样的电路从仿真图搬成可生产的AD原理图,并在转换中验证连接关系。
5. AD原理图重绘与仿真波形验证
5.1 从Protues仿真图到AD原理图的重绘路径
Protues的仿真图和AD原理图不能直接一键互导,常见做法是重绘。重绘时把发射端和接收端放在两个Sheet中,用同网络名跨页连接。在AD里放置网络标号的快捷键是P和N,放置后双击网络标号可改名;要跳转同名网络时,先点选中一个Net Label,再通过“Edit -> Jump”输入网络名即可定位到另一页相同网络,这也是处理AD原理图网络跳转最直接的方法。
重绘前先把Protues里的连接关系整理成列表,逐项检查。电源和地是AD中最容易漏掉的部分:Protues仿真里POWER端子会自动提供5V逻辑电平,但AD必须显式安排电源网络,发射端和接收端各放一个电源地网络,混合信号电路不能有悬空的电源引脚。
5.2 AD原理图规则检查与常见警告处理
AD检查原理图有没有连上,用的是工程编译功能。在工程面板右键项目名,选择Compile PCB Project,Messages面板会把所有Error和Warning列出来。这里的Error通常代表网络短路或引脚悬空,必须清零;Warning常见于未连接电源引脚或元件位号重复,例如两个AT89C51的复位引脚没有网络标号,DRC会报“Unconnected Pin”。处理方法是给未连接引脚放置“No-ERC”标记,或者直接补上网络。要注意的是,Protues允许IO悬空不报错,AD中悬空I/O引脚会被严格提示,这会逼着你去确认每一个引脚的实际用途。
5.3 用Protues虚拟示波器实测时序,验证发射与解码一致性
代码和AD原理图都完成后,回到Protues重新运行仿真,调出左侧虚拟仪器里的OSCILLOSCOPE,把通道A接RF_TX网络,通道B接接收端解码输出缓冲引脚的暂态点。运行后用示波器观察波形,重点看三点:
1. 帧与帧之间是否有间隔,间隔是否稳定 2. 同步码之后是否跟着两组数据位 3. 高电平宽度与低电平宽度比例是否符合2:1如果同步码后没有数据位,检查发射端send_frame是否在同步头之后调用send_byte;如果数据位全是等宽方波,检查WIDTH_1阈值是否把数据和同步码混在一起。你可以给示波器的横轴调到1ms/div,一轮帧总共约20ms,五格内就能看到完整帧结构。这个方法比直接看数码管显示结果更早暴露时序问题,因为数码管可能恰好显示正确,但帧边界已经错位,换一组号码就乱。把示波器截图留存,答辩时可以直接作为测试依据,也方便后续把同样的验证流程复制到其他51单片机硬件设计项目里。
本文还有配套的精品资源,点击获取