官术网_书友最值得收藏!

第3章 流水燈重現——第一次操刀

第2章向讀者介紹了幾個工具(在以后的章節中會運用到),本章主要是跟大家一起來學習AVR單片機的I/O相關知識并運用——重現流水燈。讓咱們從現在開始真正踏上單片機之路吧!

3.1 I/O簡述

本節將對AVR單片機的I/O相關知識進行介紹,并和5 1單片機的I/O進行比較。這里具體介紹的類型是ATmega88V。

3.1.1 I/O寄存器

AVR單片機的I/O框圖如圖3.1所示。

圖3.1 AVR單片機的I/O框圖

AVR單片機的所有I/O端口都具有讀/寫修改功能,可以輸出或吸收大電流,并直接驅動LED。每個I/O端口都有3個寄存器(用來設置讀/寫操作),分別為數據寄存器PORTx、數據方向寄存器DDRx和端口輸入引腳寄存器PINx。其中數據寄存器和數據方向寄存器為讀/寫寄存器,而端口輸入引腳寄存器為只讀寄存器。將數據方向寄存器清零,數據寄存器置位,再將寄存器MCUCR(MCU控制寄存器)的PUD位置位,可使I/O端口處于高阻態。

ATmega88V有23個可編程的I/O端口,其中PORTB、PORTC和PORTD分別有7個、6個和7個I/O引腳,對于一般的應用來說是足夠的。在運用過程中,不一定所有的I/O引腳都會使用到。如果有引腳未被使用,建議給其一個確定電平。例如,在一般情況下,初始化設備時,應將所有I/O端口統統設置一次,以確保I/O端口工作在已知的狀態下。

另外,大多數I/O端口都具有第二功能。

3.1.2 與51單片機的比較

使用AVR單片機時,需要設置I/O端口的方向,如果不設置,將以默認的狀態存在。而51單片機的I/O端口是準雙向輸出類型的。準雙向輸出類型可用做輸出和輸入功能而不需要重新配置I/O端口的方向。5 1單片機的I/O端口只有一個寄存器Px。

舉例說明:需要設置某個I/O端口為輸出高電平,則AVR單片機需要設置DDRx和PORTx 2個寄存器;而5 1單片機只需要設置Px為高即可。對于輸入,AVR單片機仍需要設置2個寄存器,而5 1單片機直接讀取Px的值即可。

在此做這個比較,是為了讓讀者了解AVR單片機與5 1單片機I/O端口設置的不同,在編寫程序時,這是需要特別注意的地方,尤其是對于剛從5 1單片機轉過來準備學習AVR單片機的工程師而言。長時間使用一款單片機會導致工程師將很多編寫習慣不知不覺地用在其他單片機上,這是大部分工程師都會犯的錯誤。

3.2 讓流水燈動起來

在很多不同種類的單片機教程中都是以流水燈作為第一個實驗的,最為突出的是在51單片機的教程中。這里也以它為例進行介紹。

3.2.1 流水燈程序

流水燈程序用于實現對PD口下的8個LED燈進行操作:每500ms亮一個,當全部燈點亮后,每500ms滅一個,如此重復。這里還沒接觸到定時器的相關知識,因此可以使用庫文件來實現延時500ms,不過由于該延時不是很準確,所以需要在代碼工程中的2個地方做相應的設置:在源文件中添加#include<util/delay.h>;在工程選項中寫入當前使用的晶振頻率,如圖3.2所示。圖3.2中給出了一些重要信息,如工程所選擇的器件型號“atmega88”,使用的晶振頻率為“7372800”,優化級別默認為-0s。優化級別有以下幾種。

(1)-00:無優化。

(2)-01:減少代碼尺寸和執行時間,不進行需要大量編譯時間的優化。

(3)-02:幾乎執行所有優化,而不考慮代碼大小和執行時間。

(4)-03:執行“-02”所有的優化及內聯函數,重命名寄存器的優化。

(5)-0s:針對代碼大小的優化,執行所有“-02”優化而不增加代碼的大小。

開發時,應選擇適合自己的一個優化級別。圖3.2中框選標出的1 ~4這4個復選框的意義依次如下:

(1)所有char認為是unsined char;

(2)所有bitfields認為是unsigned;

(3)定義結構體時,連續存儲;

(4)定義枚舉類型時,使用最大需要的存儲空間。

圖3.2 工程選項

流水燈程序如下:

            /**********************************************
            project:流水燈工程
            IDE:AVR Studio 4+Winavr20070525
            device:atmega88
            author:lg
            date:2012-07-11 21:10
            Goal:點亮LED,掌握GPIO的運用
            ***********************************************/
            #include "main.h"
            /*****************************
                設備初始化
            *****************************/
            void DEVICE_init(void)
            {
                //----------PB口
                DDRB=0X00;     //未使用也定義
                PORTB=0X00;
                //----------PC口
                DDRC=0X00;   //未使用也定義
                PORTC=0X00;
                //----------PD口
                DDRD| =0XFF;  //設置PD口的0~7口方向為輸出
                PORTD=0X00;  //設置PD口的0~3口電平為低
            }
            //----------------------MAIN-----------------------
            int main(void)
            {
                //------設備初始化
                DEVICE_init();
                //-----------循環-----------
                while(1)
                {
                //-------------點亮LED0~7----------------
                    PORTD| =_BV(0);        //點亮LED0
                    _delay_ms(500);         //延時500ms
                    PORTD| =_BV(1);        //點亮LED1
                    _delay_ms(500);
                    PORTD| =_BV(2);        //點亮LED2
                    _delay_ms(500);
                    PORTD| =_BV(3);        //點亮LED3
                    _delay_ms(500);
                    PORTD| =_BV(4);        //點亮LED4
                    _delay_ms(500);
                    PORTD| =_BV(5);        //點亮LED5
                    _delay_ms(500);
                    PORTD| =_BV(6);        //點亮LED6
                    _delay_ms(500);
                    PORTD| =_BV(7);        //點亮LED7
                    _delay_ms(500);
                //-------------滅掉LED0~7----------------
                    PORTD&=~_BV(0);       //點亮LED0
                    _delay_ms(500);         //延時500ms
                    PORTD&=~_BV(1);       //點亮LED1
                    _delay_ms(500);
                    PORTD&=~_BV(2);       //點亮LED2
                    _delay_ms(500);
                    PORTD&=~_BV(3);       //點亮LED3
                    _delay_ms(500);
                    PORTD&=~_BV(4);      //點亮LED4
                    _delay_ms(500);
                    PORTD&=~_BV(5);      //點亮LED5
                    _delay_ms(500);
                    PORTD&=~_BV(6);      //點亮LED6
                    _delay_ms(500);
                    PORTD&=~_BV(7);      //點亮LED7
                    _delay_ms(500);
                }
            }

在這里有一個地方需要注意,就是_BV是在sfr_defs.h中定義的,其定義為#define _BV (bit)(1 <<(bit))。當沒有其他編譯,而在移植時希望盡可能減少修改量時,就可以將該定義添加到代碼中。

3.2.2 流水燈重現

流水燈最早是用5 1單片機實現的,這里將其在AVR單片機上重現。由于沒有實際的板子,故這里采用仿真軟件來實現。

下面根據前面介紹的Proteus的運用,重新建立一個流水燈的工程。首先添加MCU、晶振、電阻、電壓表和示波器。在這里,電壓表用來查看引腳的電壓,示波器用來觀察各個引腳的延時。仿真電路如圖3.3所示。注意:仿真電路跟實際電路是有區別的,如仿真電路中的晶振需要接2個匹配的負載電容。

圖3.3 仿真電路

然后向MCU添加流水燈程序的可執行文件,并將晶振的頻率設置為7.3728MHz,其他根據需要進行相應的設置。完成一系列設置后,單擊界面右下角中的,開始仿真,如圖3.4所示。從圖中可以看到電壓表的值一直在變化(從0到3.3V變化),說明I/O端口在來回不停地被置高清零。還可以通過示波器來查看仿真結果。一個示波器只能有4個輸入源,則8個I/O端口需要使用2個示波器。在仿真中選中示波器后單擊鼠標右鍵,再單擊“Digital Oscilloscope”,會彈出如圖3.5所示的界面。

圖3.4 仿真界面

圖3.5 虛擬示波器界面1

在圖3.5中顯示出了4條不同顏色的線,黃色、藍色、紅色、綠色(從上到下)分別對應的是PD0、PD1、PD2和PD3。界面的右側是示波器的相關設置,4個通道選擇的都是DC源,電壓單位是1 V/格,掃描時間為50ms/格。將掃描時間調整為20ms/格,如圖3.6所示,這樣就比較容易看出延時的間隔。3.2.1節介紹的流水燈程序中實現的是500ms的間隔,而從圖3.6中得到的只有40ms左右的一個間隔,與程序要求相差太多,但是在這里先不做調整,只是簡單地給讀者介紹一下這個可以用來測量程序中所要求的時序的工具而已。實際中也有類似的工具,可以用來代替示波器,如邏輯分析儀,其在價格和使用方面都是比較適合單片機工程師的。在程序運行過程中可以看到LED0 ~LED7不停地來回亮滅,這樣流水燈便得以重現了。如果你手上有硬件調試板,則可以直接在板上運行以查看效果。

圖3.6 虛擬示波器界面2

3.3 小結

本章對AVR單片機的I/O相關知識做了一些簡單敘述,并通過實際程序工程來說明了I/O端口的一些設置及運用。本章還回顧了第2章提到的Proteus軟件的運用,并且簡單介紹了如何運用它來查看程序所實現的功能。希望讀者在程序運行過程中積累出一套屬于自己的調整步驟或方法。

主站蜘蛛池模板: 武邑县| 龙陵县| 台山市| 乐安县| 湖北省| 油尖旺区| 自贡市| 贡觉县| 天全县| 安多县| 岚皋县| 昂仁县| 潞西市| 堆龙德庆县| 特克斯县| 博白县| 芜湖县| 延寿县| 七台河市| 镇赉县| 额敏县| 阿瓦提县| 驻马店市| 宣武区| 泊头市| 湟源县| 准格尔旗| 上林县| 晋江市| 油尖旺区| 慈溪市| 越西县| 崇信县| 长武县| 新郑市| 民勤县| 通榆县| 济宁市| 新丰县| 镇江市| 渑池县|