一般教科書上提供的UART收發(fā)的程序往往是一段采用輪循(Polling)方式完成收發(fā)的簡單代碼。但對于高速的AVR來講,采用這種方式大大降低了 MUC的效率。在使用AVR時,應(yīng)根據(jù)芯片本身的特點(diǎn)(片內(nèi)大容量數(shù)據(jù)存儲器RAM,更適合采用高級語言編寫系統(tǒng)程序),編寫高效可靠的UART收發(fā)接口(低層)程序。下面是一個典型的ATmega128的軟件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<
#define PARITY_ERROR (1<
#define DATA_OVERRUN (1<
#define DATA_REGISTER_EMPTY (1<
#define RX_COMPLEte (1<
// USART0 Receiver buffer
#define RX_BUFFER_SIZE0 8
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
in r26,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
pop r26
out sreg,r26
pop r31
pop r30
pop r27
pop r26
#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
in r26,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
pop r26
out sreg,r26
pop r31
pop r30
pop r27
pop r26
#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程序生成器產(chǎn)生的UART接口代碼是一個非常好的、高效可靠,并且值得認(rèn)真學(xué)習(xí)和體會的。其特點(diǎn)如下:
l. 它采用兩個8字節(jié)的接收和發(fā)送緩沖器來提高M(jìn)CU的效率,如當(dāng)主程序調(diào)用Putchar()發(fā)送數(shù)據(jù)時,如果UART口不空閑,就將數(shù)據(jù)放入發(fā)送緩沖器中,MCU不必等待,可以繼續(xù)執(zhí)行其它的工作。而UART的硬件發(fā)送完一個數(shù)據(jù)后,產(chǎn)生中斷,由中斷服務(wù)程序負(fù)責(zé)將發(fā)送緩沖器中數(shù)據(jù)依次送出。
2.數(shù)據(jù)緩沖器結(jié)構(gòu)是一個線性的循環(huán)隊列,由讀、寫和隊列計數(shù)器3個指針控制,用于判斷隊列是否空、溢出,以及當(dāng)前數(shù)據(jù)在隊列中的位置。
3. 用編譯控制命令#pragma savereg-和#pragma savereg+,使得由CVAVR在生成的中斷服務(wù)程序中不進(jìn)行中斷保護(hù)(CVAVR生成中斷保護(hù)會將比較多的寄存器壓入堆棧中),而在中斷中嵌入?yún)R編,只將5個在本中斷中必須要保護(hù)的寄存器壓棧。這樣提高了UART中斷處理的速度,也意味著提高了MCU的效率。
4.由于在接口程序Putchar()、Getchar()和中斷服務(wù)程序中都要對數(shù)據(jù)緩沖器的讀、寫和隊列計數(shù)器3個指針判斷和操作,為了防止沖突,在Putchar()、Getchar()中對3個指針操作時臨時將中斷關(guān)閉,提高了程序的可靠性。
建議讀者能逐字逐句地仔細(xì)分析該段代碼,真正理解和領(lǐng)會每一句語句(包括編譯控制命令的作用)的作用,從中體會和學(xué)習(xí)如何編寫效率高,可靠性好,結(jié)構(gòu)優(yōu)良的系統(tǒng)代碼。這段程序使用的方法和技巧,對編寫SPI、I2C的串行通信接口程序都是非常好的借鑒。
作為現(xiàn)在的單片機(jī)和嵌入式系統(tǒng)的工程師,不僅要深入全面的掌握芯片和各種器件的性能,具備豐富的硬件設(shè)計能力;同時也必須提高軟件的設(shè)計能力。要學(xué)習(xí)和掌握有關(guān)數(shù)據(jù)結(jié)構(gòu)、操作系統(tǒng)、軟件工程、網(wǎng)絡(luò)協(xié)議等方面的知識,具有設(shè)計編寫大的復(fù)雜系統(tǒng)程序的能力。