目前,在众多应用领域中,出现了许多所谓“智能仪表”和“智能仪器”,这些系统大多是以单片机为核心的计算机应用系统。在众多的单片机中,Intel公司的8XC196MC/MD可谓其中的佼佼者。它是16位单片机中功能最强大的单片机之一,几乎可以胜任各种测控工作,尤其在电机控制中备受青睐。然而由于8X196MC/MD单片机没有提供硬件的通用异步收发器(UART),这给用惯了UART的用户带来了一些不便。但利用专门的PTS模式,不仅可以实现串行通信,而且操作更加灵活,效率更高,CPU的开销也更小;既可以实现异步(ASIO)功能,也可以实现同步(SSIO)功能;波特率由EPA建立,包括校验位和停止位在内,收发数据格式可达16位/字符。
1 EPA和PTS概述[12]
1.1EPA及其工作原理
EPA ( Event Processor Array,事件处理器阵列),类似于HSIO,用来处理与时间有关的输入和输出事件,但比其更灵活、更高效。在EPA中,主要提供两类模块——捕获/比较模块和独立的比较模块,用于实现捕获和比较两种功能。每个模块都与指定的一个输入/输出引脚相关联,支持其高速输入输出功能,所有模块都能产生中断。“捕获”用来捕获产生于引脚上的跳变事件,包括正跳变、负跳变和正负跳变,并记录这些事件发生的时刻;“比较”是和预定的时间作比较,时间一到即执行以下选定的输出功能:复位定时器,启动一次A/D转换,为波形发生器产生一个重装载触发信号,改变输出引脚状态等。
1.2PTS及其工作原理
PTS ( Peripheral Transaction Server,外设事务服务器),是一种特殊的中断响应方式。与普通中断响应相比,PTS响应把同一个中断映射到相应的PTS通道。该通道产生一个PTS周期,它就像DMA周期那样插入到正常指令流中,不需要额外的软件开销,因此其CPU开销要比一般的中断响应少得多。PTS有一个PTS向量表,其排列次序和优先级顺序与普通中断向量相同,但去掉了NMI、非法操作码和软件陷阱3种中断。除了NMI之外,所有的PTS通道的优先级高于任何一个普通中断。每个PTS向量都指向一个PTS控制块(PTSCB),控制块说明了应执行的微代码。它必须驻留在内部RAM空间内,每个控制块包含8个字节,其首址应能被8除尽。
8XC196MC/MD有4种PTS工作方式,要实现串行通信需使用其SIO(串行输入/输出)方式。其中SIO有两种方式:ASIO(异步串行I/O)和SSIO(同步串行I/O)。要工作于某一方式必须建立相应的控制块(PTSCB)。与SIO方式相对应的控制块有两个,如图1所示。此处只对BAUD和SAMPTIME加以说明。
图1PTS SIO方式控制块
BAUD(LO和HI):存放控制SIO运行波特率的16位数据。异步方式下,由下式计算: FXTAL/(4×波特率×EPA预置值)。其中: FXTAL为XTAL1脚的输入频率,单位为Hz。
SAMPTIME:只用于异步接收多数采样方式,用来指定采样时间间隔,由下式计算: FXTAL×Tsam/2-9。其中,Tsam为采样间隔时间(μs);FXTAL同上,但单位为MHz。
2利用EPA和PTS实现串行通信 [23]
2.1基本思想
利用EPA和PTS实现串行通信的基本思想是:首先,选择一个EPA捕获/比较模块作为串行通信的接收模块,选择一个独立比较模块(或选择一个捕获/比较模块而只利用其比较方式)作为串行通信的发送模块,构成串行通信的硬件端口。然后,对所选的EPA模块开辟相应的PTS通道,根据通信要求编写发送和接收PTS模块。EPA和PTS两者联合工作共同实现串行通信。
2.2应用实例及具体实现
下面以常用的异步串行通信方式为例介绍具体的实现原理和方法。本例用一个EPA捕获/比较模块CAPCOMP0产生移位时钟,P2.0作发送端(TXD),波特率为9 600 bps,8位数据位,无校验位,1位停止位,用定时器1作时基,16 MHz晶振。由于篇幅所限,仅介绍异步串行发送。
2.2.1建立并定位控制块
要利用PTS实现串行通信,必须首先定义相应的控制块,并将其定位于能被8整除的首地址处;还要将控制块地址赋给PTS向量。具体实现代码如下:
#define F16 /*晶振频率(MHz)*/
#define BAUD_RATE9600 /*波特率*/
#define BAUD (F *250/BAUD_RATE*1000)
/* SIO PTS 控制块定义*/
typedef struct ASIO_ptscb_t{
unsigned charptscount;
unsigned charptscon;
void*epareg;
unsigned intbaud;
struct ASIO_ptscb2_t *ptsvec;
}ASIO_ptscb;
typedef struct ASIO_ptscb2_t{
void*portreg;
unsigned charportmsk;
unsigned charptscon1;
unsigned intdata;
unsigned charsamptime;
int:8;
}ASIO_ptscb2;
ASIO_ptscb SIO_CB1;
#pragma locate(SIO_CB1=0x0110)
/*将控制块1定位在寄存器中*/
ASIO_ptscb2 SIO_CB2;
#pragma locate(SIO_CB2=0x0118)
/*将控制块2定位在寄存器中*/
#pragma pts(SIO_CB1=0X02)
/*将控制块地址赋予PTS控制向量*/
2.2.2发送初始化及PTS中断子程序
在发送时,要用到EPA中的一个比较模块,或者使用一个捕获/比较模块而使其工作于比较方式下,主要用作发送时产生移位时钟,以保证指定的位周期。其具体过程如下:首先,在指定的发送引脚上产生一个下降沿(为确保产生下降沿,应先对该引脚置1),并根据指定的波特率对EPA比较模块的时间寄存器(CAPCOMPx_TIME或COMPx_TIME)置值,以指定发送的位周期。然后,打开中断和PTS功能,启动数据发送过程;以后每隔一个位周期便会产生一个PTS周期,在每个PTS周期中,将DATA寄存器中的数据逐位移到指定的发送脚上,直到最后一位移出后,产生一次End_of_PTS中断,该帧数据发送完毕。每帧数据的位数在控制块的ptscount中指定。如果需要继续发送数据,就要在中断服务程序中对PTSCB、EPA时间寄存器和相应引脚重新初始化,再次启动发送过程。发送数据保存在transmit数组中,发送帧数,即数据个数由T_Count控制。具体实现代码如下:
unsigned char Transmit[7]={1,2,3,4,5,6,7}; /*待发送数据*/
int T_Count; /*发送计数*/
unsigned char TXDDONE=0; /*发送结束标志*/
void Init_pts_ASIO_Tran { (int t_count) /*初始化子程序*/
disable(); /*禁止中断*/
disable_pts();
/*端口及定时器初始化*/
p2_mode = 0; /*设P2口为标准I/O*/
p2_dir = 0x00; /*P2口互补输出*/
p2_reg = 0xff; /*置P2口高电平*/
t1control=0xC0; /*T1:向上计数,内部时钟,预分频系数=1*/
/*TXD方式初始化*/
SIO_CB1.ptscon=0x60;/*ASIO发送*/
SIO_CB1.epareg=(void*)&capcomp0_time;
/*capcomp0_time地址*/
SIO_CB1.baud = BAUD; /*波特率*/
SIO_CB1.ptsvec = (void *)&SIO_CB2;
/*第2个控制块地址*/
SIO_CB2.portreg = (void *)&p2_reg;
/*发送口地址P2*/
SIO_CB2.portmsk = 0X01;/*发送脚为P2.0*/
/*准备发送*/
T_Count = t_count; /*准备发送的帧数*/
TXDDONE = 0; /*清接收结束标志*/
SIO_CB1.ptscount= 9; /*每一帧的位数*/
SIO_CB2.ptscon1 = 0x00; /*无校验*/
SIO_CB2.data = Transmit[T_Count]; /*置发送数据*/
ptssel |= 0x04; /*允许capcomp0 PTS*/
p2_reg &= 0XFE; /*清p2.0*/
capcomp0_con = 0x40;
/*capcomp0比较方式,软件定时器*/
capcomp0_time =timer1+BAUD; /*起始位宽度*/
int_mask = 0x04; /*允许capcomp0中断*/
enable_pts(); /*开中断*/
enable();
}
#pragma interrupt(End_of_PTS2 = 2)
void End_of_PTS2(void) { /*PTS中断子程序*/
while( !(int_pend & 0x04) );
/*等待最后一个PTS周期的中断*/
capcomp0_con = 0x00; /*禁止CAPCOMP0*/
int_pend &= 0xFB; /*清虚假中断*/
if( --T_Count ) /*数据计数器减1并检查发送是否结束*/
{/*未发送完指定帧数,准备发送下一帧*/
SIO_CB2.data = Transmit[T_Count];
/*取下一个要发送的数据*/
SIO_CB1.ptscount=9; /*发送位数*/
SIO_CB2.ptscon1=0x00; /*无校验*/
ptssel |=0x04; /*允许capcomp0 PTS*/
p2_reg |= 0x01; /*置p2.0*/
p2_reg &= 0xFE; /*清p2.0(起始位)*/
capcomp0_con = 0x40;/*比较方式,只是中断*/
capcomp0_time = timer1+BAUD; /*位宽度*/
return;
}
else
{/*设置结束标志,退出*/
TXDDONE = 1;
return;
}
}
2.2.3在主程序中调用初始化子程序
void main( void ) {
Init_pts_ASIO_Tran(6);
while( !TXDDONE );
}
结语
该方法和实例已在实际应用中得到验证。实践证明其完全可以满足串行通信的需要,而且效率更高,更加灵活。