摘 要:本文介绍了分时共用和I/O口模拟这两种串行通信接口扩展技术。利用此技术可解决调试和实际应用过程 中,单片机串行通信接口不够或者没有串行通信接口的问题。
关键词:串行通信接口,分时共用,I/O口模拟
前言
单片机的串行通信接口提供了与外设通信极大的方便性,虽然大多数单片机都具有硬件SCI(Serial Communications
Interface),但也有一些产品没有SCI,如Motorola的MC68HC908JL3等,对于这些产品的用户来说这是一个缺憾。而且,一般八位单片机只有一个SCI,但在很多实际应用,需要更多的串行通信接口。例如在基于Modem的远程控制系统中,单片机与PC机通信的同时,还需要与Modem芯片进行通信;而在多机系统中,单片机需要与其他主机通信,另外也需要与本机控制台通信。所以利用单片机自身的资源进行串行通信接口的扩展很有意义。
笔者在开发的过程中,总结了一些串行通信接口的扩展技术:分时共用,I/O口模拟。前者利用MCU自身的硬件SCI,通过控制逻辑分时共享使用同一串行通信接口,后者在不增加硬件的条件下,充分利用MCU自身的资源利用I/O口模拟串行通信接口。
串行通信接口原理
单片机的SCI是一个通用异步接收器/发送器UART(Universal Asynchronous Receiver /Transmiter)类型的异步通信接口,通过串行通信协议(如RS-232协议)同主机系统通信。
在一般应用中,MCU简单地把数据写入数据寄存器即可实现一个字符的串行发送,SCI系统完成发送数据的所有细节工作,包括附加起始位和停止位以符合串行格式。SCI的接收器自动探测一个字节的起始位,并通过采样接收数据。接收串行数据并变换成并行数据的所有工作均由SCI完成,不需要MCU的干预。接收到数据后,MCU简单地从数据接收寄存器读取数据即可。
SCI使用标准不归零(NRZ)格式(一个起始位,8个或9个数据位和一个停止位),最常用的格式数据位是8位的,如图1所示。
NRZ数据格式的基本特点如下:
高电平为逻辑1,低电平为逻辑0;
发送/接收数据空闲时TXD、RXD线为高电平;
发送/接收数据串的第一位是起始位(逻辑0);
数据的最低位LSB首先被发送/接收;
数据串的最后一位(第10位或第11位)是停止位(逻辑1)。
分时共用技术
分时共用串行通信接口技术利用MCU自身的硬件SCI,通过控制逻辑分时共享使用同一串行通信接口。控制逻辑分时共用串行通信接口的原理图如图2所示。
图2中U1为74HC32(或门),U2为74HC04(非门),U3为74HC08(与门)。RXD、TXD为从MCU直接引出的SCI接收引脚和发送引脚,PTC0为MCU的I/O口,在此定义为输出,用于控制逻辑片选。当PTC0为高电平时,U1的2引脚和13引脚输入为高,3引脚和11引脚输出恒为高,即TXD1输出恒为高,RXD1输入被屏蔽;而U1的5引脚和10引脚输入为低,6引脚输出为TXD的电平,8引脚输出为RXD2的电平,此时TXD2的输出和RXD2的输入与MCU的TXD和RXD相一致。相反,当PTC0为低电平时,TXD2输出恒为高,RXD2输入被屏蔽,TXD1的输出和RXD1的输入与MCU的TXD和RXD相一致。10K的上来电阻R1确保在初始状态下,即使PTC0未初始化的情况下,PTC0的电平为高,即初始状态下TXD2和RXD2有效。
通过这种逻辑控制方式,在PTC0的控制下,TXD1、RXD1和TXD2、RXD2可以轮流使用MCU的串行通信接口,达到分时共享的目的。这种方式可应用于两个串行通信无需同时进行的场合。
I/O口模拟技术
I/O口模拟串行通信接口技术在不增加硬件的条件下,充分利用MCU自身的资源利用I/O口模拟串行通信接口,即利用I/O口做一个软件串口。软串口的发送和接收时序和硬件SCI一样,空闲状态为高电平,起始位为低电平,数据最低位LSB在前,依次到最高位MSB,最后是停止位,停止位为高电平,具体时序见图1。
这里给出利用MC68HC908GP32的两个普通的I/O引脚PTD4和PTD5实现的软件串口。在MCU总线时钟为2.4576MHz的条件下,波特率为4800,一个起始位,8个数据位,无校验位,一个停止位。程序用C语言编写,编译环境为Hiware
C编译器。程序清单如下。
宏定义
#define exit_critical asm sei //关中断
#define enter_critical asm cli //开中断
#define idle asm nop //空闲指令
#define bit5 0x20 //定义位值
#define bit4 0x10
#define RX bit4 //定义SCI接收引脚
#define TX bit5 //定义SCI发送引脚
主程序
主程序完成接收、发送引脚的初始化,调用输入和输出函数
void main_loop(void)
{
unsigned char input;
…… //程序其他初始化
PORTD |= bit5; //SCI发送引脚初始化
DDRD |= bit5; //定义SCI发送引脚为输出
DDRD &= ~bit4; //定义SCI接收引脚为输入
input = getchar(); //调用SCI输入函数
putchar(input); //调用SCI输出函数
……
}
延时程序
为了保证发送和接收延时的精度,采用精简的嵌入式汇编语言编写。延时时间计算公式为(6 n+4)个总线周期。
void delay_time(char n)
{
asm{
L1:
DECA //1个总线周期
CMP #0 //2个总线周期
BGT L1 //3个总线周期
RTS //4个总线周期
}
}
发送函数
MCU总线时钟为2.4576MHz,发送数据遵循NRZ格式:一个起始位,8个数据位,无校验位,一个停止位。波特率为4800,即每位208
S。发送时直接逐位发送,相邻位间的延迟时间为208 S。延时控制通过反汇编代码,参看每条反汇编指令的周期数,进行计算。
void putchar(unsigned char outchar)
{
unsigned char count;
exit_critical; //关中断
PORTD &= ~TX; //输出起始位,低电平
for(count=0;count<8;count++) //8位数据输出
{
delay_time(80); //延时,确保相邻数据位间的间隔为208 S
if((outchar&bit0)==0)
PORTD &= ~TX; //输出位为0,输出低电平
else //输出位为1,输出低电平
{
PORTD |= TX;
idle; //补足空指令,使两种情况执行指令相同
idle;
idle;
}
outchar = outchar>>1; //数据右移,欲输出的位移至最低位
}
delay_time(80); //延时,确保数据MSB与停止位间间隔为208 S
PORTD |= TX; //输出停止位,高电平
delay_time(80); //延时,控制停止位时间长度为208 S
enter_critical; //开中断
}
接收函数
接收函数循环检测起始位的发生,一旦检测到起始位,起始位后大约300 S后,也就是从数据最低位LSB的中间位置起按一定的时间间隔逐位采样,相邻位间开始采样的时间间隔位208
S,为了提高抗干扰能力,每位采样三次。
unsigned char getchar(void)
{
unsigned char inchar,count,temp,i;
do{
}while((PORTD&RX) != 0); //检测起始位
delay_time(118); //起始位后300 s开始采样
for(count=0;count<8;count++) //数据位8位,由低至高接收(LSB至MSB)
{
inchar = inchar>>1; //数据右移,保证最终接收到数据位在最高位
for(i=0,temp=0;i<3;i++) //采样三次
{
if((PORTD&RX) != 0) temp += 1; //采样值为1
else //采样值为0
{
idle; //补足空指令,使两种情况执行指令相同
idle;
idle;
idle;
idle;
}
}
temp /=3; //3次采样值之和除以3
if(temp != 0) inchar |= bit7; //商位1,接收位为高电平
else //商位0,接收位为低电平
{
inchar &= ~bit7;
idle; //补足空指令,使两种情况执行指令相同
idle;
idle;
} delay_time(64); //延时,确保相邻位间开始采样的时间间隔位208 S
}
for(i=0,temp=0;i<3;i++)
{
if((PORTD&RX) != 0) temp += 1; //采样值为1
else //采样值为0
{
idle; //补足空指令,使两种情况执行指令相同
idle;
idle;
idle;
idle;
}
}
temp /=3; //3次采样值之和除以3
if(temp != 0) return(inchar); //停止位正确返回接收数据
else return(-1); //停止位错误返回-1
}
|