LED流水燈堆疊設(shè)計(jì),且按鍵不占用CPU時(shí)間,上電顯示第1功能,單個(gè)按鍵的單擊顯示第2種功能,單按第5次返回第1功能,任意功能長按2S以上返回第2功能.程序注釋全。附件包含程序源碼和PROTUSE仿真圖。
仿真原理圖如下(proteus仿真工程文件可到本帖附件中下載)
單片機(jī)源程序如下:
代碼:
- /*****************************************************************************
- *名稱:頭文件,預(yù)定義。
- *功能:包含頭文件,預(yù)定義,端口定義
- ******************************************************************************/
- #include<reg51.h> //包含 51 單片機(jī)寄存器定義的頭文件
- #define uchar unsigned char //定義uchar可在函數(shù)中當(dāng)unsign char使用,取值不超過255。
- #define uint unsigned int //定義uint可在函數(shù)中當(dāng)unsign int使用,取值不超過65535。
- sbit KEY=P1^0; //定義按鍵KEY為P1.0
- #define LED P2 //定義LED為P2口
- #define KEY_STATE_0 0 //按鍵初始狀態(tài)
- #define KEY_STATE_1 1 //按鍵消抖
- #define KEY_STATE_2 2 //按鍵按下功能種類是單擊,雙擊還是長按
- #define KEY_STATE_3 3 //按鍵彈起
- #define SINGLE_KEY_TIME 3 //SINGLE_KEY_TIME*10MS = 30MS?判定單擊的時(shí)間長度,軟件消抖
- #define KEY_INTERVAL 30 //KEY_INTERVAL*10MS?= 300MS 判定雙擊的時(shí)間間隔
- #define LONG_KEY_TIME 200 //LONG_KEY_TIME*10MS? ?= 2S? ?判定長按的時(shí)間長度
- #define N_KEY 0 //按鍵沒動(dòng)作
- #define S_KEY 1 //單擊
- #define L_KEY 2 //長按
- #define D_KEY 3 //雙擊
- uchar code Zuoyi[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f}; //左流水?dāng)?shù)組,低電平導(dǎo)通
- uchar code Youyi[]={0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe}; //右流水?dāng)?shù)組,低電平導(dǎo)通
- uchar keySu=0; // 定義按鍵功能狀態(tài)變量
- uchar key_val; // 按鍵值
- uchar time_10ms_ok=0; //10MS定時(shí)標(biāo)志
- //define LongCount 100; //延時(shí)長按變量100X10MS=1S
- /******************************************************************************
- ***函數(shù)名稱: void delay_ms(uint x)
- ***函數(shù)功能: MS延時(shí)(按下按鍵時(shí),可以跳出循環(huán)體)
- ***輸入: 無
- ***輸出: 無
- ******************************************************************************/
- /*******/
- void delay_ms(uint x) // 延時(shí)函數(shù),延時(shí)x MS,
- {
- uint i; //定義無符號(hào)字符型變量j值域?yàn)?-65536.
- uchar j; //定義無符號(hào)字符型變量i值域?yàn)?-255.
- if(KEY) //如果按鍵為1時(shí),則執(zhí)行延時(shí)循環(huán)
- {
- for(i=0;i<x;i++) // 循環(huán)x遍
- {
- for(j=0;j<112;j++)
- ; //空語句等待一個(gè)機(jī)器周期
- }
- }
- else //如果按鍵按下則跳出循環(huán)
- return;
- }
- /******************************************************************************
- ***函數(shù)名稱: void move_l(void)
- ***函數(shù)功能: 左流水(P2口低位向高位流動(dòng)跑馬)
- ***輸入: 無
- ***輸出: 無
- ******************************************************************************/
- /******/
- void move_l(void) //1.左流水函數(shù)
- {
- uchar b; //循環(huán)變量b
- while(1) //死循環(huán)
- {
- for(b=0;b<8;b++) // 循環(huán)8遍
- {
- LED=Zuoyi[b]; //調(diào)用左流水?dāng)?shù)組
- delay_ms(500); //延時(shí)500MS
- if(KEY==0) //如果有按鍵按下
- return; //退出循環(huán)
- }
- //if(KEY==0) //此兩語句可以不用
- //return;
- }
- }
- /******************************************************************************
- ***函數(shù)名稱: void move_r(void)
- ***函數(shù)功能: 右流水(P2口高位向低位流動(dòng)跑馬)
- ***輸入: 無
- ***輸出: 無
- ******************************************************************************/
- /******/
- void move_r(void) //2.右流水函數(shù)
- {
- uchar b; //循環(huán)變量b
- while(1) //無限循環(huán)
- {
- for(b=0;b<8;b++) // 循環(huán)8遍
- {
- LED=Youyi[b]; //調(diào)用右流水?dāng)?shù)組
- delay_ms(500); //延時(shí)500MS
- if(KEY==0) //如果有按鍵按下則退出循環(huán)
- return; //退出循環(huán)
- }
- //if(KEY==0) //此兩語句可以不用
- //return;
- }
- }
- /******************************************************************************
- ***函數(shù)名稱: void flash(void)
- ***函數(shù)功能: 閃爍
- ***輸入: 無
- ***輸出: 無
- ******************************************************************************/
- /******/
- void flash(void) //3.閃爍
- {
- while(1) //無限循環(huán)
- {
- LED=0x00; //亮
- delay_ms(500); //延時(shí)500MS
- LED=0xff; //滅
- delay_ms(500); //延時(shí)500MS(毫秒)
- if(KEY==0) //如果有按鍵按下則退出循環(huán)
- return; //退出循環(huán)
- }
- }
- /******************************************************************************
- ***函數(shù)名稱: void Di_zeng1 (void)
- ***函數(shù)功能:.左遞增右遞減,,右遞增左遞減
- ***輸入: 無
- ***輸出: 無
- ******************************************************************************/
- /******/
- void Di_zeng1 (void) //4.左遞增右遞減,,右遞增左遞減
- {
- while(1)
- {
- uchar m; //循環(huán)變量
- uchar n=0; //中間變量
- uchar n1=0; //中間變量
- uchar n2=0; //中間變量
- uchar n3=0; //中間變量
- for(m=0;m<8;m++) //左遞增,循環(huán)8位
- {
- n +=(0x01<<m); // 1左移m位再賦值給n
- LED=~n; //n取反后點(diǎn)亮LED燈
- delay_ms(500); //延時(shí)500毫秒
- if(KEY==0) //如果按鍵按下
- return; //退出循環(huán)
- }
-
- for(m=0;m<8;m++) //右遞減,循環(huán)8位
- {
- n1 +=(0x80>>m); // 1右移m位再賦值給n1
- LED=n1; //n1取反后點(diǎn)亮LED燈
- delay_ms(500); //延時(shí)500毫秒
- if(KEY==0) //如果按鍵按下
- return; //退出循環(huán)
- }
-
- for(m=0;m<8;m++) //右遞增
- {
- n2 +=(0x80>>m);
- LED=~n2;
- delay_ms(500);
- if(KEY==0)
- return;
- }
- for(m=0;m<8;m++) //左遞減
- {
- n3=n3+(0x01<<m);
- LED=n3;
- delay_ms(500);
- if(KEY==0)
- return;
- }
- //if(KEY==0) //此兩語句可以不用
- //return;
- }
- }
- /******************************************************************************
- ***函數(shù)名稱: void Di_zeng2 (void)
- ***函數(shù)功能:左遞增左遞減,,右遞增右遞減
- ***輸入: 無
- ***輸出: 無
- ******************************************************************************/
- /******/
- void Di_zeng2 (void) //5.左遞增左遞減,,右遞增右遞減
- {
- while(1)
- {
- uchar m,n=0;
- uchar n1=0;
- uchar n2=0;
- uchar n3=0;
- for(m=0;m<8;m++) //左遞增
- {
- n=n+(0x01<<m);
- LED=~n;
- delay_ms(500);
- if(KEY==0)
- return;
- }
-
- for(m=0;m<8;m++)//左遞減
- {
- n1 +=(0x01<<m);
- LED=n1;
- delay_ms(500);
- if(KEY==0)
- return;
- }
-
- for(m=0;m<8;m++) //右遞增
- {
- n2 +=(0x80>>m);
- LED=~n2;
- delay_ms(500);
- if(KEY==0)
- return;
- }
- for(m=0;m<8;m++) //右遞減
- {
- n3=n3+(0x80>>m);
- LED=n3;
- delay_ms(500);
- if(KEY==0)
- return;
- }
- //if(KEY==0) //此兩語句可以不用
- //return;
- }
- }
- /******************************************************************************
- ***函數(shù)名稱: uchar key_read(void)
- ***函數(shù)功能: 鍵盤掃描函數(shù)(按鍵狀態(tài)機(jī)方式)
- ***輸入: 無
- ***輸出: 返回值。單擊,長按,雙擊
- ******************************************************************************/
- /******/
- uchar key_read(void)
- {
- static uchar key_state = 0; //按鍵初始狀態(tài)
- static uint key_time =0; //按鍵時(shí)間間隔計(jì)時(shí)變量
- uchar key_press; //按鍵是按下還是抬起變量
- uchar key_return; //按鍵函數(shù)返回
- key_return=N_KEY; //清除返回按鍵值
- key_press=KEY; //讀取當(dāng)前鍵值
- switch(key_state)
- {
- case KEY_STATE_0: //按鍵初始狀態(tài)0:判斷有無按鍵按下
- if(!key_press) //有按鍵按下
- {
- key_time=0; //一次10ms,時(shí)間間隔計(jì)數(shù)器清0
- key_state=KEY_STATE_1;//然后進(jìn)入轉(zhuǎn)到按鍵確認(rèn)態(tài)1
- }
- break;
- case KEY_STATE_1:
- //按鍵確認(rèn)狀態(tài)1:軟件消抖(確定按鍵是否有效,而不是誤觸)
- //按鍵有效的定義:按鍵持續(xù)按下超過設(shè)定的消抖時(shí)間
- if(!key_press) //按鍵仍然按下
- {
- key_time++; //一次10ms,時(shí)間間隔變量加1
- //消抖時(shí)間為SINGLE_KEY_TIME*10MS=30MS
- if(key_time>=SINGLE_KEY_TIME) //如果大于消抖時(shí)間
- {
- key_state = KEY_STATE_2;//按鍵仍然處于按下狀態(tài)
- }
- //如果按鍵時(shí)間超過消抖時(shí)間,即判定為按下的按鍵有效。
- //按鍵有效包括兩種:單擊或長按,繼續(xù)判定為那種有效按鍵
- }
- else //按鍵時(shí)間沒有超過,判定為誤觸,按鍵無效,返回狀態(tài)0
- {
- key_state = KEY_STATE_0; //返回初始狀態(tài)
- }
- break; //跳出
- case KEY_STATE_2: //按鍵狀態(tài)2:判定按鍵有效的種類:是單擊,還是長按
- if(key_press) //如果按鍵在設(shè)定的長按時(shí)間內(nèi)釋放,則判定為單擊
- {
- key_time++; //一次10ms,時(shí)間間隔變量加1
- //按鍵彈起(高電平)后計(jì)時(shí),計(jì)時(shí)大于雙擊30*10MS時(shí)間小于長按時(shí)間則為單擊
- if((key_time >=KEY_INTERVAL)&&( key_time < LONG_KEY_TIME))
- {
- key_return = S_KEY; //返回有效按鍵值: 單擊
- key_time=0; //時(shí)間間隔變量清0
- key_state = KEY_STATE_0; //返回按鍵狀態(tài)0繼續(xù)等待按鍵
- }
- else //否則在此時(shí)間段為低電平則為雙擊
- {
- key_return = D_KEY; //返回有效按鍵值: 雙擊
- key_time=0; //時(shí)間間隔變量清0
- key_state = KEY_STATE_0; //返回按鍵狀態(tài)0繼續(xù)等待按鍵
- }
- }
- else // 如果一直按下,則計(jì)算按下的時(shí)間
- {
- key_time++;
- //如果按鍵繼續(xù)按下時(shí)間超過設(shè)定的長按時(shí)間
- //(LONG_KEY_TIME*10ms=300*10ms=3000ms)則判定為長按
- if(key_time >= LONG_KEY_TIME)
- {
- key_return = L_KEY; //返回有效鍵值:長按
- key_time=0; //時(shí)間間隔變量清0
- key_state = KEY_STATE_3; //去狀態(tài)3,等待按鍵釋放
- }
- }
- break;
- case KEY_STATE_3: //狀態(tài)3為長按按鍵釋放抬起
- if(key_press) //如果按鍵為1,釋放狀態(tài)
- {
- key_state = KEY_STATE_0; //按鍵彈起,進(jìn)行下一次按鍵的判定
- }
- break;
- //特殊情況:key_state 是其他值的情況,清0key_state ,這種情況一般出現(xiàn)
- //在沒有初始key_state,第一次執(zhí)行這個(gè)函數(shù)的時(shí)候
- default: key_state = KEY_STATE_0;
- break;
- }
- return key_return; //返回按鍵值
- }
- /******************************************************************************
- ***函數(shù)名稱: void Timer0Init()
- ***函數(shù)功能: 定時(shí)器0初始化
- ***輸入: 無
- ***輸出: 無
- ******************************************************************************/
- /******/
- void Timer0Init() //10毫秒@12.00MHZ
- {
- TMOD=0x01; //設(shè)置定時(shí)器工作方式1
- TH0=(65536-10000)/256;
- TL0=(65536-10000)%256; //定時(shí)器0預(yù)設(shè)初值10000US=10MS
- TR0=0; //關(guān)閉定時(shí)器0,當(dāng)按鍵按下才開啟定時(shí)器0,計(jì)算按下的時(shí)長
- EA=1; //開總中斷
- ET0=1; //允許定時(shí)器0中斷
- TR0=1; //啟動(dòng)定時(shí)器0
- }
- /******************************************************************************
- ***函數(shù)名稱: void Timer0_Isr() interrupt 1
- ***函數(shù)功能: 定時(shí)器0中斷函數(shù)
- ***輸入: 無
- ***輸出: 無
- ******************************************************************************/
- /******/
- void Timer0_Isr() interrupt 1 //定時(shí)器0中斷服務(wù)函數(shù)
- {
- TH0=(65536-10000)/256; //預(yù)設(shè)初值50MS
- TL0=(65536-10000)%256; //預(yù)設(shè)初值50MS
- time_10ms_ok=1; //10毫秒到標(biāo)志
- }
- /**********主程序*******/
- void main(void)
- {
- KEY=0x01; //P1.0作為輸入口前必須輸出高電平
- Timer0Init(); //調(diào)用定時(shí)器0初始化函數(shù)
- while(1)
- {
- if(time_10ms_ok) //10MS定時(shí)時(shí)間到
- {
- time_10ms_ok=0; //賦值0,10MS查詢一次按鍵狀態(tài)
- key_val=key_read(); //讀取按鍵的返回值
- if(key_val==S_KEY) //如果是單擊
- {
- keySu++; //變量按鍵數(shù)加1
- if(keySu==5) //如果加到5
- keySu=0; //重新賦值0
- }
- if(key_val==L_KEY) //如果是長按
- {
- keySu=1; //按鍵數(shù)為1
- }
- if(key_val==D_KEY) //如果是雙擊
- {
- keySu=2; //按鍵數(shù)為2
- }
- switch(keySu) //5路分支
- {
- case 0: move_l();break; //當(dāng)按鍵數(shù)為0,執(zhí)行左流水功能
- case 1: move_r();break; //當(dāng)按鍵數(shù)為1,執(zhí)行右流水功能
- case 2: flash(); break; //當(dāng)按鍵數(shù)為2,執(zhí)行閃爍功能
- case 3: Di_zeng1(); break; //當(dāng)按鍵數(shù)為3執(zhí)行左遞增右遞減,右遞增左遞減功能
- case 4: Di_zeng2(); break;//當(dāng)按鍵數(shù)為4執(zhí)行左遞增左遞減,右遞增右遞減功能
- default : break; //默認(rèn)跳出
- }
- }
- }
- }
Keil代碼與Proteus8.8仿真下載: