專(zhuān)注電子技術(shù)學(xué)習(xí)與研究
當(dāng)前位置:單片機(jī)教程網(wǎng) >> MCU設(shè)計(jì)實(shí)例 >> 瀏覽文章

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ì)
       
      }
    }

     

     

     

     

    關(guān)閉窗口

    相關(guān)文章