AVR單片機(jī)學(xué)習(xí)(五)按鍵與數(shù)碼管的程序設(shè)計(jì)
作者:zww 1988 來(lái)源:本站原創(chuàng) 點(diǎn)擊數(shù):
… 更新時(shí)間:2014年04月18日 【字體:
大 中 小】
按鍵與數(shù)碼管的程序設(shè)計(jì)
AVR IO口的輸入模式與上拉電阻
選擇結(jié)構(gòu)語(yǔ)句與按鍵的查詢方式程序設(shè)計(jì)
數(shù)碼管基本原理
掃描方式顯示多位數(shù)碼管
一、輸入狀態(tài)IO寄存器設(shè)置
1、DDRx
某一位置0,相應(yīng)位的IO口被設(shè)置為輸入
2、PORTx某一位置1,使能對(duì)應(yīng)IO口相應(yīng)位的上拉電阻
3、PINx的對(duì)應(yīng)位是輸入的數(shù)據(jù),0或1

選擇結(jié)構(gòu)語(yǔ)句
一、關(guān)系運(yùn)算符和關(guān)系表達(dá)式
小于< 小于等于<=
大于> 大于或等于== 不等于!=
二、邏輯運(yùn)算符和邏輯表達(dá)式
邏輯與&&邏輯或||邏輯非!
三、if 語(yǔ)句結(jié)構(gòu)
if(表達(dá)式1)語(yǔ)句1
else
if(表達(dá)式2)語(yǔ)句2
else 語(yǔ)句3
四、switch
語(yǔ)句結(jié)構(gòu)
switch(表達(dá)式)
{case
常量1:表達(dá)式1
case
常量2:表達(dá)式2
.........
default:表達(dá)式n}

按鍵的查詢方式程序設(shè)計(jì)
一、PIND & (1<<6)
二、1<<6
1、1左移6位,即:0b01000000
怎么判斷一個(gè)按鍵按下了呢?首先看下圖是4個(gè)按鍵


第一個(gè)是PD2 上一段接VCC 其他都是一段接IO(PD3
PD6 PD7)口另一端接地線。
所以當(dāng)按鍵閉合時(shí)候相應(yīng)IO都輸入一個(gè)0,當(dāng)按鍵抬起來(lái)的時(shí)候IO輸入多少呢?
所以這些IO口必須將上拉電阻進(jìn)行使能,將按鍵打開(kāi)相當(dāng)于輸入一個(gè)1.所以我們判斷這3個(gè)按鍵按沒(méi)按下去的話,就判斷輸入是不是0就行了。
對(duì)于第一個(gè)按鍵如果按下輸入是1,當(dāng)抬起來(lái)時(shí)候由于AVR內(nèi)部不帶下拉電阻的,所以按鍵打開(kāi)時(shí)候輸入是0.
所以就需要判斷某一位是0,還是1.某一位是0還是1就用到了& 與運(yùn)算了。 1 跟1
與就1 1 與0 就是0
上面代碼(temp& (1<<6))
(temp & 0b01000000) temp本身值不變,只是結(jié)果來(lái)判斷某一位是0還是1
比如:PD6
上的K3
因?yàn)镻D6 所以 PIND
&(1<<6)的結(jié)果就行了
三、PIND & (1<<6)
1、移除第6位之外其他位清零
2、第6位保持輸入的值
四、與選擇結(jié)構(gòu)語(yǔ)句的結(jié)合
1、判斷PIND & (1<<6)的值,執(zhí)行相應(yīng)代碼

除非你上電之前一直講按鍵按下,否則上電的一瞬間程序就執(zhí)行到while(1);了所以要將他們加入到死循環(huán)里面如下圖

這樣就實(shí)現(xiàn)了按鍵的不停的檢測(cè)。 其實(shí)DDRD 上電默認(rèn)都是0
所以清0 置為輸入也沒(méi)有意義。
程序在if判斷設(shè)置斷點(diǎn)然后全速執(zhí)行可以看到只要沒(méi)有按鍵按下程序進(jìn)不去斷點(diǎn),如果我們?cè)诎遄由习聪翶3則如下圖所示進(jìn)入斷點(diǎn),再按下單步執(zhí)行蜂鳴器響了如圖。

所以這個(gè)程序就達(dá)到了我們的目的。
現(xiàn)在換一種判斷 就是按鍵被按下而不是沒(méi)被按下 用邏輯非

這樣達(dá)到了預(yù)想的目標(biāo)。但是這樣只能判斷一個(gè)按鍵如果多個(gè)按鍵怎么辦呢?2種辦法
一、采用if elseif else
if (){
elseif{
}
elseif {
}
else{
}
}
二、采用
switch (表達(dá)式){
case 相符合的條件
{
break;
}
case 相符合的條件 {
break;
}
default{
}
}
一、用if 實(shí)現(xiàn)
#include
int main(void){
//PD6 設(shè)置為輸入 K3
DDRD &= ~(1 << 6);
//輸入狀態(tài)下將數(shù)據(jù)寄存器使能上拉電阻
PORTD |= (1<<6);
//PD7 設(shè)置為輸入 K4
DDRD &= ~(1 <<
7);
//輸入狀態(tài)下將數(shù)據(jù)寄存器使能上拉電阻
PORTD |= (1<<7);
//PD2 設(shè)置為輸入 K1
DDRD &= ~(1 <<
2);
//輸入狀態(tài)下將數(shù)據(jù)寄存器使能上拉電阻
PORTD |=
(1<<2);//這個(gè)上拉不上拉沒(méi)關(guān)系因?yàn)樯侠前貹的電阻所以開(kāi)關(guān)打開(kāi)還是認(rèn)為是低電平
//PD3 設(shè)置為輸入 K2
DDRD &= ~(1 <<
3);
//輸入狀態(tài)下將數(shù)據(jù)寄存器使能上拉電阻
PORTD |= (1<<3);
//蜂鳴器PA3 設(shè)置方向寄存器為輸出
DDRA |= (1<<3);
//蜂鳴器關(guān)掉
PORTA &= ~(1<<3);
//流水燈端口全部設(shè)為輸出
DDRB = 0xff;
while(1){
//判斷PIND 這位是否為1
為真的話就是按鍵沒(méi)有按下
if (!(PIND &
(1<<6))){ //本來(lái)沒(méi)有按下 進(jìn)入 現(xiàn)在變成了沒(méi)有按下 不進(jìn)入了取非了
被按下進(jìn)入了 PD6
//按鍵被按下用蜂鳴器表示一下
PA3
PORTA
|= (1<<3);
}
else if(!(PIND &
(1<<7))){ //PD7
K4按下讓流水燈產(chǎn)生動(dòng)作 必須上面使能K4上拉電阻
PORTB |=
(1<<0);//第一個(gè)燈發(fā)光 就是等于1
}
else if (PIND &
(1<<2)){//因?yàn)榘聪碌臅r(shí)候是低電平接的是電源
//第二個(gè)燈發(fā)光
PORTB |=
(1<<1);
}
else if (!(PIND &
(1<<3))){
//第三個(gè)燈發(fā)光
PORTB |=
(1<<2);
}
else{
//變成了按鍵沒(méi)有按下
PORTA &= ~(1<<3); //蜂鳴器
PORTB =
0;//燈
}
//看到?jīng)]有按下一直響的,按下就不響了。
}
}
---------------------------------------------------------------
二、用switch 來(lái)實(shí)現(xiàn)就需要一次性將這四位讀回來(lái)。 代碼如下
#include
int main(void){
//PD6 設(shè)置為輸入 K3
DDRD &= ~(1 << 6);
//輸入狀態(tài)下將數(shù)據(jù)寄存器使能上拉電阻
PORTD |= (1<<6);
//PD7 設(shè)置為輸入 K4
DDRD &= ~(1 <<
7);
//輸入狀態(tài)下將數(shù)據(jù)寄存器使能上拉電阻
PORTD |= (1<<7);
//PD2 設(shè)置為輸入 K1
DDRD &= ~(1 <<
2);
//輸入狀態(tài)下將數(shù)據(jù)寄存器使能上拉電阻
PORTD |=
(1<<2);//這個(gè)上拉不上拉沒(méi)關(guān)系因?yàn)樯侠前貹的電阻所以開(kāi)關(guān)打開(kāi)還是認(rèn)為是低電平
//PD3 設(shè)置為輸入 K2
DDRD &= ~(1 <<
3);
//輸入狀態(tài)下將數(shù)據(jù)寄存器使能上拉電阻
PORTD |= (1<<3);
//蜂鳴器PA3 設(shè)置方向寄存器為輸出
DDRA |= (1<<3);
//蜂鳴器關(guān)掉
PORTA &= ~(1<<3);
//流水燈端口全部設(shè)為輸出
DDRB = 0xff;
while(1){
//首先一次性將這4個(gè)位都讀回來(lái)
2 3 6 7 腳
switch(PIND
& 0b11001100) {
case
0b11001100: {//只有第一個(gè)按鍵按下 0b11001100
接的電源按下是1
//LED
0 發(fā)光
PORTB
|= (1<<0);
break;
}
case
0b11000000: {//只有第 二個(gè)按鍵按下 0b11001100
接的電源抬起是0
//LED
0 發(fā)光
PORTB
|= (1<<1);
break;
}
case
0b10001100: {//只有第三個(gè)按鍵按下 0b11001100
接的電源抬起是0
//LED
0
PORTB
|= (1<<2);
break;
}
case
0b01001100: {//只有第四個(gè)按鍵按下 0b11001100
接的電源抬起是0
//LED
0 發(fā)光
PORTB
|= (1<<3);
break;
}
default
:{ //都沒(méi)有按下
0b11001000 因?yàn)橛邢吕?br>
//變成了按鍵沒(méi)有按下
PORTA &= ~(1<<3); //蜂鳴器
PORTB = 0;//燈
break;
}
}
//判斷PIND
這位是否為1 為真的話就是按鍵沒(méi)有按下
}
}
-------------------------------------------------------------------------
以上都是查詢方式因?yàn)槎际窃趙hile循環(huán)一邊一邊的查詢,按鍵有動(dòng)作就執(zhí)行相應(yīng)的代碼這樣很耽誤CPU的時(shí)間的,在下一篇博客我會(huì)稍微降講用中斷的方式來(lái)編寫(xiě)按鍵的程序。下面繼續(xù)說(shuō)呵呵、
八段數(shù)碼管
一、八段數(shù)碼管
1、八段數(shù)碼管由八段LED構(gòu)成
2、各LED陰極或陽(yáng)極并在一起,稱(chēng)為“位選線”:共陰、共陽(yáng)
3、其余8個(gè)引腳各自引出,稱(chēng)為“段選線”,各段可以分別控制



記住一般一位的數(shù)碼管有10個(gè)腳
個(gè)人理解:( 其中2腳是連在一起的是公共端。其他8個(gè)是段選
比如1、6接電源 其他接IO口個(gè)人理解的)
多位合一的數(shù)碼管
一、多位合一的數(shù)碼管
1、將多個(gè)八段數(shù)碼管的段選線分別并在一起,位選線引出如下圖

由上圖看出是4位 應(yīng)該是8個(gè)段選線(7段加一個(gè)點(diǎn)) 4個(gè)位選線
共12根線 。
com0 ---- com3 是位選
a-g 加 dp
是段選。
多位數(shù)碼管的使用
1、多位數(shù)碼管的各個(gè)位均可以單獨(dú)顯示不同的數(shù)據(jù),但一個(gè)時(shí)刻只能點(diǎn)亮一位、(點(diǎn)快點(diǎn)人眼看不出來(lái))
2、依次點(diǎn)亮多位數(shù)碼管中的各個(gè)位,由于人眼的視覺(jué)暫留效應(yīng),看起來(lái)是同時(shí)點(diǎn)亮
3、如下圖是電路圖 硬件電路是下圖設(shè)計(jì)的

它的每一段相當(dāng)于一個(gè)發(fā)光二極管,電流大約是10個(gè)mA左右(5--10)mA,因此段選可以直接用單片機(jī)的IO驅(qū)動(dòng)是足夠的不論是拉電流還是灌電流,這里面我們用的是一個(gè)共陰極的數(shù)碼管,因此應(yīng)該是向外拉電流,而段選線我們可以計(jì)算下段選線上最大電流時(shí)多少?假設(shè)每段都點(diǎn)亮沒(méi)段是10mA的話,那么位選線上也就是10*8
= 80mA
所以我們不能用IO口,一般的單片機(jī)不可能輸出這么大的電流,所以我采用一個(gè)三極管來(lái)進(jìn)行驅(qū)動(dòng),共陰極的數(shù)碼管一般要用NPN型的數(shù)碼管,它的接法如下圖的樣子。

再來(lái)張清楚點(diǎn)的下圖


可以看到C0
接的是COM0 位選線,IO口通過(guò)1K電阻接到三極管基極上,如果IO是個(gè)高電平的話電流就通過(guò)三極管到射極流下來(lái)的,因此三極管達(dá)到飽和,CO點(diǎn)相當(dāng)于導(dǎo)通相當(dāng)于接地。4個(gè)段選分別接到PA4到PA7
四個(gè)IO口上因此我們寫(xiě)程序首先將PA4 輸出一個(gè)1 PA5 PA6 PA7 全都輸出0
這樣我們選中第0個(gè)第一位數(shù)碼管此時(shí)在PB口上輸出的數(shù)據(jù)就會(huì)顯示在數(shù)碼管上面。編寫(xiě)程序:

設(shè)置一個(gè)斷點(diǎn)然后再單步調(diào)試(F10)?纯此@示的是那一段。同時(shí)流水燈也亮了,因?yàn)槭峭粋(gè)IO口。這樣對(duì)應(yīng)PB上的每一段都找到了。
好了這樣我們就去編寫(xiě)一下數(shù)碼管的段碼;
首先是顯示1
只要將需要點(diǎn)亮的各個(gè)段置1就實(shí)現(xiàn)了段碼的功能,具體的編寫(xiě)過(guò)程自己去畫(huà)畫(huà)看

這是我自己用數(shù)組的形式定義的。
首先是什么類(lèi)型的數(shù)組 名稱(chēng) 元素個(gè)數(shù)
0-9
A-F
全部顯示出來(lái)就是16個(gè)元素,一個(gè)字符型數(shù) 加一個(gè)逗號(hào)分開(kāi)。一直放16個(gè),使用時(shí)候要從第0個(gè)開(kāi)始下標(biāo)從0開(kāi)始的。
#include
int main(void){
//PD6 設(shè)置為輸入 K3
DDRD &= ~(1 << 6);
//輸入狀態(tài)下將數(shù)據(jù)寄存器使能上拉電阻
PORTD |= (1<<6);
//PD7 設(shè)置為輸入 K4
DDRD &= ~(1 <<
7);
//輸入狀態(tài)下將數(shù)據(jù)寄存器使能上拉電阻
PORTD |= (1<<7);
//PD2 設(shè)置為輸入 K1
DDRD &= ~(1 <<
2);
//輸入狀態(tài)下將數(shù)據(jù)寄存器使能上拉電阻
PORTD |= (1<<2);//這個(gè)上拉不上拉沒(méi)關(guān)系
因?yàn)樯侠前貹的電阻所以開(kāi)關(guān)打開(kāi)還是認(rèn)為是低電平
//PD3 設(shè)置為輸入 K2
DDRD &= ~(1 <<
3);
//輸入狀態(tài)下將數(shù)據(jù)寄存器使能上拉電阻
PORTD |= (1<<3);
//蜂鳴器PA3 設(shè)置方向寄存器為輸出
DDRA |= (1<<3);
//蜂鳴器關(guān)掉
PORTA &= ~(1<<3);
//數(shù)碼管全部置為輸出
DDRB = 0xff;
//位選線高四位全部置1 也是輸出 因?yàn)槭侵闷渲?位所以用|=
DDRA |= 0Xf0;
char scandata[16]={
0b10101111,//0
0b10100000,//1
0b11000111,//2
0b11100110,//3
0b11100000,//4
0b01101110,//5
0b01101111,//6
0b10100010,//7
0b11101111,//8
0b11101110,//9
0b11100111,//A
0b01101101,//b
0b00001111,//c
0b11000001,//d
0b01001111,//E
0b01001111//F
};
while(1){
//數(shù)碼管也需要掃描所以也用死循環(huán)
while(1)
//先將數(shù)碼管的第一位點(diǎn)亮
選中位選
PORTA
|=(1<<4);
//再將數(shù)據(jù)送到PB口上哪一段對(duì)應(yīng)哪一位
要事先測(cè)量下 編一個(gè)程序測(cè)量下
PORTB =
scandata[0];//顯示0
PORTB =
scandata[1];//顯示1
PORTB =
scandata[2];//顯示2
PORTB =
scandata[3];//顯示3
PORTB =
scandata[4];//顯示4
PORTB =
scandata[5];//顯示5
PORTB =
scandata[6];//顯示6
PORTB =
scandata[7];//顯示7
PORTB =
scandata[8];//顯示8
PORTB =
scandata[9];//顯示9
PORTB =
scandata[10];//顯示A
PORTB =
scandata[11];//顯示B
PORTB =
scandata[12];//顯示C
PORTB =
scandata[13];//顯示D
PORTB =
scandata[14];//顯示E
PORTB =
scandata[15];//顯示F
}
}
//這就是數(shù)碼管用段碼顯示。那么怎么對(duì)數(shù)碼管掃描顯示呢?
我們可以遵循這樣一個(gè)順序,首先將數(shù)碼管位選中,送數(shù)據(jù) PORTA
選中
PORTB送上數(shù)據(jù)然后打開(kāi)相應(yīng)這一位。讓他顯示出來(lái),顯示出來(lái)之后呢?再讓這一位熄滅可以把所有四位都熄滅
位選 PORTA &0x0f;高四位清0這樣就完成了數(shù)碼管的高四位顯示。
#include
int main(void){
int j ;
//PD6 設(shè)置為輸入 K3
DDRD &= ~(1 << 6);
//輸入狀態(tài)下將數(shù)據(jù)寄存器使能上拉電阻
PORTD |= (1<<6);
//PD7 設(shè)置為輸入 K4
DDRD &= ~(1 <<
7);
//輸入狀態(tài)下將數(shù)據(jù)寄存器使能上拉電阻
PORTD |= (1<<7);
//PD2 設(shè)置為輸入 K1
DDRD &= ~(1 <<
2);
//輸入狀態(tài)下將數(shù)據(jù)寄存器使能上拉電阻
PORTD |= (1<<2);//這個(gè)上拉不上拉沒(méi)關(guān)系
因?yàn)樯侠前貹的電阻所以開(kāi)關(guān)打開(kāi)還是認(rèn)為是低電平
//PD3 設(shè)置為輸入 K2
DDRD &= ~(1 <<
3);
//輸入狀態(tài)下將數(shù)據(jù)寄存器使能上拉電阻
PORTD |= (1<<3);
//蜂鳴器PA3 設(shè)置方向寄存器為輸出
DDRA |= (1<<3);
//蜂鳴器關(guān)掉
PORTA &= ~(1<<3);
//數(shù)碼管全部置為輸出
DDRB = 0xff;
//位選線高四位全部置1 也是輸出 因?yàn)槭侵闷渲?位所以用|=
DDRA |= 0Xf0;
char scandata[16]={
0b10101111,//0
0b10100000,//1
0b11000111,//2
0b11100110,//3
0b11100000,//4
0b01101110,//5
0b01101111,//6
0b10100010,//7
0b11101111,//8
0b11101110,//9
0b11100111,//A
0b01101101,//b
0b00001111,//c
0b11000001,//d
0b01001111,//E
0b01001111//F
};
while(1){
//數(shù)碼管也需要掃描所以也用死循環(huán)
while(1)
//先將數(shù)碼管的第一位點(diǎn)亮
選中位選
PORTA
|=(1<<4);
//再將數(shù)據(jù)送到PB口上哪一段對(duì)應(yīng)哪一位
要事先測(cè)量下 編一個(gè)程序測(cè)量下
PORTB =
scandata[0];//顯示0
for
(j=0;j<400;j++);//延時(shí)
PORTA &=
0x0f;
PORTA |=
(1<<5);
PORTB =
scandata[1];
PORTA &=
0x0f;
for
(j=0;j<400;j++);//延時(shí)
PORTA |=
(1<<6);
PORTB =
scandata[2];
PORTA &=
0x0f;
for
(j=0;j<400;j++);//延時(shí)
PORTA |=
(1<<7);
PORTB =
scandata[3];
for
(j=0;j<400;j++);//延時(shí)
PORTA &=
0x0f;
//單步仿真看看
就是1位亮了0 熄滅 2位亮1 熄滅
3位亮2 熄滅 4位亮3
熄滅 一直循環(huán)
//全速執(zhí)行看看顯示4個(gè)數(shù)字
可以看到亮度不怎么亮的,因?yàn)?點(diǎn)亮熄滅只有那么一小段時(shí)間是發(fā)光的
//所以如果要增加亮度只需要加一個(gè)延時(shí)程序
聲明一個(gè)變量j 在看效果 全速執(zhí)行亮度是明顯增加了
//這就是數(shù)碼管程序掃描的程序設(shè)計(jì)
}
}