中科蓝讯(AB32VG1)开发板(基于RT-Thread系统)--- 音乐播放器--分享
[复制链接]
转自CSDN,原创Bruceoxl 原链接https://bruceou.blog.csdn.net/article/details/116097779?spm=1001.2101.3001.6650.4&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-4.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-4.nonecase
开发环境:
RT-Thread版本:4.0.3
操作系统:Windows 10
RT-Thread Studio版本:2.0.1
开发板MCU:AB5301A
6.1前言
在前面几章,我们使用AB32VG1做了几个小实验,本章将前面的内容进行组合,做一个音乐播放器,主要功能如下:
1.可存储多首完整音乐;
2.实现歌曲切换;
3.实现音量调节。
当然,以上是最基本的功能,还可以实现歌曲播放模式的选择。本章内容主要实现以上3个基本功能,另外根据音量的大小来改变RGB灯的闪烁频率,非常的炫酷。
好了,接下来就一起来看看如何实现音乐播放器吧。
6.2音乐播放器配置
整个项目配置分三部分:音频部分,存储与文件系统部分,LED、串口等三部分。
关于项目的创建请参看笔者以前的文章:
AB32VG1新建项目
6.2.1音频配置
首先看看音频部分。使能硬件是必不可少的。
使能硬件后,我们就可以使用音频设备了,但是音频设备相对其他外设比较复杂,RT-Thread提供了操作音频设备的软件包WavPlay。只需要使能即可。
值得注意的是,WavPlay软件包依赖optparse,因此optparse软件包在 wavplayer 勾选后,自动选择。optparse模块主要用来为脚本传递命令参数,采用预先定义好的选项来解析命令行参数。
6.2.2存储文件系统配置
AB32VG1开发板内部存储很小,而音乐文件很大,因此需要使用外部存储设备来存放音乐,这样,这里使用SD卡来存储音乐。首先就需要使能SD卡设备。
使能SD卡设备后,文件系统也就默认勾选了。
当然也可适当修改里面的参数。
6.2.3其他配置
前面两部分是这个项目的重要配置,是不可缺少的,接下来就是配置,PWM、UART、KEY设备。
关于PWM和UART的配置参考笔者前面的文章:
PWM LED使用
UART使用
下面再添加一个按键的功能包即可。
MultiButton 是一个小巧简单易用的事件驱动型按键驱动模块,可无限量扩展按键,按键事件的回调异步处理方式可以简化你的程序结构,去除冗余的按键处理硬编码,非常好用。
好了,关于音乐播放器的配置就到这里了。
6.3音乐播放器实现
本文将通过按键或者通过串口发送指令来播放音乐、切歌、控制音量等操作,另外根据音量的大小来调节PWM的频率,从而改变RGB灯的闪烁频率。
6.3.1 WavPlay播放音频简析
对于音频设备,其操作流程如下:
1.首先查找 Audio 设备获取设备句柄。
2.以只写方式打开 Audio 设备。
3.设置音频参数信息(采样率、通道等)。
4.解码音频文件的数据。
5.写入音频文件数据。
6.播放完成,关闭设备。
如果自己去实现这些操作还是比较复杂的,wavplayer 软件包将音频设备的操作进行了封装。主需要简单调用几个函数接口播放音乐,主要的接口如下:
int wavplayer_play(char *uri);//音乐播放
int wavplayer_stop(void);// 结束播放
int wavplayer_pause(void);//暂停播放
int wavplayer_resume(void):// 继续播放
int wavplayer_volume_set(int volume);//音量设置
当然啦,这里只讲解应用实现,关于音频驱动请参看官方手册。
AUDIO 设备
6.3.2 PWM控制RGB灯
这部分内容在前面的章节已经讲过了,这里就不讲了,代码如下:
- #include "led_app.h"
-
- #define THREAD_PRIORITY 7
- #define THREAD_STACK_SIZE 512
- #define THREAD_TIMESLICE 3
-
- uint32_t pulse_pulse = 90000;
-
- #define PWM_DEV_NAME_R "t5pwm" /* PWM设备名称 */
- #define PWM_DEV_CHANNEL_R 1 /* PWM通道 */
-
- #define PWM_DEV_NAME_G "lpwm0" /* PWM设备名称 */
- #define PWM_DEV_CHANNEL_G 1 /* PWM通道 */
-
- #define PWM_DEV_NAME_B "lpwm2" /* PWM设备名称 */
- #define PWM_DEV_CHANNEL_B 3 /* PWM通道 */
-
- struct rt_device_pwm *pwm_dev_r;
- struct rt_device_pwm *pwm_dev_g;
- struct rt_device_pwm *pwm_dev_b;
-
- static rt_thread_t pwm_led_tid = RT_NULL;
-
-
-
- static void pwm_led_thread_entry(void *parameter)
- {
- rt_uint32_t period, pulse_r,pulse_g,pulse_b, dir_r,dir_g,dir_b;
-
- period = 655360;
- dir_r = 1;
- dir_g = 1;
- dir_b = 1;
- pulse_r = 0;
- pulse_g = 0;
- pulse_b = 0;
-
- rt_uint16_t r,g,b;
-
-
- pwm_dev_r = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME_R);
- if (pwm_dev_r == RT_NULL)
- {
- rt_kprintf("pwm led r run failed! can't find %s device!\n", PWM_DEV_NAME_G);
- }
-
- pwm_dev_g = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME_G);
- if (pwm_dev_g == RT_NULL)
- {
- rt_kprintf("pwm led g run failed! can't find %s device!\n", PWM_DEV_NAME_G);
- }
-
- pwm_dev_b = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME_B);
- if (pwm_dev_b == RT_NULL)
- {
- rt_kprintf("pwm led b run failed! can't find %s device!\n", PWM_DEV_NAME_B);
- }
-
-
- rt_pwm_set(pwm_dev_r, PWM_DEV_CHANNEL_R, period, pulse_r);
- rt_pwm_set(pwm_dev_g, PWM_DEV_CHANNEL_G, period, pulse_g);
- rt_pwm_set(pwm_dev_b, PWM_DEV_CHANNEL_B, period, pulse_b);
-
-
- rt_pwm_enable(pwm_dev_r, PWM_DEV_CHANNEL_R);
- rt_pwm_enable(pwm_dev_g, PWM_DEV_CHANNEL_G);
- rt_pwm_enable(pwm_dev_b, PWM_DEV_CHANNEL_B);
-
- while (1)
- {
- for (r =0 ; r < 8; r++)
- {
- if (dir_r)
- {
- pulse_r += pulse_pulse;
- }
- else
- {
- pulse_r -= pulse_pulse;
- }
- if ((pulse_r) >= period)
- {
- dir_r = 0;
- }
- if (81920 > pulse_r)
- {
- dir_r = 1;
- }
-
- rt_pwm_set(pwm_dev_r, PWM_DEV_CHANNEL_R, period, pulse_r);
- for(g = 0; g < 8; g++)
- {
- if (dir_g)
- {
- pulse_g += pulse_pulse;
- }
- else
- {
- pulse_g -= pulse_pulse;
- }
- if ((pulse_g) >= period)
- {
- dir_g = 0;
- }
- if (81920 > pulse_g)
- {
- dir_g = 1;
- }
- rt_pwm_set(pwm_dev_g, PWM_DEV_CHANNEL_G, period, pulse_g);
- for(b = 0; b < 8; b++)
- {
- rt_thread_mdelay(10);
- if (dir_b)
- {
- pulse_b += pulse_pulse;
- }
- else
- {
- pulse_b -= pulse_pulse;
- }
- if ((pulse_b) >= period)
- {
- dir_b = 0;
- }
- if (81920 > pulse_b)
- {
- dir_b = 1;
- }
-
- rt_pwm_set(pwm_dev_b, PWM_DEV_CHANNEL_B, period, pulse_b);
- }
- }
-
- }
- }
- }
-
-
- int pwm_led(void)
- {
-
- pwm_led_tid = rt_thread_create("pwm_led_thread",
- pwm_led_thread_entry,
- RT_NULL,
- THREAD_STACK_SIZE,
- THREAD_PRIORITY,
- THREAD_TIMESLICE);
-
-
- if (pwm_led_tid != RT_NULL)
- rt_thread_startup(pwm_led_tid);
-
- return 0;
- }
-
-
-
-
- INIT_APP_EXPORT(pwm_led);
-
6.3.3串口控制音频设备
下面先看看UART播放音乐的代码。
- #include "uart_app.h"
- #include "key_app.h"
- #include "led_app.h"
-
- #define SAMPLE_UART_NAME "uart1"
-
- uint8_t ch;
- uint8_t r_index = 0;
- uint8_t flag = 0;
- extern uint32_t cnt_music;
- extern uint32_t cnt_channels;
- extern uint32_t cnt_volume;
- extern uint32_t start_flag;
- extern char *table[NUM_OF_SONGS];
-
- extern uint32_t pulse_pulse;
-
- struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
-
-
- static struct rt_semaphore rx_sem;
- static rt_device_t serial;
-
- void analyticald_data(void)
- {
- uint8_t sum;
-
- if(ch == 0x01)
- {
- wavplayer_play(table[(cnt_music++) % NUM_OF_SONGS]);
- }
- else if(ch == 0x02)
- {
- if (cnt_volume < 11 )
- {
- if(start_flag)
- {
- start_flag = 0;
- cnt_volume = (int)saia_volume_get()/10;
- pulse_pulse = 9000;
- }
- else
- {
- saia_volume_set(cnt_volume * 10);
- pulse_pulse = cnt_volume*9000;
- }
- }
- else
- {
- saia_volume_set(10);
- cnt_volume = 1;
- rt_kprintf("The volume has been adjusted to maximum\n");
- }
- cnt_volume ++;
- rt_kprintf("vol=%d\n", saia_volume_get());
- }
- else if(ch == 0x03)
- {
- if (cnt_channels < 3)
- {
- saia_channels_set(cnt_channels);
- }
- else
- {
- saia_channels_set(cnt_channels);
- cnt_channels = 1;
- }
- cnt_channels++;
- }
- }
-
-
- static rt_err_t uart_rx_ind(rt_device_t dev, rt_size_t size)
- {
-
- if (size > 0)
- {
- rt_sem_release(&rx_sem);
- }
- return RT_EOK;
- }
-
- static char uart_sample_get_char(void)
- {
- uint8_t ch;
-
- while (rt_device_read(serial, 0, &ch, 1) == 0)
- {
- rt_sem_control(&rx_sem, RT_IPC_CMD_RESET, RT_NULL);
- rt_sem_take(&rx_sem, RT_WAITING_FOREVER);
- }
- return ch;
- }
-
-
- static void data_parsing(void)
- {
- while (1)
- {
- ch = uart_sample_get_char();
- flag = 1;
- }
- }
-
- int uart_init(void)
- {
- rt_err_t ret = RT_EOK;
- char uart_name[RT_NAME_MAX];
-
-
- rt_strncpy(uart_name, SAMPLE_UART_NAME, RT_NAME_MAX);
-
-
- serial = rt_device_find(uart_name);
- if (!serial)
- {
- rt_kprintf("find %s failed!\n", uart_name);
- return RT_ERROR;
- }
-
- config.baud_rate = BAUD_RATE_9600;
- config.data_bits = DATA_BITS_8;
- config.stop_bits = STOP_BITS_1;
- config.bufsz = 128;
- config.parity = PARITY_NONE;
-
-
- rt_device_control(serial, RT_DEVICE_CTRL_CONFIG, &config);
-
-
- rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO);
-
- rt_device_open(serial, RT_DEVICE_FLAG_INT_RX);
-
- rt_device_set_rx_indicate(serial, uart_rx_ind);
-
-
-
-
- rt_thread_t thread = rt_thread_create("serial", (void (*)(void *parameter))data_parsing, RT_NULL, 2048, 5, 5);
-
- if (thread != RT_NULL)
- {
- rt_thread_startup(thread);
- }
- else
- {
- ret = RT_ERROR;
- }
-
- return ret;
- }
-
-
- MSH_CMD_EXPORT(uart_init, uart device sample);
-
-
- #define THREAD_PRIORITY 9
- #define THREAD_TIMESLICE 5
-
- #define EVENT_FLAG (1 << 3)
-
-
- static struct rt_event event;
-
- ALIGN(RT_ALIGN_SIZE)
-
-
- static void thread1_recv_event(void *param)
- {
- rt_uint32_t e;
-
- while(1)
- {
-
- if (rt_event_recv(&event, (EVENT_FLAG ),
- RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,
- RT_WAITING_FOREVER, &e) == RT_EOK)
- {
- rt_kprintf("thread1: recv event 0x%x\n", e);
- analyticald_data();
- rt_kprintf("thread1: delay 1s to prepare the second event\n");
- }
- rt_thread_mdelay(100);
-
- }
- }
-
- ALIGN(RT_ALIGN_SIZE)
-
-
- static void thread2_send_event(void *param)
- {
- while(1)
- {
- if(flag==1)
- {
- flag = 0;
- rt_kprintf("thread2: send event\n");
- rt_event_send(&event, EVENT_FLAG);
- }
-
- rt_thread_mdelay(200);
- }
- }
-
- int event_wavplayer(void)
- {
- rt_err_t result;
-
-
- result = rt_event_init(&event, "event", RT_IPC_FLAG_FIFO);
- if (result != RT_EOK)
- {
- rt_kprintf("init event failed.\n");
- return -1;
- }
-
- rt_thread_t thread1 = rt_thread_create("serial", thread1_recv_event, RT_NULL, 512, 10, 5);
- rt_thread_startup(thread1);
-
- rt_thread_t thread2 = rt_thread_create("serial", thread2_send_event, RT_NULL, 512, 9, 5);
- rt_thread_startup(thread2);
-
- return 0;
- }
-
-
-
-
- INIT_APP_EXPORT(event_wavplayer);
-
代码还是比较简单的,有两部分内容,一部分是是串口的操作,另一部分是串口数据的解析事件,当串口收到指令后,event_wavplayer解析串口的指令,根据相应的指令来操作音频设备。串口指令如下:
6.3.4按键控制音频设备
这里使用MultiButton软件包,代码如下:
- #include "key_app.h"
- #include "led_app.h"
-
- extern uint32_t pulse_pulse;
-
- #define BUTTON_PIN_0 rt_pin_get("PF.0")
- #define BUTTON_PIN_1 rt_pin_get("PF.1")
-
- static struct button btn_0;
- static struct button btn_1;
-
- uint32_t cnt_channels = 1;
- uint32_t cnt_volume = 1;
-
- uint32_t cnt_music = 0;
-
- uint32_t start_flag = 1;
-
- char *table[NUM_OF_SONGS] =
- {
- "/Try.wav",
- "/Bad.wav",
- };
-
- static uint8_t button_read_pin_0(void)
- {
- return rt_pin_read(BUTTON_PIN_0);
- }
-
- static uint8_t button_read_pin_1(void)
- {
- return rt_pin_read(BUTTON_PIN_1);
- }
-
- static void button_0_callback(void *btn)
- {
- uint32_t btn_event_val;
-
- btn_event_val = get_button_event((struct button *)btn);
-
- switch(btn_event_val)
- {
- case SINGLE_CLICK:
- if (cnt_volume < 11 )
- {
- if(start_flag)
- {
- start_flag = 0;
- cnt_volume = (int)saia_volume_get()/10;
- pulse_pulse = 9000;
- }
- else
- {
- saia_volume_set(cnt_volume * 10);
- pulse_pulse = cnt_volume*9000;
- }
- }
- else
- {
- saia_volume_set(10);
- cnt_volume = 1;
- rt_kprintf("The volume has been adjusted to maximum\n");
- }
- cnt_volume ++;
- rt_kprintf("vol=%d\n", saia_volume_get());
- rt_kprintf("button 0 single click\n");
- break;
-
- case DOUBLE_CLICK:
- if (cnt_channels < 3)
- {
- saia_channels_set(cnt_channels);
- }
- else
- {
- saia_channels_set(cnt_channels);
- cnt_channels = 1;
- }
- cnt_channels++;
- rt_kprintf("button 0 double click\n");
- break;
-
- case LONG_PRESS_START:
- rt_kprintf("button 0 long press start\n");
- break;
-
- case LONG_PRESS_HOLD:
- rt_kprintf("button 0 long press hold\n");
- break;
- }
- }
-
- static void button_1_callback(void *btn)
- {
- uint32_t btn_event_val;
-
- btn_event_val = get_button_event((struct button *)btn);
-
- switch(btn_event_val)
- {
- case SINGLE_CLICK:
- wavplayer_play(table[(cnt_music++) % NUM_OF_SONGS]);
- rt_kprintf("button 1 single click\n");
- break;
-
- case DOUBLE_CLICK:
- rt_kprintf("button 1 double click\n");
- break;
-
- case LONG_PRESS_START:
- rt_kprintf("button 1 long press start\n");
- break;
-
- case LONG_PRESS_HOLD:
- rt_kprintf("button 1 long press hold\n");
- break;
- }
- }
-
- static void btn_thread_entry(void* p)
- {
- while(1)
- {
-
- rt_thread_delay(RT_TICK_PER_SECOND/200);
-
- button_ticks();
- }
- }
-
- static int multi_button_wavplayer(void)
- {
- rt_thread_t thread = RT_NULL;
-
-
- thread = rt_thread_create("btn", btn_thread_entry, RT_NULL, 512, 10, 10);
- if(thread == RT_NULL)
- {
- return RT_ERROR;
- }
- rt_thread_startup(thread);
-
-
- rt_pin_mode (BUTTON_PIN_0, PIN_MODE_INPUT_PULLUP);
- button_init (&btn_0, button_read_pin_0, PIN_LOW);
- button_attach(&btn_0, SINGLE_CLICK, button_0_callback);
- button_attach(&btn_0, DOUBLE_CLICK, button_0_callback);
- button_attach(&btn_0, LONG_PRESS_START, button_0_callback);
- button_attach(&btn_0, LONG_PRESS_HOLD, button_0_callback);
- button_start (&btn_0);
-
- rt_pin_mode (BUTTON_PIN_1, PIN_MODE_INPUT_PULLUP);
- button_init (&btn_1, button_read_pin_1, PIN_LOW);
- button_attach(&btn_1, SINGLE_CLICK, button_1_callback);
- button_attach(&btn_1, DOUBLE_CLICK, button_1_callback);
- button_attach(&btn_1, LONG_PRESS_START, button_1_callback);
- button_attach(&btn_1, LONG_PRESS_HOLD, button_1_callback);
- button_start (&btn_1);
-
- return RT_EOK;
- }
-
- MSH_CMD_EXPORT(multi_button_wavplayer, button wavplayer)
-
按键控制和串口控制差不多,只是按键有限,控制的内容就相对串口少。
SD设备没啥好讲的,值得注意的是,如果没有自动挂载设备,需要在手动挂载SD卡设备,自动挂载的代码如下:
- #include <rtthread.h>
-
- #ifdef BSP_USING_SDIO
-
- #include <dfs_elm.h>
- #include <dfs_fs.h>
- #include <dfs_posix.h>
- #include "drv_gpio.h"
-
-
- #define DBG_TAG "app.card"
- #include <rtdbg.h>
-
- void sd_mount(void *parameter)
- {
- while (1)
- {
- rt_thread_mdelay(500);
- if(rt_device_find("sd0") != RT_NULL)
- {
- if (dfs_mount("sd0", "/", "elm", 0, 0) == RT_EOK)
- {
- LOG_I("sd card mount to '/'");
- break;
- }
- else
- {
- LOG_W("sd card mount to '/' failed!");
- }
- }
- }
- }
-
- int ab32_sdcard_mount(void)
- {
- rt_thread_t tid;
-
- tid = rt_thread_create("sd_mount", sd_mount, RT_NULL,
- 1024, RT_THREAD_PRIORITY_MAX - 2, 20);
- if (tid != RT_NULL)
- {
- rt_thread_startup(tid);
- }
- else
- {
- LOG_E("create sd_mount thread err!");
- }
- return RT_EOK;
- }
- INIT_APP_EXPORT(ab32_sdcard_mount);
- #endif
好了,音乐播放器的实现代码就这些了,完整代码请根据后文提示获取。
6.4功能演示
首先,需要将普通的音乐文件转换成wav格式。推荐的软件是GoldWave。转换完成后,将音乐放入SD卡,将SD卡插入板子,接下来演示音乐播放。
值得注意的是,需用跳线帽连接 J6、J7、J9、J11,SD卡方可使用。
6.5总结
本文的音乐播放器只是抛砖引玉,改进的地方还很多,还需改进的地方如下:
1.播放模式改进,比如顺序播放、随机播放、单曲循环;
2.控制方式改进,可外接无线设备,当然也可使用板载蓝牙来控制音乐的播放;
3.音乐文件获取,如果有网络,还可播放网络上的音乐,这样播放的音乐就能多样多化。
总的来说,音乐播放器的应用还是比较简单的,当时音频驱动、SD卡的驱动以及文件系统的底层逻辑还是比较复杂的,有兴趣的可以深入去研究。
|