H42基于51单片机数码管双机通讯proteus设计,keil程序,c语言,AT89C51
作者:单片机仿真   类别:51单片机仿真    日期:2026-11-10 12:48:39    阅读:8 次   消耗积分:0 分

H42 用两片 51 单片机做数码管双机通讯:各带一个按键、两位数码管和两个 LED,串口交叉连线,每按一次把本地计数值发给对方(0~15 循环),对方收到即显示并用两个 LED 表示数值低两位;与 H41 共用同一套串口骨架,附 Keil 程序、Proteus 仿真与设计报告。

H42基于51单片机数码 图1

01 设计任务

功能介绍:

使用两片 51 单片机做串口双机通讯,本机按键触发、对方显示,要求如下:

1、双向数据传递:两片单片机串口交叉连线(TX 对 RX、RX 对 TX、共地),同一份程序烧两片,谁都能发谁都能收;

2、按键循环发送:本机只有一个按键,每按一次把本地计数值发出去,然后计数值加一(0~15 循环)——对方看到的是"你按到第几下";

3、接收端双重指示:数码管显示收到的数值,同时两个 LED 按数值的低两位显示四种组合(00 / 01 / 10 / 11);

4、共阴段码:table 从 0x3F 起(共阴,1 亮),与上一篇 H41 的 0xC0 起(共阳,0 亮)正好相反;

5、9600 波特率:串口方式 1、定时器方式 2 自动重装,TH1 = TL1 = 0xFD,晶振 11.0592MHz。

 特别注意:上一篇 H41 用的是 4×4 矩阵键盘,按哪个键就发哪个键值——发的是"按键编码";这一篇换成一个按键,发的是"本地计数值":

键值不为 0 时先把当前计数值送出去,紧接着让计数值自增,加过 15 就回到 0。

别小看这个改动,两者的语义完全不同:发按键是"我按了什么",发计数是"我数到几了"。后者的接收端看到的是对方的一个状态,而不是一次事件——这就是遥测(telemetry)的雏形。理解了这一点,串口就不只是"传个字符",而是"两台机器同步状态"。


H42基于51单片机数码 图2


数码管双机通讯proteus仿真整机图Keil工程代码界面

02 骨架没变:三行初始化照旧

如果你看过上一篇 H41,这段代码会非常眼熟——Init_Uart() 和 TX_data() 一个字都没改:

初始化里把串口设成方式 1 并开接收,定时器 1 切方式 2 自动重装、两个重装寄存器都装 0xFD,最后打开总中断、启动定时器;发送函数把字节写进发送缓冲,等发完标志置起后把它清掉。

这说明一件很重要的事:串口通讯是"标准件"。波特率算法、寄存器配置、发送握手,在所有 51 项目里都一样,抄过去就能用。真正要动脑的是"发什么、收到之后干什么"——这部分才因项目而异。

也正因为是标准件,那几个坑(晶振必须 11.0592MHz、TX/RX 必须交叉、发送必须等 TI)在每一篇里都要重新叮嘱一遍。这里只快速复述结论,推导过程见 H41:

· TH1 = 0xFD = 253 → 256-253 = 3 个机器周期溢出一次 → 11059200/(12×3)/32 = 9600 精确无误;

· 换 12MHz 晶振 → 12000000/(12×3)/32 ≈ 10417,误差 8.5%,超出串口约 ±5% 的容差,必乱码;

· 接线:A_TXD(P3.1) → B_RXD(P3.0),A_RXD(P3.0) ← B_TXD(P3.1),GND 必须共地。

图上两台机器之间那两根横穿全图的长线,就是这一对交叉信号线。

03 发送端:一个按键,一个计数器

H42 的键盘部分极简,就一个按键接 P1.7:

检测到引脚跳低后先复检一次电平,确认仍是低才返回 1、并记下这一次已经处理过;引脚回到高电平才把这条记录清掉,其余情况一律返回 0。

flag 是"已经处理过这次按下"的标记,保证按住不放只触发一次、松开才能再触发。5ms 延时是消抖,else if(K1==1) flag=0 是"松手复位"。

为什么 H42 用 5ms、H41 用 10ms? 都是经验值,机械按键抖动一般持续 5~20ms。延时太短消不干净(会连发两次),太长按键变迟钝。5~10ms 都是安全区间。

主循环每轮先读键,有键时调用发送函数把计数值送出去,然后计数值加一并做 0~15 的回绕,末尾统一延时 10ms。

注意先发后加的顺序:mode 从 0 开始,第一次按键发出去的是 0,然后 mode 变 1;第二次按发 1……第 16 次按发 15 然后回到 0。所以对方屏幕上的序列就是 0,1,2,...,15,0,1,...。

如果写成"先加后发",对方看到的就是 1,2,...,15,0,1——从 1 开始。这种差一位的问题,调试时最容易怀疑"串口丢帧了",其实是自己写错了顺序。发送和自增的先后,决定了对方看到的第一帧是什么,改之前先想清楚。

还有一个细节:mode 上限 15 是因为接收端用 table[mode] 查段码表,表里只有 16 项。发送端的取值范围必须和接收端的查表范围对齐,这是两个程序之间的"隐形契约"——单字节裸通讯没有类型检查,发个 20 过去,对方就查到表外面去了。

04 接收端:数码管 + 两个 LED 的双重指示

接收还是标准的中断写法,但这次收到数据后干了两件事:

中断里确认 RI 置起后清掉标志,把 SBUF 的字节取到临时变量,然后分两路用它:一路送去驱动数码管,一路对 4 取余、按余数给两个 LED 引脚赋四种高低组合。

第 ① 件是数码管:P0 直接输出段码。这里的 table 是共阴表:

十六个元素依次对应 0 到 F 的段码,从 0x3f 起排到 0x71。

0x3F = 二进制 0011 1111,低 6 位全 1(a~f 段亮、g 段灭)= 数字 0。共阴数码管是"1 点亮",所以段码里 1 越多越亮。对比 H41 的 0xC0(1100 0000,a~f 全 0 = 全灭,g 段 1 = 亮)——那是共阳表,0 点亮。

同样的数字 0,两套表的段码完全互补(0x3F 与 0xC0 逐位取反)。所以换数码管必须换段码表,接错极性的典型表现是:显示的数字缺段、或者每个数字都显示成一个"8"缺一块。

第 ② 件是两个 LED 显示数值的低两位,temp % 4 得到 0~3 四种状态:

temp % 4LED1LED2二进制含义
0亮(0)灭(1)00
1灭(1)亮(0)01
2亮(0)亮(0)10
3灭(1)灭(1)11

两个 IO 表达 4 种状态,正好是 2 bit 的信息量——这就是"用最少的引脚表达最多状态"的经典做法。实际项目里,设备的运行/待机/故障/报警四态、或者通讯链路的"正常/重发/超时/断开"四态,都可以用两个灯的组合表示,不占屏幕、不用额外器件。

不过这里有个逻辑上的小别扭:LED 亮灭和二进制位是反的(t==0 时 LED1 亮,而 00 的两位都是 0)。原因是 LED 低电平点亮(LED1=0 才亮),所以"亮"对应"0"。想让灯的组合直观反映二进制,把判断里的值取反就行:

最低位用位与取出来直接赋给第一个 LED,次低位同样用位与取出、右移一位后赋给第二个 LED。

两行搞定 4 种分支,而且灯灭=0、灯亮=1,和二进制一致。能用位运算就别写 if 链——这是这份程序可以改进的地方。

05 一个值得注意的地方:两位数码管同显

老实说,这份程序的显示部分有个"硬件比软件多走了一步"的地方:图上是两位一体的数码管,但程序里只有 P0 = table[temp],没有任何位选(扫描)代码。

这意味着两件事:

1. 如果两位的位选线接的是固定电平(都选通),那两位会同时显示同一个数字(比如收到 5,屏上显示 55);

2. 想让两位显示不同内容(比如左边显示十位、右边显示个位),必须加动态扫描——按 H23(74HC138 位选 + 段码)、H26(5 根位选线)、H37(三极管位选)的写法,用定时器每几毫秒切一位。

看懂这一点,就明白"数码管显示"这个词在不同项目里含金量完全不同:一位数码管只要送段码,多位数码管就必须"送段码 + 选位 + 快速轮换"三件事配合。后面如果你要把这个设计升级成"显示两位数字",第一步不是改 table,而是先把位选扫描补上。

06 中断里该做什么、不该做什么

两篇通讯程序(H41、H42)的接收中断都只做了三件事:清标志、取数据、输出。H42 多了 LED 判断,但依然没有延时、没有循环、没有等待。

这是刻意守的纪律。中断服务程序里最忌讳两件事:

· 长时间阻塞:一个 Delay_1ms 就会让主循环的按键扫描变迟钝,还会影响串口接收下一帧的时机;

· 调用会"等"的函数:比如 TX_data 里的 while(!TI) 就不能放进接收中断——一旦在中断里发数据、又等发送完成,整个系统就卡在那 1ms 里了。

所以正确的分工是:中断只做"搬运",复杂处理(判断、计算、显示刷新)放到主循环。H42 里 LED 判断只有几行位运算,放中断里没问题;但如果收到数据后要查表、算 CRC、刷整屏 LCD,那就该在中断里只置一个"收到新数据"的标志,让主循环去处理。

顺带提醒一个这份程序没做、但产品里必须做的检查:

取到字节后先判它是否小于 16,小于才去查段码表显示,否则这一帧直接丢掉。

单字节裸通讯没有帧头、没有校验,一旦线路干扰或对方发了超范围的值,table[temp] 就会读到表外。课程设计可以不管,做成产品必须加。

07 排错清单

1、对方乱码 / 收到怪字符:先看晶振是不是 11.0592MHz。12MHz 下 9600 波特率误差 8.5%,串口必乱;Proteus 里双击晶振看属性;

2、完全没反应:查 TX/RX 是否交叉、是否共地。TX 接 TX 是新手第一坑;

3、显示的数字缺段 / 所有数字都像"8"少一块:段码表共阳/共阴用反了。这份是共阴表(0x3F 起),接共阳数码管要换成 0xC0 起那套(H41 的表可直接抄);

4、按一次键,对方跳了好几个数:flag 防连发失效(比如 static 丢了),或者 Delay_1ms(5) 消抖被去掉;

5、对方从 1 开始数、不是从 0 开始:发送和自增的顺序反了,见 03 节;

6、两位数码管显示两个一样的数字:正常——程序没有位选扫描,两位同时选通(见 05 节)。想显示不同内容要加动态扫描;

7、LED 组合看起来和二进制反着:LED 低电平点亮,t==0 时灯反而亮。用 LED1 = t & 0x01; 这种位运算写法更直观(见 04 节)。

08 总结

H41 和 H42 是同一套串口骨架的两个应用,放在一起看特别清楚:


H41 双机通讯H42 数码管双机通讯
发送源4×4 矩阵键盘,16 键单按键 + 本地计数器
发送内容按键编码(一次事件)计数值(一个状态)
接收表现一位共阳数码管两位数码管 + 两个 LED(低 2 位)
段码表共阳(0xC0 起)共阴(0x3F 起)
初始化/发送/接收完全相同完全相同

通讯层是标准件,业务层才是差异——记住这句话,以后遇到"双机通讯"类的题目,代码框架可以直接搬,只需要改"发什么"和"收到后怎么表现"。

再往下走,串口能干的事还有很多:加帧头校验做可靠传输、一发多收(多机通讯用 SM2 和第 9 位)、跟电脑上位机通讯(串口助手看数据)。这些都是在今天这套三行初始化的基础上长出来的。

欢迎关注,持续更新!


09 文件日期修改

如果你想把文件修改为最新的日期,又不想新建工程,那么你可以参考下面的文章。

"如何不打开程序的情况下更改proteus和keil程序的日期?"

工具和仿真文件在一起,见下面的下载链接。


10 资料下载

全套资料包含 Keil 工程(C 语言源码 + hex 文件)、Proteus 仿真工程、设计报告、程序流程图、讲解视频,以及编译截图和仿真截图。下载后可直接打开运行,也可用于课程设计、毕业设计参考。

点我下载全套资料 →

也可以关注公众号「单片机仿真」,回复 H42 获取下载入口。公众号内还有 A / T / H 全系列 400 多个单片机仿真实例,回复「目录」可查看完整清单。

同类设计合集:H 系列单片机仿真合集目录(含设计报告、视频讲解、流程图);全部系列合集总目录

版权所有,转载本站文章请注明出处:proteus单片机
上一篇:H41基于51单片机双机通讯proteus设计,keil程序,c语言,AT89C51
下一篇:H43基于51单片机秒表proteus设计,keil程序,c语言,AT89C51
${comment['nickname']}   ${comment['createtime']}
  
       
${comment.content}
${reply.nickname} 回复 ${comment.nickname}    ${reply.createtime}
     
  
回复内容:${reply.content}