目錄
一 背景說明
二 參考資料
三 MultiButton開源庫移植
四 設計實現(xiàn)--驅(qū)動按鍵
五 設計實現(xiàn)--界面處理
一 背景說明
? ? ? ? 需要做一個通過不同按鍵控制多級界面切換以及界面動作的程序。
????????查閱相關資料,發(fā)現(xiàn)網(wǎng)上大多數(shù)的應用都比較繁瑣,且對于多級界面的切換邏輯可讀性較差。所幸找到一篇使用開源庫 MultiButton 來驅(qū)動按鍵,并控制多級界面切換的博文。按圖索驥實現(xiàn)了預期的需求。
?????????開源庫 MultiButton 是一個小巧簡單易用的事件驅(qū)動型按鍵驅(qū)動模塊,作者 0x1abin。這個項目非常精簡,只有兩個文件,可無限量擴展按鍵,按鍵事件的回調(diào)異步處理方式可以簡化程序結(jié)構,去除冗余的按鍵處理硬編碼,讓你的按鍵業(yè)務邏輯更清晰。
????????MultiButton 支持如下的按鈕事件:
? ? ? ? MultiButton的狀態(tài)機如下:
二 參考資料
? ? ? ? 【1】MultiButton開源庫:mirrors / 0x1abin / MultiButton · GitCode
? ? ? ? 【2】MultiButton博文:MultiButton | 一個小巧簡單易用的事件驅(qū)動型按鍵驅(qū)動模塊-CSDN博客
? ? ? ? 【3】MultiTimer開源庫:mirrors / 0x1abin / MultiTimer · GitCode
? ? ? ? 【4】MultiTimer博文:【嵌入式開源庫】MultiTimer 的使用,一款可無限擴展的軟件定時器_multi_timer-CSDN博客
? ? ? ? 【5】MultiButton+MultiTimer+菜單操作博文:開源按鍵組件MultiButton支持菜單操作(事件驅(qū)動型)-阿里云開發(fā)者社區(qū)
? ? ? ? 【注】:我下面的實現(xiàn)沒有用到MultiTimer,僅單列出來備查。
三 MultiButton開源庫移植
? ? ? ? 從上面的開源庫或者github下載該開源庫,主要用到就兩個文件 multi_button.c/multi_button.h 。將這兩個文件直接添加到自己的工程中,并關聯(lián)頭文件。
? ? ? ? 到這邊編譯應該沒有問題。
四 設計實現(xiàn)--驅(qū)動按鍵
? ? ? ? 【1】首先初始化自己用到的幾個按鍵GPIO口:
void KNOB_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = KNOB_1_PIN | KNOB_2_PIN | KNOB_3_PIN | KNOB_4_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //設置成上拉輸入
GPIO_Init(KNOB_PORT, &GPIO_InitStructure);
}
????????【2】由于這邊用到了四個按鍵,申請四個按鍵結(jié)構:
struct Button knob_1;
struct Button knob_2;
struct Button knob_3;
struct Button knob_4;
? ? ? ? 【3】編寫回調(diào)函數(shù),綁定按鍵的GPIO電平讀取接口:
u8 knobRead(u8 button_id)
{
switch(button_id)
{
case 0:
return GPIO_ReadInputDataBit(KNOB_PORT,KNOB_1_PIN);
case 1:
return GPIO_ReadInputDataBit(KNOB_PORT,KNOB_2_PIN);
case 2:
return GPIO_ReadInputDataBit(KNOB_PORT,KNOB_3_PIN);
case 3:
return GPIO_ReadInputDataBit(KNOB_PORT,KNOB_4_PIN);
default:
return 0;
}
}
? ? ? ? 【4】關聯(lián)?MultiButton ,使用上面的按鍵結(jié)構以及回調(diào)函數(shù)初始化按鍵對象:
button_init(&knob_1, knobRead, 0, 0);
button_init(&knob_2, knobRead, 0, 1);
button_init(&knob_3, knobRead, 0, 2);
button_init(&knob_4, knobRead, 0, 3);
????????【5】注冊按鍵事件(根據(jù)實際需要注冊按鍵事件,不必一次性全注冊,我這邊只用到點按和長按,所以只注冊了 SINGLE_CLICK 和 LONG_PRESS_START 兩個事件)。
? ? ? ? ? ? ? ? 其中的回調(diào)函數(shù) knobCallback_1/2/3/4 先空著,后面需要結(jié)合界面切換來實現(xiàn):
button_attach(&knob_1, SINGLE_CLICK, knobCallback_1);
button_attach(&knob_1, LONG_PRESS_START, knobCallback_1);
button_attach(&knob_2, SINGLE_CLICK, knobCallback_2);
button_attach(&knob_2, LONG_PRESS_START, knobCallback_2);
button_attach(&knob_3, SINGLE_CLICK, knobCallback_3);
button_attach(&knob_3, LONG_PRESS_START, knobCallback_3);
button_attach(&knob_4, SINGLE_CLICK, knobCallback_4);
button_attach(&knob_4, LONG_PRESS_START, knobCallback_4);
? ? ? ? 【6】啟動按鍵:
button_start(&knob_1);
button_start(&knob_2);
button_start(&knob_3);
button_start(&knob_4);
? ? ? ? 【7】將上面【4】【5】【6】的三個步驟整個成一個按鍵注冊接口:
void KNOB_Reg(void)
{
button_init(&knob_1, knobRead, 0, 0);
button_init(&knob_2, knobRead, 0, 1);
button_init(&knob_3, knobRead, 0, 2);
button_init(&knob_4, knobRead, 0, 3);
button_attach(&knob_1, SINGLE_CLICK, knobCallback_1);
button_attach(&knob_1, LONG_PRESS_START, knobCallback_1);
button_attach(&knob_2, SINGLE_CLICK, knobCallback_2);
button_attach(&knob_2, LONG_PRESS_START, knobCallback_2);
button_attach(&knob_3, SINGLE_CLICK, knobCallback_3);
button_attach(&knob_3, LONG_PRESS_START, knobCallback_3);
button_attach(&knob_4, SINGLE_CLICK, knobCallback_4);
button_attach(&knob_4, LONG_PRESS_START, knobCallback_4);
button_start(&knob_1);
button_start(&knob_2);
button_start(&knob_3);
button_start(&knob_4);
}
? ? ? ? 【8】至此,按鍵驅(qū)動還不能生效,還需要添加一個心跳,一般采用5ms間隔定時器來循環(huán)調(diào)用這個心跳函數(shù),定時器相關函數(shù)如下:
//Timer14 5ms定時器
#define TIMER14_ARR (500-1)
#define TIMER14_PSC (960-1)
void Timer14_Config(void)
{
TIM_TimeBaseInitTypeDef TIM_StructInit;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM14, ENABLE);//使能定時器時鐘
//定時器基礎配置
TIM_StructInit.TIM_Period = TIMER14_ARR; //自動重裝值
TIM_StructInit.TIM_Prescaler = TIMER14_PSC; //預分頻系數(shù)
TIM_StructInit.TIM_ClockDivision = TIM_CKD_DIV1; //時鐘分頻
TIM_StructInit.TIM_CounterMode = TIM_CounterMode_Up;//向上計數(shù)
TIM_StructInit.TIM_RepetitionCounter = 0; //不重復計數(shù)
TIM_TimeBaseInit(TIM14, &TIM_StructInit);
//NVIC中斷配置
NVIC_InitStructure.NVIC_IRQChannel = TIM14_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPriority = 3; //數(shù)字越小優(yōu)先級越高
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
TIM_ClearFlag(TIM14, TIM_FLAG_Update);
TIM_ITConfig(TIM14, TIM_IT_Update, ENABLE); //使能更新中斷
TIM_Cmd(TIM14, ENABLE);
}
extern void button_ticks(void);
void TIM14_IRQHandler(void)
{
if(TIM_GetITStatus(TIM14, TIM_IT_Update) != RESET)
{
button_ticks(); //旋鈕驅(qū)動心跳
TIM_ClearITPendingBit(TIM14, TIM_IT_Update);
}
}
? ? ? ? 【9】在主函數(shù)的初始化中加上上面幾個接口:
void main(void)
{
//定時器初始化
Timer14_Config();
//旋鈕初始化/注冊
KNOB_Init();
KNOB_Reg();
while(1)
{
//...
}
}
????????至此,MultiButton 開源庫移植完畢,并將所用的四個按鈕關聯(lián)到?MultiButton ,按鍵事件待擴展。
五 設計實現(xiàn)--界面處理
? ? ? ? 【1】新建頭文件,新增界面相關的結(jié)構體定義等:
typedef enum tagMenuTree //菜單樹
{
MENU_MAIN = 0,
MEUN_LOG
}MENU_TREE;
typedef enum tagEventCode //事件值
{
NULL_EVENT = 0,
KNOB_1_SHORT = 1,
KNOB_1_LONG = 2,
KNOB_2_SHORT = 3,
KNOB_2_LONG = 4,
KNOB_3_SHORT = 5,
KNOB_3_LONG = 6,
KNOB_4_SHORT = 7,
KNOB_4_LONG = 8
}EVENT_CODE;
typedef struct tagMenuInfo //界面信息
{
u8 cur_page; //正在執(zhí)行的界面
u8 knb_evnt; //當前觸發(fā)的事件
}MENU_INFO;
extern MENU_INFO menu;
extern void Menu_Init(MENU_INFO *handle, u8 p_page, u8 p_evnt);
extern void Set_Menu(MENU_INFO *handle, u8 p_page);
extern u8 Get_Menu(MENU_INFO *handle);
extern void Set_Event_Code(MENU_INFO *handle, u8 p_evnt);
extern int Get_Event_Code(MENU_INFO *handle);
extern void Menu_Handler(MENU_INFO *handle);
? ? ? ? 【2】新建源文件,新增界面相關的接口函數(shù)等:
/**************************************************************************
* 函數(shù)名稱: Menu_Init
* 功能描述: 菜單初始化
**************************************************************************/
void Menu_Init(MENU_INFO *handle, u8 p_page, u8 p_evnt)
{
memset(handle, 0, sizeof(MENU_INFO));
handle->cur_page = p_page;
handle->knb_evnt = p_evnt;
}
/**************************************************************************
* 函數(shù)名稱: Set_Menu/Get_Menu
* 功能描述: 菜單跳轉(zhuǎn)/獲取當前菜單
**************************************************************************/
void Set_Menu(MENU_INFO *handle, u8 p_page)
{
handle->cur_page = p_page;
}
u8 Get_Menu(MENU_INFO *handle)
{
return handle->cur_page;
}
/**************************************************************************
* 函數(shù)名稱: Set_Event_Code/Get_Event_Code
* 功能描述: 設置當前事件值/獲取當前事件值
**************************************************************************/
void Set_Event_Code(MENU_INFO *handle, u8 p_evnt)
{
handle->knb_evnt = p_evnt;
}
int Get_Event_Code(MENU_INFO *handle)
{
return handle->knb_evnt;
}
????????【3】結(jié)合上述菜單處理函數(shù),關聯(lián)“設計實現(xiàn)--驅(qū)動按鍵”中的【5】,完善 knobCallback_1/2/3/4 的實現(xiàn)。
????????????????主要邏輯就是將按鍵的動作,通過回調(diào),傳遞給菜單結(jié)構 menu (單列出knobCallback_1,其他按鈕的回調(diào)一樣實現(xiàn)):
void knobCallback_1(void *p_btn)
{
u8 btn_event_val;
btn_event_val = get_button_event((struct Button *)p_btn);
switch(btn_event_val)
{
case SINGLE_CLICK:
Set_Event_Code(&menu, KNOB_1_SHORT);
break ;
case LONG_PRESS_START:
Set_Event_Code(&menu, KNOB_1_LONG);
break ;
default:
break ;
}
}
????????【4】菜單處理函數(shù) Menu_Handler 的實現(xiàn):
void Menu_Handler(MENU_INFO *handle)
{
switch(handle->cur_page)
{
case MENU_MAIN:
menuMainHandle(handle->knb_evnt);
break ;
case MEUN_LOG:
menuLogHandle(handle->knb_evnt);
break ;
default:
break ;
}
Set_Event_Code(handle, NULL_EVENT); //及時將事件清除,防止重復觸發(fā)
}
? ? ? ? 其中,menuMainHandle/menuLogHandle 就是每個界面的具體實現(xiàn)了:
void menuMainHandle(u8 p_evnt)
{
cleanAll(); //清屏
//主界面顯示
switch(p_evnt)
{
case KNOB_1_SHORT:
break ;
case KNOB_1_LONG:
Set_Menu(&menu, MEUN_LOG); //進入登錄界面
break ;
default:
break;
}
}
void menuLogHandle(u8 p_evnt)
{
cleanAll(); //清屏
//登錄界面的顯示
switch(p_evnt)
{
case KNOB_2_SHORT:
break ;
case KNOB_2_LONG:
Set_Menu(&menu, MENU_MAIN); //返回主界面
break ;
default:
break;
}
}
? ? ? ? 【5】在主函數(shù)的初始化中加上上面界面初始化接口,同時界面處理函數(shù)置于主循環(huán)中執(zhí)行:
void main(void)
{
//定時器初始化
Timer14_Config();
//旋鈕初始化/注冊
KNOB_Init();
KNOB_Reg();
//界面初始化
Menu_Init(&menu, MENU_MAIN, NULL_EVENT);
while(1)
{
Menu_Handler(&menu); //界面處理函數(shù)
LCD_Update(); //用緩存刷新屏幕
//...
}
}
? ? ? ? 至此,完成了通過 MultiButton 開源庫驅(qū)動按鍵并控制多級界面切換的工作。文章來源:http://www.zghlxwxcb.cn/news/detail-730065.html
? ? ? ? 上述DEMO中,上電默認進入主界面,可以通過長按 knob_1 進入登陸界面。在登陸界面中,通過長按 knob_2 返回主界面(長按的時間可以在 multi_button.h 中設置)。文章來源地址http://www.zghlxwxcb.cn/news/detail-730065.html
到了這里,關于【嵌入式】使用MultiButton開源庫驅(qū)動按鍵并控制多級界面切換的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!