《APM32芯得》系列內容為用戶使用APM32系列產品的經驗總結,均轉載自21ic論壇極海半導體專區,全文未作任何修改,未經原文作者授權禁止轉載。
最近在編寫DMA_ADC例程的過程中出現了一個中斷配置的問題,在ADC采集過程中,結合手冊進行ADC連續轉換模式配置采集,手冊上給出需要進行中斷配置的信息,但是真實情況不需要進行中斷配置也可以進行ADC連續轉換采集,因此,我沒過濾掉ADC采集中開啟中斷配置的信息,開啟了ADC中斷采集,因此這次以APM32F411官方例程中的DMA_ADC例程,復刻了此次出現的問題。
APM32F103具有USB全速接口,可以做USB從機的多種功能。USB協議中為了提供對多樣設備的支持,定義了許多外部設備子類,常見的包括:
人機交互類設備HID(Human Interface Device)
通信類設備CDC(Communicate Device Class)
大容量存儲設備MSC(Mass Storage Class)
視頻類設備UVC(USB Video Class)
音頻類設備UAC(USB Audio Class)
HID (Human Interface Device)是一種用于連接人機交互設備的USB設備類別。它定義了一組通用的協議和規范,用于支持鍵盤、鼠標、游戲控制器等各種輸入設備的連接和交互。
CDC(Communication Device Class)是USB組織定義的一類專門給各種通信設備(電信通信設備和中速網絡通訊設備)使用的USB子類,常用于虛擬串口。
USB大容量存儲設備類(The USB mass storage device class)是一種計算機和移動設備之間的傳輸協議,它允許一個通用串行總線(USB)設備來訪問主機的計算設備,使兩者之間進行文件傳輸,常用于存儲器讀寫和模擬U盤。
SDK中提供的例程就有虛擬串口CDC、鼠標HID、模擬U盤MSC的功能。
除了這些USB做單項功能的,USB還可以配置復合設備,能同時集成了多個不同類型的外設,可以實現多個功能的同時使用
接下來就嘗試在極海SDK中增加HID KeyBoard+CDC 虛擬串口的復合設備配置,修改就基于極海APM32F10x_SDK_V1.8中已有的USB_CDC_VirtualCOMPort例程,就不用重新配置虛擬串口部分了。
實現要對USB的描述符進行修改,能讓電腦識別出來是個什么設備。
1、USB設備描述符
重點是bDeviceClass改成0xEF,告訴電腦這是個復合設備。
const uint8_t g_usbDeviceDescriptor[USB_DEVICE_DESCRIPTOR_SIZE] =
{
0x12, /*bLength:長度,設備描述符的長度為18字節*/
USBD_DESC_DEVICE, /*bDescriptorType*/
0x00,0x02, /*bcdUSB = 2.00 */
0xEF, /*bDeviceClass*/
0x02, /*bDeviceSubClass*/
0x01, /*bDeviceProtocol*/
0x40, /*bMaxPacketSize40---------------------------*/
0x83,0x05, /*idVendor (0x0583)*/
0x50,0x57, /*idProduct = 0x5750*/
0x00, /*bcdDevice rel. 2.00*/
0x02,
1, /*Index of string descriptor describing
manufacturer */
2, /*Index of string descriptor describing
product*/
3, /*Index of string descriptor describing the
device serial number */
0x01 /*bNumConfigurations*/
};
2、USB配置描述符
這里配置USB的配置描述符、接口描述符和端點描述符。
HID鍵盤使用兩個端點,端點4(IN)和端點4(OUT)
VCP虛擬串口使用三個端點,端點1(IN)、端點1(OUT)和端點2(IN)
const uint8_t g_usbConfigDescriptor[USB_CONFIG_DESCRIPTOR_SIZE] =
{
0x09, /* bLength: Configuration Descriptor size */
USBD_DESC_CONFIGURATION, /* bDescriptorType: Configuration */
USB_CONFIG_DESCRIPTOR_SIZE,
/* wTotalLength: Bytes returned */
0x00,
0x03, /* bNumInterfaces: 1 interface */
0x01, /* bConfigurationValue: Configuration value */
0x00, /* iConfiguration: Index of string descriptor describing
the configuration*/
0xC0, /* bmAttributes: Self powered */
0x32, /* MaxPower 100 mA: this current is used for detecting Vbus */
/*************************************功能1 HID鍵盤**************************************/
/*IAD描述符*/
0x08, //bLength:IAD描述符大小
0x0B, //bDescriptorType:IAD描述符類型
0x00, //bFirstInterface:功能1 HID鍵盤的第一個接口描述符是在總的配置描述符中的第幾個從0開始數
0x01, //bInferfaceCount:功能1 HID鍵盤有1個接口描述符
0x03, //bFunctionClass:同單HID功能時,設備符中的bDeviceClass
0x00, //bFunctionSubClass:同單HID功能時,設備符中的bDeviceSubClass
0x01, //bFunctionProtocol:同單HID功能時,設備符中的bDeviceProtocol
0x00, //iFunction:字符串描述中關于此設備的索引(個人理解是一個字符串描述符中有比如0~5是功能1的字符串,
//6~10是功能2的字符串,如果是功能2的話,此值為6)
/************** Descriptor of Custom HID interface ****************/
/* 09 */
0x09, /* bLength: Interface Descriptor size */
USBD_DESC_INTERFACE,/* bDescriptorType: Interface descriptor type */
0x00, /* bInterfaceNumber: Number of Interface */
0x00, /* bAlternateSetting: Alternate setting */
0x02, /* bNumEndpoints */
0x03, /* bInterfaceClass: HID */
0x01, /* bInterfaceSubClass : 1=BOOT, 0=no boot */
0x01, /* nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse */
0, /* iInterface: Index of string descriptor */
/******************** Descriptor of Custom HID HID ********************/
/* 18 */
0x09, /* bLength: HID Descriptor size */
0x21, /* bDescriptorType: HID */
0x10, /* bcdHID: HID Class Spec release number */
0x01,
0x00, /* bCountryCode: Hardware target country */
0x01, /* bNumDescriptors: Number of HID class descriptors to follow */
0x22, /* bDescriptorType */
KEYBOARD_SIZ_REPORT_DESC,
//KEYBOARD_SIZ_REPORT_DESC,/* wItemLength: Total length of Report descriptor */
0x00,
/******************** Descriptor of Custom HID endpoints ******************/
/* 27 */
0x07, /* bLength: Endpoint Descriptor size */
USBD_DESC_ENDPOINT, /* bDescriptorType: */
0x84, /* bEndpointAddress: Endpoint Address (IN) */
0x03, /* bmAttributes: Interrupt endpoint */
0x08, /* wMaxPacketSize: 8 Bytes max */
0x00,
0x20, /* bInterval: Polling Interval (32 ms) */
/* 34 */
0x07, /* bLength: Endpoint Descriptor size */
USBD_DESC_ENDPOINT, /* bDescriptorType: */
/* Endpoint descriptor type */
0x04, /* bEndpointAddress: */
/* Endpoint Address (OUT) */
0x03, /* bmAttributes: Interrupt endpoint */
0x01, /* wMaxPacketSize: 2 Bytes max */
0x00,
0x20, /* bInterval: Polling Interval (20 ms) */
/* 41 */
/********************************功能2 VCP虛擬串口*****************************/
/*IAD描述符*/
/* Interface Association Descriptor(IAD Descriptor) */
0x08, /* bLength */
0x0B, /* bDescriptorType*/
0x01, /* bFirstInterface*/
0x02, /* bInterfaceCount*/
0x02, /* bFunctionClass --CDC*/
0x02, /* bFunctionSubClass*/
0x01, /* bFunctionProtocoll*/
0x00, /* iFunction */
/**VCP虛擬串口**/
/*Interface Descriptor接口描述符*/
0x09, /* bLength: Interface Descriptor size */
USBD_DESC_INTERFACE, /* bDescriptorType: Interface */
/* Interface descriptor type */
0x01, /* bInterfaceNumber: Number of Interface */ //<接口 1>
0x00, /* bAlternateSetting: Alternate setting */
0x01, /* bNumEndpoints: One endpoints used 該接口非0端點數*/
0x02, /* bInterfaceClass: Communication Interface Class */
0x02, /* bInterfaceSubClass: Abstract Control Model */
0x01, /* bInterfaceProtocol: Common AT commands */
0x00, /* iInterface: */
/*Header Functional Descriptor類描述符*/
0x05, /* bLength: Endpoint Descriptor size */
0x24, /* bDescriptorType: CS_INTERFACE */
0x00, /* bDescriptorSubtype: Header Func Desc */
0x10, /* bcdCDC: spec release number */
0x01,
/*Call Management Functional Descriptor*/
0x05, /* bFunctionLength */
0x24, /* bDescriptorType: CS_INTERFACE */
0x01, /* bDescriptorSubtype: Call Management Func Desc */
0x00, /* bmCapabilities: D0+D1 */
0x01, /* bDataInterface: 1 */
/*ACM Functional Descriptor*/
0x04, /* bFunctionLength */
0x24, /* bDescriptorType: CS_INTERFACE */
0x02, /* bDescriptorSubtype: Abstract Control Management desc */
0x02, /* bmCapabilities */
/*Union Functional Descriptor*/
0x05, /* bFunctionLength */
0x24, /* bDescriptorType: CS_INTERFACE */
0x06, /* bDescriptorSubtype: Union func desc */
0x00, /* bMasterInterface: Communication class interface */
0x01, /* bSlaveInterface0: Data Class Interface */
/*Endpoint 2 Descriptor端點描述符*/
0x07, /* bLength: Endpoint Descriptor size */
USBD_DESC_ENDPOINT, /* bDescriptorType: Endpoint */
0x82, /* bEndpointAddress: (IN2) */
0x03, /* bmAttributes: Interrupt */
VIRTUAL_COM_PORT_INT_SIZE, /* wMaxPacketSize: */
0x00,
0xFF, /* bInterval: */
/*Data class interface descriptor類描述符*/
0x09, /* bLength: Endpoint Descriptor size */
USBD_DESC_INTERFACE, /* bDescriptorType: */
0x02, /* bInterfaceNumber: Number of Interface */
0x00, /* bAlternateSetting: Alternate setting */
0x02, /* bNumEndpoints: Two endpoints used */
0x0A, /* bInterfaceClass: CDC */
0x00, /* bInterfaceSubClass: */
0x00, /* bInterfaceProtocol: */
0x00, /* iInterface: */
/*Endpoint 3 Descriptor端點描述符*/
0x07, /* bLength: Endpoint Descriptor size */
USBD_DESC_ENDPOINT, /* bDescriptorType: Endpoint */
0x01, /* bEndpointAddress: (OUT1) */
0x02, /* bmAttributes: Bulk */
VIRTUAL_COM_PORT_DATA_SIZE, /* wMaxPacketSize: */
0x00,
0x00, /* bInterval: ignore for Bulk transfer */
/*Endpoint 1 Descriptor 端點描述符*/
0x07, /* bLength: Endpoint Descriptor size */
USBD_DESC_ENDPOINT, /* bDescriptorType: Endpoint */
0x81, /* bEndpointAddress: (IN1) */
0x02, /* bmAttributes: Bulk */
VIRTUAL_COM_PORT_DATA_SIZE, /* wMaxPacketSize: */
0x00,
0x00 /* bInterval */
};
3、USB HID報告描述符
const uint8_t s_hidKeyboardReportDescriptor[HID_REPORT_DESCRIPTOR_SIZE] =
{
/*short Item D7~D4:bTag;D3~D2:bType;D1~D0:bSize
**bTag ---主條目 1000:輸入(Input) 1001:輸出(Output) 1011:特性(Feature) 1010:集合(Collection) 1100:關集合(End Collection)
** 全局條目 0000:用途頁(Usage Page) 0001:邏輯最小值(Logical Minimum) 0010:邏輯最大值(Logical Maximum) 0011:物理最小值(Physical Minimum)
** 0100:物理最大值(Physical Maximum) 0101:單元指數(Unit Exponet) 0110:單元(Unit) 0111:數據域大小(Report Size)
** 1000:報告ID(Report ID) 1001:數據域數量(Report Count) 1010:壓棧(Push) 1011:出棧(Pop) 1100~1111:保留(Reserved)
** 局部條目 0000:用途(Usage) 0001:用途最小值(Usage Minimum) 0010:用途最大值(Usage Maximum) 0011:標識符索引(Designator Index)
** 0100:標識符最小值(Designator Minimum) 0101:標識符最大值(Designator Maximum) 0111:字符串索引(String Index) 1000:字符串最小值(String Minimum)
** 1001:字符串最大值(String Maximum) 1010:分隔符(Delimiter) 其他:保留(Reserved)
**bType---00:主條目(main) 01:全局條目(globle) 10:局部條目(local) 11:保留(reserved)
**bSize---00:0字節 01:1字節 10:2字節 11:4字節*/
//0x05:0000 01 01 這是個全局條目,用途頁選擇為普通桌面頁
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
//0x09:0000 10 01 這是個全局條目,用途選擇為鍵盤
0x09, 0x06, // USAGE (Keyboard)
//0xa1:1010 00 01 這是個主條目,選擇為應用集合,
0xa1, 0x01, // COLLECTION (Application)
//0x05:0000 01 11 這是個全局條目,用途頁選擇為鍵盤/按鍵
0x05, 0x07, // USAGE_PAGE (Keyboard/Keypad)
//0x19:0001 10 01 這是個局部條目,用途的最小值為0xe0,對應鍵盤上的左ctrl鍵
0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)
//0x29:0010 10 01 這是個局部條目,用途的最大值為0xe7,對應鍵盤上的有GUI(WIN)鍵
0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)
//0x15:0001 01 01 這是個全局條目,說明數據的邏輯值最小值為0
0x15, 0x00, // LOGICAL_MINIMUM (0)
//0x25:0010 01 01 這是個全局條目,說明數據的邏輯值最大值為1
0x25, 0x01, // LOGICAL_MAXIMUM (1)
//0x95:1001 01 01 這是個全局條目,數據域的數量為8個
0x95, 0x08, // REPORT_COUNT (8)
//0x75:0111 01 01 這是個全局條目,每個數據域的長度為1位
0x75, 0x01, // REPORT_SIZE (1)
//0x81:1000 00 01 這是個主條目,有8*1bit數據域作為輸入,屬性為:Data,Var,Abs
0x81, 0x02, // INPUT (Data,Var,Abs)
//0x95:1001 01 01 這是個全局條目,數據域的數量為1個
0x95, 0x01, // REPORT_COUNT (1)
//0x75:0111 01 01 這是個全局條目,每個數據域的長度為8位
0x75, 0x08, // REPORT_SIZE (8)
//0x81:1000 00 01 這是個主條目,有1*8bit數據域作為輸入,屬性為:Cnst,Var,Abs
0x81, 0x03, // INPUT (Cnst,Var,Abs)
//0x95:1001 01 01 這是個全局條目,數據域的數量為6個
0x95, 0x06, // REPORT_COUNT (6)
//0x75:0111 01 01 這是個全局條目,每個數據域的長度為8位
0x75, 0x08, // REPORT_SIZE (8)
//0x25:0010 01 01 這是個全局條目,邏輯最大值為255
0x25, 0xFF, // LOGICAL_MAXIMUM (255)
//0x19:0001 10 01 這是個局部條目,用途的最小值為0
0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))
//0x29:0010 10 01 這是個局部條目,用途的最大值為0x65
0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application)
//0x81:1000 00 01 這是個主條目,有6*8bit的數據域作為輸入,屬相為屬性為:Data,Var,Abs
0x81, 0x00, // INPUT (Data,Ary,Abs)
//0x25:0010 01 01 這是個全局條目,邏輯的最大值為1
0x25, 0x01, // LOGICAL_MAXIMUM (1)
//0x95:1001 01 01 這是個全局條目,數據域的數量為2
0x95, 0x02, // REPORT_COUNT (2)
//0x75:0111 01 01 這是個全局條目,每個數據域的長度為1位
0x75, 0x01, // REPORT_SIZE (1)
//0x05:0000 01 01 這是個全局條目,用途頁選擇為LED頁
0x05, 0x08, // USAGE_PAGE (LEDs)
//0x19:0001 10 01 這是個局部條目,用途的最小值為0x01,對應鍵盤上的Num Lock
0x19, 0x01, // USAGE_MINIMUM (Num Lock)
//0x29:0010 10 01 這是個局部條目,用途的最大值為0x02,對應鍵盤上的Caps Lock
0x29, 0x02, // USAGE_MAXIMUM (Caps Lock)
//0x91:1001 00 01 這是個主條目,有2*1bit的數據域作為輸出,屬性為:Data,Var,Abs
0x91, 0x02, // OUTPUT (Data,Var,Abs)
//0x95:1001 01 01 這是個全局條目,數據域的數量為1個
0x95, 0x01, // REPORT_COUNT (1)
//0x75:0111 01 01 這是個全局條目,每個數據域的長度為6bit,正好與前面的2bit組成1字節
0x75, 0x06, // REPORT_SIZE (6)
//0x91:1001 00 01 這是個主條目,有1*6bit數據域最為輸出,屬性為:Cnst,Var,Abs
0x91, 0x03, // OUTPUT (Cnst,Var,Abs)
0xc0 // END_COLLECTION
};
4、修改端點緩存地址
在 usb_config.h中,調整每個USB端點分配的地址。
#define ENDP0_RXADDR (0x40)
#define ENDP0_TXADDR (0x80)
#define ENDP1_TXADDR (0xC0)
#define ENDP1_RXADDR (0x0F0)
#define ENDP2_TXADDR (0x110)
#define ENDP4_RXADDR (0x150)
#define ENDP4_TXADDR (0x190)
這時候就完成基本的配置,可以讓電腦識別到鍵盤和虛擬串口的設備,但不能正常使用。
接下來就是代碼初始化部分的修改。
基于本來虛擬串口的初始化增加HID配置
void CDC_Init(void)
{
USBD_InitParam_T usbParam;
USBD_InitParamStructInit(&usbParam);
usbParam.classReqHandler = USBD_ClassHandler;
usbParam.stdReqExceptionHandler = KeyBoard_ReportDescriptor;//增加HID配置
usbParam.resetHandler = VCP_Reset;
usbParam.inEpHandler = USBD_VCP_InEpCallback;
usbParam.outEpHandler = USBD_VCP_OutEpCallback;
usbParam.pDeviceDesc = (USBD_Descriptor_T *)&g_deviceDescriptor;
usbParam.pConfigurationDesc = (USBD_Descriptor_T *)&g_configDescriptor;
usbParam.pStringDesc = (USBD_Descriptor_T *)g_stringDescriptor;
usbParam.pStdReqCallback = &stdReqCallback;
USBD_Init(&usbParam);
}
void KeyBoard_ReportDescriptor(USBD_DevReqData_T *reqData)
{
uint8_t len;
if((reqData->byte.bRequest == USBD_GET_DESCRIPTOR) &&
(reqData->byte.bmRequestType.bit.recipient == USBD_RECIPIENT_INTERFACE) &&
(reqData->byte.bmRequestType.bit.type == USBD_REQ_TYPE_STANDARD))
{
if(reqData->byte.wValue[1] == 0x21)
{
len = USB_MIN(reqData->byte.wLength[0], 9);
USBD_CtrlInData((uint8_t *)&g_configDescriptor.pDesc[0x12], len);
}
else if(reqData->byte.wValue[1] == 0x22)
{
len = USB_MIN(reqData->byte.wLength[0], g_ReportDescriptor.size);
USBD_CtrlInData((uint8_t *)g_ReportDescriptor.pDesc, len);
}
}
else
{
USBD_SetEPTxRxStatus(USBD_EP_0, USBD_EP_STATUS_STALL, USBD_EP_STATUS_STALL);
}
}
到此配置部分基本完成,接下來到main中增加段測試代碼,驗證USB配置是否正確。
配置虛擬串口一直打印dataBuf[5]= {0xaa,0xbb,0xcc,0xdd,0xff};
HID鍵盤根據鍵值表輸出數字1
void Delay(uint32_t i)
{
while(i--);
}
unsigned char dataBuf[5]= {0xaa,0xbb,0xcc,0xdd,0xff};
uint8_t Keyboad_Buf[8]={0,0,0x1E,0,0,0,0,0};
int main(void)
{
CDC_Init();
while(1)
{
USBD_TxData(USBD_EP_1, dataBuf, 5);
USBD_TxData(USBD_EP_4, Keyboad_Buf, 8);
Delay(0xFFFFFF);
}
}

燒錄代碼到開發板,接上USB線到電腦,可以看到Bushound能看到配置的復合設備。

使用串口助手能看到虛擬串口能正確打印我們的數據,鍵盤也正常在發送數字1,證明代碼配置沒問題,兩個功能使用一個USB接口實現了。SDK的虛擬串口也有做接收回傳的功能,可以直接使用。
到此鍵盤+虛擬串口的例程就簡單修改完成了。
注:文章作者在原帖中提供了例程文件,有需要請至原文21ic論壇下載
原文地址:https://bbs.21ic.com/icview-3321226-1-1.html
-
usb
+關注
關注
60文章
8438瀏覽量
284434 -
adc
+關注
關注
100文章
7511瀏覽量
555902 -
鍵盤
+關注
關注
4文章
872瀏覽量
41881 -
虛擬串口
+關注
關注
3文章
64瀏覽量
14574
原文標題:APM32芯得 EP.62 | 基于APM32F103的USB鍵盤與虛擬串口復合設備配置詳解
文章出處:【微信號:geehysemi,微信公眾號:Geehy極海半導體】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
新品發布丨極海半導體推出工業級標準型APM32S103系列MCU
APMEMIC/APM32F103系列
關于APM32F1與Sxx32F1的兼容性問題
國產品牌完美替代STM32F103系列產品簡介
基于APM32F103開發板無法點亮燈是何原因
艾派克APM32F103系列MCU,助力產品性能優勢更大化!
stm32f103&gd32的usb虛擬串口,打印類printer組合設備
國產M3替代STM32F103XX
極海APM32F103RCT7通過AEC-Q100車規認證
基于極海APM32F103的USB鍵盤與虛擬串口例程
評論