主机:WIN10 64位
设备:STM32F411+PCM5102
STM32CubeMX版本:V6.0.1
JAVA:JDK 11
______________________________________
一、硬件环境
STM32F411CEU6核心板,USB Type C直接供电
PCM5102模块,采用5V供电,I2S接口
管脚连接:
PCM5102:BCK ------> STM32F411:PA5
PCM5102:DIN ------> STM32F411:PA7
PCM5102:LCK ------> STM32F411:PA4
二、STM32CubeMX配置
1、时钟选择:采用外部晶振
2、I2S配置:半双工,只有音频播放,没有音频采集;采用48K采样率,I2S标准
3、DMA配置:I2S采用DMA进行发送数据,16bit数据宽度
4、USB配置:选择audio device class设备类型,采样率48K
5、时钟配置:外部晶振位8M
6、项目配置:注意堆栈大小设置,如果太小,导致程序运行异常,会电脑识别USB失败的现象
7、生成项目代码
二、打开项目代码
CubeMX只是生成了基础框架,在usbd_audio_if.c文件里提供好了usb audio的上层接口函数,但函数里面都是空的,需要自己添加代码实现音频数据的播放,这里我们是采用DMA把音频数据发送到I2S接口,而I2S和DMA的功能函数已经由CubeMX生成好了,我们直接调用即可。
1、把音频数据通过DMA发送到I2S
usbd_audio_if.c代码里面我们唯一需要关心的就是AUDIO_AudioCmd_FS()函数,它在收到电脑发过来的音频数据时被调用,在这个函数里我们要把音频数据通过DMA发送给I2S,需要增加如下两行代码
hi2s1是在main.c里定义的,usbd_audio_if.c在使用时要用extern声明一下。
2、DMA完成数据发送后,处理回调
我们向I2S注册两个回调函数,一个是数据发送到一半时的回调,一个是数据完全发送后的回调,首先我们要先在stm32f4xx_hal_conf.h头文件里打开注册回调的宏
#define USE_HAL_I2S_REGISTER_CALLBACKS 1U /* I2S register callback enable */
然后在main.c里面对I2S初始化时进行注册
static void MX_I2S1_Init(void)
{
/* USER CODE BEGIN I2S1_Init 0 */
/* USER CODE END I2S1_Init 0 */
/* USER CODE BEGIN I2S1_Init 1 */
/* USER CODE END I2S1_Init 1 */
hi2s1.Instance = SPI1;
hi2s1.Init.Mode = I2S_MODE_MASTER_TX;
hi2s1.Init.Standard = I2S_STANDARD_PHILIPS;
hi2s1.Init.DataFormat = I2S_DATAFORMAT_16B;
hi2s1.Init.MCLKOutput = I2S_MCLKOUTPUT_DISABLE;
hi2s1.Init.AudioFreq = I2S_AUDIOFREQ_48K;
hi2s1.Init.CPOL = I2S_CPOL_LOW;
hi2s1.Init.ClockSource = I2S_CLOCK_PLL;
hi2s1.Init.FullDuplexMode = I2S_FULLDUPLEXMODE_DISABLE;
if (HAL_I2S_Init(&hi2s1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN I2S1_Init 2 */
HAL_I2S_RegisterCallback(&hi2s1, HAL_I2S_TX_COMPLETE_CB_ID, I2S_TX_Complete_Callback);
HAL_I2S_RegisterCallback(&hi2s1, HAL_I2S_TX_HALF_COMPLETE_CB_ID, I2S_TX_HalfComplete_Callback);
/* USER CODE END I2S1_Init 2 */
}
这两个回调函数的处理很简单,只是调用一下usbd_audio_if.c的传输完成的回调函数,TransferComplete_CallBack_FS()和HalfTransfer_CallBack_FS()都是调用USBD_AUDIO_Sync()函数把后续的音频数据继续通过DMA发送到I2S。
void I2S_TX_Complete_Callback(I2S_HandleTypeDef *hi2s1)
{
TransferComplete_CallBack_FS();
}
void I2S_TX_HalfComplete_Callback(I2S_HandleTypeDef *hi2s1)
{
HalfTransfer_CallBack_FS();
}
三、编译,下载
如果硬件没有什么问题的话,下载后,把设备连接到电脑就可以正常识别的音频设备了
用bus hound可以看到设备的枚举过程
把电脑的音频播放设备切换成自己的设备
可以看到已经有音频数据发送到设备了
四、遇到的问题
1、堆栈设置的太小,导致USB识别失败
2、CubeMX生成的项目代码默认的编译优化等级为LEVEL2,在USB进行get_cur获取配置时,程序异常,进入HardFault,导致USB识别失败,暂时先把优化等级设置成0
3、硬件一开始采用杜邦线连接,播放的声音失真,是由于接触不良导致信号传输不完整,最好采用焊接方式连接。
4、间隔4秒左右,声音出现一点杂音,通过调试信息输出发现,声音正常时,每次的play size为7680字节,当出现7676时,声音就有杂音,目前这个问题还不知道是什么原因。