串行通信是单片机的一个重要功能。但当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接受数据的具体处理也就可由外部中断服务程序来进行了。外部中断服务子程序如下:

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,以保障中断接收时能可靠地读取数据。
小结
在单片机的运用中,程序的设计编写往往都是以相关理论的计算推导为准的。但也有很多情况下,单片机的运行却往往在“理论之外”,需要通过具体的实验来调整,尤其是在晶振频率比较高的场合。
文/张春峰