因爲在學STM32,所以閒着無事找了找有沒有什麼簡單的利於上手開工的小項目,恰巧碰到了“北京交通大學2019年校內電子設計競賽單片機部分”。想着練一下手也不錯。
首先來看一下題目:
要求很簡單,“單擊、雙擊、長按”三個動作對應三種不同的響應,可以用狀態機來完成。
這裏一個一個分解每個要求:
- 單擊按鍵
單擊按鍵要求實現四檔亮度“滅-低亮度-中亮度-高亮度”,這個很簡單,我們可以通過PWM波來實現。這裏我就直接貼代碼上來了。不難讀懂,直接利用了STM32庫函數的TIM_SetCompare2()
實現。
int ValueBright[4] = { 1,3,5,0 };
void Single_Press(void)
{
if(stateNow == StateSinglePress)
TIM_SetCompare2(TIM3,ValueBright[valueIndex]*100);
if(stateNow == StateNotPressed)
{
TIM_SetCompare2(TIM3,0);
}
}
- 雙擊按鍵
雙擊按鍵要求進入呼吸燈狀態,這個如果是跟我一樣學正點原子的STM32的話是自然不陌生的,我也不多說了,直接使用PWM波完成。上代碼。
void Double_Press(void)
{
if(stateNow == StateDoublePress)
{
delay_ms(10);
if(flagPwm) valuePwm += 2;
else valuePwm -= 2;
if(valuePwm > 500) flagPwm = 0;
if(valuePwm == 0) flagPwm = 1;
TIM_SetCompare2(TIM3,valuePwm);
}
}
- 長按按鍵
長按按鍵有點不同,因爲這個地方要求實現LED以50%的佔空比循環亮滅,並且整個週期的時間是根據自己的按鍵時長來決定的。所以這裏給我們提出的要求就是:使用PWM波實現50%佔空比和使用定時器記錄按鍵時長
因此這個地方就不能像上面兩個那樣定義一個普通函數來完成響應動作,而應該通過TIM的中斷處理函數來實現這個功能。這裏解釋一下:
通過設置TIM4每10ms觸發一次中斷,在中斷處理函數內累加timePressed
來記錄長按時間,如果長按時間大於預設的閾值TimeLongPress
那我們就認爲進入長按狀態了。但是進入了長按狀態之後我們不能馬上就開始響應動作,因爲這個時候如果還沒有擡起按鍵就會出錯的。所以通過一句if(valueBefore == 1 && valueNow == 0)
來判斷是否擡起按鍵了。
void TIM4_IRQHandler(void)
{
case StatePressed:
timePressed++;
if(valueBefore == 1 && valueNow == 0)
{
if(timePressed >= TimeLongPress)
stateNow = StateLongPress;
else
stateNow = StateNextState;
}
break;
case StateLongPress:
cnt++;
if(cnt <= timePressed/2)
TIM_SetCompare2(TIM3,450);
else if(cnt >= timePressed/2)
TIM_SetCompare2(TIM3,0);
if(cnt >= timePressed)
cnt = 0;
break;
}
- 單擊按鍵退出當前工作模式
這個地方需要我們在進行相應動作的時候能根據按鍵退出當前的響應動作,所以我選用了簡單的外部中斷處理。這裏我就直接上代碼了。
void EXTI4_IRQHandler(void)
{
delay_ms(50);
if(isIntHappen == 0 && GPIO_ReadInputDataBit(KEY_PORT,KEY_PIN) == 0)
{
isIntHappen = 1;
if(stateNow == StateSinglePress)
{
valueIndex++;
if(valueIndex == 3)
{
EXTIX_Disable();//關閉外部按鍵中斷
TIM_SetCompare2(TIM3,0);
CLEAR_ALL_PARA();//還原所有數據至初始狀態
TIM_Cmd(TIM4, ENABLE);//開啓TIM4中斷
flagQuit = 1;
}
}
else if(stateNow == StateDoublePress)
{
EXTIX_Disable();//關閉外部按鍵中斷
TIM_SetCompare2(TIM3,0);
CLEAR_ALL_PARA();//還原所有數據至初始狀態
TIM_Cmd(TIM4, ENABLE);//開啓TIM4中斷
flagQuit = 1;
}
else if(stateNow == StateLongPress)
{
EXTIX_Disable();//關閉外部按鍵中斷
stateNow = StateNotPressed;
TIM_SetCompare2(TIM3,0);
CLEAR_ALL_PARA();//還原所有數據至初始狀態
GPIO_ResetBits(GPIOE,GPIO_Pin_5);
}
isIntHappen = 0;
}
EXTI_ClearITPendingBit(EXTI_Line4);
}
其實功能很簡單,就是檢測到外部按鍵中斷後進入外部中斷處理函數EXTI4_IRQHandler(void)
中,在這個中斷處理函數裏面把工作狀態還原到最初的工作狀態,然後清除掉那些臨時變量的值,然後關閉外部中斷(後面說明爲什麼要關閉外部中斷)。
最後把上面的代碼合併一次,構思一下整體。在上面的代碼中,我用到了外部中斷EXTI4_IRQHandler(void)
,用到了定時器中斷處理函數TIM4_IRQHandler(void)
,用到了PWM波TIM_SetCompare2()
函數。所以這裏我們需要使用兩個TIM,一個用來做普通的計時檢測按鍵狀態,一個用來專門PWM相關。
所以整體的系統工作爲:
- 通過
TIM4
實現按鍵狀態的檢測,通過改變stateNow
這個變量來記錄現在的工作狀態。提前設置了下面幾種工作狀態:
#define StateNotPressed 0x11//空閒
#define StatePressed 0x22//被按下了
#define StateLongPress 0x33//長按狀態
#define StateDoublePress 0x44//雙擊狀態
#define StateSinglePress 0x55//單擊狀態
#define StateNextState 0x66//被按下後擡起等待是否會雙擊狀態
- 當進入某一工作狀態後進行相應的響應動作,這個時候激活外部中斷
EXTI4
檢測是否有外部中斷觸發中斷處理函數退出工作狀態 - 當退出工作狀態時要再次使失活外部中斷
EXTI4
,防止下次按鍵被讀取爲外部中斷。
最後的最後放個鏈接,整個工程寫的比較倉促,用了很多正點原子的代碼,有些也沒有細改了,如果發現有些地方名字和實際不和還以實際情況爲主(只是我偷懶沒改)。大家多多包容,新手上路。歡迎交流
鏈接:https://pan.baidu.com/s/1asVjmNFozLC1_etJ4pBYKw
提取碼:ux7f