- AVR單片機菜鳥進階
- 沈添國主編
- 111字
- 2019-01-09 15:47:43
第4章HELLO WORLD——重現經典
“HELLO WORLD”是一個經典程序,在一些編程書籍或教程中幾乎都有出現,并作為入門級的簡單練習提供給學習者。這里不僅僅將其重現,更重要的是讓讀者了解USART的相關知識及使用,這是因為在單片機開發的過程中經常會使用到USART。
4.1 USART簡述
對于USART,下面從寄存器和相關電路兩方面來進行說明。
4.1.1 USART的寄存器
ATmega88V里只有一個USART,其主要特點為:全雙工操作,異步或同步操作,有高精度的波特率發生器及3個獨立中斷等。USART的方框圖如圖4.1所示。從圖4.1中可以看出它包含4部分,分別是時鐘發生器、發送器、接收器和控制及狀態寄存器。時鐘發生器主要用于對USART的時鐘進行設置,與其相關的寄存器是波特率寄存器。發送器與接收器共同使用同一個數據寄存器UDRn,但它們都有各自的移位寄存器,用來處理發送或接收的數據。而控制及狀態部分主要是采用3個狀態和控制寄存器對整個USART進行設置的。
由上述表述可知,USART包含6個寄存器,分別是USART數據寄存器(UDRn)、USART狀態和控制寄存器A/B/C(UCSRnA/B/C)和USART波特率寄存器(UBRRL/H)。
(1)USART數據寄存器(UDRn):UDRn為發送/接收數據緩沖寄存器。當設置為接收模式時,從UDRn讀取接收到的數據;而設置為發送模式時,將要發送的數據賦予UDRn即可。
(2)USART狀態和控制寄存器A/B/C(UCSRnA/B/C):這3個寄存器用于對USART工作在哪種模式下進行設置并提供狀態的讀取。
(3)USART波特率寄存器(UBRRL/H,也即UBRRn):UBRRL/H用于對USART的波特率進行設置,在ATmega88V手冊中也提供了一些參考值。在指定晶振下,對應波特率,該寄存器有對應的參考值。選擇晶體時,一般情況下應使用推薦的晶體。
雖然這幾個寄存器很簡單,但它們的每個位都需要按照需求來設置,一般使用的是7.3728MHz的晶體,其波特率設置為9600bps。

圖4.1 USART的方框圖
4.1.2 USART的相關電路
如果要將USART輸出的數據顯示在計算機上,需要增加一個RS -232及一些相關器件。這是因為USART輸出的是TTL電平,而計算機需要的CMOS電平,需要一個轉換器件——RS-232對電平進行轉換,如圖4.2所示。該連接圖在很多單片機中都是可以使用的,只是需要將USART的輸出/輸入端口對應好。

圖4.2 RS-232外圍電路圖
需要注意的是:運用該電路時,應先下載一個RS -232的手冊看一下,以對其外圍電路有一定的了解。
使用USART傳輸數據時,需要設置具體的傳輸速率或通信速率。如果收/發兩設備設置的通信速率不一致,則它們不能正常通信。如表4.1所示是在通用振蕩器頻率下設置UBRRn的例子。
表4.1 在通用振蕩器頻率下設置UBRRn的例子

注:[1]當波特率最大時,UBRRn=0,誤差=0.0%。
RS-232和RS-485的比較如下。
MCU的數據傳輸到PC時使用的是RS-232,傳輸到其他MCU時使用的是RS-485或直接連接(如果在同一個板上,MCU的供電電壓一致,則可以直接連接);RS-485是一個半雙工器件,即同一時間只能發送或接收,兩者不可同時進行,而RS-232是一個全雙工器件。
RS-485在很多工業設備中被廣泛應用。RS -485芯片如圖4.3所示,其中RO/DI接單片機,A/B線上掛載多個設備。每次只有一個設備向A/B總線發送數據,其他設備都在接收,若數據格式正確,則對應的設備就進行相應的操作。

圖4.3 RS-485芯片
多設備掛接485總線的示意圖如圖4.4所示。

圖4.4 多設備掛接485總線的示意圖
4.2 在Proteus中顯示“HELLO WORLD”
本節將基于Proteus仿真軟件實現在Proteus中顯示“HELLO WORLD”。
4.2.1 驅動USART
在驅動USART之前,先來了解一下USART的初始化配置(要想驅動USART,首先需要對其進行初始化):
(1)給對應的I/O設置好方向及電平的高低;
(2)設置USART的波特率;
(3)設置數據的字符長度;
(4)設置是否使能收發,是否中斷。
對于上述4個步驟,后3個沒有固定的先后順序。
注意:編寫程序時,應盡量使用通用的定義,這樣有利于代碼的移植;而且代碼書寫應使用標準風格,以方便代碼的維護。
打開AVR Studio 4,首先新建一個工程,命名為USART(該工程的設置與前面介紹的流水燈工程的設置一致),然后在HAL_usart.h中添加以下代碼:
/******************************************************************/ /* AVR USART驅動H文件 */ /******************************************************************/ #ifndef _HAL_USART_H_ #define _HAL_USART_H_ //-------------------------------------------------INCLUDE #include "main.h" //包含庫文件 //--------------------------------------------------PAR #define fosc 7372800 //晶體值 #define baud 9600 //波特率 //--------------------------------FUNCTION,將外部要調用到的函數書寫到此 void USART_init(void); void USART_txrom(const char*data,uint leng); void USART_txram(uchar*data,uint leng); #endif
接著在HAL_usart.c中添加以下代碼:
/******************************************************************/ /* AVR USART驅動C文件 */ /******************************************************************/ void USART_init(void) { UBRR0H=(fosc/16/(baud+1))/256; //設置波特率 UBRR0L=(fosc/16/(baud+1))%256; UCSR0B=(1 <<RXEN0)|(1 <<TXEN0); //使能發送接收 UCSR0C=(1 <<UCSZ01)|(1 <<UCSZ00); //8bit }/*------------------------------------------------------ 向USART發送一個字節 ------------------------------------------------------*/ void USART_txbyte(uchar x) { while(!((UCSR0A)&(1 <<UDRE0))); UDR0=x; } /*------------------------------------------------------ 向USART發送常字符串 ------------------------------------------------------*/ void USART_txrom(const char*data,uint leng) { while(leng) { while(!((UCSR0A)&(1 <<UDRE0))); UDR0=*data; data++; leng--; } } /*------------------------------------------------------ 向USART發送字符串 ------------------------------------------------------*/ void USART_txram(uchar*data,uint leng) { while(leng) { while(!((UCSR0A)&(1 <<UDRE0))); UDR0=*data; data++; leng--; } }
驅動USART時,除了需要編寫初始化函數外,還需要編寫發送與接收函數,后面的4.2.3節將詳細介紹雙機通信。
4.2.2 顯示“HELLO WORLD”
main.c中的main()代碼如下:
/********************************************** project:USART工程 IDE:AVR Studio 4+Winavr20070525 device:atmega88 author:lg date:2012-07-22 21:10 goal:重現HELLO WORLD,并熟悉運用USART ***********************************************/ #include "main.h" /************************************************ 設備初始化 ************************************************/ void DEVICE_init(void) { CLOSE_INT(); //關全局中斷 //----------PB口 DDRB=0X00; //未使用也定義 PORTB=0X00; //----------PC口 DDRC=0X00; //未使用也定義 PORTC=0X00; //----------PD口 DDRD| =0XFF; //設置PD口的0~7口方向為輸出 PORTD=0X00; //設置PD口的0~3口電平為低 USART_init(); //UART初始化 OPEN_INT(); //開全局中斷 } //----------------------MAIN----------------------- int main(void) { //------設備初始化 DEVICE_init(); //-----------循環 ----------- while(1) { //----------------------------- _delay_ms(500); //延時500ms _delay_ms(500); //延時500ms _delay_ms(500); //延時500ms _delay_ms(500); //延時500ms _delay_ms(500); //延時500ms _delay_ms(500); //延時500ms _delay_ms(500); //延時500ms _delay_ms(500); //延時500ms USART_txrom("Hello World!",12); LED1_XOR(); } }
仿真電路如圖4.5所示,圖中使用了虛擬設備中的串口終端,這樣在仿真的過程中可以顯示USART發送過來的數據。在本仿真電路中,MCU的設置與前述電路相同,不同的是代碼編譯文件。

圖4.5 仿真電路
單擊“運行”按鈕,可以在彈出的提示界面中看到“Hello World!usart test!”等字符,如圖4.6所示。

圖4.6 虛擬示波器顯示的實驗結果1
4.2.3 雙機通信
雙機通信是指在兩個設備之間通過某種協議或關系進行信息傳輸、交換等操作。
由于我們剛剛接觸單片機,所以在這里先做一個簡單的實驗:主機通過串口發字符到從機后,從機通過串口將其發送到虛擬終端。
主機的代碼可以延用4.2.2節中工程的代碼。由于從機只有在接收到主機發送過來的字符后才向虛擬終端發送字符,所以這里需要使用到串口中斷。AVR提供了很多外設中斷,其使用沒有什么特殊的,只是需要注意中斷的觸發條件是什么。
從機HAL_usart.c的代碼如下:
/******************************************************** HAL_usart.c /********************************************************/ //-------------------------------------------頭文件 #include "HAL_usart.h" //#define fosc 7372800 //#define baud 9600 /*------------------------------------------------------ USART初始化函數 ------------------------------------------------------*/ void USART_init(void) { UBRR0H=(fosc/16/(baud+1))/256; //設置波特率 UBRR0L=(fosc/16/(baud+1))%256; UCSR0B=(1 <<RXCIE0)|(1 <<RXEN0)|(1 <<TXEN0); //使能發送接收 UCSR0C=(1 <<UCSZ01)|(1 <<UCSZ00);//8bit } /*------------------------------------------------------ 向USART發送一個字節 ------------------------------------------------------*/ void USART_txbyte(uchar x) { while(!((UCSR0A)&(1 <<UDRE0))); UDR0=x; } /*------------------------------------------------------ 向USART發送常字符串 ------------------------------------------------------*/ void USART_txrom(const char*data,uint leng) { while(leng) { while(!((UCSR0A)&(1 <<UDRE0))); UDR0=*data; data++; leng--; } } /*------------------------------------------------------ 向USART發送字符串 ------------------------------------------------------*/ void USART_txram(uchar*data,uint leng) { while(leng) { while(!((UCSR0A)&(1 <<UDRE0))); UDR0=*data; data++; leng--; } } //--------------------------------------------------------------------- // 串口接收結束中斷 //--------------------------------------------------------------------- ISR(SIG_USART_RECV) { uchar tmp; tmp=UDR0; //獲取接收到的字符 USART_txbyte(tmp); //將接收到的字符發送出去 }
在main.h中添加以下幾條語句:
#define CLOSE_INT() cli() //關全局中斷 #define OPEN_INT() sei() //開全局中斷
在DEVICE_init()函數中添加以上語句后,除了需要開啟對應的外設中斷位外,還需要打開全局中斷,否則中斷是不運行的或無效的。這個讀者可以自己嘗試一下。
而從機的main()函數除了初始化設備外,只需要再添加一個while(1);語句,等著主機發送字符即可。
打開Proteus軟件,建立雙機調試工程,其仿真電路如圖4.7所示。

圖4.7 雙機通信的仿真電路
按照要求分別對主/從機添加HEX文件,然后單擊“運行”按鈕,結果如圖4.8所示。

圖4.8 虛擬示波器顯示的實驗結果2
本實驗的效果是:主機向從機發送“Hello World!”,從機接收到后將其轉發到虛擬終端進行顯示。
這里只給出簡單的雙機通信,想要更復雜的,讀者可以自己去發揮、實踐。
4.2.4 USART開發的用處
本章完成了HELLO WORLD的經典重現及雙機通信,但是USART的運用遠不只于此,如在前面提到的485總線及一些通信上的應用等。在一些開發過程中,當不存在顯示設備或指示設備,但是需要查看數據時,也可以使用USART。由于在實際工程中不可能進行仿真,所以需要使用一些小技巧來解決問題,如使用USART將數據發送到界面上顯示,或是在需要的觀察點附近做上標記,輸出設定字符,查看是否運行到此或輸出該字符等——這是US-ART在程序調試中的應用。
另外,USART可以制定出一套屬于自己的通信協議來,如485協議就是一個很廣泛的應用。
4.3 小結
本章介紹了ATmega88V的USART(從其寄存器到相關電路再到驅動USART),并對一些相關開發軟件進行了練習和使用。讀者在自己的開發過程中應多多積累一些開發技巧。
- 數據展現的藝術
- Getting Started with MariaDB
- WOW!Illustrator CS6完全自學寶典
- 精通Windows Vista必讀
- 數據庫原理與應用技術學習指導
- 機器學習流水線實戰
- 變頻器、軟啟動器及PLC實用技術260問
- 精通數據科學算法
- Prometheus監控實戰
- 愛犯錯的智能體
- Mastering Geospatial Analysis with Python
- 手機游戲策劃設計
- Dreamweaver+Photoshop+Flash+Fireworks網站建設與網頁設計完全實用
- 算法設計與分析
- Hands-On Agile Software Development with JIRA