目录
参考链接1:
stm32 USB系列-UAC1.0-声卡(录放)-上_哔哩哔哩_bilibili
参考链接2:
参考链接3:
I2S音频开发(使用USB音频进行验证)_i2s pdm麦克风-优快云博客
1.前言
UAC是USB Audio Class的缩写,有时也叫UAD,UAD是USB Audio Device的缩写。从名字就可以看出UAC就是USB下的音频类
在本文中关注的主要是使用UAC可以实现读取电脑音频到STM32,也可以发送麦克风的声音到电脑。由于UAC的相关描述符较多所以建议到参考链接2处仔细查看,本文重点关注如何进行编程。
2.建立模板工程(以F4为例)
本文只关注USB相关,I2S相关请关注另一篇文章:
I2S音频开发(使用USB音频进行验证)_i2s pdm麦克风-优快云博客
我们先看一下UAC设备相关的参数,
使用的端点是0x01,是一个OUT端点传输方式是同步传输。
音频频率我们刚刚设置的是16khz,工程默认的音频数据是立体声,且音频数据位数是16位即两个uint8_t数据
所以有了
所以一包数据(1ms)有16*2*2=64个数据。
2.1未完成同步 IN 传输中断详解
相比于HID设备UAC设备多了一个USBD_AUDIO_IsoOutIncomplete这个函数,是用来处理未完成的同步传输的。
static uint8_t USBD_AUDIO_IsoOutIncomplete(USBD_HandleTypeDef *pdev, uint8_t epnum)
{
USBD_AUDIO_HandleTypeDef *haudio;
if (pdev->pClassDataCmsit[pdev->classId] == NULL)
{
return (uint8_t)USBD_FAIL;
}
haudio = (USBD_AUDIO_HandleTypeDef *)pdev->pClassDataCmsit[pdev->classId];
/* Prepare Out endpoint to receive next audio packet */
(void)USBD_LL_PrepareReceive(pdev, epnum,
&haudio->buffer[haudio->wr_ptr],
AUDIO_OUT_PACKET);
return (uint8_t)USBD_OK;
}
从中可以看出这个例程对这种现象的处理是直接再次调用一下USBD_LL_PrepareReceive函数
这个函数的作用是:准备输出端点接收下一个音频数据包
如果是数据正常传输完成,则是调用的USBD_AUDIO_DataOut函数,里面也调用了USBD_LL_PrepareReceive函数用于接收。
那么为什么未完成的同步传输需要再次调用这个函数呢?
我们可以看一下ST提供的F4参考手册中的建议处理方式
由于提供的建议是针对IN端点的所以我们以IN端点的同步数据传输未完成为例进行分析
在中断函数中可以看到上面提到的未完成同步 IN 传输中断
相当于将上面的处理前5步进行完了,
然后就要接着找到端点禁止中断,只有这个中断响应了,才证明端点被禁止掉了。
我们在手册中的OTG_HS 设备端点 x 中断寄存器 (OTG_HS_DIEPINTx)
中可以找到端点禁止中断位是
然后在中断函数中就不难找到对应的处理如下所示
在前文中对此端点进行了中止操作并且刷新过了FIFO,所以理论上该端点已经有了再次发送数据的能力。从图中可以看到最后一步调用的回调函数其实就是USBD_AUDIO_IsoINIncomplete这个函数,如果这个函数也像USBD_AUDIO_IsoOutIncomplete函数那样再次调用USBD_LL_Transmit
就相当于将端点再次启动重新应用于数据传输。这也就是为什么USBD_AUDIO_IsoOutIncomplete函数中的处理仅仅是再次调用USBD_LL_PrepareReceive。(至于前面提到的禁止端点,前文中的图片中也说到了在进入端点禁止中断之前就已经将禁止位清零了所以应用程序不需要去清除禁止位。)
3.验证现象
播放音乐时可以看到软件中捕获的音频流数据,在不断刷新,并且数据包长度也和之前计算的吻合。
4.修改工程添加麦克风功能
具体如何修改?可以查看参考链接1也可以看下面我的实操。
修改之前我们可以先整理一下思路,当前工程已经可以被电脑的USB端口识别为扬声器,并能够接收到电脑发送的音频数据,所以抛开音频数据不管,理论上我们只需要修改对应的配置描述符让电脑识别单片机为麦克风即可。
4.1端点的FIFO
这里需要注意的是,在修改之前需要先确定IN端点,因为USB麦克风需要使用IN端点向电脑发送音频流数据。
在示例工程中端点分配在这个位置
MX_USB_DEVICE_Init---》USBD_Init--》USBD_LL_Init
在USBD_LL_Init这个函数的后面可以看到端点的FIFO分配情况
具体如何分配可以参考此分类专栏中的STM32USB设备库简介。
从上图我们可以看到工程中已经分配了一个序号为1的端点的TXFIFO
那么这个端点的地址就是0x81//位7为1代表是IN端点,
4.2描述符修改
当然了修改方法分两种,一种是将麦克风的描述符添加进去,保留原来的扬声器的描述符,还有一种呢就是直接将描述符替换为麦克风的。
这里我们选择添加麦克风的描述符,这样比较好验证。,并且与参考链接1的方法保持一致。
需要注意如果使用此修改方式需要将前文中在cubemx中的设置的最大接口数改为3
因为会有 :麦克风+扬声器+音频控制 共三个接口
这里仅说明需要配置描述符中需要重点关注的地方,其他的描述符可以自行了解然后改写
4.2.1配置描述符修改
变更接口数量,预期的接口:扬声器接口+控制接口+麦克风接口
4.2.2UAC 类特定音频控制接口头描述符修改
1.bLength 该接口本身的大小,固定为8+N。N即为音频接口数量
2.wTotalLength 总长度这一项也应特别注意,是此描述符的长度加上后面所有的终端描述符的长度
3.修改音频流数量以及对应音频流的接口号索引
4.2.3终端描述符修改
主要是修改为终端输入为麦克风,输出为USB流,注意终端号在描述符中必须是唯一的
4.2.4UAC类特定音频流接口描述符修改
主要需要注意bTerminalLink&#