利用PCA解决软件串口的问题。PCA包含一个专用的16位计数器/定时器和五个16位的捕捉比较模块,每个模块都可以在被设置为在PCA计数器与相对应的比较模块的内容一致时产生中断。由于PCA计数器在产生中断时并不停止运行,所以可避免中断延迟累加的问题。本例还编写了用硬件串口HW_UART来测试模拟串口SW_UART。由HW_UART发送15个字符,SW_UART接收。同时由SW_UART发送15个字符,HW_UART接收。
#include<c8051f000.h>
#define BAUD_RATE 57600 //用户定义的波特率
#define SYSCLK 18432000 //系统时钟取自外部18.432MHZ晶体
#define TIME_COUNT SYSCLK/BAUD_RATE/4 //对应一个位时间的PCA计数值
#define TH_TIME_COUNT TIME_COUNT*3/2 //3/2位时间,在接受到一个起始位之后
//使用。在起始位边沿之后RX应在一个位
//时间内保持低电平,第一个位采样在下一
//个位时间的中间开始
#define HW_TIME_COUNT SYSCLK/BAUD_RATE/16 //用于产生HW_UART波特率的定时计数值。
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
bit SRI; //接收完成标志
bit STI; //发送完成标志
bit STXBSY; //发送忙标志
bit SREN; //接收允许
sbit SW_RX=P0^2; //接收引脚
sbit SW_TX=P0^3; //发送引脚
char TDR; //发送数据寄存器
char RDR; //接收数据寄存器
//测试变量
char k,m; //测试计数器
char idata SW_BUF[20]; //测试接收缓冲区
bit HW_DONE; //HW发送结束标志(发送完15个字符)
bit SW_DONE; //SW发送结束标志
//-----------------------------------------------------------------------------------------------
//函数原型
//-----------------------------------------------------------------------------------------------
void SW_UART_INIT(); //SW_UART初始化程序
void SW_UART_ENEABLE(); //SW_UART允许程序
void PCA_ISR(); //SW_UART中断服务程序
void POLLED_TEST(void); //SW_UART查询方式测试程序
void HWU_INIT(void); //HW_UART初始化程序
void HW_UART_ISR(void); //HW_UART中断服务程序
//----------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------
void MAIN(void)
{
int delay;
OSCXCN=0x66; //允许外部晶体
WDTCN=0xDE; //禁止看门狗定时器
WDTCN=0xAD;
XBR0=0x0C;
XBR2=0x40;
PRT0CF=0x09;
delay=256; //在查询XTLVLD之前延时>1ms
while(!(OSCXCN & 0x80)); //等待外部晶体起振
OSCXCN=0x0C; //切换到外部振荡器
OSCXCN=0x88;
while(!(OSCXCN & 0x80)); //等待外部晶体起振
OSCXCN=0x08; //切换到外部振荡器
POLLED_TEST(); //调用查询方式SW_UART测试程序
while(1);
}
//----------------------------------------------------------------------------------------------
//函数
//----------------------------------------------------------------------------------------------
void POLLED_TEST(void)
{
SW_UART_INIT(); //初始化SW_UART
SW_UART_ENEABLE(); //允许SW_UART
SREN=1; //允许SW_UART接收器
HWU_INIT(); //配置HW_UART,用于测试程序
k=m=0; //请0测试计数器变量
HW_DONE=0; //请0传送完成标志
SW_DONE=0;
IE|=0x10; //允许HW_UART中断
TI=1; //HW_UART发送
//-----------------------------------------------------------------------------------------------
//用SW_UART接收15个字符;用HW_UART发送
while(SREN) //在SW_UART被允许时执行
{
if(SRI){ //如果接收完成
SRI=0; //清楚接收标志
SW_BUF[k++]=RDR; //读接收缓冲区
if(k==15){ //如果已收到15个字符
SREN=0; //禁止SW_UART接收器
}
}
}
//=================================================================================================
//用SW_UART发送15个字符;用HW_UART接收
while(STXBSY); //查询忙标志
STXBSY=1; //占用SW_UART发送器
TDR=m++; //装发送数据
CCF1=1; //通过强制PCA模块1中断来启动第一次SW_UART发送
while(!SW_DONE)
{
if(STI){ //如果发送完成
STI=0; //清除发送标志
if(m<16){ //发送15个字符
STXBSY=1; //占用SW_UART发送器
TDR=m++; //发送,变量增1
CCF1=1; //强制模块1中断以启动发送
}
else
SW_DONE=1;
}
}
}
//-------------------------------------------------------------------------------------
void HWU_INIT(void)
{
PCON|=0x80; //SMOD=1(HW_UART使用定时器1溢出,不分频)
TMOD=0x20; //配置定时器1为HW_UART所用
CKCON|=0x10; //定时器1用SYSCLK作为时钟
TH1=-HW_TIME_COUNT; //定时器1初始值
TL1=-HW_TIME_COUNT; //定时器1重装载
TR1=1; //启动定时器1
RI=0; //清除HW_UART接受和发送
TI=0;
SCON=0x50; //配置HW_UART为方式1,允许接受
}
//-------- --------------------------------------------------------------------------
void SW_UART_INIT(void)
{
PCA0CPM0=0x10; //模块0为负沿捕捉方式,禁止模块0中断
PCA0CPM1=0x48; //模块1为软件定时器方式,禁止模块1中断
PCA0CN=0; //PCA保持禁止状态
PCA0MD=0x02; //PCA时基=SYSCLK/4;禁止PCA计数器中断
CCF0=0; //清除PCA模块0和1捕捉比较模块
CCF1=0;
SRI=0; //清楚接受完成标志
STI=0; //清除发送完成标志
SW_TX=1; //将TX线初始化为高电平
STXBSY=0; //清除SW_UART忙标志
}
//----------------------------------------------------------------------------------
void SW_UART_ENEABLE(void){
PCA0CPM0|=0x01; //允许PCA模块0(接收中断)
PCA0CPM1|=0x01; //允许PCA模块1(发送中断)
CR=1; //启动PCA计数器
EIE1|=0x08; //允许PCA中断
EA=1; //全局中断允许
}
#include<c8051f000.h>
#define BAUD_RATE 57600 //用户定义的波特率
#define SYSCLK 18432000 //系统时钟取自外部18.432MHZ晶体
#define TIME_COUNT SYSCLK/BAUD_RATE/4 //对应一个位时间的PCA计数值
#define TH_TIME_COUNT TIME_COUNT*3/2 //3/2位时间,在接受到一个起始位之后
//使用。在起始位边沿之后RX应在一个位
//时间内保持低电平,第一个位采样在下一
//个位时间的中间开始
#define HW_TIME_COUNT SYSCLK/BAUD_RATE/16 //用于产生HW_UART波特率的定时计数值。
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
bit SRI; //接收完成标志
bit STI; //发送完成标志
bit STXBSY; //发送忙标志
bit SREN; //接收允许
sbit SW_RX=P0^2; //接收引脚
sbit SW_TX=P0^3; //发送引脚
char TDR; //发送数据寄存器
char RDR; //接收数据寄存器
//测试变量
char k,m; //测试计数器
char idata SW_BUF[20]; //测试接收缓冲区
bit HW_DONE; //HW发送结束标志(发送完15个字符)
bit SW_DONE; //SW发送结束标志
//-----------------------------------------------------------------------------------------------
//函数原型
//-----------------------------------------------------------------------------------------------
void SW_UART_INIT(); //SW_UART初始化程序
void SW_UART_ENEABLE(); //SW_UART允许程序
void PCA_ISR(); //SW_UART中断服务程序
void POLLED_TEST(void); //SW_UART查询方式测试程序
void HWU_INIT(void); //HW_UART初始化程序
void HW_UART_ISR(void); //HW_UART中断服务程序
//----------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------
void MAIN(void)
{
int delay;
OSCXCN=0x66; //允许外部晶体
WDTCN=0xDE; //禁止看门狗定时器
WDTCN=0xAD;
XBR0=0x0C;
XBR2=0x40;
PRT0CF=0x09;
delay=256; //在查询XTLVLD之前延时>1ms
while(!(OSCXCN & 0x80)); //等待外部晶体起振
OSCXCN=0x0C; //切换到外部振荡器
OSCXCN=0x88;
while(!(OSCXCN & 0x80)); //等待外部晶体起振
OSCXCN=0x08; //切换到外部振荡器
POLLED_TEST(); //调用查询方式SW_UART测试程序
while(1);
}
//----------------------------------------------------------------------------------------------
//函数
//----------------------------------------------------------------------------------------------
void POLLED_TEST(void)
{
SW_UART_INIT(); //初始化SW_UART
SW_UART_ENEABLE(); //允许SW_UART
SREN=1; //允许SW_UART接收器
HWU_INIT(); //配置HW_UART,用于测试程序
k=m=0; //请0测试计数器变量
HW_DONE=0; //请0传送完成标志
SW_DONE=0;
IE|=0x10; //允许HW_UART中断
TI=1; //HW_UART发送
//-----------------------------------------------------------------------------------------------
//用SW_UART接收15个字符;用HW_UART发送
while(SREN) //在SW_UART被允许时执行
{
if(SRI){ //如果接收完成
SRI=0; //清楚接收标志
SW_BUF[k++]=RDR; //读接收缓冲区
if(k==15){ //如果已收到15个字符
SREN=0; //禁止SW_UART接收器
}
}
}
//=================================================================================================
//用SW_UART发送15个字符;用HW_UART接收
while(STXBSY); //查询忙标志
STXBSY=1; //占用SW_UART发送器
TDR=m++; //装发送数据
CCF1=1; //通过强制PCA模块1中断来启动第一次SW_UART发送
while(!SW_DONE)
{
if(STI){ //如果发送完成
STI=0; //清除发送标志
if(m<16){ //发送15个字符
STXBSY=1; //占用SW_UART发送器
TDR=m++; //发送,变量增1
CCF1=1; //强制模块1中断以启动发送
}
else
SW_DONE=1;
}
}
}
//-------------------------------------------------------------------------------------
void HWU_INIT(void)
{
PCON|=0x80; //SMOD=1(HW_UART使用定时器1溢出,不分频)
TMOD=0x20; //配置定时器1为HW_UART所用
CKCON|=0x10; //定时器1用SYSCLK作为时钟
TH1=-HW_TIME_COUNT; //定时器1初始值
TL1=-HW_TIME_COUNT; //定时器1重装载
TR1=1; //启动定时器1
RI=0; //清除HW_UART接受和发送
TI=0;
SCON=0x50; //配置HW_UART为方式1,允许接受
}
//-------- --------------------------------------------------------------------------
void SW_UART_INIT(void)
{
PCA0CPM0=0x10; //模块0为负沿捕捉方式,禁止模块0中断
PCA0CPM1=0x48; //模块1为软件定时器方式,禁止模块1中断
PCA0CN=0; //PCA保持禁止状态
PCA0MD=0x02; //PCA时基=SYSCLK/4;禁止PCA计数器中断
CCF0=0; //清除PCA模块0和1捕捉比较模块
CCF1=0;
SRI=0; //清楚接受完成标志
STI=0; //清除发送完成标志
SW_TX=1; //将TX线初始化为高电平
STXBSY=0; //清除SW_UART忙标志
}
//----------------------------------------------------------------------------------
void SW_UART_ENEABLE(void){
PCA0CPM0|=0x01; //允许PCA模块0(接收中断)
PCA0CPM1|=0x01; //允许PCA模块1(发送中断)
CR=1; //启动PCA计数器
EIE1|=0x08; //允许PCA中断
EA=1; //全局中断允许
}