51单片机开发入门(3)-IO口应用
前言
根據前面對51單片機的了解和基礎理論知識的掌握,接下來就正式進入單片機開發——實踐。引用最近幾天追的電視劇《覺醒年代》里新文化領袖陳獨秀、李大釗等革命前輩們一直在說的和在踐行的馬克思的一句話“實踐是檢驗真理的唯一標準”。“理論基礎決定上層建筑”這句話到哪兒都是適用的,同樣在這里,在前續知識的了解和掌握就進入了程序編寫以及電路調試等工程開展中,真正做到理論與實踐結合,這里采用的是C語言來編寫的51單片機開發程序,這里通篇可能出現一些不專業的話術和專業名詞,這里希望看到文章的朋友僅把此文當作參考,對于存在錯誤的地方可在下方評論區進行留言或者私信博主允于指導和糾正。那么接下來進入我們的入門之旅。
博主自建博客網站原鏈接:https://www.little-demon.cn/archives/11/
總體思維腦圖
這里我把這單片機入門具體應用分類繪制成了一張簡易的思維腦圖,可以通過預覽這里的思維腦圖建立大概的知識框架以及對應的整文目錄預覽以及后續的入門學習有個整體的學習思想,具體思維腦圖如下:
開發工具應用
這里單片機的概念以及最小系統前面兩篇文章已經介紹過了,這里就不再過多的贅述,這里在單片機實踐開始之前,我們應該先想個事情,我們應該采用什么樣的工具來輔助我們進行軟件的開發,用上一款得心應手的工具軟件對我們開發來說,那一定是事半功倍的。那么我先介紹一下幾款要用的開發工具:
- 編程開發工具——Keil
- 單片機電路仿真工具——Proteus
- 單片機電路原理圖、PCB繪制工具——Altium Designer
具體軟件的安裝包以及軟件的安裝教程這里可以在本博客進行搜索找到,對應的介紹也已經在文章中進行描述,可以自行前往了解,那么對于具體該怎么去操作利用,那么在接下來的時間以及文章篇幅允許下將進行具體的介紹,如果這篇文章沒有,那么也不要灰心,對應軟件的教程網絡上一搜一大把,后期在我有時間的基礎上我也將進行博文的描述或者進行錄制視頻來進行進一步的描述。
IO控制應用
從這里開始正式開始本篇博文的重點以及核心知識點。IO口在經過前面兩張文章尤其第51單片機開發入門(2)的介紹已經比較完善,在51單片機里共計有四組單片機IO口,對應的功能、理論知識前面可以進行查閱。我們整篇文章將闡述怎么利用它,以及利用它來實現什么功能。
一位輸出:流水燈
電路功能分析
這里首先了解一位是指單片機指利用一組IO口進行操作,那么對應在最小系統基礎上完成與8 個發光二極管的驅動電路設計;另外需要編寫測試程序,實現循環點亮8 個燈,時間間隔約1 秒。
硬件電路設計
這里將每個LED發光二極管的陰極接到了對應的單片機P1端口上,同時每個LED發光二極管的陽極通過串聯一個限流電阻連接到VCC(+5v)電源上。當P1端口輸出為低電平時,LED點亮,反之LED熄滅。
軟件程序編寫
在硬件電路完成的基礎上,我們開始軟件程序的編寫,首先我們先梳理一下單片機是怎么工作的。單片機接通電源后,開始工作,P1端口依次輸出低電平并且每次間隔1秒鐘,輪流交替。那么程序編寫方面該如何實現呢?我們接下來通過三種不同方法的程序來實現。
第一種:位操作
- 第一步、確定LED的端口,定義單片機IO口
- 第二步、根據設計要求,編寫延時函數
這里注意程序中編寫的函數需要在程序中進行函數聲明。
- 第三步、根據設計要求,編寫主函數程序
通過while(1){}死循環操作,使得單片機在一位一位的點亮和熄滅,但這里發現需要編寫的變量比較多,雖然能夠實現對應設計要求,但是看起來也比較復雜,可讀性不強,程序在編寫過程中也十分容易出錯,所以我們在程序設計過程也要追求程序的簡化,增強可讀性,那么開始第二種方法。
第二種:移位操作
- 第一步、確定LED的端口,宏定義單片機的IO口
這里也可以不用宏定義,直接在程序中寫對應的IO號,但是最好養成習慣,在單片機程序編寫過程中最好用宏定義,那么后期在更換IO口時只需要,在這里更改就可以了,而不用整個程序中去搜索更改對應的IO標號。
- 第二步、根據設計要求,編寫延時函數
這里延時程序和上面第一種方法的函數一致,這里就不再重復,具體可以參考上面第一種方法的延時函數程序。
- 第三步、根據設計要求,編寫主函數程序
這里通過設定P0初始值為0xfe(1111 1110)通過每間隔1秒循環8次左移,完成程序設計要求。
第三種:數組方式
- 第一步、確定LED的端口,宏定義單片機的IO口
這里也可以不用宏定義,直接在程序中寫對應的IO號,但是最好養成習慣,在單片機程序編寫過程中最好用宏定義,那么后期在更換IO口時只需要,在這里更改就可以了,而不用整個程序中去搜索更改對應的IO標號。
- 第二步、變量定義
通過數組將8種狀態所對應的字節歸納定義為數組變量。
- 第三步、根據設計要求,編寫延時函數
這里延時程序和上面第一種方法的函數一致,這里就不再重復,具體可以參考上面第一種方法的延時函數程序。
- 第四步、根據設計,要求編寫主函數
通過數組種對應元素的變化,改變P1端口對應輸出的字節,控制LED燈進行流水燈操作。
燒錄程序,仿真調試
這里通過Proteus仿真工具根據電路原理圖進行繪制對應的單片機最小系統以及所需要的外圍電路,將前面編寫的單片機程序編譯生成的HEX文件燒錄進單片機里,查看具體效果。
整體程序整理:
#include <reg51.h>/*****引腳定義*****/ sbit LED0=P1^0; sbit LED1=P1^1; sbit LED2=P1^2; sbit LED3=P1^3; sbit LED4=P1^4; sbit LED5=P1^5; sbit LED6=P1^6; sbit LED7=P1^7;/*****變量定義*****/ unsigned char LED_DAT[8]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};/*****函數聲明*****/ void delay_ms(unsigned char ms) ; void shagua();//傻瓜方式 void shuzu();//數組方式 void yiwei();//移位方式/*****主函數*****/ void main(void) {while(1){shagua();yiwei();shuzu(); } }void shagua()//傻瓜方式 {LED0=0;delay_ms(1000);LED0=1;//LED0點亮,其余熄滅LED1=0;delay_ms(1000);LED1=1;//LED1點亮,其余熄滅LED2=0;delay_ms(1000);LED2=1;//LED2點亮,其余熄滅LED3=0;delay_ms(1000);LED3=1;//LED3點亮,其余熄滅LED4=0;delay_ms(1000);LED4=1;//LED4點亮,其余熄滅LED5=0;delay_ms(1000);LED5=1;//LED5點亮,其余熄滅LED6=0;delay_ms(1000);LED6=1;//LED6點亮,其余熄滅LED7=0;delay_ms(1000);LED7=1;//LED7點亮,其余熄滅 }void shuzu()//數組方式 { unsigned char i;for(i=0;i<8;i++){P1=LED_DAT[i];delay_ms(1000;} }void yiwei()//移位方式 {unsigned char temp=0xfe,i;temp=0xfe; // 1111 1110for(i=0;i<8;i++){P1=temp;temp=(temp<<1)+1; //1111 1101delay_ms(100);} }/******延時函數******/ void delay_ms(unsigned char ms) {unsigned int i,j;for(i=0;i<ms;i++){for(j=0;j<333;j++);} }一位輸入:獨立按鍵
電路功能分析
這里首先了解一位是指單片機指利用一組IO口進行操作,那么對應在最小系統基礎上完成2個獨立按鍵與8 個發光二極管的驅動電路設計;另外需要編寫測試程序,實現實現按鍵控制循環點亮8 個燈,時間間隔約1 秒。
硬件電路設計
通過上圖發現:這里將每個LED發光二極管的陰極接到了對應的單片機P1端口上,同時每個LED發光二極管的陽極通過串聯一個限流電阻連接到VCC(+5v)電源上。當P1端口輸出為低電平時,LED點亮,反之LED熄滅。
在上面LED流水燈的基礎之上,引入添加了獨立按鍵模塊,根據電路設計要求,具體添加的按鍵模塊,根據按鍵開關(非自鎖開關)原理。當按鍵沒有按下的時候,VCC(+5v)經過470歐姆限流電阻 輸入進單片機的IO口,按鍵所對應的單片機IO口輸入信號為高電平;當按鍵按下的時候,輸入信號對地,這樣子按鍵輸入信號對地,即輸入信號為低電平,單片機輸入信號為低電平。通過高低電平檢測,判斷按鍵是否按下。
單獨按鍵主要功能簡化:
按鍵K1按鍵時,P3.0輸入為“0“;
按鍵K1松下時,P3.0輸入為“1“。
圖中,按鍵和電阻可以交換位置,交換后輸入邏輯剛好相反。
按鍵抖動問題
- 按鍵抖動問題分析
這里認識了解了按鍵單個按鍵的工作原理后,我們還需要了解按鍵抖動的影響 :當用手按下一個按鍵時,往往所按按鍵在閉合位置和斷開位置之間彈跳幾下才會穩定到閉合狀態;在釋放一個按鍵時,也會出現類似的情況。這就是按鍵抖動。這是由機械結構的固有特性決定的,不可避免。
? 按鍵抖動的持續時間大小不一,一般在10ms 左右。
-
按鍵抖動問題舉例
當存在按鍵抖動時,下面程序將**對抖動時的每一個下降沿進行計數**,從而出錯。因此應當采取一定的措施消除抖動的影響
- 按鍵抖動問題消除方法
要求消除按鍵抖動的影響,實現一次按鍵計數值只增加一次。
實現方法:
軟件:延遲再掃描
硬件:外接額外電路 (觸發器、濾波電路)
# include <reg51.h> # define uchar unsigned char sbit KEY = P2^0;void main( ) {uchar ucCounter, ucPreKey ;ucCounter = 0x00; //計數初值KEY = 1; //準雙向口ucPreKey = 1;while(1){if ((KEY==0) && (ucPreKey==1)){delay(2); //延時20msif(KEY == 0){ucCounter++; //計數值加一} } ucPreKey = KEY; //存儲前一狀態} }軟件程序編寫
在硬件電路完成的基礎上,我們開始軟件程序的編寫,首先我們先梳理一下單片機是怎么工作的。單片機接通電源后,開始工作,兩個獨立按鍵在這里,一個按鍵按下時控制P1口所接的LED燈進行左移,另一個按鍵按下時控制P1口所接的LED燈右移,那么程序編寫方面該如何實現呢?
- 第一步、確定LED的端口,宏定義單片機的IO口以及確定兩個獨立按鍵的端口,位定義兩個按鍵
- 第二步、變量定義
通過數組將8種狀態所對應的字節歸納定義為數組變量。
- 第三步、根據設計要求,編寫延時函數
- 第四步、根據設計要求,編寫按鍵掃描函數
通過按鍵掃描函數,當沒有按鍵按下時,輸出按鍵變量keynum=0;當按鍵1按下時,輸出按鍵變量keynum=1;當按鍵2按下時,輸出按鍵變量keynum=2;這里按鍵進行延時消抖。
- 第五步、根據設計要求,編寫數組左移以及右移函數
這里先將數組的左移以及右移函數提前寫好,到時候在主函數種可以進行調用,但是同時也可以在主函數中進行左移、右移操作,同時也可以進行添加相關頭文件,直接調用函數進行左移、右移。
- 第六步、根據設計要求,編寫主函數
在上面已經梳理清楚了獨立按鍵控制8個LED左移流水燈以及右移流水燈,那么通過while(1){}死循環將單片機一開始就先進行按鍵掃描,當按鍵沒有任何操作的時候,按鍵值沒有任何變化為0;當按鍵發生變化的時候,對應的按鍵值返回到鍵盤掃描函數,再根據if()的判斷函數或者可以利用Switch()選擇函數來進行編寫后來實現具體的功能。
燒錄程序,仿真調試
這里通過Proteus仿真工具根據電路原理圖進行繪制對應的單片機最小系統以及所需要的外圍電路,將前面編寫的單片機程序編譯生成的HEX文件燒錄進單片機里,查看具體效果。
整體程序梳理;
#include <reg51.h>//獨立按鍵管腳IO口定義 sbit KEY1=P3^0; sbit KEY2=P3^1; //8個LED燈端口宏定義 #define LED P1/*****函數聲明*****/ void shuzu_zuo(); void shuzu_you(); unsigned char key_scan(); void delay_ms(unsigned char ms);/*****變量定義*****/ unsigned char LED_DAT[8]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};/*****延時函數*****/ void delay_ms(unsigned char ms) {unsigned int i,j;for(i=0;i<ms;i++){for(j=0;j<333;j++);} } //******數組操作******// void shuzu_you()//數組方式_右移 {char i=0; for(i=0;i<8;i++) //循環右移{P1=LED_DAT[i];delay_ms(1000); } } void shuzu_zuo()//數組方式_左移 {char n;for(n=7;n>=0;n--) //循環左移{P1=LED_DAT[n];delay_ms(1000);} }unsigned char key_scan() //按鍵掃描函數 {unsigned char keynum;keynum=0;if(KEY1==0) //按鍵1按下{delay_ms(10); //軟件消除按鍵抖動if(KEY1==0){while(KEY1==1);keynum=1; //按鍵1按下輸出變量為1}}if(KEY2==0){delay_ms(10); //軟件消除按鍵抖動if(KEY2==0) //按鍵2按下{while(KEY2==1);keynum=2; //按鍵2按下輸出變量為2}}return(keynum); }void main() {while(1){if(key_scan()==1) //按鍵1按下{shuzu_you(); //執行右移函數}if(key_scan()==2) //按鍵2按下{shuzu_zuo(); //執行左移函數}} }多位輸出:數碼管
數碼管:由發光二極管陣列構成。用于顯示數字和簡單英文字符。
數碼管的工作原理
- 數碼管的結構
共陽極數碼管
共陰極數碼管
- 數碼管的顯示
段碼,數碼管顯示的內容:
位碼,(即com端)數碼管是否點亮
- 數碼管的段碼分析(以共陰極數碼管為例)
上方圖片即為共陰極數碼管的段選表,通過不同段位的電平情況,顯示不同的字符。dp為小數點位
- 多個數碼管的控制
靜態顯示,每個數碼管單獨控制,所有數碼管同時點亮
📌📌動態顯示,動態顯示,段碼共用,位碼分別控制,每個數碼管**循環點亮**
I/O端口1不斷送待顯示字符的段選碼,I/O端口2不斷送出不同的位掃描碼,并使每位顯示字符停留顯示一段時間,一般為1ms~5ms,利用眼睛的視覺暫留,從顯示器上便可以見到相當穩定的數字顯示。
單個數碼管顯示
電路功能分析
根據前面的數碼管的基礎原理的分析和學習,在51單片機足校系統的基礎上添加單個共陰極數碼管循環顯示0-9對應的編碼。
硬件電路設計
這里在51單片機最小系統的基礎上添加了一個共陰極數碼管,這里注意電路的連接,同時在該基礎上,在單片機連接數碼管的IO口上同時連接8個LED燈,以此來查看數碼管編碼發生變化時對應的各端口的變化情況。
📢(這里注意數碼管段選端口以及LED端口接在P0端口上需要接上上拉電阻,這里采用8腳排阻)
軟件程序編寫
在硬件電路完成的基礎上,我們開始軟件程序的編寫,首先我們先梳理一下單片機是怎么工作的。單片機接通電源后,開始工作,單片機IO口P0輸出不同字節的電平,控制數碼管段選端,那么程序編寫方面該如何實現呢?
- 第一步、確定LED的端口,宏定義單片機的IO口以及確定兩個獨立按鍵的端口,位定義兩個按鍵
- 第二步、變量定義
這里將共陰極數碼管的段選表通過數組來記錄下來,那么在下面數碼管顯示程序中只要通過選定數組中對應的元素即可顯示相對應的數值。
- 第三步、根據要求,編寫延時函數
這里延時函數已經是老生常談了,那么后面的延時函數我就不再過多的贅述。
- 第四步、根據設計要求,編寫主函數程序
在上面已經梳理清楚了獨立按鍵控制8個LED左移流水燈以及右移流水燈,那么這里數碼管顯示不同的字符也一樣通過while(1){}死循環將單片機一開始就通過for循環,依次從數組中0-9個元素依次循環選擇,并且通過端口賦值,使得數碼管循環顯示不同的字符。編寫后來實現具體的功能。
燒錄程序,仿真調試
這里通過Proteus仿真工具根據電路原理圖進行繪制對應的單片機最小系統以及所需要的外圍電路,將前面編寫的單片機程序編譯生成的HEX文件燒錄進單片機里,查看具體效果。
整體程序梳理如下:
#include <reg51.h>//8個LED燈、單個數碼管端口宏定義 #define LED P1 /*****函數聲明*****/ void delay_ms(unsigned char ms);/*****變量定義*****/ unsigned char shumaguan[16]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71}; //共陰數碼管代碼表"0-F" /*****延時函數*****/ void delay_ms(unsigned char ms) {unsigned int i,j;for(i=0;i<ms;i++){for(j=0;j<333;j++);} } /*****主函數*****/ void main(void) {int i;while(1){for(i=0;i<10;i++) //0~9順序循環{LED=shumaguan[i];delay_ms(5000);} } }多位數碼管顯示
電路功能分析
添加多位數碼管模塊電路,利用單片機輸出控制實現四位共陰極數碼管循環0-9999;并且按鍵1按下顯示1234,按鍵2按下恢復顯示循環0-9999。
硬件電路設計
這里首先分析一下三極管的段選端,這里段選端口接到單片機的P0端口上,這里和前面的單個數碼管一樣在P0端口需要接上上拉電阻。這里數碼管的位選端口通過NPN三極管以及幾個電阻組成了數碼管的驅動電路,這里拿位選端口1舉例來說,當位端口輸出低電平時候,Q1NPN二極管截至,位選端1為低電平;當位端口輸出高電平時候,Q1NPN二極管導通,位選端1為高電平。
同時按鍵電路的設計:當按鍵1按下時,單片機P3.0檢測到電平變化,進行按鍵對應功能操作。當按鍵2按下時,單片機P3.1檢測到電平變化恢復數碼管循環0-9999顯示。
📢(這里注意三極管采用的是NPN三極管)
軟件程序編寫
在硬件電路完成的基礎上,我們開始軟件程序的編寫,首先我們先梳理一下單片機是怎么工作的。單片機接通電源后,開始工作,兩個獨立按鍵在這里,一個按鍵按下時控制數碼管顯示1234,另一個按鍵按下時控制數碼管進行0-9999循環顯示,那么程序編寫方面該如何實現呢?
- 第一步、確定 數碼管的位選端口,宏定義單片機的段選 IO 口以及確定兩個獨立按鍵的端口,位定義兩個按鍵
- 第二步、變量定義
- 第三步、根據設計要求,編寫延時函數
- 第四步、根據設計要求,編寫按鍵掃描函數
通過按鍵掃描函數,當沒有按鍵按下時,輸出按鍵變量 keynum=0;當按鍵 1 按下時,輸出按鍵變量 keynum=1;當按鍵 2 按下時,輸出按鍵變量 keynum=2;這里按鍵進行延時消抖。
- 第五步、根據設計要求,編寫數碼管顯示函數(四位整數)
這里將四位數碼管每一位的顯示都利用求余和除法來計算得到,這里需要主要的是數碼管動態顯示,記得每一位都需要做消影處理。
- 第六步、根據整體設計要求,這里編寫主函數調用函數
在按鍵掃描函數以及數碼管顯示函數完成的情況下,這里只需要在主函數中調用即可,這里利用Switch語句來判斷選擇兩個按鍵的功能,同時給每個按鍵對應的功能添加上去,并且根據題目要求需要添加0-9999循環顯示,這里就利用for循環語句進行編寫。至此,該多位數碼管程序就全都完成了。
燒錄程序,仿真調試
這里通過 Proteus 仿真工具根據電路原理圖進行繪制對應的單片機最小系統以及所需要的外圍電路,將前面編寫的單片機程序編譯生成的 HEX 文件燒錄進單片機里,查看具體效果。
整體程序梳理;
#include<reg51.h>/*****數碼管段選宏定義*****/ #define SEG_Port P0/*****數碼管位選管腳定義*****/ sbit wei1=P2^0; sbit wei2=P2^1; sbit wei3=P2^2; sbit wei4=P2^3; //按鍵端口定義 sbit KEY1=P3^0; sbit KEY2=P3^1;/*****變量定義*****/ unsigned char shumaguan[16]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71}; //共陰極數碼管的段選表unsigned int dis_dat=0;//0-65535; 5535 -> 0000 unsigned char cnt=0,stu_flag=0;/*****函數聲明*****/ void dis_seg(unsigned int dat); unsigned char key_scan();/*****延時函數****/ void delay_ms(unsigned char ms) {unsigned int i,j;for(i=0;i<ms;i++){for(j=0;j<333;j++);} }/*****數碼管顯示函數****/ void dis_seg(unsigned int dat)//顯示四位整型數據的函數 {/*****千位*****/SEG_Port=shumaguan[dat/1000];//送段碼到數碼管wei1=0;//控制第一位數碼管公共端截止delay_ms(3);wei1=1;SEG_Port=0xff;//消影/*****百位*****/SEG_Port=shumaguan[dat/100%10];wei2=0;delay_ms(3);wei2=1;SEG_Port=0xff;/*****十位*****/SEG_Port=shumaguan[dat/10%10];wei3=0;delay_ms(3);wei3=1;SEG_Port=0xff;/*****個位*****/SEG_Port=shumaguan[dat%10];wei4=0;delay_ms(3);wei4=1;SEG_Port=0xff; }/*****按鍵掃描函數函數****/ unsigned char key_scan() //按鍵掃描函數 {unsigned char keynum;keynum=0;if(KEY1==0) //按鍵1按下{delay_ms(10); //軟件消除按鍵抖動if(KEY1==0){while(KEY1==1);keynum=1; //按鍵1按下輸出變量為1}}if(KEY2==0){delay_ms(10); //軟件消除按鍵抖動if(KEY2==0) //按鍵2按下{while(KEY2==1);keynum=2; //按鍵2按下輸出變量為2}}return(keynum); }void main(void) {while(1){switch(key_scan()) //按鍵掃描函數判斷{case 1:stu_flag=1;break;case 2:stu_flag=0;break;default:break;}if(stu_flag==1){dis_seg(1234);//按鍵1按下顯示1234}else //按鍵2按下恢復0-9999{for(cnt=0;cnt<10;cnt++){dis_seg(dis_dat);}dis_dat++;dis_dat%=10000;}} }多位輸入:矩陣按鍵
行列式鍵盤的控制原理
當單片機系統所需的按鍵數量比較多時,如果繼續采用一個IO引腳控制一個按鍵的接口方式,所需的IO引腳較多;為了節約引腳資源,一般采用行列式鍵盤的接口方式。
如下圖,需要控制16個按鍵。
如果采用行列式鍵盤只需要8個引腳就可以了。
分析電路
當沒有按鍵按下時
列信號P1.4~P1.5輸入均為**‘1’**
當有按鍵按下時
如果行信號P1.0~P1.3輸出為‘0’,則按鍵對應的列信號輸入為****‘0’。
編程思路
- 行列式鍵盤的控制(掃描法)
1、判斷是否有按鍵按下
2、確定按下的是哪一個按鍵(確定按鍵代碼)
🎯:簡要來說就是確定行,確定坐標也就得到了所按下按鍵的坐標。
為了識別按鍵,必須對鍵盤中的按鍵進行編碼,每一個按鍵都有一個確定的按鍵代碼。如果使用非編碼鍵盤,按鍵的編碼方式可以由用戶自由定義。
步驟如下:
1、全掃描:
四行一起掃描,以判斷是否有鍵按下;同時采用了延遲再掃描的方法進行消抖。
2、逐行掃描:
四行依次掃描,判斷按鍵的行列位置;
3、生成按鍵代碼
使用不同的編程語句可以生成不同的按鍵代碼。
- 行列式鍵盤的控制(線反轉法)
掃描法要逐列掃描查詢,有時則要多次掃描。而線反轉法則很簡練,無論被按鍵是處于第一列或最后一列,均只需經過兩步便能獲得此按鍵所在的行列值
(1)讓行線編程為輸入線,列線編程為輸出線 ,并使輸出線輸出為全低電平,則行線中電平由高變低的所在行為按鍵所在行。
(2) 再把行線編程為輸出線,列線編程為輸入線,并使輸出線輸出為全低電平,則列線中電平由高變低所在列為按鍵所在列。
典型編程:
#include < reg51.h > #define uchar unsigned char #define uint unsigned intvoid delay(uchar ucData); uchar kbscan( );void main( ) {uchar key ;while(1){key = kbscan( ) ; //掃描按鍵,獲取按鍵代碼switch( key ){case 0x00 : … … ; //如果不按鍵, …break ;case 0x11 : … … ; //如果按下鍵1, …break ;case 0x21 : … … ; //如果按下鍵2, …break ;… … ;default : break ;}} }電路功能分析
采用 4*4 鍵盤與6 位共陰(CC:Common Cathode)數碼管模擬一電話撥號與顯示;
(1)、基本功能
沒有按鍵時,數碼管不顯示;
按下 1 鍵,最低位顯示,按住還是顯示1;
松開后再按下 2,低兩位顯示12(要求有移位功能);
依次類推實現 6 位撥號功能。
(2)、擴展功能
增加退格與修改功能
硬件電路設計
這里通過將電路模塊化處理,這里將電路分為三大部分。第一部分,51單片機最小系統,這里毋庸置疑;第二部分,輸入部分——矩陣鍵盤以及第三部分輸出部分——6位共陰極數碼管以及其數碼管驅動電路。
通過這樣的細化后,我們就可以進行軟件程序對應的編寫。在此之前,我們先分析一下數碼管驅動電路,分析一下數碼管的段選端以及位選端口.這里采用的是6位共陰極數碼管,那么對應的數碼管位選端口也就是6個,那么這里位選端口直接接到了7406(六高壓輸出反相緩沖器)上,再由7406的管腳接到單片機的P2端口上,同時將數碼管的段選端口接到了單片機的P0端口。
軟件程序編寫
在硬件電路完成的基礎上,我們開始軟件程序的編寫,首先我們先梳理一下單片機是怎么工作的。程序也可以進行分模塊編寫,同時在我們日常程序的編寫中也要注意,將程序進行模塊編程,這樣不僅方便我們及時查閱程序代碼,同時便于我們及時調試修改程序,后期將出一篇文博文專門介紹模塊化編程。
那我們書歸正傳,這里我根據題目設計要求,需要我們進行矩陣按鍵的處理以及數碼管的輸出控制,那么再程序方面我們也是分這么兩個大方向,我們首先確定矩陣按鍵采用什么方法(掃描法還是線反轉法),那么接下來就開始吧。
- 根據題目要求,編寫矩陣按鍵程序
(KEY4X4.c)
#include "key4x4.h" #include "delay.h"unsigned char j=0; //按鍵次數 unsigned char str[7];//定義變量//掃描鍵盤,判斷哪行那列有鍵按下void keyscan(void){unsigned char temp;KEY_PORT=0xfe;//開始讀取第一行temp=KEY_PORT;//讀取KEY_PORT口temp=temp&0xf0;//判斷是否有鍵按下 按位相與if(temp!=0xf0)//有鍵按下{delay_ms(10); //按鍵消抖temp=KEY_PORT;temp=temp&0xf0;//再次判斷是否有鍵按下if(temp!=0xf0) //有鍵按下{temp=KEY_PORT;//按鍵值賦值給temp變量switch(temp)//判斷按鍵值{case(0xee):str[j]=0;j++; //第一行第一列為'0' 鍵break;case(0xde):str[j]=1;j++; // 第一行第二列為'1' 鍵break;case(0xbe):str[j]=2;j++; // 第一行第三列為'2' 鍵break;case(0x7e):str[j]=3;j++; // 第一行第三列為'3' 鍵break; }while(temp!=0xf0) //判斷按鍵是否抬起,如果按鍵未抬起,while語句成立,再次讀取KEY_PORT的值,進行判斷,直至按鍵抬起 ,退出循壞{temp=KEY_PORT; //按鍵值賦值給temp變量temp=temp&0xf0; //判斷是否有鍵按下 按位相與}}}KEY_PORT=0xfd; //開始讀取第二行temp=KEY_PORT; //讀取KEY_PORT口temp=temp&0xf0; if(temp!=0xf0) //有鍵按下{delay_ms(10); //按鍵消抖temp=KEY_PORT; //按鍵值賦值給temp變量temp=temp&0xf0;if(temp!=0xf0){temp=KEY_PORT;switch(temp){case(0xed):str[j]=4;j++; //第二行第一列為'4' 鍵break;case(0xdd):str[j]=5;j++; //第二行第二列為'5' 鍵break;case(0xbd):str[j]=6;j++; //第二行第三列為'6' 鍵break;case(0x7d):str[j]=7;j++; //第二行第四列為'7' 鍵break;}while(temp!=0xf0) //判斷按鍵是否抬起,如果按鍵未抬起,while語句成立,再次讀取KEY_PORT的值,進行判斷,直至按鍵抬起 ,退出循壞{temp=KEY_PORT;temp=temp&0xf0;} }}KEY_PORT=0xfb;temp=KEY_PORT;temp=temp&0xf0;if(temp!=0xf0){delay_ms(10);temp=KEY_PORT;temp=temp&0xf0;if(temp!=0xf0){temp=KEY_PORT;switch(temp){case(0xeb):str[j]=8;j++;break; //第三行第一列為'8' 鍵case(0xdb):str[j]=9;j++;break; //第三行第二列為'9' 鍵case(0xbb): if(j>0) j=j-1;break; //第三行第三列為'退格' 鍵case(0x7b): j=0;break; //第三行第四列為'清零' 鍵}while(temp!=0xf0) //等待按鍵釋放,未釋放執行此程序{temp=KEY_PORT; temp=temp&0xf0;} }}}先確定哪一行的按鍵發生了操作,開始讀取第一行,讀取KEY_PORT口。判斷是否有鍵按下 按位相與(這里0xf0),這里需要注意按鍵的消抖的處理,這里將按鍵產生的數值賦值給定義的變量。同時這里需要注意按鍵的長按的問題。判斷按鍵是否抬起,如果按鍵未抬起,while語句成立,再次讀取KEY_PORT的值,進行判斷,直至按鍵抬起 ,退出循壞。通過確定行和列后,得到按鍵掃描的返回值進行對應功能的操作。這里通過每個按鍵定義返回的不同的數值,確定不同的功能。
(KEY4X4.h)
#ifndef _KEY4x4_H_ #define _KEY4x4_H_ #include <reg51.h>//矩陣按鍵管腳IO口宏定義 #define KEY_PORT P3/*****函數聲明*****/ void keyscan(void);//掃描鍵盤,判斷哪行那列有鍵按下#endif這里對.h函數進行函數聲明;進行矩陣按鍵管腳IO口宏定義
- 根據題目要求,編寫數碼管程序
(SEG.c)
#include "seg.h" #include "delay.h"extern unsigned char j; //調用按鍵次數 extern unsigned char str[7];//調用數組變量//共陰極數碼管段碼表 unsigned char code seg_dat[]={0x3f,/*0*/0x06,/*1*/0x5b,/*2*/0x4f,/*3*/0x66,/*4*/ 0x6d,/*5*/0x7d,/*6*/0x07,/*7*/0x7f,/*8*/0x6f,/*9*/}; void display(void) //數碼管顯示函數 {unsigned char i;Wei_PORT = 0x00;//數碼管位碼線端口初始化for(i=0;i<j;i++){Wei_PORT=0x01<<i;//(左移)送位碼(0x01,0x02,0x04,0x08,0x10,0x20)SEG_PORT=seg_dat[str[j-i-1]]; //str[]數組用來存儲按鍵值,j-1-i作為指針指向seg_dat數組,保證之前輸入的往左移delay_ms(3);}if(j==7) //如果按鍵按下超過超過6位,也是數值顯示位7位以上,顯示最后按下的按鍵的值{j=1;str[0]=str[6];} //按到第七個鍵時,重新從最低位開始 }(SEG.h)
#ifndef _SEG_H_ #define _SEG_H_ #include <reg51.h>//數碼管管腳宏定義 #define Wei_PORT P0 //定義數碼管位碼線端口宏定義 #define SEG_PORT P2 //定義數碼管段碼線端口宏定義/*****函數聲明*****/ void display(void);//數碼管顯示函數#endif這里對.h函數進行函數聲明;進行矩陣數碼管段選管腳IO口宏定義,同時定義數碼管位選端口。
- 根據設計要求,編寫按鍵掃描函數
(DELAY.c)
#include "delay.h"//延時函數(1)--1ms void delay_ms(unsigned char ms) {unsigned int i,j;for(i=0;i<ms;i++){for(j=0;j<333;j++);} }(DELAY.h)
#ifndef _DELAY_H_ #define _DELAY_H_/*****函數聲明*****/ void delay_ms(unsigned char ms);//延時函數#endif- 根據整體設計要求,這里編寫主函數調用函數
這里單片機一開機就先檢測鍵盤具體情況,通過掃描鍵盤確定是哪行哪列按鍵按下,記錄按鍵次數以及存儲數組中,在通過數碼管顯示的函數,確定數碼管的顯示。
燒錄程序,仿真調試
這里通過 Proteus 仿真工具根據電路原理圖進行繪制對應的單片機最小系統以及所需要的外圍電路,將前面編寫的單片機程序編譯生成的 HEX 文件燒錄進單片機里,查看具體效果。
總結
這里將IO口控制應用進行了分類,首先對一位輸出進行闡述:流水燈控制,在流水燈控制方面采用了三種方法:復雜的進行一位一位控制;采用數組方式;采用移位操作方式。第二部分進行一位輸入控制:獨立按鍵,具體闡述了按鍵操作的工作原理以及獨立按鍵存在的抖動問題,并且通過軟件硬件的控制消去按鍵的抖動。第三部分介紹了多位輸出數碼管:這里也是分成了兩大部分,單個數碼管顯示以及多位數碼管顯示,以及數碼管的動靜態顯示。在上面按鍵的學習基礎上對實驗內容也進行了整合,通過樣例進行了分析解釋。最后把矩陣鍵盤原理進行了分析,以及經典電路,樣例程序進行理解分析,對于矩陣鍵盤兩種方法都進行了介紹,同時分析出各自的優缺點。和前面一樣都用了一個經典的樣例程序進行分析闡述。
到這里為止,IO口控制應用就先到這里了,后續將添加多位輸出:LCD1602的顯示控制,如果你認為文章中有什么問題和錯誤,可以在下方留言或者直接與我聯系,我將第一時間回復以及糾正。同時如果你認為該片文章對你有什么幫助,可以點擊下方打賞,對博主一點小獎勵,恰波奶茶🥂🥂,你的打賞是對我最大的鼓勵,謝謝。
總結
以上是生活随笔為你收集整理的51单片机开发入门(3)-IO口应用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring Cloud Eureka
- 下一篇: USF MSDS501 计算数据科学中文