打印
[其他ST产品]

STM32 USB声卡 CUBEMX配置 极简配置十分钟解决 STM32+PCM5102A

[复制链接]
366|15
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
1.综述
USB声卡无非就是USB将PCM数据给I2S外设或SAI外设通过I2S输出给DAC转为模拟信号后,连接上耳机。

就算名贵的意大利数字界面也不例外。

2.分步
2.1 USB接收音频数据
2.2 将音频数据发送给DAC
3.开工
开工前啰嗦两句:

笔者分别使用了STM32F103ZET6\STM32F411CEU\STM32F407VET6\STM32F429IGT6\STM32H743IIT6\STM32H750VBT6做了USB声卡,这些芯片在配置时USB部分几乎无差异,最大的区别在于F103\F411\F407将音频数据发送给DAC使用的是I2S外设,F429\H743\H750有SAI外设,可以选择使用SAI发送。

下面的详细介绍以STM32F411CEU为例,因为STM32F411CEU封装小,能效高,适于用于低功耗体积小且需要一定性能的场合中。相较于上面提到的其它芯片更加适合用于数字声卡中。

使用特权

评论回复
沙发
lalallalala|  楼主 | 2023-6-29 15:40 | 只看该作者
USB硬件设置



在第一步处选择Device_Only,第二步默认即可

使用特权

评论回复
板凳
lalallalala|  楼主 | 2023-6-29 15:40 | 只看该作者
USB中间件配置



第一步找到Middleware,选择USB_DEVICE,在第二步选择Audio_Device_Class,第三步实测默认直接就是这个值不需要改。

到此,USB部分设置完辽

使用特权

评论回复
地板
lalallalala|  楼主 | 2023-6-29 15:41 | 只看该作者
I2S基本设置



第一步设置为主模式。主模式全双工和半双工都行,如果想同时实现USB麦克风,且麦克风通过I2S发送到电脑时需要设置为全双工模式。之后记得选择主时钟输出(Master Clock Output)

使用特权

评论回复
5
lalallalala|  楼主 | 2023-6-29 15:43 | 只看该作者
音频数据传输DMA设置
此部分无论使用SAI还是I2S在设置时均相同。



第一步选择DMA设置,并且选择DMA请求为发送,DMA数据流只有带有DMA复用器的芯片才可以自由选择本例中芯片只能默认。其次如果数字声卡只是其中之一的功能,且有多个功能用到了DMA,需要注意DMA的优先级,实时性要求高的请提高DMA的优先级。本例中仅数字声卡故跟随默认选择了低优先级

第二步配置DMA的基本参数,DMA模式设置为循环模式,数据带宽设置为半字(与上节中I2S发送数据帧16bits相对应)

第三步配置DMA的FIFO,DMA的FIFO可以使得DMA请求封装成更大的数据块从而减少访存次数并充分利用SRAM的猝发式读写功能。该部分设置可自由发挥,本例中使用了所有FIFO并最大限度地封装成大数据块,将猝发尺寸(Brust Size)设置到了最大。注意:Brust Size不能任意改,由于FIFO深度为4个字,故传输半字数据时FIFO中最多可以装的下8个数据,故最大猝发式读取的尺寸为8

使用特权

评论回复
6
lalallalala|  楼主 | 2023-6-29 15:43 | 只看该作者
时钟配置

配置时钟使得I2S或SAI界面中的误差系数尽量小。若有缘分和我使用了相同的芯片和相同的采样率(48KHZ)可以直接参考我的设置。

使用特权

评论回复
7
lalallalala|  楼主 | 2023-6-29 15:45 | 只看该作者
误差值在哪看? 第一步选择I2S或SAI

第二步在选择的标准频率下面会显示当前的实际频率和误差,让误差(Error between Selected and Real)尽量小。此部分参数与时钟配置是直接关联的。调参数的时候请两边切换这调。

使用特权

评论回复
8
lalallalala|  楼主 | 2023-6-29 15:46 | 只看该作者
修改堆栈大小 第一步选择工程管理(Project Manager)选项卡

第二步修改堆栈,请改大,改成和我一样,因为USB功能会定义很多带有很多信息的结构体,其次数字声卡的缓冲区也是USB功能直接从堆中申请的。所以需要把堆栈调大。当然如果使用了操作系统或有自己的内存分配函数可以在缓冲区定义的位置缓冲内存分配函数

使用特权

评论回复
9
lalallalala|  楼主 | 2023-6-29 15:46 | 只看该作者
这个函数在usbd_conf.c内,源码如下

/**
  * [url=home.php?mod=space&uid=247401]@brief[/url]  Static single allocation.
  * @param  size: Size of allocated memory
  * @retval None
  */
void *USBD_static_malloc(uint32_t size)
{
  static uint32_t mem[(sizeof(USBD_AUDIO_HandleTypeDef)/4)+1];/* On 32-bit boundary */
  return mem;
}

使用特权

评论回复
10
lalallalala|  楼主 | 2023-6-29 15:47 | 只看该作者
改代码配置
USB原本是非常复杂的,代码部分原本也是不例外得复杂,但是ST官方帮我们完成了几乎所有工作,我们只需要牵线让USB和I2S认识一下就行了。

整个修改过程USB部分只需要填写usbd_audio_if.c文件中的各个功能函数和封装I2S的功能函数。

usbd_audio_if.c中,最简修改是只修改AUDIO_AudioCmd_FS函数并在I2S发生中断和半传输中断时分别调用该文件中的同步函数TransferComplete_CallBack_FS和HalfTransfer_CallBack_FS。

封装I2S的功能有I2S以DMA方式发送、DMA中断处理函数。其次也可封装DMA 的暂停、恢复、停止等功能以背后续使用。

void HAL_I2S_TxHalfCpltCallback(I2S_HandleTypeDef *hi2s)
{
        if(hi2s == &hi2s2){
                HalfTransfer_CallBack_FS();//osSemaphoreRelease(SAI3_TX_BUFF0Handle);
        }
}

void HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *hi2s)
{
        if(hi2s == &hi2s2){
                TransferComplete_CallBack_FS();//osSemaphoreRelease(SAI3_TX_BUFF1Handle);
        }
}

AudioPlayerInfo.DMA_TX_NUM);
}
void AudioDMA_Stop(void)//停止DMA结束播放
{
        HAL_I2S_DMAStop(&hi2s2);
}

void AudioDMA_Pause(void)//暂停DMA暂停播放
{
        HAL_I2S_DMAPause(&hi2s2);
}

void AudioDMA_Resume(void)//从暂停恢复播放
{
        HAL_I2S_DMAResume(&hi2s2);
}

void AudioCard_Play(uint16_t* buff, uint16_t size)//声卡模式开始播放
{
        HAL_I2S_Transmit_DMA(&hi2s2, buff, size);
}

使用特权

评论回复
11
lalallalala|  楼主 | 2023-6-29 15:48 | 只看该作者
修改usbd_audio_if.c中的AUDIO_AudioCmd_FS函数
/**
  * @brief  Handles AUDIO command.
  * @param  pbuf: Pointer to buffer of data to be sent
  * @param  size: Number of data to be sent (in bytes)
  * @param  cmd: Command opcode
  * @retval USBD_OK if all operations are OK else USBD_FAIL
  */
static int8_t AUDIO_AudioCmd_FS(uint8_t* pbuf, uint32_t size, uint8_t cmd)
{
  /* USER CODE BEGIN 2 */
  switch(cmd)
  {
    case AUDIO_CMD_START:
                AudioCard_Play((uint16_t*)pbuf, size);
    break;

    case AUDIO_CMD_PLAY:
                AudioCard_Play((uint16_t*)pbuf, size);
    break;
  }
  UNUSED(pbuf);
  UNUSED(size);
  UNUSED(cmd);
  return (USBD_OK);
  /* USER CODE END 2 */
}

使用特权

评论回复
12
lalallalala|  楼主 | 2023-6-29 15:48 | 只看该作者
到此为止就可以插到电脑上并且选择该声卡了,连接电脑后从I2S接口便能源源不断的输出数据,连接上PCM5120A插上耳机就能听歌了。由于PCM5120A只是一个DAC没有寄存器需要配置,连上就能用简单粗暴且音质好,电源噪音抑制好。在Windows环境下可以自由调节音量(虽然usbd_audio_if.c文件中指出没有调节音量的功能)。理论上这种调整音量的方式牺牲了数据精度然而在实际使用中,在听感几乎不影响。

使用特权

评论回复
13
lalallalala|  楼主 | 2023-6-29 15:53 | 只看该作者
添加静音功能
经营功能对应usbd_audio_if.c文件的AUDIO_MuteCtl_FS(uint8_t cmd)函数。该函数在设置静音和接触静音时均会调用,故需要增加标志位进行判断是静音还是接触静音。此功能可以用得着之前封装I2S功能的暂停和恢复功能。修改后代码如下

/**
  * @brief  Controls AUDIO Mute.
  * @param  cmd: command opcode
  * @retval USBD_OK if all operations are OK else USBD_FAIL
  */
static int8_t AUDIO_MuteCtl_FS(uint8_t cmd)
{
  /* USER CODE BEGIN 4 */
  static uint8_t state=0;
  if(state){
          AudioDMA_Pause();
          state = 0;
  } else{
          AudioDMA_Resume();
          state = 1;
  }
  UNUSED(cmd);
  return (USBD_OK);
  /* USER CODE END 4 */
}

使用特权

评论回复
14
lalallalala|  楼主 | 2023-6-29 15:53 | 只看该作者
踩坑
新版本的CUBEMX已经和之前有很大的区别,很多问题已经解决,但是有时候还是会阴差阳错发生一些错误。本例中,在上述配置后,编译烧写代码发现不出声音。调试在I2S传输完成函数打断点发现根本不会运行,即I2S没有持续工作。之后检查DMA寄存器发现DMA的寄存器中控制寄存器根本没有写入初始化的值。最终发现在DMA初始化时,CUBEMX没有考虑到初始化I2S时即初始化DMA,将DMA初始化的代码放到了I2S初始化的后面。



应当如图所示,将DMA的初始化改到I2S初始化的前面。

使用特权

评论回复
15
Bblythe| | 2023-12-10 07:14 | 只看该作者

输入电压端上测得的值比它实际

使用特权

评论回复
16
周半梅| | 2023-12-10 09:10 | 只看该作者

引线的长和宽影响它的电阻和电感量

使用特权

评论回复
17
Pulitzer| | 2023-12-10 10:13 | 只看该作者

它们的放置要尽可能靠近

使用特权

评论回复
18
童雨竹| | 2023-12-10 12:09 | 只看该作者

大部分单片机都带有加密锁定位或者加密字节

使用特权

评论回复
19
Wordsworth| | 2023-12-10 13:12 | 只看该作者

输入电源电流环路

使用特权

评论回复
20
Clyde011| | 2023-12-10 14:15 | 只看该作者

交流节点会引起特有的问题

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

7

主题

82

帖子

0

粉丝