CVAVR生成的典型USART收發(fā)的接口程序
一般教科書上提供的UART收發(fā)的程序往往是一段采用輪循(Polling)方式完成收發(fā)的簡(jiǎn)單代碼。但對(duì)于高速的AVR來講,采用這種方式大大降低了MUC的效率。在使用AVR時(shí),應(yīng)根據(jù)芯片本身的特點(diǎn)(片內(nèi)大容量數(shù)據(jù)存儲(chǔ)器RAM,更適合采用高級(jí)語言編寫系統(tǒng)程序),編寫高效可靠的UART收發(fā)接口(低層)程序。下面是一個(gè)典型的USART的接口程序。
//usart.h
//常量定義
#define BAUDRATE 9600 //波特率
//#define F_CPU 4000000 //晶振頻率4.0MHz
#define RXB8 1
#define TXB8 0
#define PE 2 //M16
//#define UPE 2 //M128
#define OVR 3
#define FE 4
#define UDRE 5
#define RXC 7
//宏定義
#define FRAMING_ERROR (1< #define PARITY_ERROR (1< //#define PARITY_ERROR (1< #define DATA_OVERRUN (1< #define DATA_REGISTER_EMPTY (1< #define RX_COMPLETE (1< // USART Receiver buffer // 全局變量,會(huì)在中斷服務(wù)程序中被修改,須加volatile限定,不要就會(huì)出錯(cuò)啦 #define RX_BUFFER_SIZE 16 // 接收緩沖區(qū)大小,可根據(jù)需要修改 volatile char rx_buffer[RX_BUFFER_SIZE]; // 接收緩沖區(qū),為char型變量組成的數(shù)組,該數(shù)組構(gòu)成環(huán)形隊(duì)列,個(gè)數(shù)為RX_BUFFER_SIZE volatile unsigned char rx_wr_index,rx_rd_index,rx_counter; // This flag is set on USART Receiver buffer overflow volatile char rx_buffer_overflow; //接收緩沖區(qū)溢出標(biāo)志 // USART Transmitter buffer #define TX_BUFFER_SIZE 16 volatile char tx_buffer[TX_BUFFER_SIZE]; volatile unsigned char tx_wr_index,tx_rd_index,tx_counter; // 函數(shù)聲明 char get_c(void); void put_c(char c); void put_s(char *ptr); void init_USART(void); //usart.c #include #include #include #include "usart.h" /*接收中斷*/ ISR(USART_RXC_vect) { char status,data; status=UCSRA; //讀取接收狀態(tài)標(biāo)志位,必須先讀,當(dāng)讀了UDR后,UCSRA便自動(dòng)清零了 data=UDR; //讀取USART數(shù)據(jù)寄存器,這句與上句位置不能顛倒的 if ((status & (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0) //判斷本接收到的數(shù)據(jù)是否有數(shù)據(jù)幀、校驗(yàn)或數(shù)據(jù)溢出錯(cuò)誤(此處指USART的硬件接收溢出) { rx_buffer[rx_wr_index]=data; // 將數(shù)據(jù)填充到接收緩沖隊(duì)列中 if (++rx_wr_index == RX_BUFFER_SIZE) //寫指針指向下一個(gè)單元,并判斷是否到了隊(duì)列的尾部,(不表示接受緩沖區(qū)是否滿?。?/p> rx_wr_index=0; //到了尾部,則指向頭部(構(gòu)成環(huán)狀) if (++rx_counter == RX_BUFFER_SIZE) //隊(duì)列中收到字符加1,并判斷是否隊(duì)列已滿 { rx_counter=0; // 隊(duì)列滿了,隊(duì)列中收到字符個(gè)數(shù)為0,表示隊(duì)列中所有以前的數(shù)據(jù)作廢,因?yàn)樽詈蟮臄?shù)據(jù)已經(jīng)把最前邊的數(shù)據(jù)覆蓋了 rx_buffer_overflow=1; //置緩沖區(qū)溢出標(biāo)志。在主程序中必要的地方需要判斷該標(biāo)志,以證明讀到數(shù)據(jù)的完整性 }; }; } /*接收單個(gè)字符*/ char get_c(void) { char data; while (rx_counter==0); //接收數(shù)據(jù)隊(duì)列中沒有數(shù)據(jù)可以讀取,等待......(注2) data=rx_buffer[rx_rd_index]; //讀取緩沖隊(duì)列中的數(shù)據(jù) if (++rx_rd_index == RX_BUFFER_SIZE) rx_rd_index=0; //讀取指針指向下一個(gè)未讀的數(shù)據(jù),如果指到了隊(duì)列尾部,則指回到隊(duì)列頭步 cli(); // 關(guān)中斷!非常重要 --rx_counter; //隊(duì)列中未讀數(shù)據(jù)個(gè)數(shù)減1。因?yàn)樵撟兞吭诮邮罩袛嘀幸淖兊模瑸榱朔乐箾_突,所以改動(dòng)前臨時(shí)關(guān)閉中斷。程序相當(dāng)可靠了。 sei(); // 開中斷 return data; } //發(fā)送中斷 ISR(USART_TXC_vect) { if (tx_counter) { --tx_counter; UDR=tx_buffer[tx_rd_index]; if (++tx_rd_index == TX_BUFFER_SIZE) tx_rd_index=0; }; } /*發(fā)送單個(gè)字符*/ void put_c(char c) { while (tx_counter == TX_BUFFER_SIZE); //發(fā)送數(shù)據(jù)隊(duì)列中還有數(shù)據(jù)沒有發(fā)送完,等待 cli(); if (tx_counter || ((UCSRA & DATA_REGISTER_EMPTY)==0)) //若發(fā)送數(shù)據(jù)隊(duì)列有數(shù)據(jù)或者數(shù)據(jù)寄存器UDR非空時(shí)執(zhí)行(因?yàn)殛?duì)列先進(jìn)先出的原因,所以,c要放進(jìn)非空的發(fā)送數(shù)據(jù)隊(duì)列里面) { tx_buffer[tx_wr_index]=c; if (++tx_wr_index == TX_BUFFER_SIZE) tx_wr_index=0; ++tx_counter; } else UDR=c; sei(); } /*發(fā)送字符串*/ void put_s(char *ptr) { while (*ptr) { put_c(*ptr++); } put_c(0x0D); put_c(0x0A); //結(jié)尾發(fā)送回車換行 } /*USART 初始化*/ void init_USART(void) { //USART 9600 8, n,1 PC上位機(jī)軟件(超級(jí)終端等)也要設(shè)成同樣的設(shè)置才能通訊 UCSRC = (1< UBRRL= (F_CPU/BAUDRATE/16-1)%256; UBRRH= (F_CPU/BAUDRATE/16-1)/256; UCSRA = 0x00; //接收使能,發(fā)送使能,接收中斷使能,發(fā)送中斷使能 UCSRB=(1< } /*********************************************** **** 名 稱:AVR USART(RS232)低層驅(qū)動(dòng)+中間層軟件示例 **** **** 作 者:zhiyu **** 編譯器:WINAVR20070525 **** **** 參 考:http://www.ouravr.com/bbs/bbs_content.jsp?mother_form=bbs_content.jsp&bbs_id=1000&bbs_page_no=1&bbs_sn=147242 《高檔8位單片機(jī)ATmega128原理與開發(fā)應(yīng)用指南(上)》--馬潮 P320 《嵌入式C編程與ATMEL AVR》-- 國外計(jì)算機(jī)經(jīng)典教材 P141 **** 日 期:2007.07.19 **** **** 芯 片:M16L **** 時(shí)鐘源:外部4M晶振 **** **** 結(jié) 果:測(cè)試成功 **** 問 題:暫無 ***********************************************/ //#include //#include #include #include "usart.h" int main(void) { init_USART(); sei(); //總中斷允許 put_s("Hello!"); put_s("這是一個(gè)簡(jiǎn)單的高速的串口驅(qū)動(dòng)程序"); put_s("請(qǐng)你輸入任意的字符,單片機(jī)將返回你輸入的字符"); while (1) { put_c(get_c()); } } //Makefile,主要的幾項(xiàng),只是針對(duì)我這里的程序,要靈活運(yùn)用哦 MCU = atmega16 F_CPU = 4000000 TARGET = main SRC = $(TARGET).c usart.c //多文件編譯才會(huì)用到這一項(xiàng),可以參考這個(gè)帖子: http://www.mcublog.com/blog/user1/4266/archives/2006/6145.html *****************************************************/ 這段由CVAVR程序生成器產(chǎn)生的UART接口代碼是一個(gè)非常好的、高效可靠,并且值得認(rèn)真學(xué)習(xí)和體會(huì)的。其特點(diǎn)如下: l.它采用兩個(gè)8字節(jié)的接收和發(fā)送緩沖器來提高M(jìn)CU的效率.當(dāng)主程序調(diào)用getchar()函數(shù)時(shí),按順序執(zhí)行到while (rx_counter==0)處,接收數(shù)據(jù)隊(duì)列里面就沒有數(shù)據(jù),如果再?zèng)]有數(shù)據(jù)輸入,那么就只能死在那里等待.如果有數(shù)據(jù)輸入的話,中斷很快就響應(yīng),數(shù)據(jù)就會(huì)迅速地填充接收數(shù)據(jù)隊(duì)列,rx_counter!=0,這個(gè)死等待也就給瓦解了,讓程序執(zhí)行接下來的那句data=rx_buffer[rx_rd_index]了.最后return data;,返回輸入的值;如當(dāng)主程序調(diào)用Putchar()發(fā)送數(shù)據(jù)時(shí),如果UART口不空閑,就將數(shù)據(jù)放入發(fā)送緩沖器中,MCU不必等待,可以繼續(xù)執(zhí)行其它的工作。而UART的硬件發(fā)送完一個(gè)數(shù)據(jù)后,產(chǎn)生中斷,由中斷服務(wù)程序負(fù)責(zé)將發(fā)送緩沖器中數(shù)據(jù)依次自動(dòng)送出。 C語言書本里有其中一段: getchar()函數(shù)(字符輸入函數(shù))的作用是從終端(或系統(tǒng)隱含指定的輸入設(shè)備)輸入一個(gè)字符.getchar()函數(shù)沒有參數(shù).當(dāng)你輸入一個(gè)字符時(shí)候,比如'a'后,要按'Enter'鍵,字符才能送到內(nèi)存!你一旦按了這個(gè)'Enter',上面的程序就會(huì)執(zhí)行中斷響應(yīng)了, 2.數(shù)據(jù)緩沖器結(jié)構(gòu)是一個(gè)線性的循環(huán)隊(duì)列,由讀、寫和隊(duì)列計(jì)數(shù)器3個(gè)指針控制,用于判斷隊(duì)列是否空、溢出,以及當(dāng)前數(shù)據(jù)在