聚豐項(xiàng)目 > 基于手勢(shì)識(shí)別控制的音樂(lè)播放器
中科藍(lán)訊公司推出的 AB32VG1 開(kāi)發(fā)板,這個(gè)開(kāi)發(fā)板自帶 按鍵模塊、audio 模塊和 TF card 模塊,基于該開(kāi)發(fā)板結(jié)合 OLED12864 模塊作為顯示屏幕,加上 PAJ7620U2 手勢(shì)識(shí)別模塊,制作了一款基于手勢(shì)識(shí)別的音樂(lè)播放器,這樣就可以實(shí)現(xiàn)非接觸式控制音樂(lè)播放,這個(gè)模塊作為一個(gè)基礎(chǔ),之后也能構(gòu)造出更炫酷的作品 該音樂(lè)播放器目前實(shí)現(xiàn)了如下功能: 1、可以通過(guò) OLED12864 模塊顯示播放器信息。 2、可以通過(guò)按鍵控制音樂(lè)播放和設(shè)置一些信息。 3、讀取 SD 卡的音樂(lè)文件并播放。 4、通過(guò)手勢(shì)識(shí)別模塊控制播放器
Eureka1024
Eureka1024
團(tuán)隊(duì)成員
Eureka1024 軟件開(kāi)發(fā)
按鍵模塊使用如下 GPIO 引腳:
S2 -- PF1 (單擊為向下選擇)
S3 -- PF0(單擊為確定,雙擊為返回)
S4 -- PA2(單擊為向上選擇)
使用按鍵軟件包:multibutton
使用的引腳如下:
SDA -- PA6
SCL -- PA0
沒(méi)有使用 u8g2 的驅(qū)動(dòng)軟件包,因?yàn)閮?nèi)存不夠。使用了網(wǎng)上的軟件 IIC 實(shí)現(xiàn),做了一些修改。
使用的 SDIO 協(xié)議對(duì)應(yīng)的引腳如下:
SD_CMD -- PB0
SD_CLK -- PB1
SD_DAT -- PB2
SD_DET -- PE5
使用到 RT-Thread 提供的 SDIO 驅(qū)動(dòng),使用到的組件和服務(wù)層有 DFS、Fatfs 和 POSIX。
VOUTRP -- DACR
VOUTLP -- DACL
FMANT -- FM_ANT
MICIN -- MICL/PF2
使用到的驅(qū)動(dòng) audio device, 軟件包為 wavplayer(需要optparse軟件包),可以用來(lái)播放 wav 格式的音樂(lè)文件。
SDA -- PE2
SCL -- PE3
INT -- PA5
使用了軟件 IIC 驅(qū)動(dòng)、PAJ7620軟件包。
開(kāi)發(fā)環(huán)境:
- RT-Thread 版本 latest(2021-10-20)
- RT-Thread Studio版本 V2.12
- AB32VG1 開(kāi)發(fā)板 BSP 版本 V1.08
音樂(lè)播放器雖說(shuō)功能不多,但是不同的狀態(tài)切換還是蠻多的,所以理所當(dāng)然會(huì)想到使用狀態(tài)機(jī)的方式實(shí)現(xiàn)。
目前實(shí)現(xiàn)的狀態(tài)主要包括如下幾個(gè),同時(shí),該樹(shù)狀圖也表明了狀態(tài)間的轉(zhuǎn)移關(guān)系。
網(wǎng)上關(guān)于狀態(tài)機(jī)的實(shí)現(xiàn)有很多方法,我看了好多文章都是直接一個(gè)狀態(tài)對(duì)應(yīng)一個(gè)動(dòng)作函數(shù),但是應(yīng)用在音樂(lè)播放器上會(huì)有點(diǎn)問(wèn)題,因?yàn)槿绻阋粋€(gè)狀態(tài)對(duì)應(yīng)一個(gè)函數(shù)的話,如果你的音樂(lè)歌曲有幾百首,那豈不是有幾百個(gè)函數(shù),所以我對(duì)一般的狀態(tài)機(jī)做了點(diǎn)改進(jìn)。
如下代碼所示,主要是狀態(tài)轉(zhuǎn)移表的實(shí)現(xiàn),該表給出了所有狀態(tài)對(duì)應(yīng)的情況,以及狀態(tài)轉(zhuǎn)移的跳轉(zhuǎn)位置。
typedef struct { uint8_t coordinate; //當(dāng)前狀態(tài)索引號(hào) uint8_t back; //返回 void (*enter_operation)(int8_t* ); //指向執(zhí)行函數(shù),作為當(dāng)前狀態(tài)執(zhí)行的操作 //當(dāng) entry鍵按下時(shí) sub_base_addr提供子狀態(tài)的基地址索引,sub_offset_addr提供偏移量,兩者相加,得到下一個(gè)狀態(tài) uint8_t sub_base_addr; //子狀態(tài)基地址 uint8_t sub_offset_addr; //子狀態(tài)偏移地址 (用于記錄下一步要跳轉(zhuǎn)到的子狀態(tài)) uint8_t leaf_node_flag; //標(biāo)志是否為葉子節(jié)點(diǎn)(最底層的狀態(tài)),方便在該狀態(tài)下按確定可以實(shí)現(xiàn)返回功能 } Menu_table; //針對(duì)下表,一些想法:其實(shí)可以增加一個(gè)變量來(lái)實(shí)現(xiàn)樹(shù)的收縮的(解決音樂(lè)播放時(shí),點(diǎn)擊任意一首歌進(jìn)入的是同一個(gè)狀態(tài),目前是enter事件給特殊,如果還有,就思考了) //初始化菜單所處在的各個(gè)狀態(tài) Menu_table table[]= { //索引 - back - function(當(dāng)前) - 子狀態(tài)基地址(固定) - 子狀態(tài)偏移地址 - 標(biāo)志是否為葉子節(jié)點(diǎn) { 0, 0, (*load_menu), 1, 0, 0}, //0加載界面 { 1, 1, (*main_menu), 2, 0, 0}, //1主菜單界面 { 2, 1, (*playlists), 4, 0, 0}, //歌單 { 3, 1, (*settings_list), 5, 0, 0}, //設(shè)置 //播放的子菜單 { 4, 2, (*music_play), 0, 0, 0}, //音樂(lè)播放控制:(2-1) //設(shè)置菜單的子菜單 { 5, 3, (*volume_control), 0, 1, 1}, //音量控制 { 6, 3, (*language_setting), 0, 1, 1}, //語(yǔ)言設(shè)置 { 7, 3, (*brightness_setting), 0, 1, 1}, //亮度設(shè)置 };
狀態(tài)轉(zhuǎn)移主要是按鍵來(lái)實(shí)現(xiàn)的,每個(gè)按鍵按下,就會(huì)觸發(fā)狀態(tài)轉(zhuǎn)移,狀態(tài)轉(zhuǎn)移的代碼如下所示:
/* 菜單界面顯示線程入口函數(shù) */ static void menu_thread_entry(void *parameter) { rt_uint32_t e; int8_t k; while(1) { if (rt_event_recv(&control_event, (UP_FLAG | ENTRY_FLAG | RETURN_FLAG | DOWN_FLAG), RT_EVENT_FLAG_OR, RT_WAITING_FOREVER, &e) == RT_EOK) { //向上 if (rt_event_recv(&control_event, UP_FLAG, RT_EVENT_FLAG_AND | RT_EVENT_FLAG_CLEAR, RT_WAITING_NO, &e) == RT_EOK) { table[func_index].sub_offset_addr -= 1; table[func_index].enter_operation(&table[func_index].sub_offset_addr); } //確定 if (rt_event_recv(&control_event, ENTRY_FLAG, RT_EVENT_FLAG_AND | RT_EVENT_FLAG_CLEAR, RT_WAITING_NO, &e) == RT_EOK) { if(table[func_index].leaf_node_flag) { func_index = table[func_index].back; //返回上一級(jí)菜單位置 table[func_index].enter_operation(&table[func_index].sub_offset_addr); } else if(func_index == 2) //音樂(lè)播放則特殊點(diǎn)(所有音樂(lè)進(jìn)入同一個(gè)狀態(tài)) { k = table[func_index].sub_offset_addr; func_index = 4; table[func_index].sub_offset_addr = k; table[func_index].enter_operation(&table[func_index].sub_offset_addr); } else { func_index = table[func_index].sub_base_addr + table[func_index].sub_offset_addr; table[func_index].enter_operation(&table[func_index].sub_offset_addr); } } //返回 if (rt_event_recv(&control_event, RETURN_FLAG, RT_EVENT_FLAG_AND | RT_EVENT_FLAG_CLEAR, RT_WAITING_NO, &e) == RT_EOK) { func_index = table[func_index].back; table[func_index].enter_operation(&table[func_index].sub_offset_addr); } //向下 if (rt_event_recv(&control_event, DOWN_FLAG, RT_EVENT_FLAG_AND | RT_EVENT_FLAG_CLEAR, RT_WAITING_NO, &e) == RT_EOK) { table[func_index].sub_offset_addr += 1; table[func_index].enter_operation(&table[func_index].sub_offset_addr); } } } }
演示效果如下:
以下是代碼的 gitee 地址:
https://gitee.com/Eureka1024/MusicPlayerBasedOnGestureRecognition