首頁 > 評測 > 【技術(shù)分享】基于正點(diǎn)原子開發(fā)板的中文菜單音樂播放器實現(xiàn)

【技術(shù)分享】基于正點(diǎn)原子開發(fā)板的中文菜單音樂播放器實現(xiàn)

  
  • 作者:
  • 來源:
  • [導(dǎo)讀]
  • 本帖最后由 jinglixixi 于 2023-5-17 18:48 編輯 #申請原創(chuàng)# @21小跑堂 正點(diǎn)原子開發(fā)板以外設(shè)豐富而著稱,基本可以全部支撐起多媒體方面的應(yīng)用,如數(shù)碼相框功能(已在前面介紹過)、視頻播放功能、文本閱讀器

本帖最后由 jinglixixi 于 2023-5-17 18:48 編輯

#申請原創(chuàng)# @21小跑堂

正點(diǎn)原子開發(fā)板以外設(shè)豐富而著稱,基本可以全部支撐起多媒體方面的應(yīng)用,如數(shù)碼相框功能(已在前面介紹過)、視頻播放功能、文本閱讀器功能及音頻播放功能等。
這次,就利用這些外設(shè)來實現(xiàn)一個具有中文菜單的音樂播放器,所涉及的主要外設(shè)為:I2S語音播放電路及字庫存儲電路,參見圖1和圖2所示。
圖1 I2S語音播放電路
圖2 字庫存儲電路

這里之所以使用閃存W25Q128來構(gòu)建字庫是因為,它的通用性更強(qiáng),可以直接在編程時就以掌握來設(shè)計菜單。而在以往,要設(shè)計中文菜單,均是采用構(gòu)建專用小字庫的方式來實現(xiàn)。這就導(dǎo)致在輸出漢字時,多是以漢字在小子庫中的次序編號來調(diào)用字模,故通用性極差。

 

在W25Q128中,其字模是按列行式來取模的,見圖3所示。此外,它共含有3種字庫,即GBK12、GBK16及GBK24。
圖3 取模方式

此外,為了便于中西為的混排,其文字顯示函數(shù)為:
  1. void text_show_font(uint16_t x, uint16_t y, uint8_t *font, uint8_t size, uint8_t mode, uint16_t color)
  2. {
  3.     uint8_t temp, t, t1;
  4.     uint16_t y0 = y;
  5.     uint8_t *dzk;
  6.     uint8_t csize = (size / 8 + ((size % 8) ? 1 : 0)) * (size);
  7.     /* 得到狀態(tài)一個字符對應(yīng)點(diǎn)陣集所占的字節(jié)數(shù) */
  8.     if (size != 12 && size != 16 && size != 24 && size != 32)
  9.     {
  10.         return;                       /*不支持的size */
  11.     }
  12.     dzk = mymalloc(SRAMIN, size);        /* 申請內(nèi)存 */
  13.     if (dzk == 0) return;                  /* 內(nèi)存不足 */
  14.     text_get_hz_mat(font, dzk, size);       /* 得到相應(yīng)讀寫的點(diǎn)陣數(shù)據(jù) */
  15.     for (t = 0; t < csize; t++)               // 一個字符
  16.     {
  17.         temp = dzk[t];                  /* 得到點(diǎn)陣數(shù)據(jù) */
  18.         for (t1 = 0; t1 < 8; t1++)           // 一個字節(jié)
  19.         {
  20.             if (temp & 0x80)            // 1000 0000  從左到右
  21.             {
  22.                 //lcd_draw_point(x, y, color);  /* 畫需要顯示的點(diǎn) */
  23.                 ili9341_draw_pixel(color, x, y);
  24.             }
  25.             else if (mode == 0)         /* 如非疊加模式,則不需要顯示的點(diǎn),用背景色填充 */
  26.             {
  27.                 //lcd_draw_point(x, y, g_back_color); /* 填充背景色 */
  28.                 ili9341_draw_pixel(g_back_color, x, y);
  29.             }
  30.             temp <<= 1;
  31.             y++;                    //由上至下
  32.             if ((y - y0) == size)
  33.             {
  34.                 y = y0;
  35.                 x++;                //換列   --->存放形式:列行式
  36.                 break;
  37.             }
  38.         }
  39.     }
  40.     myfree(SRAMIN, dzk);    /* 釋放內(nèi)存 */
  41. }
  42.  
復(fù)制代碼

 

由于本人所所用的顯示屏為MDM-2802顯示模塊,故對原顯示函數(shù)中的畫點(diǎn)函數(shù)進(jìn)行了相應(yīng)地替換,并注釋掉了原來的函數(shù)。
圖4 中文播放界面

要實現(xiàn)圖4所示的中文播放界面,其主程序如下:
  1. int main(void)
  2. {
  3.     uint8_t key;
  4.     HAL_Init();                                     /* 初始化HAL庫 */
  5.     sys_stm32_clock_init(336, 8, 2, 7);         /* 設(shè)置時鐘,168Mhz */
  6.     delay_init(168);                          /* 延時初始化 */
  7.     usart_init(115200);                     /* 串口1初始化為115200 */
  8.     usmart_dev.init(84);                    /* 初始化USMART */
  9.     led_init();                                   /* 初始化LED */
  10.     key_init();                                  /* 初始化按鍵 */
  11.     sram_init();                                /* SRAM初始化 */
  12.     norflash_init();                           /* 初始化NORFLASH */
  13.     LCD_Init();
  14.     delay_ms(10);
  15.     ili9341_init();
  16.     ili9341_clear(RED);
  17.     BACK_COLOR=RED;
  18.     POINT_COLOR=YELLOW;
  19.     LCD_DrawLine(WHITE ,0, 35, 239, 35);
  20.     LCD_DrawLine(WHITE ,0, 275, 239, 275);
  21.     my_mem_init(SRAMIN);                  /* 初始化內(nèi)部SRAM內(nèi)存池 */
  22.     my_mem_init(SRAMEX);                  /* 初始化外部SRAM內(nèi)存池 */
  23.     while (sd_init())                              /* 檢測SD卡 */
  24.     {
  25.            LCD_ShowString(30,50,"SD Card Failed!");
  26.            delay_ms(200);
  27.            delay_ms(200);
  28.     }
  29.     exfuns_init();                           /* 為fatfs相關(guān)變量申請內(nèi)存 */
  30.     f_mount(fs[0], "0:", 1);             /* 掛載SD卡 */
  31.     f_mount(fs[1], "1:", 1);             /* 掛載FLASH */
  32.     while (fonts_init())                    /* 檢查字庫 */
  33.     {
  34.             LCD_ShowString(30,50,"Font Error!");
  35.             delay_ms(200);
  36.     }
  37.     es8388_init();                    /* ES8388初始化 */
  38.     es8388_adda_cfg(1, 0);      /* 開啟DAC關(guān)閉ADC */
  39.     es8388_output_cfg(1, 1);    /* DAC選擇通道輸出 */
  40.     es8388_hpvol_set(25);        /* 設(shè)置耳機(jī)音量 */
  41.     es8388_spkvol_set(26);       /* 設(shè)置喇叭音量 */
  42.     g_back_color=RED;
  43.     text_show_string(30, 13, 200, 16, "中文菜單音樂播放器", 16, 0, YELLOW);
  44.     POINT_COLOR=WHITE;
  45.     text_show_string(30, 50, 200, 16, "1 - 最美的期待", 16, 0, WHITE);
  46.     text_show_string(30, 70, 200, 16, "2 - 野百合也有春天", 16, 0, WHITE);
  47.     text_show_string(30, 90, 200, 16, "3 - 光陰的故事", 16, 0, WHITE);
  48.     text_show_string(30, 110, 200, 16, "4 - 莫斯科郊外的晚上", 16, 0, WHITE);
  49.     text_show_string(30, 130, 200, 16, "5 - 東方之珠", 16, 0, WHITE);
  50.     text_show_string(30, 150, 200, 16, "6 - 光輝歲月", 16, 0, WHITE);
  51.     text_show_string(30, 170, 200, 16, "7 - 同桌的你", 16, 0, WHITE);
  52.     text_show_string(30, 190, 200, 16, "8 - 亞洲雄風(fēng)", 16, 0, WHITE);
  53.     text_show_string(30, 210, 200, 16, "9 - 咖啡屋", 16, 0, WHITE);
  54.     text_show_string(30, 230, 200, 16, "10- 軍港之夜", 16, 0, WHITE);
  55.     POINT_COLOR=YELLOW;
  56.     text_show_string(30, 280, 200, 16, "KEY0:下一首 KEY2:上一首", 16, 0, YELLOW);
  57.     text_show_string(30, 300, 200, 16, "KEY_UP:暫停/播放", 16, 0, YELLOW);
  58.     POINT_COLOR=WHITE;
  59.     key = key_scan(0);
  60.     while(key != KEY1_PRES)
  61.     {
  62.             key = key_scan(0);
  63.     }
  64.     while (1)
  65.     {
  66.             audio_play();           /* 播放音樂 */
  67.     }
  68. }
  69.  
復(fù)制代碼

 

有了可以進(jìn)行播放功能的設(shè)計了,其中標(biāo)識當(dāng)前播放曲目的函數(shù)為:
  1. uint8_t audio_play_song(char* fname)
  2. {
  3.     uint8_t res;
  4.     res = exfuns_file_type(fname);
  5.     switch (res)
  6.     {
  7.         case T_WAV:
  8.             POINT_COLOR=0xF800;
  9.             text_show_string(12, 50+(np*20), 200, 16, "*", 16, 0, 0xF800);
  10.             POINT_COLOR=0xFFE0;
  11.             text_show_string(12, 50+(n*20), 200, 16, "*", 16, 0, 0xFFE0);
  12.             np=n;
  13.             res = wav_play_song(fname);
  14.             break;
  15.         default:
  16.             res = KEY0_PRES;
  17.             break;
  18.     }
  19.     return res;
  20. }
  21.  
復(fù)制代碼

 

而獲取相應(yīng)音頻播放文件的函數(shù)為:
  1. uint16_t audio_get_tnum(char *path)
  2. {
  3.     uint8_t res;
  4.     uint16_t rval = 0;
  5.     DIR tdir;                 /* 臨時目錄 */
  6.     FILINFO* tfileinfo;   /* 臨時文件信息 */
  7.     tfileinfo = (FILINFO*)mymalloc(SRAMIN, sizeof(FILINFO));     /* 申請內(nèi)存 */
  8.     res = f_opendir(&tdir, (const TCHAR*)path);                         /* 打開目錄 */
  9.     if ((res == FR_OK) && tfileinfo)
  10.     {
  11.         while (1)       /* 查詢總的有效文件數(shù) */
  12.         {
  13.             res = f_readdir(&tdir, tfileinfo);                  /* 讀取目錄下的一個文件 */
  14.             if ((res != FR_OK) || (tfileinfo->fname[0] == 0))
  15.             {
  16.                 break;  /* 錯誤了/到末尾了,退出 */
  17.             }
  18.             res = exfuns_file_type(tfileinfo->fname);
  19.             if ((res & 0xF0) == 0x40)   /* 取高四位,看看是不是音樂文件 */
  20.             {
  21.                 rval++; /* 有效文件數(shù)增加1 */
  22.             }
  23.         }
  24.     }
  25.     myfree(SRAMIN, tfileinfo);    /* 釋放內(nèi)存 */
  26.     return rval;
  27. }
  28.  
復(fù)制代碼

 

實現(xiàn)音頻播放與樂曲選擇的功能函數(shù)為:
  1. void audio_play(void)
  2. {
  3.     uint8_t res;
  4.     DIR wavdir;                  /* 目錄 */
  5.     FILINFO *wavfileinfo;    /* 文件信息 */
  6.     char *pname;               /* 帶路徑的文件名 */
  7.     uint16_t totwavnum;     /* 音樂文件總數(shù) */
  8.     uint16_t curindex;         /*當(dāng)前索引 */
  9.     uint8_t key;                 /* 鍵值 */
  10.     uint32_t temp;
  11.     uint32_t *wavoffsettbl;     /* 音樂offset索引表 */
  12.     es8388_adda_cfg(1, 0);    /* 開啟DAC關(guān)閉ADC */
  13.     es8388_output_cfg(1, 1);  /* DAC選擇通道1輸出 */
  14.     while (f_opendir(&wavdir, "0:/MUSIC"))  /* 打開音樂文件夾 */
  15.     {
  16.             LCD_ShowString(30,190,"MUSIC ERROR");
  17.             delay_ms(200);
  18.             delay_ms(200);
  19.     }
  20.     totwavnum = audio_get_tnum("0:/MUSIC");  /* 得到總有效文件數(shù) */
  21.     while (totwavnum == NULL)                         /* 音樂文件總數(shù)為0 */
  22.     {
  23.              LCD_ShowString(30,190,"NO MUSIC !");
  24.              delay_ms(200);
  25.              delay_ms(200);
  26.     }
  27.     wavfileinfo = (FILINFO*)mymalloc(SRAMIN, sizeof(FILINFO)); /* 申請內(nèi)存 */
  28.     pname = mymalloc(SRAMIN, FF_MAX_LFN * 2 + 1);               /* 為帶路徑的文件名分配內(nèi)存 */
  29.     wavoffsettbl = mymalloc(SRAMIN, 4 * totwavnum);             /* 申請4*totwavnum個字節(jié)的內(nèi)存,用于存放音樂文件off block索引 */
  30.     while (!wavfileinfo || !pname || !wavoffsettbl)             /* 內(nèi)存分配出錯 */
  31.     {
  32.             LCD_ShowString(30,190,"memory Failed");
  33.             delay_ms(200);
  34.             delay_ms(200);
  35.     }
  36.     /* 記錄索引 */
  37.     res = f_opendir(&wavdir, "0:/MUSIC");   /* 打開目錄 */
  38.     if (res == FR_OK)
  39.     {
  40.         curindex = 0;   /* 當(dāng)前索引為0 */
  41.         while (1)       /* 全部查詢一遍 */
  42.         {
  43.             temp = wavdir.dptr;                     /* 記錄當(dāng)前index */
  44.             res = f_readdir(&wavdir, wavfileinfo);  /* 讀取目錄下的一個文件 */
  45.             if ((res != FR_OK) || (wavfileinfo->fname[0] == 0))
  46.             {
  47.                 break;  /* 錯誤了/到末尾了,退出 */
  48.             }
  49.             res = exfuns_file_type(wavfileinfo->fname);
  50.             if ((res & 0xF0) == 0x40)           /* 取高四位,看看是不是音樂文件 */
  51.             {
  52.                 wavoffsettbl[curindex] = temp;  /* 記錄索引 */
  53.                 curindex++;
  54.             }
  55.         }
  56.     }
  57.     curindex = 0;           /* 從0開始顯示 */
  58.     res = f_opendir(&wavdir, (const TCHAR*)"0:/MUSIC"); /* 打開目錄 */
  59.     while (res == FR_OK)    /* 打開成功 */
  60.     {
  61.         dir_sdi(&wavdir, wavoffsettbl[curindex]);     /* 改變當(dāng)前目錄索引 */
  62.         res = f_readdir(&wavdir, wavfileinfo);         /* 讀取目錄下的一個文件 */
  63.         if ((res != FR_OK) || (wavfileinfo->fname[0] == 0))
  64.         {
  65.             break;          /* 錯誤了/到末尾了,退出 */
  66.         }
  67.         strcpy((char*)pname, "0:/MUSIC/");                  /* 復(fù)制路徑(目錄) */
  68.         strcat((char*)pname, (const char*)wavfileinfo->fname);  /* 將文件名接在后面 */
  69.         audio_index_show(curindex + 1, totwavnum);
  70.         key = audio_play_song(pname);     /* 播放這個音頻文件 */
  71.         if (key == KEY2_PRES)             /* 上一曲 */
  72.         {
  73.             if (curindex)
  74.             {
  75.                 curindex--;
  76.             }
  77.             else
  78.             {
  79.                 curindex = totwavnum - 1;
  80.             }
  81.         }
  82.         else if (key == KEY0_PRES)  /* 下一曲 */
  83.         {
  84.             curindex++;
  85.             if (curindex >= totwavnum)
  86.             {
  87.                 curindex = 0;       /* 到末尾的時候,自動從頭開始 */
  88.             }
  89.         }
  90.         else
  91.         {
  92.             break;                  /* 產(chǎn)生了錯誤 */
  93.         }
  94.         n=curindex;
  95.     }
  96.     myfree(SRAMIN, wavfileinfo);       /* 釋放內(nèi)存 */
  97.     myfree(SRAMIN, pname);          /* 釋放內(nèi)存 */
  98.     myfree(SRAMIN, wavoffsettbl);     /* 釋放內(nèi)存 */
  99. }
  100.  
復(fù)制代碼

 

其中:為板載4個按鍵所賦予的功能為:
KEY1:控制進(jìn)入播放功能
KEY0:播放下一首
KEY2:播放前一首
KEY_UP:暫停與恢復(fù)播放

 

經(jīng)程序的編譯與下載,其播放界面如圖5所示。
圖5 播放界面

視頻演示:

 

  • 本文系21ic原創(chuàng),未經(jīng)許可禁止轉(zhuǎn)載!

網(wǎng)友評論

  • 聯(lián)系人:巧克力娃娃
  • 郵箱:board@21ic.com
  • 我要投稿
  • 歡迎入駐,開放投稿

熱門標(biāo)簽
項目外包 more+