[STM32F4]【技术分享】基于正点原子开发板的中文菜单音乐播放器实现

正点原子开发板以外设丰富而著称,基本可以全部支撑起多媒体方面的应用,如数码相框功能(已在前面介绍过)、视频播放功能、文本阅读器功能及音频播放功能等。

这次,就利用这些外设来实现一个具有中文菜单的音乐播放器,所涉及的主要外设为:I2S语音播放电路及字库存储电路,参见图1和图2所示。

1 I2S语音播放电路

2 字库存储电路

这里之所以使用闪存W25Q128来构建字库是因为,它的通用性更强,可以直接在编程时就以掌握来设计菜单。而在以往,要设计中文菜单,均是采用构建专用小字库的方式来实现。这就导致在输出汉字时,多是以汉字在小子库中的次序编号来调用字模,故通用性极差。

W25Q128中,其字模是按列行式来取模的,见图3所示。此外,它共含有3种字库,即GBK12GBK16GBK24

3 取模方式

此外,为了便于中西为的混排,其文字显示函数为:

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);

    /* 得到状态一个字符对应点阵集所占的字节数 */

    if (size != 12 && size != 16 && size != 24 && size != 32)

    {

        return;                       /*不支持的size */

    }

    dzk = mymalloc(SRAMIN, size);        /* 申请内存 */

    if (dzk == 0) return;                  /* 内存不足 */

    text_get_hz_mat(font, dzk, size);       /* 得到相应读写的点阵数据 */

    for (t = 0; t < csize; t++)               // 一个字符

    {

        temp = dzk[t];                  /* 得到点阵数据 */

        for (t1 = 0; t1 < 8; t1++)           // 一个字节

        {

            if (temp & 0x80)            // 1000 0000  从左到右

            {

                //lcd_draw_point(x, y, color);  /* 画需要显示的点 */

                ili9341_draw_pixel(color, x, y);

            }

            else if (mode == 0)         /* 如非叠加模式,则不需要显示的点,用背景色填充 */

            {

                //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);    /* 释放内存 */

}

由于本人所所用的显示屏为MDM-2802显示模块,故对原显示函数中的画点函数进行了相应地替换,并注释掉了原来的函数。

图4 中文播放界面
要实现图4所示的中文播放界面,其主程序如下:
int main(void)

{

    uint8_t key;

    HAL_Init();                                     /* 初始化HAL库 */

    sys_stm32_clock_init(336, 8, 2, 7);         /* 设置时钟,168Mhz */

    delay_init(168);                          /* 延时初始化 */

    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);                  /* 初始化内部SRAM内存池 */

    my_mem_init(SRAMEX);                  /* 初始化外部SRAM内存池 */

    while (sd_init())                              /* 检测SD卡 */

    {

           LCD_ShowString(30,50,"SD Card Failed!"); 

           delay_ms(200);

           delay_ms(200);

    }

    exfuns_init();                           /* 为fatfs相关变量申请内存 */

    f_mount(fs[0], "0:", 1);             /* 挂载SD卡 */

    f_mount(fs[1], "1:", 1);             /* 挂载FLASH */

    while (fonts_init())                    /* 检查字库 */

    {

            LCD_ShowString(30,50,"Font Error!");

            delay_ms(200);

    }

    es8388_init();                    /* ES8388初始化 */

    es8388_adda_cfg(1, 0);      /* 开启DAC关闭ADC */

    es8388_output_cfg(1, 1);    /* DAC选择通道输出 */

    es8388_hpvol_set(25);        /* 设置耳机音量 */

    es8388_spkvol_set(26);       /* 设置喇叭音量 */

    g_back_color=RED;

    text_show_string(30, 13, 200, 16, "中文菜单音乐播放器", 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 - 亚洲雄风", 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();           /* 播放音乐 */

    }

}


有了可以进行播放功能的设计了,其中标识当前播放曲目的函数为:
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;

}

而获取相应音频播放文件的函数为:
uint16_t audio_get_tnum(char *path)

{

    uint8_t res;

    uint16_t rval = 0;

    DIR tdir;                 /* 临时目录 */

    FILINFO* tfileinfo;   /* 临时文件信息 */

    tfileinfo = (FILINFO*)mymalloc(SRAMIN, sizeof(FILINFO));     /* 申请内存 */

    res = f_opendir(&tdir, (const TCHAR*)path);                         /* 打开目录 */

    if ((res == FR_OK) && tfileinfo)

    {

        while (1)       /* 查询总的有效文件数 */

        {

            res = f_readdir(&tdir, tfileinfo);                  /* 读取目录下的一个文件 */

            if ((res != FR_OK) || (tfileinfo->fname[0] == 0))

            {

                break;  /* 错误了/到末尾了,退出 */

            }

            res = exfuns_file_type(tfileinfo->fname);

            if ((res & 0xF0) == 0x40)   /* 取高四位,看看是不是音乐文件 */

            {

                rval++; /* 有效文件数增加1 */

            }

        }

    }

    myfree(SRAMIN, tfileinfo);    /* 释放内存 */

    return rval;

}

实现音频播放与乐曲选择的功能函数为:

void audio_play(void)

{

    uint8_t res;

    DIR wavdir;                  /* 目录 */

    FILINFO *wavfileinfo;    /* 文件信息 */

    char *pname;               /* 带路径的文件名 */

    uint16_t totwavnum;     /* 音乐文件总数 */

    uint16_t curindex;         /*当前索引 */

    uint8_t key;                 /* 键值 */

    uint32_t temp;

    uint32_t *wavoffsettbl;     /* 音乐offset索引表 */

    es8388_adda_cfg(1, 0);    /* 开启DAC关闭ADC */

    es8388_output_cfg(1, 1);  /* DAC选择通道1输出 */

    while (f_opendir(&wavdir, "0:/MUSIC"))  /* 打开音乐文件夹 */

    {

            LCD_ShowString(30,190,"MUSIC ERROR");

            delay_ms(200);

            delay_ms(200);

    }

    totwavnum = audio_get_tnum("0:/MUSIC");  /* 得到总有效文件数 */

    while (totwavnum == NULL)                         /* 音乐文件总数为0 */

    {

             LCD_ShowString(30,190,"NO MUSIC !");

             delay_ms(200);

             delay_ms(200);

    }

    wavfileinfo = (FILINFO*)mymalloc(SRAMIN, sizeof(FILINFO)); /* 申请内存 */

    pname = mymalloc(SRAMIN, FF_MAX_LFN * 2 + 1);               /* 为带路径的文件名分配内存 */

    wavoffsettbl = mymalloc(SRAMIN, 4 * totwavnum);             /* 申请4*totwavnum个字节的内存,用于存放音乐文件off block索引 */

    while (!wavfileinfo || !pname || !wavoffsettbl)             /* 内存分配出错 */

    {

            LCD_ShowString(30,190,"memory Failed");

            delay_ms(200);

            delay_ms(200);

    }

    /* 记录索引 */

    res = f_opendir(&wavdir, "0:/MUSIC");   /* 打开目录 */

    if (res == FR_OK)

    {

        curindex = 0;   /* 当前索引为0 */

        while (1)       /* 全部查询一遍 */

        {

            temp = wavdir.dptr;                     /* 记录当前index */

            res = f_readdir(&wavdir, wavfileinfo);  /* 读取目录下的一个文件 */

            if ((res != FR_OK) || (wavfileinfo->fname[0] == 0))

            {

                break;  /* 错误了/到末尾了,退出 */

            }

            res = exfuns_file_type(wavfileinfo->fname);

            if ((res & 0xF0) == 0x40)           /* 取高四位,看看是不是音乐文件 */

            {

                wavoffsettbl[curindex] = temp;  /* 记录索引 */

                curindex++;

            }

        }

    }

    curindex = 0;           /* 从0开始显示 */

    res = f_opendir(&wavdir, (const TCHAR*)"0:/MUSIC"); /* 打开目录 */

    while (res == FR_OK)    /* 打开成功 */

    {

        dir_sdi(&wavdir, wavoffsettbl[curindex]);     /* 改变当前目录索引 */

        res = f_readdir(&wavdir, wavfileinfo);         /* 读取目录下的一个文件 */

        if ((res != FR_OK) || (wavfileinfo->fname[0] == 0))

        {

            break;          /* 错误了/到末尾了,退出 */

        }

        strcpy((char*)pname, "0:/MUSIC/");                  /* 复制路径(目录) */

        strcat((char*)pname, (const char*)wavfileinfo->fname);  /* 将文件名接在后面 */

        audio_index_show(curindex + 1, totwavnum);

        key = audio_play_song(pname);     /* 播放这个音频文件 */

        if (key == KEY2_PRES)             /* 上一曲 */

        {

            if (curindex)

            {

                curindex--;

            }

            else

            {

                curindex = totwavnum - 1;

            }

        }

        else if (key == KEY0_PRES)  /* 下一曲 */

        {

            curindex++;

            if (curindex >= totwavnum)

            {

                curindex = 0;       /* 到末尾的时候,自动从头开始 */

            }

        }

        else

        {

            break;                  /* 产生了错误 */

        }

        n=curindex;

    }

    myfree(SRAMIN, wavfileinfo);       /* 释放内存 */

    myfree(SRAMIN, pname);          /* 释放内存 */

    myfree(SRAMIN, wavoffsettbl);     /* 释放内存 */

}

其中:为板载4个按键所赋予的功能为:
KEY1:控制进入播放功能
KEY0:播放下一首
KEY2:播放前一首
KEY_UP:暂停与恢复播放

经程序的编译与下载,其播放界面如图5所示。

5 播放界面
---------------------
作者:jinglixixi
链接:https://bbs.21ic.com/icview-3302934-1-1.html
来源:21ic.com
此文章已获得原创/原创奖标签,著作权归21ic所有,任何人未经允许禁止转载。

 

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值