首頁(yè) > 評(píng)測(cè) > 【技術(shù)分享】基于正點(diǎn)原子開(kāi)發(fā)板的中文菜單音樂(lè)播放器實(shí)現(xiàn)
【技術(shù)分享】基于正點(diǎn)原子開(kāi)發(fā)板的中文菜單音樂(lè)播放器實(shí)現(xiàn)
- [導(dǎo)讀]
- 本帖最后由 jinglixixi 于 2023-5-17 18:48 編輯 #申請(qǐng)?jiān)瓌?chuàng)# @21小跑堂 正點(diǎn)原子開(kāi)發(fā)板以外設(shè)豐富而著稱,基本可以全部支撐起多媒體方面的應(yīng)用,如數(shù)碼相框功能(已在前面介紹過(guò))、視頻播放功能、文本閱讀器
本帖最后由 jinglixixi 于 2023-5-17 18:48 編輯
#申請(qǐng)?jiān)瓌?chuàng)# @21小跑堂
正點(diǎn)原子開(kāi)發(fā)板以外設(shè)豐富而著稱,基本可以全部支撐起多媒體方面的應(yīng)用,如數(shù)碼相框功能(已在前面介紹過(guò))、視頻播放功能、文本閱讀器功能及音頻播放功能等。
這次,就利用這些外設(shè)來(lái)實(shí)現(xiàn)一個(gè)具有中文菜單的音樂(lè)播放器,所涉及的主要外設(shè)為:I2S語(yǔ)音播放電路及字庫(kù)存儲(chǔ)電路,參見(jiàn)圖1和圖2所示。


圖1 I2S語(yǔ)音播放電路

圖2 字庫(kù)存儲(chǔ)電路
這里之所以使用閃存W25Q128來(lái)構(gòu)建字庫(kù)是因?yàn),它的通用性更?qiáng),可以直接在編程時(shí)就以掌握來(lái)設(shè)計(jì)菜單。而在以往,要設(shè)計(jì)中文菜單,均是采用構(gòu)建專用小字庫(kù)的方式來(lái)實(shí)現(xiàn)。這就導(dǎo)致在輸出漢字時(shí),多是以漢字在小子庫(kù)中的次序編號(hào)來(lái)調(diào)用字模,故通用性極差。
在W25Q128中,其字模是按列行式來(lái)取模的,見(jiàn)圖3所示。此外,它共含有3種字庫(kù),即GBK12、GBK16及GBK24。

圖3 取模方式
此外,為了便于中西為的混排,其文字顯示函數(shù)為:
- void text_show_font(uint16_t x, uint16_t y, uint8_t *font, uint8_t size, uint8_t mode, uint16_t color)
- {
- uint8_t temp, t, t1;
- uint16_t y0 = y;
- uint8_t *dzk;
- uint8_t csize = (size / 8 + ((size % 8) ? 1 : 0)) * (size);
- /* 得到狀態(tài)一個(gè)字符對(duì)應(yīng)點(diǎn)陣集所占的字節(jié)數(shù) */
- if (size != 12 && size != 16 && size != 24 && size != 32)
- {
- return; /*不支持的size */
- }
- dzk = mymalloc(SRAMIN, size); /* 申請(qǐng)內(nèi)存 */
- if (dzk == 0) return; /* 內(nèi)存不足 */
- text_get_hz_mat(font, dzk, size); /* 得到相應(yīng)讀寫的點(diǎn)陣數(shù)據(jù) */
- for (t = 0; t < csize; t++) // 一個(gè)字符
- {
- temp = dzk[t]; /* 得到點(diǎn)陣數(shù)據(jù) */
- for (t1 = 0; t1 < 8; t1++) // 一個(gè)字節(jié)
- {
- if (temp & 0x80) // 1000 0000 從左到右
- {
- //lcd_draw_point(x, y, color); /* 畫需要顯示的點(diǎn) */
- ili9341_draw_pixel(color, x, y);
- }
- else if (mode == 0) /* 如非疊加模式,則不需要顯示的點(diǎn),用背景色填充 */
- {
- //lcd_draw_point(x, y, g_back_color); /* 填充背景色 */
- ili9341_draw_pixel(g_back_color, x, y);
- }
- temp <<= 1;
- y++; //由上至下
- if ((y - y0) == size)
- {
- y = y0;
- x++; //換列 --->存放形式:列行式
- break;
- }
- }
- }
- myfree(SRAMIN, dzk); /* 釋放內(nèi)存 */
- }
由于本人所所用的顯示屏為MDM-2802顯示模塊,故對(duì)原顯示函數(shù)中的畫點(diǎn)函數(shù)進(jìn)行了相應(yīng)地替換,并注釋掉了原來(lái)的函數(shù)。

圖4 中文播放界面
要實(shí)現(xiàn)圖4所示的中文播放界面,其主程序如下:
- int main(void)
- {
- uint8_t key;
- HAL_Init(); /* 初始化HAL庫(kù) */
- sys_stm32_clock_init(336, 8, 2, 7); /* 設(shè)置時(shí)鐘,168Mhz */
- delay_init(168); /* 延時(shí)初始化 */
- usart_init(115200); /* 串口1初始化為115200 */
- usmart_dev.init(84); /* 初始化USMART */
- led_init(); /* 初始化LED */
- key_init(); /* 初始化按鍵 */
- sram_init(); /* SRAM初始化 */
- norflash_init(); /* 初始化NORFLASH */
- LCD_Init();
- delay_ms(10);
- ili9341_init();
- ili9341_clear(RED);
- BACK_COLOR=RED;
- POINT_COLOR=YELLOW;
- LCD_DrawLine(WHITE ,0, 35, 239, 35);
- LCD_DrawLine(WHITE ,0, 275, 239, 275);
- my_mem_init(SRAMIN); /* 初始化內(nèi)部SRAM內(nèi)存池 */
- my_mem_init(SRAMEX); /* 初始化外部SRAM內(nèi)存池 */
- while (sd_init()) /* 檢測(cè)SD卡 */
- {
- LCD_ShowString(30,50,"SD Card Failed!");
- delay_ms(200);
- delay_ms(200);
- }
- exfuns_init(); /* 為fatfs相關(guān)變量申請(qǐng)內(nèi)存 */
- f_mount(fs[0], "0:", 1); /* 掛載SD卡 */
- f_mount(fs[1], "1:", 1); /* 掛載FLASH */
- while (fonts_init()) /* 檢查字庫(kù) */
- {
- LCD_ShowString(30,50,"Font Error!");
- delay_ms(200);
- }
- es8388_init(); /* ES8388初始化 */
- es8388_adda_cfg(1, 0); /* 開(kāi)啟DAC關(guān)閉ADC */
- es8388_output_cfg(1, 1); /* DAC選擇通道輸出 */
- es8388_hpvol_set(25); /* 設(shè)置耳機(jī)音量 */
- es8388_spkvol_set(26); /* 設(shè)置喇叭音量 */
- g_back_color=RED;
- text_show_string(30, 13, 200, 16, "中文菜單音樂(lè)播放器", 16, 0, YELLOW);
- POINT_COLOR=WHITE;
- text_show_string(30, 50, 200, 16, "1 - 最美的期待", 16, 0, WHITE);
- text_show_string(30, 70, 200, 16, "2 - 野百合也有春天", 16, 0, WHITE);
- text_show_string(30, 90, 200, 16, "3 - 光陰的故事", 16, 0, WHITE);
- text_show_string(30, 110, 200, 16, "4 - 莫斯科郊外的晚上", 16, 0, WHITE);
- text_show_string(30, 130, 200, 16, "5 - 東方之珠", 16, 0, WHITE);
- text_show_string(30, 150, 200, 16, "6 - 光輝歲月", 16, 0, WHITE);
- text_show_string(30, 170, 200, 16, "7 - 同桌的你", 16, 0, WHITE);
- text_show_string(30, 190, 200, 16, "8 - 亞洲雄風(fēng)", 16, 0, WHITE);
- text_show_string(30, 210, 200, 16, "9 - 咖啡屋", 16, 0, WHITE);
- text_show_string(30, 230, 200, 16, "10- 軍港之夜", 16, 0, WHITE);
- POINT_COLOR=YELLOW;
- text_show_string(30, 280, 200, 16, "KEY0:下一首 KEY2:上一首", 16, 0, YELLOW);
- text_show_string(30, 300, 200, 16, "KEY_UP:暫停/播放", 16, 0, YELLOW);
- POINT_COLOR=WHITE;
- key = key_scan(0);
- while(key != KEY1_PRES)
- {
- key = key_scan(0);
- }
- while (1)
- {
- audio_play(); /* 播放音樂(lè) */
- }
- }
有了可以進(jìn)行播放功能的設(shè)計(jì)了,其中標(biāo)識(shí)當(dāng)前播放曲目的函數(shù)為:
- uint8_t audio_play_song(char* fname)
- {
- uint8_t res;
- res = exfuns_file_type(fname);
- switch (res)
- {
- case T_WAV:
- POINT_COLOR=0xF800;
- text_show_string(12, 50+(np*20), 200, 16, "*", 16, 0, 0xF800);
- POINT_COLOR=0xFFE0;
- text_show_string(12, 50+(n*20), 200, 16, "*", 16, 0, 0xFFE0);
- np=n;
- res = wav_play_song(fname);
- break;
- default:
- res = KEY0_PRES;
- break;
- }
- return res;
- }
而獲取相應(yīng)音頻播放文件的函數(shù)為:
- uint16_t audio_get_tnum(char *path)
- {
- uint8_t res;
- uint16_t rval = 0;
- DIR tdir; /* 臨時(shí)目錄 */
- FILINFO* tfileinfo; /* 臨時(shí)文件信息 */
- tfileinfo = (FILINFO*)mymalloc(SRAMIN, sizeof(FILINFO)); /* 申請(qǐng)內(nèi)存 */
- res = f_opendir(&tdir, (const TCHAR*)path); /* 打開(kāi)目錄 */
- if ((res == FR_OK) && tfileinfo)
- {
- while (1) /* 查詢總的有效文件數(shù) */
- {
- res = f_readdir(&tdir, tfileinfo); /* 讀取目錄下的一個(gè)文件 */
- if ((res != FR_OK) || (tfileinfo->fname[0] == 0))
- {
- break; /* 錯(cuò)誤了/到末尾了,退出 */
- }
- res = exfuns_file_type(tfileinfo->fname);
- if ((res & 0xF0) == 0x40) /* 取高四位,看看是不是音樂(lè)文件 */
- {
- rval++; /* 有效文件數(shù)增加1 */
- }
- }
- }
- myfree(SRAMIN, tfileinfo); /* 釋放內(nèi)存 */
- return rval;
- }
實(shí)現(xiàn)音頻播放與樂(lè)曲選擇的功能函數(shù)為:
- void audio_play(void)
- {
- uint8_t res;
- DIR wavdir; /* 目錄 */
- FILINFO *wavfileinfo; /* 文件信息 */
- char *pname; /* 帶路徑的文件名 */
- uint16_t totwavnum; /* 音樂(lè)文件總數(shù) */
- uint16_t curindex; /*當(dāng)前索引 */
- uint8_t key; /* 鍵值 */
- uint32_t temp;
- uint32_t *wavoffsettbl; /* 音樂(lè)offset索引表 */
- es8388_adda_cfg(1, 0); /* 開(kāi)啟DAC關(guān)閉ADC */
- es8388_output_cfg(1, 1); /* DAC選擇通道1輸出 */
- while (f_opendir(&wavdir, "0:/MUSIC")) /* 打開(kāi)音樂(lè)文件夾 */
- {
- LCD_ShowString(30,190,"MUSIC ERROR");
- delay_ms(200);
- delay_ms(200);
- }
- totwavnum = audio_get_tnum("0:/MUSIC"); /* 得到總有效文件數(shù) */
- while (totwavnum == NULL) /* 音樂(lè)文件總數(shù)為0 */
- {
- LCD_ShowString(30,190,"NO MUSIC !");
- delay_ms(200);
- delay_ms(200);
- }
- wavfileinfo = (FILINFO*)mymalloc(SRAMIN, sizeof(FILINFO)); /* 申請(qǐng)內(nèi)存 */
- pname = mymalloc(SRAMIN, FF_MAX_LFN * 2 + 1); /* 為帶路徑的文件名分配內(nèi)存 */
- wavoffsettbl = mymalloc(SRAMIN, 4 * totwavnum); /* 申請(qǐng)4*totwavnum個(gè)字節(jié)的內(nèi)存,用于存放音樂(lè)文件off block索引 */
- while (!wavfileinfo || !pname || !wavoffsettbl) /* 內(nèi)存分配出錯(cuò) */
- {
- LCD_ShowString(30,190,"memory Failed");
- delay_ms(200);
- delay_ms(200);
- }
- /* 記錄索引 */
- res = f_opendir(&wavdir, "0:/MUSIC"); /* 打開(kāi)目錄 */
- if (res == FR_OK)
- {
- curindex = 0; /* 當(dāng)前索引為0 */
- while (1) /* 全部查詢一遍 */
- {
- temp = wavdir.dptr; /* 記錄當(dāng)前index */
- res = f_readdir(&wavdir, wavfileinfo); /* 讀取目錄下的一個(gè)文件 */
- if ((res != FR_OK) || (wavfileinfo->fname[0] == 0))
- {
- break; /* 錯(cuò)誤了/到末尾了,退出 */
- }
- res = exfuns_file_type(wavfileinfo->fname);
- if ((res & 0xF0) == 0x40) /* 取高四位,看看是不是音樂(lè)文件 */
- {
- wavoffsettbl[curindex] = temp; /* 記錄索引 */
- curindex++;
- }
- }
- }
- curindex = 0; /* 從0開(kāi)始顯示 */
- res = f_opendir(&wavdir, (const TCHAR*)"0:/MUSIC"); /* 打開(kāi)目錄 */
- while (res == FR_OK) /* 打開(kāi)成功 */
- {
- dir_sdi(&wavdir, wavoffsettbl[curindex]); /* 改變當(dāng)前目錄索引 */
- res = f_readdir(&wavdir, wavfileinfo); /* 讀取目錄下的一個(gè)文件 */
- if ((res != FR_OK) || (wavfileinfo->fname[0] == 0))
- {
- break; /* 錯(cuò)誤了/到末尾了,退出 */
- }
- strcpy((char*)pname, "0:/MUSIC/"); /* 復(fù)制路徑(目錄) */
- strcat((char*)pname, (const char*)wavfileinfo->fname); /* 將文件名接在后面 */
- audio_index_show(curindex + 1, totwavnum);
- key = audio_play_song(pname); /* 播放這個(gè)音頻文件 */
- if (key == KEY2_PRES) /* 上一曲 */
- {
- if (curindex)
- {
- curindex--;
- }
- else
- {
- curindex = totwavnum - 1;
- }
- }
- else if (key == KEY0_PRES) /* 下一曲 */
- {
- curindex++;
- if (curindex >= totwavnum)
- {
- curindex = 0; /* 到末尾的時(shí)候,自動(dòng)從頭開(kāi)始 */
- }
- }
- else
- {
- break; /* 產(chǎn)生了錯(cuò)誤 */
- }
- n=curindex;
- }
- myfree(SRAMIN, wavfileinfo); /* 釋放內(nèi)存 */
- myfree(SRAMIN, pname); /* 釋放內(nèi)存 */
- myfree(SRAMIN, wavoffsettbl); /* 釋放內(nèi)存 */
- }
其中:為板載4個(gè)按鍵所賦予的功能為:
KEY1:控制進(jìn)入播放功能
KEY0:播放下一首
KEY2:播放前一首
KEY_UP:暫停與恢復(fù)播放
經(jīng)程序的編譯與下載,其播放界面如圖5所示。

圖5 播放界面
視頻演示:
-
- 本文系21ic原創(chuàng),未經(jīng)許可禁止轉(zhuǎn)載!
網(wǎng)友評(píng)論
- 聯(lián)系人:巧克力娃娃
- 郵箱:board@21ic.com
- 我要投稿
-
歡迎入駐,開(kāi)放投稿
行業(yè)新聞
熱門標(biāo)簽
論壇活動(dòng)
more+
公開(kāi)課
more+
項(xiàng)目外包
more+
- NRF52810藍(lán)牙數(shù)字耳機(jī)找人定制
預(yù)算:¥30005天前
- 125KW模塊式PCS軟硬件外包開(kāi)發(fā)
預(yù)算:¥1100000015小時(shí)前
- 12V汽車啟動(dòng)電源項(xiàng)目BMS設(shè)計(jì)
預(yù)算:¥50000023小時(shí)前
- 數(shù)據(jù)可視化軟件 開(kāi)發(fā)
預(yù)算:¥5000023小時(shí)前
- PLC項(xiàng)目調(diào)試修改
預(yù)算:¥100001天前
- 起動(dòng)電機(jī)控制器開(kāi)發(fā)
預(yù)算:¥1100001天前