实验室札记
用普通I/O口进行串行通信产生的问题

🏠 首页 《无线电》杂志 2005年 🔗 第10期 🔗 第45页 分类:电脑·单片机·通信 🔗 张春峰 🔗

串行通信是单片机的一个重要功能。但当MCU的固有串口资源不够时,可否用其他普通的I/O口来进行单片机与单片机之间的串行通信呢?显然是可以的。不过对初学者来说,要实现这普通I/O口之间的串行数据通信,可能会碰上不少麻烦。

实例

笔者在设计研制电力变压器自动排线绕线机的智能控制装置时,由于现有串行口的资源不够,单片机与单片机之间只好用普通的I/O口进行串行数据传送了,其电路结构如附图所示。主单片机MCU 1用的是P0.4、P0.5两个普通I/O口;副单片机MCU 2用的是一个具有外部中断功能的P3.2和一个普通口P3.5。其中普通口P3.5作为数据传送口,外部中断0的输入端P3.2运用其下降沿触发中断的特性来作为串行数据传送的“同步时钟”输入。这样,一来能便捷有效地与主单片机MCU 1协调同步,二来副单片机MCU 2接受数据的具体处理也就可由外部中断服务程序来进行了。外部中断服务子程序如下:

图1
图1 🔍原图 (567×457)

INTO:

PUSH A;中断处理前保

护“现场”

PUSH PSW

MOV C,SDA;读入数据

RLC A ;带进位左移

DJNZ 3AH, I0FW ;8位数据

是否读齐了?

MOV 32H,A ;将读齐的8位数

据存入32H单元中

MOV 3AH,#08H;赋8次中断

循环的初值

SETB 29H;置“1”已读入一个

字节数据的标志位

I0FW:

POP PSW ;中断恢复“现场”

POP A

RETI ;中断返回

这段MCU 2上的中断服务小程序乍一看似乎很合理。MCU 1每传送一位数据,其作为同步时钟控制的引脚P0.5都将输出一个由“1”到“0”电平的“负”跳变以作MCU 2外部中断的下降沿触发信号。MCU 2每中断一次也就接收一位数据,待一个字节8位接收齐了就把数据存入MCU 2内部RAM的32H单元中。然而,用仿真器监察32H中接收的数据,不是“00H”就是“01H”,根本不是MCU 1所发传送的数据!

问题分析

为什么接收到的只是十六进制数“00H”或“01H”,而不是所发送的数据呢?仔细查看上面那段中断子程序,发觉确有漏洞,因为,累加器A是“公用”的,虽然中断程序里执行了“进栈”、“出栈”的处理,但由于是同一个累加器A,所以必定有内存资源冲突“之嫌”。累加器A虽然经过“PUSH A”处理,但不管中断前累加器A中是什么内容都将被带到中断服务程序中,而把前一次中断读入(移入到累加器A)的数据替代了。如果中断前累加器A的内容是“00H”,那么读入结果只能是最后送来的一位“0”或“1”了。

解决措施

基于以上的感悟,在中断接收处理中引入一个RAM中的35H单元作为暂存的缓冲寄存器。这样中断服务子程序就改成如下所示:

INTO:

PUSH A;中断处理前保护“现场”

PUSH PSW

MOV A,35H;将35H单元中的

内容还原给A

MOV C,SDA;读入数据

RLC A ;带进位左移

MOV 35H,A;将读取的数据暂存

到35H单元

DJNZ 3AH, I0FW;8位数据是否

读齐了?

MOV 32H,A;将读齐的8位数据存

入32H单元中

MOV 35H,#00H;清“0”35H单

元为下一次暂存新数据作准备

MOV 3AH,#08H;赋8次中断循环

的初值

SETB 29H;置“1”已读入一个

字节数据的标志位

I0FW:

POP PSW;中断恢复“现场”

POP A

RETI

这样每读入一位数据都将被暂存到35H单元中,直到最终结果存入32H中。可是结果“有喜有忧”,接收存到32H单元中的数据虽能随发送数据的变化而变化,但数据还是很乱,也就是说接收到的数据还不是MCU 1所发送来的数据!怎么回事?我们用两台仿真器分别仿真两个MCU进行断点监察,发送一位监察一位,结果发现这样一位一位地发送,一位一位地接收,其最后结果却是准确无误的!

为什么一位一位地进行,结果是对的,而一串数据连起来发送就乱了呢?这个问题困扰了好长一段时间,后来意识到可能是数据传送的速率太高所致。因为输出引脚电平(不管是高或是低)的建立都需要一个过程,若频率太高,时间太短,端口上电平的建立就不稳定了,也就容易出错了。在此想法下,将MCU 1发送子程序的相关处插入了好多条“空操作”指令(NOP)以作延时,来缓和数据端口变化电平的稳定建立。结果效果很显著。将发送子程序进行缓冲改进后,MCU 1发送出去的数据就能被MCU 2准确地接收了。改进后的发送子程序如下:

sSEND:

MOV 50H,#08H;设置传送的

数据是8位

CLR C;清“0”进位“位”

sSEND1:

SETB cSclk;将“同步时钟”

端置高电平

NOP;执行“空操作”

NOP

……

NOP

RLC A;将累加器A连同进位

“位”C左移一位

MOV cData,C;将移到进位“位”

C的数据传送出去

CLR cSclk;将“同步时钟”端置

低电平

NOP;执行“空操作”

NOP

……

NOP

NOP

DJNZ 50H,sSEND1;是否传送8

位了?

SETB cSclk;“同步时钟”端置

高电平

RET ;调用返回

其实,在MCU 2中断接收子程序中也插入了若干条“空操作”指令NOP,以保障中断接收时能可靠地读取数据。

小结

在单片机的运用中,程序的设计编写往往都是以相关理论的计算推导为准的。但也有很多情况下,单片机的运行却往往在“理论之外”,需要通过具体的实验来调整,尤其是在晶振频率比较高的场合。

文/张春峰