一般教科书上供给的UART收发的程序往往是一段选用轮循(Polling)办法完结收发的简略代码。但关于高速的AVR来讲,选用这种办法大大降低了MUC的功率。在运用AVR时,应根据芯片自身的特色(片内大容量数据存储器RAM,更适合选用高档言语编写体系程序),编写高效牢靠的UART收发接口(低层)程序。下面是一个典型的USART的接口程序。
#include
#define RXB8 1
#define TXB8 0
#define UPE 2
#define OVR 3
#define FE 4
#define UDRE 5
#define RXC 7
#define FRAMING_ERROR (1<
char rx_buffer0[RX_BUFFER_SIZE0];
unsigned char rx_wr_index0,rx_rd_index0,rx_counter0;
// This flag is set on USART0 Receiver buffer overflow
bit rx_buffer_overflow0;
// USART0 Receiver interrupt service routine
#pragma savereg-
interrupt [USART0_RXC] void uart0_rx_isr(void)
{
char status,data;
#asm
push r26
push r27
push r30
push r31
inr26,sreg
push r26
#endasm
status=UCSR0A;
data=UDR0;
if ((status & (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0)
{
rx_buffer0[rx_wr_index0]=data;
if (++rx_wr_index0 == RX_BUFFER_SIZE0) rx_wr_index0=0;
if (++rx_counter0 == RX_BUFFER_SIZE0)
{
rx_counter0=0;
rx_buffer_overflow0=1;
};
};
#asm
popr26
outsreg,r26
popr31
popr30
popr27
popr26
#endasm
}
#pragma savereg+
#ifndef _DEBUG_TERMINAL_IO_
// Get a character from the USART0 Receiver buffer
#define _ALTERNATE_GETCHAR_
#pragma used+
char getchar(void)
{
char data;
while (rx_counter0==0);
data=rx_buffer0[rx_rd_index0];
if (++rx_rd_index0 == RX_BUFFER_SIZE0) rx_rd_index0=0;
#asm(“cli”)
–rx_counter0;
#asm(“sei”)
return data;
}
#pragma used-
#endif
// USART0 Transmitter buffer
#define TX_BUFFER_SIZE0 8
char tx_buffer0[TX_BUFFER_SIZE0];
unsigned char tx_wr_index0,tx_rd_index0,tx_counter0;
// USART0 Transmitter interrupt service routine
#pragma savereg-
interrupt [USART0_TXC] void uart0_tx_isr(void)
{
#asm
push r26
push r27
push r30
push r31
inr26,sreg
push r26
#edasm
if (tx_counter0)
{
–tx_counter0;
UDR0=tx_buffer0[tx_rd_index0];
if (++tx_rd_index0 == TX_BUFFER_SIZE0) tx_rd_index0=0;
};
#asm
popr26
outsreg,r26
popr31
popr30
popr27
popr26
#endasm
}
#pragma savereg+
#ifndef _DEBUG_TERMINAL_IO_
// Write a character to the USART0 Transmitter buffer
#define _ALTERNATE_PUTCHAR_
#pragma used+
void putchar(char c)
{
while (tx_counter0 == TX_BUFFER_SIZE0);
#asm(“cli”)
if (tx_counter0 || ((UCSR0A & DATA_REGISTER_EMPTY)==0))
{
tx_buffer0[tx_wr_index0]=c;
if (++tx_wr_index0 == TX_BUFFER_SIZE0) tx_wr_index0=0;
++tx_counter0;
}
else
UDR0=c;
#asm(“sei”)
}
#pragma used-
#endif
// Standard Input/Output functions
#include
// Declare your global variables here
void main(void)
{
// USART0 initialization
// Communication Parameters: 8 Data, 1 Stop, No Parity
// USART0 Receiver: On
// USART0 Transmitter: On
// USART0 Mode: Asynchronous
// USART0 Baud rate: 9600
UCSR0A=0x00;
UCSR0B=0xD8;
UCSR0C=0x06;
UBRR0H=0x00;
UBRR0L=0x67;
// Global enable interrupts
#asm(“sei”)
while (1)
{
// Place your code here
};
}
这段由CVAVR程序生成器发生的UART接口代码是一个非常好的、高效牢靠,而且值得认真学习和领会的。其特色如下:
l.它选用两个8字节的接纳和发送缓冲器来进步MCU的功率,如当主程序调用Putchar()发送数据时,假如UART口不闲暇,就将数据放入发送缓冲器中,MCU不用等候,能够持续履行其它的作业。而UART的硬件发送完一个数据后,发生中止,由中止服务程序担任将发送缓冲器中数据顺次送出。
2.数据缓冲器结构是一个线性的循环行列,由读、写和行列计数器3个指针操控,用于判别行列是否空、溢出,以及当时数据在行列中的方位。
3.用编译操控指令#pragma savereg-和#pragma savereg+,使得由CVAVR在生成的中止服务程序中不进行中止维护(CVAVR生成中止维护会将比较多的寄存器压入仓库中),而在中止中嵌入汇编,只将5个在本中止中必需要维护的寄存器压栈。这样进步了UART中止处理的速度,也意味着进步了MCU的功率。
4.因为在接口程序Putchar()、Getchar()和中止服务程序中都要对数据缓冲器的读、写和行列计数器3个指针判别和操作,为了避免抵触,在Putchar()、Getchar()中对3个指针操作时暂时将中止封闭,进步了程序的牢靠性。
主张读者能逐字逐句地仔细分析该段代码,真实了解和领会每一句句子(包含编译操控指令的效果)的效果,从中领会和学习怎么编写功率高,牢靠性好,结构优秀的体系代码。这段程序运用的办法和技巧,对编写SPI、I2C的串行通讯接口程序都是非常好的学习。
作为现在的单片机和嵌入式体系的工程师,不只要深化全面的把握芯片和各种器材的功能,具有丰厚的硬件规划才能;一起也有必要进步软件的规划才能。要学习和把握有关数据结构、操作体系、软件工程、网络协议等方面的常识,具有规划编写大的杂乱体系程序的才能。
USART运用实例
运用ATmega128完成一个工业设备的主操控板,它与由ATmega8办理的按键和LED显现构成的操控面板距离在2米左右,两者之间选用USART通讯联系。考虑到在实践运用中,俩者之间交流的数据很少,通讯速度也不需要很高,重要的是确保通讯的牢靠和抗干扰,因此在硬件规划上选用电流环的衔接办法,见图5.4。
在图中通讯两边选用光隔和三极管,将USART的电平改变变成电流改变后传送衔接,好像工业上运用的20mA电流环通讯相同,大大进步了通讯的抗干扰才能。
通讯协议和规程的拟定:
l.通讯速率选用2400bps(速率太高时电流环的改变会跟不上)。
2. 用户数据包选用定长格局,每个数据包长度为6个字节,其间第1个字节是数据包开始字节0xBB,第6字节为数据包完毕字节0xEE,其它为用户指令、数据和体系状态参数。
3.每次通讯由A端建议,下发一个数据包;B端收到一个正确的数据包后,有必要回来一个数据包应对。
4.A端下发一个数据包后,在300ms内没有正确收到应对包时(在2400bps时传送6个字节的时刻约为30ms),将再次重发;3次重发均不能正确收到应对包则报警。
5.在体系正常作业时,A端每隔250ms下发一个数据包,B端假如在1s内没有正确收到一个下发的数据包,将进入安全维护程序。
在这个运用实例中,USART接口的发送程序与前面给出的典型例程中的相同,而对USART的接纳程序进行了改动和简化,使其愈加符合在本体系中运用。
#define UART_BEGIN_STX0xBB
#define UART_END_STX0xEE
#define RX_BUFFER_SIZE06
char rx_buffer0[RX_BUFFER_SIZE0];
unsigned char rx_counter;
bit Uart_RecvFlag
// USART Receiver interrupt service routine
#pragma savereg-
interrupt [USART_RXC] void uart_rx_isr(void)
{
unsigned char status,data;
#asm
push r26
push r27
push r30
push r31
inr26,sreg
push r26
#endasm
status=UCSRA;
data=UDR;
if ((status & (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0)
{
if (!Uart_RecvFlag)
{
rx_buffer[rx_counter] = data;
switch (rx_counter)
{
case 0:
if (data == UART_BEGIN_STX)rx_counter = 1;
break;
case 1:
case 2:
case 3:
case 4:
rx_counter++;
break;
case 5:
rx_counter = 0;
if (data == UART_END_STX)Uart_RecvFlag = 1;
break;
}
}
}
else
rx_counter = 0;
#asm
popr26
outsreg,r26
popr31
popr30
popr27
popr26
#endasm
}
#pragma savereg+
…………
void main(void)
{
while(1)
{
if (Uart_RecvFlag)
{
…………//处理收到的数据包
Uart_RecvFlag = 0;//答应USART承受新的数据包
}
…………//处理其它使命
}
}
在这段代码中,接纳中止服务程序直接对数据包的开始字符和完毕字符进行判别,并完结对整个数据包的接纳。当接纳到正确的6个字符的数据包后,将“Uart_RecvFlag”标志置位,告诉上层程序处理收到的数据。一旦“Uart_RecvFlag”标志置位后,中止服务程序将不再接纳新的数据(抛弃掉收到的字节),使得数据缓冲区不会溢出。
上层程序的规划,应确保以200ms左右的距离对“Uart_RecvFlag”标志位进行一次判别。一旦判别“Uart_RecvFlag”标志置位后,立刻进行处理,回送应对数据。处理完后将“Uart_RecvFlag”标志铲除,答应USART接纳新的数据包。
还能够考虑在数据包中添加“数据包编号”和“数据校验”2个字节,以进一步进步通讯的牢靠性。