USB AUDIO 系列
STM32 USB AUDIO系列 (一) 48k采样率 0进2出 16bit
前言
本项目基于STM32 USB AUDIO系列 (一) 48k采样率 0进2出 16bit进行修改。
添加了2路麦克风输入,实现48k采样率,2进2出usb通道数,2字节模式,16bit。
本项目未添加I2S输入,上传到PC数据为PC下发的数据。仅通过USB OUT -> USB IN进行回环测试。
USB接收PCM音频流数据存放在缓冲区,一路用DMA通过I2S发送音频流数据到DAC芯片进行解码,最后输出模拟信号到耳机或音响等设备,另一路直接发送回到PC。
一、开发环境
开发板:正点原子STM32F407ZGT6最小系统板
芯片:STM32F407ZGT6
CubeMX版本:6.13.0
keil 5版本:V5.41.0.0
ARM Compiler:V5.06
二、工程修改
1.描述符相关修改
修改文件:usbd_audio.c
1.接口数由2更改为3(1 AUDIO CONTROL+1 AUDIO OUT接口+1 AUDIO IN接口)
2.音频控制接口描述符修改,添加了麦克风输入控制
3.按照扬声器的格式,添加麦克风的输入端子,输出端子,功能单元描述符
/* USB Mic Input Terminal Descriptor */
0x0c, /* bLength */
AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */
AUDIO_CONTROL_INPUT_TERMINAL, /* bDescriptorSubtype */
0x04, /* bTerminalID */
0x01, /* wTerminalType AUDIO_TERMINAL_USB_STREAMING 0x0101 */
0x02,
0x00, /* bAssocTerminal */
0x01, /* bNrChannels */
0x00, /* wChannelConfig 0x0000 Mono */
0x00,
0x00, /* iChannelNames */
0x00, /* iTerminal */
/* 12 byte*/
/* USB Mic Output Terminal Descriptor */
0x09, /* bLength */
AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */
AUDIO_CONTROL_OUTPUT_TERMINAL, /* bDescriptorSubtype */
0x05, /* bTerminalID */
0x01, /* wTerminalType 0x0301 */
0x01,
0x00, /* bAssocTerminal */
0x04, /* bSourceID */
0x00, /* iTerminal */
/* 09 byte */
/* USB Mic Audio Feature Unit Descriptor */
0x09, /* bLength */
AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */
AUDIO_CONTROL_FEATURE_UNIT, /* bDescriptorSubtype */
0x06, /* bUnitID */
0x04, /* bSourceID */
0x01, /* bControlSize */
AUDIO_CONTROL_MUTE, /* bmaControls(0) */
0, /* bmaControls(1) */
0x00, /* iTerminal */
4.按照扬声器的格式,添加麦克风接口,端点等描述符
/* USB Mic Standard AS Interface Descriptor - Audio Streaming Zero Bandwidth */
/* Interface 1, Alternate Setting 0 */
AUDIO_INTERFACE_DESC_SIZE, /* bLength */
USB_DESC_TYPE_INTERFACE, /* bDescriptorType */
0x02, /* bInterfaceNumber */
0x00, /* bAlternateSetting */
0x00, /* bNumEndpoints */
USB_DEVICE_CLASS_AUDIO, /* bInterfaceClass */
AUDIO_SUBCLASS_AUDIOSTREAMING, /* bInterfaceSubClass */
AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */
0x00, /* iInterface */
/* 09 byte*/
/* USB Mic Standard AS Interface Descriptor - Audio Streaming Operational */
/* Interface 1, Alternate Setting 1 */
AUDIO_INTERFACE_DESC_SIZE, /* bLength */
USB_DESC_TYPE_INTERFACE, /* bDescriptorType */
0x02, /* bInterfaceNumber */
0x01, /* bAlternateSetting */
0x01, /* bNumEndpoints */
USB_DEVICE_CLASS_AUDIO, /* bInterfaceClass */
AUDIO_SUBCLASS_AUDIOSTREAMING, /* bInterfaceSubClass */
AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */
0x00, /* iInterface */
/* 09 byte*/
/* USB Mic Audio Streaming Interface Descriptor */
AUDIO_STREAMING_INTERFACE_DESC_SIZE, /* bLength */
AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */
AUDIO_STREAMING_GENERAL, /* bDescriptorSubtype */
0x05, /* bTerminalLink */
0x01, /* bDelay */
0x01, /* wFormatTag AUDIO_FORMAT_PCM 0x0001 */
0x00,
/* 07 byte*/
/* USB Mic Audio Type III Format Interface Descriptor */
0x0B, /* bLength */
AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */
AUDIO_STREAMING_FORMAT_TYPE, /* bDescriptorSubtype */
AUDIO_FORMAT_TYPE_I, /* bFormatType */
0x02, /* bNrChannels */
0x02, /* bSubFrameSize : 2 Bytes per frame (16bits) */
16, /* bBitResolution (16-bits per sample) */
0x01, /* bSamFreqType only one frequency supported */
AUDIO_SAMPLE_FREQ(USBD_AUDIO_FREQ), /* Audio sampling frequency coded on 3 bytes */
/* 11 byte*/
/* Endpoint 1 - Standard Descriptor */
AUDIO_STANDARD_ENDPOINT_DESC_SIZE, /* bLength */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType */
AUDIO_IN_EP, /* bEndpointAddress 1 out endpoint */
USBD_EP_TYPE_ISOC, /* bmAttributes */
AUDIO_PACKET_SZE(USBD_AUDIO_FREQ), /* wMaxPacketSize in Bytes (Freq(Samples)*2(Stereo)*2(HalfWord)) */
AUDIO_FS_BINTERVAL, /* bInterval */
0x00, /* bRefresh */
0x00, /* bSynchAddress */
/* 09 byte*/
/* Endpoint - Audio Streaming Descriptor */
AUDIO_STREAMING_ENDPOINT_DESC_SIZE, /* bLength */
AUDIO_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */
AUDIO_ENDPOINT_GENERAL, /* bDescriptor */
0x00, /* bmAttributes */
0x00, /* bLockDelayUnits */
0x00, /* wLockDelay */
0x00,
/* 07 byte*/
5.修改配置描述大小
2.麦克风输入端点相关修改
修改文件:usbd_audio.c
1.定义输入端点变量
2.按照输出端点的格式,修改 USBD_AUDIO_Init 函数
static uint8_t USBD_AUDIO_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx)
{
UNUSED(cfgidx);
USBD_AUDIO_HandleTypeDef *haudio;
/* Allocate Audio structure */
haudio = (USBD_AUDIO_HandleTypeDef *)USBD_malloc(sizeof(USBD_AUDIO_HandleTypeDef));
if (haudio == NULL)
{
pdev->pClassDataCmsit[pdev->classId] = NULL;
return (uint8_t)USBD_EMEM;
}
pdev->pClassDataCmsit[pdev->classId] = (void *)haudio;
pdev->pClassData = pdev->pClassDataCmsit[pdev->classId];
#ifdef USE_USBD_COMPOSITE
/* Get the Endpoints addresses allocated for this class instance */
AUDIOOutEpAdd = USBD_CoreGetEPAdd(pdev, USBD_EP_OUT, USBD_EP_TYPE_ISOC, (uint8_t)pdev->classId);
AUDIOInEpAdd = USBD_CoreGetEPAdd(pdev, USBD_EP_IN, USBD_EP_TYPE_ISOC, (uint8_t)pdev->classId);
#endif /* USE_USBD_COMPOSITE */
if (pdev->dev_speed == USBD_SPEED_HIGH)
{
pdev->ep_out[AUDIOOutEpAdd & 0xFU].bInterval = AUDIO_HS_BINTERVAL;
pdev->ep_in[AUDIOInEpAdd & 0xFU].bInterval = AUDIO_HS_BINTERVAL;
}
else /* LOW and FULL-speed endpoints */
{
pdev->ep_out[AUDIOOutEpAdd & 0xFU].bInterval = AUDIO_FS_BINTERVAL;
pdev->ep_in[AUDIOInEpAdd & 0xFU].bInterval = AUDIO_FS_BINTERVAL;
}
/* Open EP OUT */
(void)USBD_LL_OpenEP(pdev, AUDIOOutEpAdd, USBD_EP_TYPE_ISOC, AUDIO_OUT_PACKET);
pdev->ep_out[AUDIOOutEpAdd & 0xFU].is_used = 1U;
/* Open EP IN */
(void)USBD_LL_OpenEP(pdev, AUDIOInEpAdd, USBD_EP_TYPE_ISOC, AUDIO_IN_PACKET);
pdev->ep_in[AUDIOInEpAdd & 0xFU].is_used = 1U;
haudio->alt_setting = 0U;
haudio->offset = AUDIO_OFFSET_UNKNOWN;
haudio->wr_ptr = 0U;
haudio->rd_ptr = 0U;
haudio->rd_enable = 0U;
/* Initialize the Audio output Hardware layer */
if (((USBD_AUDIO_ItfTypeDef *)pdev->pUserData[pdev->classId])->Init(USBD_AUDIO_FREQ,
AUDIO_DEFAULT_VOLUME,
0U) != 0U)
{
return (uint8_t)USBD_FAIL;
}
/* Prepare Out endpoint to receive 1st packet */
(void)USBD_LL_PrepareReceive(pdev, AUDIOOutEpAdd, haudio->buffer,
AUDIO_OUT_PACKET);
(void)USBD_LL_Transmit(pdev,AUDIOInEpAdd, haudio->buffer, AUDIO_IN_PACKET);
return (uint8_t)USBD_OK;
}
2.按照输出端点的格式,修改 USBD_AUDIO_DeInit 函数
static uint8_t USBD_AUDIO_DeInit(USBD_HandleTypeDef *pdev, uint8_t cfgidx)
{
UNUSED(cfgidx);
#ifdef USE_USBD_COMPOSITE
/* Get the Endpoints addresses allocated for this class instance */
AUDIOOutEpAdd = USBD_CoreGetEPAdd(pdev, USBD_EP_OUT, USBD_EP_TYPE_ISOC, (uint8_t)pdev->classId);
AUDIOInEpAdd = USBD_CoreGetEPAdd(pdev, USBD_EP_IN, USBD_EP_TYPE_ISOC, (uint8_t)pdev->classId);
#endif /* USE_USBD_COMPOSITE */
/* Open EP OUT */
(void)USBD_LL_CloseEP(pdev, AUDIOOutEpAdd);
pdev->ep_out[AUDIOOutEpAdd & 0xFU].is_used = 0U;
pdev->ep_out[AUDIOOutEpAdd & 0xFU].bInterval = 0U;
(void)USBD_LL_CloseEP(pdev, AUDIOInEpAdd);
pdev->ep_in[AUDIOInEpAdd & 0xFU].is_used = 0U;
pdev->ep_in[AUDIOInEpAdd & 0xFU].bInterval = 0U;
/* DeInit physical Interface components */
if (pdev->pClassDataCmsit[pdev->classId] != NULL)
{
((USBD_AUDIO_ItfTypeDef *)pdev->pUserData[pdev->classId])->DeInit(0U);
(void)USBD_free(pdev->pClassDataCmsit[pdev->classId]);
pdev->pClassDataCmsit[pdev->classId] = NULL;
pdev->pClassData = NULL;
}
return (uint8_t)USBD_OK;
}
3.在 USBD_AUDIO_DataIn 函数添加发送代码
static uint8_t USBD_AUDIO_DataIn(USBD_HandleTypeDef *pdev, uint8_t epnum)
{
USBD_AUDIO_HandleTypeDef *haudio;
haudio = (USBD_AUDIO_HandleTypeDef *)pdev->pClassDataCmsit[pdev->classId];
if (haudio == NULL)
{
return (uint8_t)USBD_FAIL;
}
if(epnum == (AUDIOInEpAdd&0xf))
{
haudio->in_rd_ptr += AUDIO_IN_PACKET;
if(haudio->in_rd_ptr >= AUDIO_TOTAL_BUF_SIZE)
haudio->in_rd_ptr = 0;
(void)USBD_LL_Transmit(pdev,AUDIOInEpAdd, &haudio->buffer[haudio->in_rd_ptr], AUDIO_IN_PACKET);
}
return USBD_OK;
}
4.在 USBD_AUDIO_IsoINIncomplete 函数添加发送代码
static uint8_t USBD_AUDIO_IsoINIncomplete(USBD_HandleTypeDef *pdev, uint8_t epnum)
{
USBD_AUDIO_HandleTypeDef *haudio;
if (pdev->pClassDataCmsit[pdev->classId] == NULL)
{
return (uint8_t)USBD_FAIL;
}
(void)USBD_LL_Transmit(pdev,epnum, &haudio->buffer[haudio->in_rd_ptr], AUDIO_IN_PACKET);
return USBD_OK;
}
5.修改输入端点和输出端点的定义
3.其他修改
修改最大接口数量
添加麦克风最大包定义,并根据需求修改包数量。这里使用默认的80
三、测试
1.枚举测试
枚举正常,显示麦克风+扬声器设备
2.录音测试
使用reaper播放1KHz 0dB进行测试。
设备选择STM32麦克风和扬声器
添加两条轨道并进行播放和录音。
查看录音收到的信号,与播放的信号一致,无异常。
四、注意事项
修改描述符后,需要卸载驱动重新连接或者更换一个电脑未连接过的VID,PID,否则可能枚举异常