MCU接收数据是随机的。发送数据是预知的。这个是什么意思?通俗的说其他设备向单片机什么时候发送数据,它当然是不知道的。但是单片机什么时候向其他设备发送数据的,这个单片机是知道的。
所以我们继续来分析一下上一节的代码。
#include
#define FOSC 11059200L
#define BAUD 9600
/***********************************/
// 串口初始化程序
/***********************************/
void uartInit( )
{
SCON = 0x50;
TMOD |= 0x20;
TH1=TL1 = -(FOSC/12/32/BAUD);
TR1 =1;
ES = 1;
EA =1;
}
/***********************************/
// 串口1发送一个字节到上位机
/***********************************/
void uartSendData(unsigned char dat)
{
SBUF=dat;
while(TI==0);
TI=0;
}
/***********************************/
// 串口发送一个数组到上位机
/***********************************/
void uartSendArray(unsigned char *dat, unsigned char len )
{
unsigned char i;
for(i=0; i
{
uartSendData(*dat);
dat ;
}
}
//-----------------------------------
// 串口1中断程序
//------------------------------------
void uart_Isr( )interrupt 4
{
unsigned char dat =SBUF;
if(RI)
{
RI=0;
uartRecive(dat); //接收数据函数
}
if(TI)
{
/ / TI=0;
}
}
void main( )
{
unsigned char m, n;
char buf[ ]="hello world!\r\n";
uartInit( );
while(1)
{
for(m=0;m<200;m )
for(n=0;n<200;n );
uartSendArray(buf, strlen(buf));
}
}
uart_Isr( )interrupt 4中断函数是不会被主函数main调用的。当中断事件发生时自动调用的。中断事件指的是TI、RI为1。当TI、RI置位为1时MCU便会中断当前的主程序执行,转向执行uart_Isr( )interrupt 4中的函数,TI、RI清0.uart_Isr( )interrupt 4函数执行结束后继续返回主函数。
我们将接收数据函数uartRecive(dat)置于uart_Isr( )interrupt 4函数中。因为我们并不知道其他设备什么时候发来数据,但是uart_Isr( )interrupt 4函数只要执行,不是接收到数据、就是发送数据完成。通过TI、RI是否置位为1判断到底是发生了接收事件、还是发送事件。
所以在art_Isr( )interrupt 4函数中有两个if语句是为了判断是接收到数据还是需要发送数据。SBUF是串口数据缓存,在物理上MCU有两个SBUF。但是那个是接收SBUF、那个是输出SBUF?这个很容易区别。
我们来看看两个语句:SBUF =0x1234;
dat =SBUF;
第一条语句中SBUF是发送数据串口缓存器,第二条语句中SBUF是接收数据串口缓存器,也就是SBUF在“=”左边就是发送数据串口缓存器,在右边就是接收数据串口缓存器。
在上段代码中我们使用了uartSendArray(unsigned char *dat, unsigned char len )函数,而该函数有调用了uartSendData(unsigned char dat)。我们来分析一下uartSendData(unsigned char dat)函数。
void uartSendData(unsigned char dat)
{
SBUF=dat; //将dat的值存入SBUF数据中,开始发送数据
while(TI==0);//当数据发送未结束时TI的值为0,发送结束TI的值为1.所以该句
//等待数据完成。
TI=0; //数据发送完成 TI的清零
}
流程图应该是:
其实真正的程序流程不是这样。因为TI的值为1,会引起中断程序的。所以完整的程序应该如下:
如果是因为TI置1而执行uart_Isr( )interrupt 4,依照以下代码,其实我们什么都没做。
if(TI)
{
/ / TI=0;
}
由以上我们可以得知使用uartSendData(unsigned char dat)发送数据的效率并不高。因为MCU必须等待一个字节完成才能发送下一个字节。中间时间MCU是什么都不做的。如果按照9600波特率发送数据,大约1mS一个字节。如果单片机1uS执行一条指令,那么1mS可以执行1K条指令。所以大大浪费了MCU的性能。记得又一次一个朋友设计了LED显示功能的产品,当串口发送数据时LED会抖动。就是这个原因。
我们来看看怎么解决这个问题。
#define FOSC 11059200L
#define BAUD 9600
#define TX_MAX 100
typedef struct UART_ST
{
unsiged char tXLEN;
unsiged char txByteNUmber;
unsigned char txBUf[TX_MAX];
}UART_ST;
UART_ST UART0;
/***********************************/
// 串口初始化程序
/***********************************/
void uartInit( )
{
SCON = 0x50;
TMOD |= 0x20;
TH1=TL1 = -(FOSC/12/32/BAUD);
TR1 =1;
ES = 1;
EA =1;
}
/*********************************
//发送缓存中txBUf中的数据
*********************************/
void uartSenddat( )
{
TI=0;
txByteNUmber++;
if(txByteNUmber<txlen)
{
SBUF = UART0.txBUf[txByteNUmber];
}
}
/*********************************
//发送缓存中txBUf中的数据
*********************************/
void uartStartSend(unsigned char *buf,unsigned char len )
{
unsigned char i;
for(i=0;i<len;i++)< span="">
{
UART0.txBUf[i] = *buf++;
}
UART0.tXLEN=len;
SBUF = UART0.txBUf[0];
txByteNUmber=0;
}
//-----------------------------------
// 串口1中断程序
//------------------------------------
void uart_Isr( )interrupt 4
{
unsigned char dat =SBUF;
if(RI)
{
RI=0;
uartRecive(dat); //接收数据函数
}
if(TI)
{
uartSenddat( );
}
}
void main( )
{
unsigned char m, n;
char buf[ ]="hello world!\r\n";
uartInit( );
while(1)
{
for(m=0;m<200;m )
for(n=0;n<250;n );
uartStartSend(buf,strlen(buf));
}
}
橙色部分是我修改代码,
for(m=0;m<200;m )
for(n=0;n<250;n );延时必须够串口将数据发送出去。代码我只是介绍了一下思路,我并没有调试。所以如果需要直接用于工程可能需要修改。
uartStartSend(buf,strlen(buf)); 完成工作时将需要发送的数据拷贝至 UART0.txBUf中。并且第一个要发送的数据UART0.txBUf[0]赋值给 SBUF寄存器。 其余数据发送交给uartSenddat( )函数。当第一数据发送完成后MCU进入中断函数会继续发送剩余数据。
这样我们就可以在数据发送时间间隔去做其他事情!提高了系统效率。