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