實現效果描述:初始狀態爲全滅,根據按下不同的矩陣鍵盤鍵位有不同的流水現象:包括兩種速度的拉幕式和閉幕式流水,以及另外四種花式流水現象一共八種現象。
程序清單如下:
/***********************************************************************************************************************************************
按鍵控制拉閉幕式流水燈有兩種速度。
現象:程序下載,上電之後,通過矩陣鍵盤的八個按鍵控制可以分別進入兩種速度的拉幕式流水燈和閉幕式流水燈共四種,以及另外四種花式流水現象,一共八種。每種現象在運行週期內不會被打斷,同一按鍵做了防誤觸或長時間按下設置爲連按無效。
**********************************************************************************************************************************************/
#include<reg52.h>
typedef unsigned int u16; //對數據類型進行聲明定義
typedef unsigned char u8;
#define GPIO_LED P0 //宏定義提高可移植性和代碼可讀性
#define GPIO_KEY P2
//各流水現象對應數組以及防連按的flag值定義
unsigned char code led[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};
unsigned char code led_ON[]={0x00,0x18,0x3c,0x7e};
unsigned char code led_ON1[]={0xe7,0xdb,0xbd,0x7e};
unsigned char code led_OUT[]={0x7e,0x3c,0x18,0x00} ;
unsigned char code led_OUT1[]={0x7e,0xbd,0xdb,0xe7};
static unsigned char flag=100;
static unsigned char flag_now=66;
static unsigned char flag_last=66;
/********************************************************************
* 函 數 名 : delay_ms
* 函數功能 : 毫秒級延時函數
* 入口參數 : 以毫秒爲單位的延時時間
********************************************************************/
void delay_ms(unsigned int s)//毫秒級延時
{
unsigned char i;
while(s--)
{
for(i=0;i<125;i++);
}
}
/********************************************************************
* 函 數 名 : keyvalue_get
* 函數功能 : 鍵盤掃描函數
* 函數說明 : 未設置返回值,鍵值通過全局變量傳遞。
********************************************************************/
void keyvalue_get(void) //矩陣鍵盤掃描
{
char f=0;
GPIO_KEY=0x0f;
if(GPIO_KEY!=0x0f)//讀取按鍵是否按下
{
delay_ms(10);//延時10ms進行消抖
if(GPIO_KEY!=0x0f)//再次檢測鍵盤是否按下
{
flag_last=flag_now;
//測試列
GPIO_KEY=0X0F;
switch(GPIO_KEY)
{
case(0X07): flag=0;break;
case(0X0b): flag=1;break;
case(0X0d): flag=2;break;
case(0X0e): flag=3;break;
}
//測試行
GPIO_KEY=0XF0;
switch(GPIO_KEY)
{
case(0X70): flag=flag;break;
case(0Xb0): flag=flag+4;break;
case(0Xd0): flag=flag+8;break;
case(0Xe0): flag=flag+12;break;
}
while((f<50)&&(GPIO_KEY!=0xf0)) //檢測按鍵鬆手檢測
{
delay_ms(10);
f++;
}
flag_now=flag;
}
}
}
/********************************************************************
* 函 數 名 :main
* 函數功能 : 主函數
* 函數說明 : 根據鍵值全局變量進入不同的流水現象顯示模塊。
不同速度通過單位延時長短的不同設置實現。
********************************************************************/
void main()
{
int i=0;
while(1)
{
GPIO_LED=0xff;
keyvalue_get();
if((flag==0)&&(flag_last!=flag_now))//閉幕式_較快
{
for(i=0;i<4;i++)
{
GPIO_LED=led_ON[i];
delay_ms(200);
}
}
if((flag==1)&&(flag_last!=flag_now))//開幕式_較快
{
for(i=0;i<4;i++)
{
GPIO_LED=led_OUT[i];
delay_ms(200);
}
}
if((flag==2)&&(flag_last!=flag_now))//跑馬燈1
{
for(i=0;i<8;i++)
{
GPIO_LED=led[i];
delay_ms(200);
}
}
if((flag==3)&&(flag_last!=flag_now))//跑馬燈2_反向
{
for(i=7;i>0;i--)
{
GPIO_LED=led[i];
delay_ms(200);
}
}
if((flag==4)&&(flag_last!=flag_now))//花式流水3
{
for(i=0;i<4;i++)
{
GPIO_LED=led_ON1[i];
delay_ms(200);
}
}
if((flag==5)&&(flag_last!=flag_now))//花式流水4
{
for(i=0;i<4;i++)
{
GPIO_LED=led_OUT1[i];
delay_ms(200);
}
}
if((flag==6)&&(flag_last!=flag_now))//閉幕式_較慢
{
for(i=0;i<4;i++)
{
GPIO_LED=led_ON[i];
delay_ms(400);
}
}
if((flag==7)&&(flag_last!=flag_now))//開幕式_較慢
{
for(i=0;i<4;i++)
{
GPIO_LED=led_OUT[i];
delay_ms(400);
}
}
}
}
硬件仿真電路如下(proteus)
採用自設計51最小系統,用洞洞板焊接。所需要的矩陣鍵盤作爲外設單獨焊接,使用時外接到最小系統板上,一組八個LED燈作爲常用外設,直接設計並焊接在了單片機最小版上。
首先是先完成最小系統板的設計與焊接,每一條線路焊接完畢之後都用萬用表測試,通過後才進行下一步,來減少硬件問題錯誤排查的工作量。後面在老師引導下按鍵控制從獨立按鍵加外部中斷控制改爲矩陣鍵盤加鍵盤掃描控制,所以加了個矩陣鍵盤,因爲不在最初的系統設想內,所以是用的另外焊接的作爲外設接入使用。
硬件電路如下:
設計結果及錯誤分析
設計結果:完整完成題目預期的兩種速度下的拉幕式和閉幕式流水,之外還額外加入四種花式流水展示。用到了矩陣鍵盤,八個LED燈,燈和鍵盤各佔用一組IO口。
錯誤分析:
1.程序設計思路的調整:之前習慣了整體處在流水燈循環的狀態,然後通過外部中斷的按鍵來控制變速或者狀態切換,因此就一直陷入這個誤區中,加之矩陣鍵盤的檢測是要一直進行掃描的,一度想過把鍵掃程序嵌入到每一個流水燈交替閃爍的延時函數裏,在後來跟老師的交流中明白這樣不僅計算困難而且會將原本就不精確的delay函數延時變得更加不確定不穩定,最後在老師的建議和引導下,採用了主函數中鍵掃程序和單週期流水順序執行的思路,利用單片機執行速度的優勢來近似模擬實時檢測按鍵以及相關控制,同時爲了防止誤觸或長按設置了標誌位每次都會和前一次的鍵值比對。
2.編程風格的調整:除了之前課程設計中就學到並堅持實踐的模塊化代碼編程,這次在細節上比如宏定義的使用上,因爲開始實現功能測試時爲了排除自己設計焊接的板子的問題我是先在開發板上做的程序,在移植過程中因爲端口的採用不盡相同,所以每次更改或多或少會因爲遺漏而產生錯誤,發現並改正之後意識到這樣不僅可以提高系統的可移植性,而且用同一有意義的宏定義來代替端口編號的話也同時可以增加代碼的可讀性。