本帖最后由 正点原子运营 于 2023-9-21 17:51 编辑
第六十四章 USB声卡(Slave)实验
1)实验平台:正点原子探索者STM32F407开发板
2) 章节摘自【正点原子】STM32F407开发指南 V1.1
6)STM32技术交流QQ群:151941872
上一章我们向大家介绍了如何利用STM32F407的USB接口来做一个USB读卡器,本章我们将利用STM32F407的USB来做一个声卡。本章分为如下几个部分: 64.1 USB声卡简介 64.2 硬件设计 64.3 软件设计 64.4 下载验证
64.1 USB声卡简介 USB声卡依赖于USB音频类(USB Audio Class),一个像USB通用的数据接口,可以实现数字音频的数据传输。
正点原子探索者STM32F407开发板板载了一颗高性能CODEC芯片:ES8388,我们可以利用STM32F407的I2S接口,控制ES8388播放音乐,同样,如果结合STM32F407的USB功能,就可以实现一个USB声卡。
同上一章一样,我们直接移植官方的USB AUDIO例程,官方例程路径:8,STM32参考资料à1,STM32CubeF4固件包à STM32Cube_FW_F4_V1.26.0àProjectsà STM324xG_EVAL àApplicationsàUSB_DeviceàAudio_Standalone,该例程采用USB同步传输来传输音频数据流并且支持某些控制命令(比如静音控制),例程仅支持USB FS模式(不支持HS),同时例程不需要特殊的驱动支持,大多数操作系统直接就可以识别。
64.2 硬件设计 1. 例程功能本节实验功能简介:先将开发板通过USB_SLAVE连接电脑,开发板开机的时候先提示一些信息,然后开始USB配置,在配置成功之后就可以在电脑上发现多出一个声卡。我们在LCD液晶屏上显示USB的连接状态,如果连接成功,此时,我们可以通过耳机或开发板板载喇叭,可以听到来自电脑电脑的音频信号,还可以通过两个按键调节ES8388音量:按键KEY0可以增大音量,按键KEY2可以减少音量。LED0闪烁,提示程序运行。USB和电脑连接成功,LED1常亮,USB断开连接,LED1灭。
2. 硬件资源1)LED灯 LED0 – PF9 LED1 – PF10 2)独立按键 KEY0 – PE4 KEY1 – PE3 KEY2 – PE2 KEY_UP – PA0 (程序中的宏名:WK_UP) 3)串口1 (PA9/PA10连接在板载USB转串口芯片CH340上面) 4)正点原子 2.8/3.5/4.3/7寸TFTLCD模块(仅限MCU屏,16位8080并口驱动) 5)ES8388音频CODEC芯片,通过I2S驱动 6)I2S音频接口 7)USB_SLAVE接口(D-/D+连接在PA11/PA12上) 8)外部SRAM芯片,通过FSMC驱动
64.3 程序设计 62.3.1 程序流程图 64.3.2 程序解析这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。
本章,我们在实验43 音乐播放器实验的基础上修改,把不需要的文件从工程中移除,在Middlewares文件夹所在文件夹下新建一个USB的文件夹,同上一章一样,对照官方AUDIO例子,将相关文件拷贝到USB文件夹下。
然后,我们在工程里面去掉一些不必要的代码,并添加USB相关代码,最终得到如图64.3.2.1所示的工程: 1. USB驱动代码可以看到,USB部分代码,同上一章的在结构上是一模一样的,只是.c文件稍微有些变化。同样,我们移植需要修改的代码,就是USB_APP里面的这四个.c文件了。
usbd_conf.c代码,和上一章基本一样,可以用上一章的代码直接替换即可正常使用,但是需要在usbd_conf.h中定义音频频率,代码如图下。 - /* AUDIO Class Config */
- /* ST USB库目前只支持这一个采样率, 设置其他采样率无法正常工作 */
- #define USBD_AUDIO_FREQ 48000
复制代码usbd_desc.c/usbd_desc.h代码,同上一章不一样,上一章描述符是大容量存储设备,本章变成了USB声卡了,所以直接用ST官方USB Audio例程的就行。
usbd_audio_if代码,是USB声卡的接口文件,usbd_audio.c对外的所有接口函数,都在该.c文件里面,然后通过stm32f407_audio.c文件里面提供的相关函数,分别实现各个接口函数。该文件不需要做太多修改,这里我们就不做介绍了,详见本例程源码。
stm32f407_audio.c代码,是我们重点要修改的,该文件是配合USB声卡的CS43L22底层驱动相关代码,官方STM32F4xG的板子,用的是CS43L22,而我们用的是ES8388,需要修改代码,修改后代码如下: - uint8_t g_volume = 0; /* 当前音量 */
- /* USB数据输出状态,在usbd_audio.c里面定义 */
- extern uint8_t g_usbd_audio_dataout_en;
- /**
- *@brief 播放时,I2S DMA传输回调函数
- *@param 无
- *@retval 无
- */
- voidbsp_audio_i2s_dma_callback(void)
- {
- BSP_AUDIO_OUT_TransferComplete_CallBack(); /* 执行回调函数,更新数据 */
- }
- /**
- *@brief 配置音频接口
- *@param OutputDevice :输出设备选择,未用到.
- *@param Volume :音量大小, 0~100
- *@param AudioFreq :音频采样率
- *@retval 操作结果
- * @arg 0 , 成功
- * @arg 其他 , 错误代码
- */
- uint8_t BSP_AUDIO_OUT_Init(uint16_t OutputDevice, uint8_t Volume,
- uint32_t AudioFreq)
- {
- es8388_i2s_cfg(0, 3); /* 飞利浦标准,16位数据长度 */
- /*飞利浦标准,主机发送,时钟低电平有效,16位扩展帧长度 */
- i2s_init(I2S_STANDARD_PHILIPS, I2S_MODE_MASTER_TX, I2S_CPOL_LOW,
- I2S_DATAFORMAT_16B_EXTENDED);
- i2s_samplerate_set(AudioFreq); /* 设置采样率 */
- BSP_AUDIO_OUT_SetVolume(Volume); /* 设置音量 */
- /* 回调函数指 bsp_audio_i2s_dma_callback */
- i2s_tx_callback = bsp_audio_i2s_dma_callback;
-
- printf("EVAL_AUDIO_Init:%d,%d\r\n", Volume, AudioFreq);
-
- return 0;
- }
- /**
- *@brief 开始播放音频数据
- *@param pBuffer : 音频数据流首地址指针
- *@param Size :数据流大小(单位:字节)
- *@retval 操作结果
- * @arg 0 , 成功
- * @arg 其他 , 错误代码
- */
- uint8_t BSP_AUDIO_OUT_Play(uint16_t* pBuffer, uint32_t Size)
- {
- i2s_tx_dma_init((uint8_t*) pBuffer, 0, Size); /* 配置I2S TX DMA */
- I2S_TX_DMASx->CR &= ~(1 << 8); /* 单次模式 */
- I2S_TX_DMASx->CR &= ~(1 << 18); /* 改成单缓冲模式 */
- i2s_play_start(); /* 开启DMA */
-
- printf("EVAL_AUDIO_Play:%x,%d\r\n", pBuffer, Size);
-
- return 0;
- }
- /**
- *@brief 切换音频数据流buffer,每次DMA中断完成会调用该函数
- *@param pData : 音频数据流缓存首地址
- *@param Size : 音频数据流大小(单位:harf word,也就是2个字节)
- *@retval : 无
- */
- voidBSP_AUDIO_OUT_ChangeBuffer(uint16_t *pData, uint16_t Size)
- {
- if (g_usbd_audio_dataout_en== 1) /* USB有音频数据输出 */
- {
- g_usbd_audio_dataout_en = 0; /* 清除标记位 */
- }
- else if (g_usbd_audio_dataout_en== 0) /* 没有收到任何数据,全部填充0 */
- {
- my_mem_set(pData, 0, AUDIO_TOTAL_BUF_SIZE);
- g_usbd_audio_dataout_en = 2; /* 标记已经清零了buffer,停止数据输出 */
- }
- if (Size !=AUDIO_TOTAL_BUF_SIZE)
- { /* 想观察数据,可以printf,但是会有轻微的影响音质 */
- // printf("chgbuf:%x,%d\r\n",pData, Size);
- }
- I2S_TX_DMASx->CR &= ~(1 << 0); /* 关闭DMA传输 */
- while (I2S_TX_DMASx->CR & 0X1); /* 确保DMA可以被设置 */
- I2S_TX_DMASx->NDTR = Size ; /* 设置传输长度 */
- I2S_TX_DMASx->CR |= 1 << 0; /* 开启DMA传输 */
- }
- /**
- *@brief 停止播放
- *@retval 操作结果
- * @arg 0 , 成功
- * @arg 其他 , 错误代码
- */
- uint8_t BSP_AUDIO_OUT_Pause(void)
- {
- printf("EVAL_AUDIO_Stop\r\n");
- return 0;
- }
- /**
- *@brief 停止播放
- *@param Option : 未用到
- *@retval 操作结果
- * @arg 0 , 成功
- * @arg 其他 , 错误代码
- */
- uint8_t BSP_AUDIO_OUT_Stop(uint32_t Option)
- {
- printf("EVAL_AUDIO_Stop\r\n");
- return 0;
- }
- /**
- *@brief 音量设置
- *@param Volume:音量大小, 0~100
- *@retval 操作结果
- * @arg 0 , 成功
- * @arg 其他 , 错误代码
- */
- uint8_t BSP_AUDIO_OUT_SetVolume(uint8_t Volume)
- {
- g_volume = Volume;
- es8388_hpvol_set(g_volume * 0.3);
- es8388_spkvol_set(g_volume * 0.3);
- return 0;
- }
- /**
- *@brief 静音设置
- *@param Cmd : 0,不静音; 1,静音
- *@retval 操作结果
- * @arg 0 , 成功
- * @arg 其他 , 错误代码
- */
- uint8_t BSP_AUDIO_OUT_SetMute(uint32_t Cmd)
- {
- if(Cmd)
- {
- es8388_output_cfg(0, 0); /* 关闭输出 ,就是静音 */
- }else
- {
- es8388_output_cfg(1, 1); /* 打开输出 ,取消静音 */
- }
-
- printf("BSP_AUDIO_OUT_SetMute:%d\r\n", Cmd);
- return 0;
- }
复制代码本例程工作的时候,当USB连接成功,并完成识别以后,会先调用BSP_AUDIO_OUT_Init函数,设置音频接口相关参数,包括:ES8388的I2C配置、I2S配置、音量设置和I2S DMA发送完成中断回调函数等。
然后,当上电后首次有音乐输出的时候,会先调用BSP_AUDIO_OUT_Play函数,对I2S的DMA进行配置(使用单次模式,且单缓冲传输),然后启动I2S DMA传输,开始播放电脑的声音。然后,当DMA传输完成后,会自动停止DMA(单次模式),并通过bsp_audio_i2s_dma_callback函数调用BSP_AUDIO_OUT_TransferComplete_CallBack回调函数,在回调函数里面,经过处理如果有新的音频数据,则会调用BSP_AUDIO_OUT_ChangeBuffer函数,重启DMA传输,开启下一次数据DMA传送,然后DMA传输完成又会进入bsp_audio_i2s_dma_callback函数,依次循环。
这样,就实现了USB传过来的音频数据,通过I2S DMA传输出去,最终通过ES8388输出给耳机或开发板板载喇叭,实现USB声卡功能。
USB AUDIO相关代码,就给大家介绍到这里,详细的介绍,请大家参考:UM1734(STM32CubeUSB device library).pdf这个文档。
2. main.c代码下面是main.c的程序,具体如下: - USBD_HandleTypeDefUSBD_Device; /* USB Device处理结构体 */
- extern volatile uint8_t g_device_state; /* USB连接 情况 */
- extern uint8_t g_volume; /* 音量(可通过按键设置) */
- int main(void)
- {
- uint8_t key;
- uint8_t t = 0;
- uint8_t device_sta = 0xFF;
-
- HAL_Init(); /* 初始化HAL库 */
- sys_stm32_clock_init(336, 8, 2, 7); /* 设置时钟,168Mhz */
- delay_init(168); /* 延时初始化 */
- usart_init(115200); /* 串口初始化为115200 */
- led_init(); /* 初始化LED */
- lcd_init(); /* 初始化LCD */
- key_init(); /* 初始化按键 */
- sram_init(); /* 初始化外部SRAM */
-
- my_mem_init(SRAMIN); /* 初始化内部SRAM内存池 */
- my_mem_init(SRAMEX); /* 初始化外部SRAM内存池 */
- my_mem_init(SRAMCCM); /* 初始化CCM内存池 */
-
- lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
- lcd_show_string(30, 70, 200, 16, 16, "USB Sound Card TEST", RED);
- lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
- lcd_show_string(30, 110, 200, 16, 16, "KEY2:Vol- KEY0:Vol+", RED);
-
- lcd_show_string(30, 140, 200, 16, 16, "VOLUME:", BLUE); /* 提示音量 */
- /* 显示音量值 */
- lcd_show_xnum(30 + 56, 140, AUDIO_DEFAULT_VOLUME, 3, 16, 0x80, BLUE);
- /* 提示正在建立连接 */
- lcd_show_string(30, 180, 200, 16, 16, "USB Connecting...", RED);
-
- es8388_init(); /* ES8388初始化 */
- es8388_adda_cfg(1, 0); /* 开启DAC关闭ADC */
- es8388_output_cfg(1, 1); /* DAC选择通道输出 */
- es8388_hpvol_set(25); /* 设置耳机音量 */
- es8388_spkvol_set(30); /* 设置喇叭音量 */
-
- USBD_Init(&USBD_Device, &AUDIO_Desc, DEVICE_FS); /* 初始化USB */
- USBD_RegisterClass(&USBD_Device, USBD_AUDIO_CLASS); /* 添加类 */
- /* 为MSC类添加回调函数 */
- USBD_AUDIO_RegisterInterface(&USBD_Device, &USBD_AUDIO_fops);
- USBD_Start(&USBD_Device); /* 开启USB */
-
- delay_ms(1800);
- while(1)
- {
- key = key_scan(1); /* 支持连按 */
- if (key)
- {
- if (key == KEY0_PRES) /* KEY0按下,音量增加 */
- {
- g_volume++;
- if (g_volume > 100) /* 范围限定 */
- {
- g_volume = 100;
- }
- }
- else if (key == KEY2_PRES) /* KEY2按下,音量减少 */
- {
- if (g_volume) /* 范围限定 */
- {
- g_volume--;
- }
- }
- es8388_hpvol_set(g_volume * 0.3);
- es8388_spkvol_set(g_volume * 0.3);
- /* 显示音量值 */
- lcd_show_xnum(30 + 56, 140, g_volume, 3, 16, 0x80, BLUE);
- delay_ms(20);
- }
- if (device_sta != g_device_state) /* 状态改变了 */
- {
- if (g_device_state == 1)
- { /* 提示USB连接已经建立 */
- lcd_show_string(30, 180, 200, 16, 16, "USB Connected ", RED);
- LED1(0); /* LED1亮 */
- }
- else
- { /* 提示USB连接失败 */
- lcd_show_string(30, 180, 200, 16, 16, "USB DisConnected ", RED);
- LED1(1); /* LED1灭 */
- }
- device_sta = g_device_state;
- }
- delay_ms(20);
- t++;
- if (t > 10)
- {
- t = 0;
- LED0_TOGGLE(); /* 提示系统在运行 */
- }
- }
- }
复制代码此部分代码比较简单,同上一章一样定义了USBD_Device结构体,然后通过USBD_Init等函数初始化USB,不过本章实现的是USB声卡功能。本章我们保留了原例程(实验43)的USMART部分,同样可以通过串口1设置ES8388相关参数。
其他部分我们就不详细介绍了,软件设计部分就为大家介绍到这里。
64.4下载验证 在代码编译成功之后,我们通过下载代码到探索者STM32F407开发板上,在USB配置成功后(注意:USB数据线,要插在USB_SLAVE端口!不是USB_UART端口!另外,USB_HOST接口不要插任何外设!),LCD显示效果如图64.4.1所示:
此时,电脑提示发现新硬件,并自动完成驱动安装。
等USB配置成功后,LED1常亮,LED0闪烁,并且在设备管理器à声音、视频和游戏控制器里面看到多了设备,如图64.4.2所示: 图64.4.2 设备管理器找到STM32 USB声卡 然后,设置STM32AUDIO Steaming in FS Mode为电脑的默认播放设备,则电脑的所有声音都被切换到开发板输出,将耳机插入探索者STM32F407开发板的PHONE端口,即可听到来自电脑的声音,开发板板载喇叭也可以听到来自电脑的声音。通过按键KEY0/KEY2可以增大/减少音量,默认音量设置的是70,大家可以自己调节(范围:0~100)。
|