AVR I/O口使用方法
AVR單片機寄存器 DDRx PORTx PINx 與對應IO端口之間的關系(x代表某個端口,如A端口、B端口等)
下表以端口B的第2位PB2為例子加以說明,并且假設PB2為懸空狀態(tài)
DDRB.2
PORTB.2
讀取PINB.2的結果
引腳PB2的狀態(tài)
1
1
1
PB2推挽輸出
1
1
0
0
PB2推挽輸出
?0
0
1
1
PB2弱上拉,可作輸入
0
0
×
PB2高阻抗,可作輸入
讀取PINB.2時,就是讀取PB2引腳的實際電平,
如果PB2直接接VCC,那么任何時候讀取PINB.2的結果都是1
如果PB2直接接GND,那么任何時候讀取PINB.2的結果都是0
下面是一個標準C語言例子:
#include
unsigned char abc;?????? //定義一個變量
void main(void)????????? //主函數(shù)
{
DDRB = 0b11110000;
PORTB = 0b11001100;????
while (1)?????????????? //主循環(huán)
{???
??? abc?? = PINB;??????? //讀取B端口的實際電平
}
}
如果整個B端口都是懸空的話,
那么abc的結果就是:0b110011**
如果B端口第7位接GND 、第0位接VCC 、其它位懸空,
那么abc的結果就是:0b010011*1 (PB7工作在“短路”狀態(tài))
其中“*”表示不確定,理想狀態(tài)下可以看作0
端口聲明:include
#include "D:\ICC_H\CmmICC.H"
#define OUT_BUZ sbi(DDRB,3) //PB3
#define BUZ_ON cbi(PORTB,3)
#define BUZ_OFF sbi(PORTB,3)
/*--------------------------------------------------------------------
程序名稱:
程序功能:
注意事項:
提示說明:
輸 入:
返 回:
--------------------------------------------------------------------*/
void main(void)
{
OUT_BUZ; //設置相應的IO口為輸出
while(1)
{
BUZ_ON; //我叫
delay50ms(20);
BUZ_OFF; //我不叫
delay50ms(20);
}
}
系統(tǒng)調(diào)試
將語句:delay50ms(20);改為語句:delay50ms(1);可以聽到叫的頻率更高,吵死人了!
以ATMEGA16為例,用輕松幽默的講解方式,講解AVR的每個功能部件,配合給出Protel電路圖及ICCAVR源代碼。
都是網(wǎng)上找的資料,整理了一下,大伙湊或者學吧!
第一課 AVR IO輸出之LED顯示程序
系統(tǒng)功能
使用AVR控制8位LED,做到想閃就閃,不想閃就不閃,左閃右閃,拚命閃,演示AVR單片機之“點燈術”。
硬件設計
關于AVR的I/O結構及相關介紹詳見Datasheet,這里僅對作部分簡單介紹,下面是AVR的I/O引腳配置表:
AVR I/O 口引腳配置表
DDRXn PORTXn PUD I/O 方式 內(nèi)部上拉電阻 引腳狀態(tài)說明
0 0 X 輸入 無效 三態(tài)(高阻)
0 1 0 輸入 有效 外部引腳拉低時輸出電流 (uA)
0 1 1 輸入 無效 三態(tài)(高阻)
1 0 X 輸出 無效 推挽 0 輸出,吸收電流 (20mA)
1 1 X 輸出 無效 推挽 1 輸出,輸出電流 (20mA)
雖然AVR的I/O口單獨輸出“1”時,可輸出較大電流足已點亮一盞燈,但AVR總的I/O輸出畢竟是有限的,所以,有經(jīng)驗的點燈者考慮到除了點燈外可能還有其它費勁的活兒要干,會將AVR的I/O口設計為輸出“0”時點燈,輸出“1”時熄燈。這種接法亦叫“灌電流接法”。
AVR主控電路原理圖(點擊圖片放大,不需要放大鏡! )
LED控制電路原理圖(點擊圖片放大,不需要放大鏡! )
軟件設計
下面部分從TXT拷出,拷到網(wǎng)頁,代碼部分缺省了很多空格,比較凌亂,請諒解!
//目標系統(tǒng): 基于AVR單片機
//應用軟件: ICC AVR
/*01010101010101010101010101010101010101010101010101010101010101010101
----------------------------------------------------------------------
實驗內(nèi)容:
點燈,讓燈左閃右閃,拼命閃。
----------------------------------------------------------------------
硬件連接:
將PD口的LED指示燈使能開關切換到"ON"狀態(tài)。
----------------------------------------------------------------------
注意事項:
(1)若有加載庫程序,請將光盤根目錄下的“庫程序”下的“ICC_H”文件夾拷到D盤
(2)請詳細閱讀:光盤根目錄下的“產(chǎn)品資料\開發(fā)板實驗板\SMK系列\(zhòng)SMK1632\說明資料”
----------------------------------------------------------------------
10101010101010101010101010101010101010101010101010101010101010101010*/
#include
#include "D:\ICC_H\CmmICC.H"
#define LED_DDR DDRD
#define LED_PORT PORTD
/*--------------------------------------------------------------------
程序名稱:
程序功能:
注意事項:
提示說明:
輸 入:
返 回:
--------------------------------------------------------------------*/
void main(void)
{
uint8 i,j;
LED_DDR=0XFF;
while(1)
{
for(i=0;i<4;i++)
{
LED_PORT^=0xFF; //我閃!拚命閃!
delay50ms(10);
}
j=0x01;
for(i=0;i<8;i++)
{
j<<=1;
LED_PORT=j; //我左閃!
delay50ms(10);
}
j=0x80;
for(i=0;i<8;i++)
{
j>>=1;
LED_PORT=j; //我右閃!
delay50ms(10);
}
}
}
系統(tǒng)調(diào)試
本節(jié)的目的在于學習AVR的IO輸出功能,對于AVR來說,它和傳統(tǒng)的51單片機不同,需要設置IO引腳方向。
作如下調(diào)試:
(1)改變IO方向,即將“LED_DDR=0XFF;”改為“0X00”,觀察現(xiàn)象。
(2)將語句:delay50ms(10);改為語句:delay50ms(1);可以看到LED閃的更快,眼都花了!
東西在于靈活運用,下面是用LED做的手表,內(nèi)部是用AVR,ATmega48做的,請思考實現(xiàn)如何下面的功能。
AVR 單片機的IO口是標準的雙向端口,首先要設置IO口的狀態(tài),即:輸入還是輸出
DDRx寄存器就是AVR單片機的端口方向寄存器,通過設置DDRx可以設置x端口的狀態(tài)。
DDRx端口方向寄存器相應位設置為1則對應的x端口相應位為輸出狀態(tài),DDRx端口方向寄存器相應位設置為0則對應的x端口相應位為輸入狀態(tài)。
例如:
DDRA = 0xFF; //設置端口A所有口為輸出狀態(tài),因為0xFF對應的二進制為11111111b
DDRA = 0x0F //設置端口A高4位為輸入狀態(tài),低4位為輸出狀態(tài),因為0x0F對應的二進制為00001111b
PORTx寄存器是AVR單片機的輸出寄存器,端口輸出狀態(tài)設定好后通過設置PORTx可以使端口x的相應位輸入高電平或低電平來控制外部設備。
例如:
PORTA = 0xFF; //端口A所有口線輸出高電平
PORTA = 0x0F; //端口A高4位輸出低電平,低4位輸出高電平
小貼士:
利用位邏輯運算符對特定的端口進行設定。
PORTA = 1<<3; //端口A第4位置為高電平,其它為低電平,應為00000001左移3位后是00001000
PORTA = 1<<7; //同理,第8位置高電平
有時候我們期望端口某一位設置成高電平,但是其它位的高低電平要保持不變,如何做呢?C語言是很強大的,有辦法!如下:
PORTA |=1<<3; //實現(xiàn)端口A第4位置為高電平,其它位的高低電平不受影響
上面的語句是簡化的寫法,分解一下就是:
PORTA = PORTA | (1<<3); //數(shù)字1左移3位后與端口A進行按位或,結果就是端口A第4位置為高電平,其它位的高低電平不受影響
那么大家就會問了,如何實現(xiàn)設置某一位為低電平,其它位的高低電平不變呢?建議大家思考1分鐘再看下面的內(nèi)容。
PORTA &=~(1<<3); //解釋一下,首先將1左移3位變成00001000b,然后再按位取反變成11110111b,然后再與端口A做按位與運算,這樣就實現(xiàn)了設置端口A第4位為低電平,其它位的高低電平不變。
分解后的語句為:
PORTA = PORTA & (~(1<<3)); //結果是一樣的
將某端口相應位的高低電平翻轉(zhuǎn),即原來高電平變?yōu)榈碗娖?,低電平變?yōu)楦唠娖?,呵呵!好簡單呦?/P>
PORTA = ~PORTA; //將PORTA按位取反后再賦值給PORTA
按位邏輯運算還有一個異或,這個也非常有意思,它能實現(xiàn)電平翻轉(zhuǎn),有興趣大家看看書,算是給大家留個想頭吧!
再出個小題目!
大家都知道已知a,b兩個變量,再編程中要交換兩個變量常用的方法是定義一個中間變量c,然后:
c=a;
a=b;
b=c;
通過中間變量c完成a、b變量內(nèi)容的交換!
不過大家想一想使用C語言能不能不用中間變量來完成a、b變量的交換呢?答案肯定是能,因為C語言很強大!
不過還是希望大家先想一想再看答案,看完答案后再認真分析一下,體會編程的巧妙之處!
答案:
使用到了C語言的按位異或邏輯操作,由于沒有中間變量,同時邏輯運算的速度很快,整個交換過程比常規(guī)方法要快不少!
a ^= b;
b ^= a;
a ^= b;
過程就是a異或b,b異或a,然后a再異或b就完成了!
異或的邏輯表
1 ^ 1 0
0 ^ 1 1
1 ^ 0 1
0 ^ 0 0
adm 真厲害,這個你都知道,看來是編程的行家。
交換變量這樣的問題,如果你沒看過相關的資料,初學者很難自己想出來的。
int a,b;
a=3;
b=5;
a=a+b //a=8 b=5
b=a-b //a=8 b=3
a=a-b //a=5 a=3
這樣僅僅是算法技巧的問題,現(xiàn)在很難遇到內(nèi)存不夠 的情況了。
交換變量這樣的問題,如果你沒看過相關的資料,初學者很難自己想出來的。
int a,b;
a=3;
b=5;
a=a+b //a=8 b=5
b=a-b //a=8 b=3
a=a-b //a......
又學一招,確實也很巧妙!有異曲同工之處。
我這些是看資料從別人那學來的,不過邏輯運算要比算術運算快一倍以上,寫了個程序在AVR Studio 中軟件仿真了一下!
程序如下:
#include
void main (void)
{
int a=10,b=20;
unsigned char x=30,y=40;
a = a + b;
b = a - b;
a = a - b;
x ^= y;
y ^= x;
x ^= y;
while (1);
}
首先僅僅運算,算術運算用了8個時鐘單位,邏輯運算用了3個時鐘單位,因為算術運算牽扯到了負數(shù)。
那變量賦值呢,int 賦值用了4個時鐘單位,unsigned char賦值用了2個時鐘單位。
綜合一下,算術運算用時12個單位,邏輯運算用時5個單位,效率要高2.4倍! 項目編譯完后會生成一個.cof的調(diào)試文件(我是用ICC,CV應該也有),用AVR Studio打開這個.cof文件,選好處理器型號(M16)就會進入軟件仿真,按Alt+O快捷鍵設置處理器的頻率,這樣可以看運行的時間,否則只能看運行時鐘,時間就不準了。再下來就是按F11單步執(zhí)行,F(xiàn)10是一下執(zhí)行完一個過程,如:循環(huán)、函數(shù)等。時鐘和運行時間可以在任意時間用鼠標右鍵清零,這樣數(shù)字比較直觀,不用再加減。
評論
查看更多