使用USART发送数据,需要一定时间,若是用传统方法,等发送完再处理其它任务(如语句 while(!(UCSRA&0x40));),那么,将大大降低了高速的AVR的执行效率!
那么怎样处理才可以解决低速串口与高速AVR之间的矛盾呢?可以采用开辟发送缓冲区的做法:
当AVR需要发送数据时,如果USART口不空闲或者发送缓冲区还有待发送的数据,就将数据放入发送缓冲器中(如果缓冲器未满),AVR不必等待,可以转去执行其它任务。而后,等USART的硬件发送完一个数据后产生中断,由中断服务程序负责将发送缓冲器中数据依次送出。
发送缓冲器数据结构的设计:循环队列,由读、写2个指针及一个队列计数器控制,用于判断当前写入数据、读出数据在队列中的位置,并判断队列是否为空,是否已满。
程序设计时需注意,为了防止处理冲突,在对数据缓冲器的读、写过程中,要将中断关闭,避免错误产生,从而提高程序的可靠性。
#include <iom16v.h>
#define DISP_PORT PORTB
#define DISP_DDR DDRB
#define TX_BUFFER_SIZE 255
#define UDR_EMPTY (1<<UDRE)
//UDRE:USART数据寄存器空标志,该标志位可以产生中断
// Bit 5 – UDRE: USART 数据寄存器空
//UDRE标志指出发送缓冲器(UDR)是否准备好接收新数据。
//UDRE为1说明缓冲器为空,已准备好进行数据接收。
uint8 x_buffer[TX_BUFFER_SIZE],tx_wr_index=0,tx_rd_index=0,tx_counter=0;
/*---------------------------------------------------------
程序名称:UART初始化程序
程序功能:初始化UART为:8位,9.6K,接收中断
注意事项:基于7.3728M晶振 */
void uart0_init(void)
{
UCSRB = 0x00; //disable while setting baud rate
UCSRA = 0x00; //U2X = 0,不加倍数率
UCSRC = 0x86; //8位
//固定的
/* 设置帧格式 : 8 个数据位 , 2 个停止位 */
//UCSRC = (1<<URSEL)|(1<<USBS)|(3<<UCSZ0);
UBRRL = 47; //set baud rate lo,波特率为9.6K
UBRRH = 0x00; //set baud rate hi
UCSRB = 0x58; //发送中断允许,接收缓冲自动清空,接收允许
}
/*---------------------------------------------------------
程序名称:UART发送中断服务程序*/
#pragma interrupt_handler uart0_tx_isr:14
void uart0_tx_isr(void)
{
if (tx_counter) //如果缓冲区有数据则进入发送程序
{
tx_counter--;
UDR=tx_buffer[tx_rd_index];
if (++tx_rd_index == TX_BUFFER_SIZE) //如果缓冲区读指针满
tx_rd_index=0; //读指针置0
}
}
/*---------------------------------------------------------
程序功能:UART发送程序*/
void uart0_putchar(uint8 c)
{
while (tx_counter == TX_BUFFER_SIZE); //如果缓冲区满则等待
CLI();
if (tx_counter || ((UCSRA & UDR_EMPTY)==0)) //如果缓冲区有数据或者UDR不空闲
{
tx_buffer[tx_wr_index]=c; //将数据写入缓冲
if (++tx_wr_index == TX_BUFFER_SIZE) //如果已经写满
tx_wr_index=0; //写指针置0
tx_counter++;
}
else //如果缓冲区没有数据且UDR空闲
UDR=c; //没什么事就发了吧
//USART发送数据缓冲寄存器和USART接收数据缓冲寄存器共享相同的I/O地址,
//称为USART 数据寄存器或UDR。
//将数据写入UDR时实际操作的是发送数据缓冲器存器(TXB),
//读UDR时实际返回的是接收数据缓冲寄存器 (RXB) 的内容。
SEI();
}
void mcu_init(void)
{
CLI();
uart0_init();
MCUCR = 0x00;
GICR = 0x00;
TIMSK = 0x00; //timer interrupt sources
SEI(); //re-enable interrupts
}
void main()
{
uint8 sendChar=0;
mcu_init();
DISP_DDR=0xFF;
while(1)
{
uart0_putchar(sendChar); //发送数据,数据为变量sendChar
delay50us(20);
DISP_PORT=tx_counter; //显示发送区数据量
}
}
(1)缓冲区数据量tx_counter的值取决于AVR的发送频率及UART的波特率。假定UART的波特率一定,若是AVR的发送频率过高,低速的UART的发送速度将跟不上,导致缓冲区数据量tx_counter不断增加。用LED指示tx_counter,将可以观察到向上加的现象。
(2)改变UART初始化语句:UBRRL = 47; 将“47”改为“40”、“60”、“80”,从而改变UART的波特率。可以看到波特率越高,数据量tx_counter向上加的速度越低;当波特率高到一定程度,发送时间将小于延时时间,UART空闲,程序不将待发送的数据放入缓冲,即缓冲区没有数据,数据量tx_counter一直为“0”;波特率越低,数据量tx_ounter向上加的速度越高。用LED指示tx_counter的数据量,观察现象。