目录
链接快速定位
前沿
1 描述符讲解
1.1 设备描述符
1.2 配置描述符
1.3 接口描述符
1.4 功能描述符
1.4.1 音频控制接口描述符(AudioControl Interface Descriptors)
1.4.2 音频流接口描述符(AudioStreaming Interface Descriptors)
1.4.3 音频流端点描述符(AudioControl Endpoint Descriptors)
1.5 端点描述符
1.6 字符串描述符
2 关键函数讲解
2.1 GPIO配置讲解
2.2 非零端点函数讲解
2.3 中断函数讲解--USB复位函数讲解
2.4 主函数讲解
3 运行演示
USB -- 初识USB协议(一)
源码下载请参考链接:USB -- STM32-FS-USB-Device驱动代码简述(二)
USB -- STM32F103虚拟串口bulk传输讲解(三)
USB -- STM32F103自定义HID设备及HID上位机中断传输讲解(四)
USB -- STM32F103 U盘(MassStorage)SDIO接口SCSI协议Bulk传输讲解(五)
USB -- STM32F103 USB DFU设备固件升级(IAP)控制传输讲解(六)
audio数据手册
前面两节主要是对USB的基本概念做了简单讲解,学习USB的最本质目的还是要回到USB的应用方向,接下来的几章主要讲解USB的各类应用,包括:
主要还是带领大家找相关的资料及相关的工具和代码。
描述符是USB能够正常通信的前提,没有描述符,USB就不知道当前是什么样的设备,所以描述符在USB整个通信过程中占有十分重要的地位,所以这里重点讲解一下USB的各类描述符。
ST的例程为我们配置好了应用的描述符,我们不需要关注也能正常运行程序,但是我们这里讲解一下怎么通过查找资料来知道具体描述符的含义。
以下是STM32F103系列的audio_speak的设备描述符代码(每个字节对应的含义可以在USB -- 初识USB协议(一)中查看):
const uint8_t Speaker_DeviceDescriptor[] =
{
SPEAKER_SIZ_DEVICE_DESC, /* bLength */
USB_DEVICE_DESCRIPTOR_TYPE, /* bDescriptorType */
0x00, /* 2.00 */ /* bcdUSB */
0x02,
0x00, /* bDeviceClass */
0x00, /* bDeviceSubClass */
0x00, /* bDeviceProtocol */
0x40, /* bMaxPacketSize 40 */
0x83, /* idVendor */
0x04,
0x30, /* idProduct = 0x5730*/
0x57,
0x00, /* 2.00 */ /* bcdDevice */
0x02,
1, /* iManufacturer */
2, /* iProduct */
3, /* iSerialNumber */
0x01 /* bNumConfigurations */
};
设备描述符顾名思义就是描述USB设备的基本信息,主要包括以下信息:
设备类型可以通过USB官网Class查询,我们这里是CDC设备,所以bDeviceClass=0x01,其他两项任意填写。
但实际这里的bDeviceClass=0x00,是因为0x00表示class可以在接口与描述符中定义。
制造商的字符串描述符索引、产品的字符串描述符索引和设备序号的字符串描述符索引主要是制造商、产品和设备序列号在字符串描述符的索引位置。
比如这里举个例子,以下三个是字符串描述符,分别是制造商描述符、产品描述符和序列号描述符:
const uint8_t Speaker_StringVendor[SPEAKER_SIZ_STRING_VENDOR] =
{
SPEAKER_SIZ_STRING_VENDOR, /* Size of manufacturer string */
USB_STRING_DESCRIPTOR_TYPE, /* bDescriptorType*/
/* Manufacturer: "STMicroelectronics" */
'S', 0, 'T', 0, 'M', 0, 'i', 0, 'c', 0, 'r', 0, 'o', 0, 'e', 0,
'l', 0, 'e', 0, 'c', 0, 't', 0, 'r', 0, 'o', 0, 'n', 0, 'i', 0,
'c', 0, 's', 0
};
const uint8_t Speaker_StringProduct[SPEAKER_SIZ_STRING_PRODUCT] =
{
SPEAKER_SIZ_STRING_PRODUCT, /* bLength */
USB_STRING_DESCRIPTOR_TYPE, /* bDescriptorType */
'S', 0, 'T', 0, 'M', 0, '3', 0, '2', 0, ' ', 0,
'S', 0, 'p', 0, 'e', 0, 'a', 0, 'k', 0, 'e', 0, 'r', 0
};
uint8_t Speaker_StringSerial[SPEAKER_SIZ_STRING_SERIAL] =
{
SPEAKER_SIZ_STRING_SERIAL, /* bLength */
USB_STRING_DESCRIPTOR_TYPE, /* bDescriptorType */
'S', 0, 'T', 0, 'M', 0, '3', 0, '2', 0
};
由下面代码可知,他们的位置也是根据索引号排列的,所以主机请求index为1的描述符,那么设备就发送制造商描述符给主机,告诉主机USB的制造商是什么。
ONE_DESCRIPTOR String_Descriptor[4] =
{
{(uint8_t*)Speaker_StringLangID, SPEAKER_SIZ_STRING_LANGID},
{(uint8_t*)Speaker_StringVendor, SPEAKER_SIZ_STRING_VENDOR},
{(uint8_t*)Speaker_StringProduct, SPEAKER_SIZ_STRING_PRODUCT},
{(uint8_t*)Speaker_StringSerial, SPEAKER_SIZ_STRING_SERIAL},
};
如果设备描述符的索引值修改了,那么String_Descriptor数组存放数据的顺序也需要相应的修改。
VID可以通过USB官网:USB_Members查看,比如这里的是ST的VID,0x0483的10进制是1155,我们在USB官网查看,如下:
配置描述符顾名思义就是描述USB设备的配置信息,主要包括以下信息:
const uint8_t Speaker_ConfigDescriptor[] =
{
0x09, /* bLength */
USB_CONFIGURATION_DESCRIPTOR_TYPE, /* bDescriptorType */
0x6D, /* wTotalLength 110 bytes*/
0x00,
0x02, /* bNumInterfaces */
0x01, /* bConfigurationValue */
0x00, /* iConfiguration */
0xC0, /* bmAttributes Self Powered*/
0x32, /* bMaxPower = 100 mA*/
}
这里的接口描述符有两个,由配置描述符指定个数。
第一个接口描述符是标准音频控制接口描述符(Standard AudioControl Interface Descriptors)。
第二个接口描述符是标准音频流接口描述符(Standard AudioStreaming Interface Descriptors),它又分为两个子类:
uint8_t Speak_InterfaceDescriptor[ ] =
{
/* USB Speaker Standard interface descriptor */
SPEAKER_SIZ_INTERFACE_DESC_SIZE, /* bLength */
USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */
0x00, /* bInterfaceNumber */
0x00, /* bAlternateSetting */
0x00, /* bNumEndpoints */
USB_DEVICE_CLASS_AUDIO, /* bInterfaceClass */
AUDIO_SUBCLASS_AUDIOCONTROL, /* bInterfaceSubClass */
AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */
0x00, /* iInterface */
。
。
。
/* USB Speaker Standard AS Interface Descriptor - Audio Streaming Zero Bandwidth */
/* Interface 1, Alternate Setting 0 */
SPEAKER_SIZ_INTERFACE_DESC_SIZE, /* bLength */
USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */
0x01, /* bInterfaceNumber */
0x00, /* bAlternateSetting */
0x00, /* bNumEndpoints */
USB_DEVICE_CLASS_AUDIO, /* bInterfaceClass */
AUDIO_SUBCLASS_AUDIOSTREAMING, /* bInterfaceSubClass */
AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */
0x00, /* iInterface */
/* 09 byte*/
/* USB Speaker Standard AS Interface Descriptor - Audio Streaming Operational */
/* Interface 1, Alternate Setting 1 */
SPEAKER_SIZ_INTERFACE_DESC_SIZE, /* bLength */
USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */
0x01, /* bInterfaceNumber */
0x01, /* bAlternateSetting */
0x01, /* bNumEndpoints */
USB_DEVICE_CLASS_AUDIO, /* bInterfaceClass */
AUDIO_SUBCLASS_AUDIOSTREAMING, /* bInterfaceSubClass */
AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */
0x00, /* iInterface */
}
首先下载audio数据手册,打开《audio10.pdf》文件,找到4.3.1小节,这里有对接口描述符的全部解释,由手册可知,bInterfaceSubClass为Abstract Control Model,bInterfaceProtocol为Common AT commands,其它位也相同,查找手册,找出对应的解释.
USB功能描述符这里分为三个部分:
打开《audio10.pdf》文件,在4.3.2章节查看具体描述符的具体含义,这里不做详细说明,音频本身涉及的问题比较复杂,这里只是针对USB讲解,不过多的讲解音频的具体协议。
uint8_t Speak_FunctionalDescriptor[ ] =
{
/* USB Speaker Class-specific AC Interface Descriptor */
SPEAKER_SIZ_INTERFACE_DESC_SIZE, /* bLength */
AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */
AUDIO_CONTROL_HEADER, /* bDescriptorSubtype */
0x00, /* 1.00 */ /* bcdADC */
0x01,
0x27, /* wTotalLength = 39*/
0x00,
0x01, /* bInCollection */
0x01, /* baInterfaceNr */
/* 09 byte*/
/* USB Speaker Input Terminal Descriptor */
AUDIO_INPUT_TERMINAL_DESC_SIZE, /* bLength */
AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */
AUDIO_CONTROL_INPUT_TERMINAL, /* bDescriptorSubtype */
0x01, /* bTerminalID */
0x01, /* wTerminalType AUDIO_TERMINAL_USB_STREAMING 0x0101 */
0x01,
0x00, /* bAssocTerminal */
0x01, /* bNrChannels */
0x00, /* wChannelConfig 0x0000 Mono */
0x00,
0x00, /* iChannelNames */
0x00, /* iTerminal */
/* 12 byte*/
/* USB Speaker Audio Feature Unit Descriptor */
0x09, /* bLength */
AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */
AUDIO_CONTROL_FEATURE_UNIT, /* bDescriptorSubtype */
0x02, /* bUnitID */
0x01, /* bSourceID */
0x01, /* bControlSize */
AUDIO_CONTROL_MUTE, /* bmaControls(0) */
0x00, /* bmaControls(1) */
0x00, /* iTerminal */
/* 09 byte*/
/*USB Speaker Output Terminal Descriptor */
0x09, /* bLength */
AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */
AUDIO_CONTROL_OUTPUT_TERMINAL, /* bDescriptorSubtype */
0x03, /* bTerminalID */
0x01, /* wTerminalType 0x0301*/
0x03,
0x00, /* bAssocTerminal */
0x02, /* bSourceID */
0x00, /* iTerminal */
/* 09 byte*/
}
uint8_t Speak_FunctionalDescriptor[ ] =
{
/* USB Speaker Audio Streaming Interface Descriptor */
AUDIO_STREAMING_INTERFACE_DESC_SIZE, /* bLength */
AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */
AUDIO_STREAMING_GENERAL, /* bDescriptorSubtype */
0x01, /* bTerminalLink */
0x01, /* bDelay */
0x02, /* wFormatTag AUDIO_FORMAT_PCM8 0x0002*/
0x00,
/* 07 byte*/
/* USB Speaker Audio Type I Format Interface Descriptor */
0x0B, /* bLength */
AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */
AUDIO_STREAMING_FORMAT_TYPE, /* bDescriptorSubtype */
AUDIO_FORMAT_TYPE_I, /* bFormatType */
0x01, /* bNrChannels */
0x01, /* bSubFrameSize */
8, /* bBitResolution */
0x01, /* bSamFreqType */
0xF0, /* tSamFreq 22000 = 0x55F0 */
0x55,
0x00,
/* 11 byte*/
}
uint8_t Speak_FunctionalDescriptor[ ] =
{
/* 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*/
}
这里用到1个端点,具体的解释可参见USB -- 初识USB协议(一)。
uint8_t Speak_EndpointDescriptor[ ] =
{
/* Endpoint 1 - Standard Descriptor */
AUDIO_STANDARD_ENDPOINT_DESC_SIZE, /* bLength */
USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */
0x01, /* bEndpointAddress 1 out endpoint*/
USB_ENDPOINT_TYPE_ISOCHRONOUS, /* bmAttributes */
0x16, /* wMaxPacketSize 22 bytes*/
0x00,
0x01, /* bInterval */
0x00, /* bRefresh */
0x00, /* bSynchAddress */
/* 09 byte*/
}
在设备描述符中已经对制造商描述符、产品描述符和设备序号描述符做了说明,这里仅对语言ID描述符做简单说明。
语言ID描述符相对很简单,就是告诉主机用的哪种语言编码,这里一般选择0x0409,使用美国的编码。一般使用最多的也是美国编码。
/* USB String Descriptor (optional) */
const uint8_t Speaker_StringLangID[SPEAKER_SIZ_STRING_LANGID] =
{
0x04,
0x03,
0x09,
0x04
}
; /* LangID = 0x0409: U.S. English */
这里对USB的D+外部上拉电阻进行了配置,如果读者使用的开发板和ST官网的外部上拉不一致,需要修改这个位置的IO口定义。这里也需要注意,ST开发板使用的是PNP型三极管作为上拉电阻的使能信号,所以这里GPIO输出低D+上拉,GPIO输出高D+为低。
非0端点传输函数主要在“usb_endp.c”文件中实现,我们打开usb_endp.c文件,看到只有1个函数,因为是Speak,所以是OUT传输,并且同步传输不回ACK,类似于UDP协议。
USB复位函数十分的重要,因为在USB主机识别到设备的时候,首先发出复位指令,USB设备收到复位指令之后,会去初始化一些使用到的端点(端点0必须初始化),然后再通过相应的端点实现设备与主机间的通信。
下图是利用DSVIEW抓取的USB枚举过程的波形,每次在USB设备枚举之前,都会产生一个复位信号。
下面代码是USB复位信号来临之后,USB所做的一些事情,主要就是初始化了端点的状态,为后续枚举过程做准备,这里把端点1配置为双buffer模式,为了提高传输效率,后面有机会可以给大家讲解。
/*******************************************************************************
* Function Name : Speaker_Reset.
* Description : Speaker reset routine.
* Input : None.
* Output : None.
* Return : None.
*******************************************************************************/
void Speaker_Reset()
{
/* Set Speaker device as not configured state */
pInformation->Current_Configuration = 0;
/* Current Feature initialization */
pInformation->Current_Feature = Speaker_ConfigDescriptor[7];
SetBTABLE(BTABLE_ADDRESS);
/* Initialize Endpoint 0 */
SetEPType(ENDP0, EP_CONTROL);
SetEPTxStatus(ENDP0, EP_TX_NAK);
SetEPRxAddr(ENDP0, ENDP0_RXADDR);
SetEPRxCount(ENDP0, Device_Property.MaxPacketSize);
SetEPTxAddr(ENDP0, ENDP0_TXADDR);
Clear_Status_Out(ENDP0);
SetEPRxValid(ENDP0);
/* Initialize Endpoint 1 */
SetEPType(ENDP1, EP_ISOCHRONOUS);
SetEPDblBuffAddr(ENDP1, ENDP1_BUF0Addr, ENDP1_BUF1Addr);
SetEPDblBuffCount(ENDP1, EP_DBUF_OUT, 0x40);
ClearDTOG_RX(ENDP1);
ClearDTOG_TX(ENDP1);
ToggleDTOG_TX(ENDP1);
SetEPRxStatus(ENDP1, EP_RX_VALID);
SetEPTxStatus(ENDP1, EP_TX_DIS);
SetEPRxValid(ENDP0);
/* Set this device to response on default address */
SetDeviceAddress(0);
bDeviceState = ATTACHED;
In_Data_Offset = 0;
Out_Data_Offset = 0;
}
主函数代码相对简单,就是实现了收到的数据然后发送出去和音频芯片的初始化,初始化ST已经为我们做好了,如果想进一步了解需要自行下载AK4343芯片手册,并查看相应的寄存器位的功能,并配置,音频芯片使用的是AK4343芯片。
/*******************************************************************************
* Function Name : main.
* Description : Main routine.
* Input : None.
* Output : None.
* Return : None.
*******************************************************************************/
int main(void)
{
Set_System();
Set_USBClock();
USB_Config();
USB_Init();
Speaker_Config();
while (1)
{}
}
这里附图一张ST官网的AK4343的原理图。
但是经过我们的实际测试,如果接耳机,ST官网提供的原理图是不能识别到声音的,所以需要修改一下原理图,修改的原理图如下图所示,主要修改了输出声道的串联电容,把它去掉了。
1. 正确下载程序,能够在设备管理器查看到Speak设备。
2. 打开音乐播放器,并且禁止所有增强效果。
3. 通过声音属性查看STM32 Speak正在播放音乐,并且逻辑分析仪住区波形也有数据(注意查看这里的OUT包没有ACK应答)。
4. 抓取USB波形每包数据为22字节,这是因为设置的频率是22000Hz,1个SOF是1ms,22000Hz就是1ms传输22个字节,所以每个SOF传输是22字节。
接下来讲解STM32 USB AUDIO Microphone同步传输(In传输)通信实验,敬请期待。。。