移植STM32固件库用于HID双向通信
USB的应用中HID类是比较常见的方式。通过修改STM32 USB固件库V4.0的JOYSTICK应用,我们实现一个双向USB通信。
一、移植
使用STM32源程序为点亮LED灯程序。
首先将USB固件库中有用的函数复制到源函数中,
建立LIB文件夹其中放入USB2.0协议函数
建立CFG文件夹放入USB应用函数
将两个文件夹都放到源工程目录下将文件添加进来,设置好,配置好KEIL软件设置。
二、修改文件
1、首先修改platform_config.h函数。
该文件是对于多种芯片对于USB库的支持。我们使用STM32F103ZET6芯片,所以只保留与之相关的ID项,与USB_DISCONNECT线(PG11)的配置。修改之后的头文件如下所示
2、然后修改hw_config.c中Set_System函数
实际上STM32F103zet6不需要对USB端口进行单独的配置,删掉Set_System函数中没用的部分,只对USB_DISCONNECT线(PG11)进行初始化。修改后的函数如下图所示。
3、接下来修改hw_config.c文件中USB_Cable_Config函数因为我使用的是比较老的神舟III开发板,当PG11为高电平时实现上拉,所以修改后的函数为
4、修改USB_Interrupt_Config函数,配置USB_LP_CAN1_RX0_IRQn和USBWakeUp_IRQn中断修改后的函数如下图所示。
5、接下来删掉hw_config.c文件中GPIO_AINConfig函数,没有什么用,只会报错
6、删掉与按键相关的设置,因为我们并没有用到,主要是先清除JoyState和Joystick_Send函数中内容,不用管。
7、最后我们处理USB挂起相关的问题。在USB固件库中提供挂起相关的处理函数,主要有2个函数需要修改。它们是Suspend函数和Enter_LowPowerMode函数。其中,在Suspend函数中注释掉PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI);语句。修改Suspend函数,如下图所示
OK接下来应该就没有什么需要修改的地方了(如果有的话请酌情修改)
三、描述符修改
使用USB HID类进行通信调试。所以要对USB的描述符要做部分修改(usb_desc.c)。下面我们贴程序说明(只对重要修改做说明)
设备描述符
修改idVendor和idProduct的值,为任意其他值,我里我用给的是0x0413和0x5724,修改的时候注意大小端
配置描述符集合
[cpp] view plain copy
1. <span style="font-family:SimSun;font-size:10px;">const uint8_t Joystick_ConfigDescriptor[JOYSTICK_SIZ_CONFIG_DESC] =
2. {
3. /* bLength: Configuration Descriptor size */
4. /* bDescriptorType: Configuration */
5. JOYSTICK_SIZ_CONFIG_DESC,
6. /* wTotalLength: Bytes returned */
7. 0x00,
8. /*bNumInterfaces: 1 interface*/
9. /*bConfigurationValue: Configuration value*/
10. /*iConfiguration: Index of string descriptor describing
11. the configuration*/
12. /*bmAttributes: Self powered */
13. /*MaxPower 100 mA: this current is used for detecting Vbus*/
14.
15. /************** Descriptor of Joystick Mouse interface ****************/
16. /* 09 */
17. /*bLength: Interface Descriptor size*/
18. /*bDescriptorType: Interface descriptor type*/
19. /*bInterfaceNumber: Number of Interface*/
20. /*bAlternateSetting: Alternate setting*/
21. /*bNumEndpoints除端点0外,需要1输入1输出*/
22. /*bInterfaceClass: HID*/
23. /*bInterfaceSubClass : 1=BOOT, 0=no boot选择no boot*/
24. /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse选择none*/
25. /*iInterface: Index of string descriptor*/
26. /******************** Descriptor of Joystick Mouse HID ********************/
27. /* 18 */
28. /*bLength: HID Descriptor size*/
29. /*bDescriptorType: HID*/
30. /*bcdHID: HID Class Spec release number*/
31. 0x01,
32. /*bCountryCode: Hardware target country*/
33. /*bNumDescriptors: Number of HID class descriptors to follow*/
34. /*bDescriptorType*/
35. /*wItemLength: Total length of Report descriptor*/
36. 0x00,
37. /******************** Descriptor of Joystick Mouse endpoint ********************/
38. /* 27 */
39. /*bLength: Endpoint Descriptor size*/
40. /*bDescriptorType:*/
41.
42. /*bEndpointAddress: Endpoint Address (IN)*/
43. /*bmAttributes: Interrupt endpoint*/
44. /*wMaxPacketSize: 64 Byte max修改最大包大小为64字节 */
45. 0x00,
46. /*bInterval: Polling Interval (10 ms)修改轮询间隔为10ms*/
47. /******************** Descriptor of Joystick Mouse endpoint ********************/
48. /* 34 */
49. /*bLength: Endpoint Descriptor size*/
50. /*bDescriptorType:*/
51.
52. /*bEndpointAddress: Endpoint Address (OUT)*/
53. /*bmAttributes: Interrupt endpoint*/
54. /*wMaxPacketSize: 64 Byte max修改最大包大小为64字节 */
55. 0x00,
56. /*bInterval: Polling Interval (10 ms) 修改轮询间隔为10ms */
57. /*41 */
58. "font-family:SimSun;font-size:14px;">
59. </span>
报告描述符
[cpp] view plain copy
1. <span style="font-family:SimSun;font-size:10px;">const uint8_t Joystick_ReportDescriptor[JOYSTICK_SIZ_REPORT_DESC] =
2. {
3. /*Usage Page(User Define)*/
4. /*Usage(User Define)*/
5. /*Collection(application)*/
6. /*Usage Page(1)*/
7. /*Usage Minimum(0)*/
8. /*Usage Maximum(255)*/
9. /*Logical Minimum(0)*/
10. /*Logical Maximum(255)*/
11. /*Report Count(3)*/
12. /*Report Size(1)*/
13. 0x81, 0x02, /*Input(Data,Var,Abs)*/
14. 0x05, 0x02 /*Usage Page(2)*/
15. 0x19, 0x00, /*Usage Minimum(0)*/
16. /*Usage Maximum(255)*/
17. /*Logical Minimum(0)*/
18. /*Logical Maximum(255)*/
19. /*Report Count(64)*/
20. /*Report Size(8)*/
21. 0x91, 0x02, /*Input(Data,Var,Abs)*/
22. 0xc0 /*关集合*/
23. /* Joystick_ReportDescriptor */</span><span style="font-family:SimSun;font-size:14px;">
24. </span>
上面我们修改了数组内容,导致数组大小发生变化,所以根据变化做响应修改,在usb_desc.c中
[cpp] view plain copy
1. <span style="font-family:SimSun;font-size:10px;">#define JOYSTICK_SIZ_CONFIG_DESC 41
2. #define JOYSTICK_SIZ_REPORT_DESC 39</span>
四、修改函数
修改设备RESET函数Joystick_Reset,这个函数是在RESET中断中被调用,用于端口的初始化。因为我们增加了端点1的输出,修改了包大小,所以要做响应的修改。这个函数位于usb_prop.c中,修改后的函数为
[cpp] view plain copy
1. <span style="font-family:SimSun;font-size:10px;">void Joystick_Reset(void)
2. {
3. /* Set Joystick_DEVICE as not configured */
4. pInformation->Current_Configuration = 0;
5. /*the default Interface*/
6.
7. /* Current Feature initialization */
8. pInformation->Current_Feature = Joystick_ConfigDescriptor[7];
9. SetBTABLE(BTABLE_ADDRESS);
10. /* Initialize Endpoint 0 */
11. SetEPType(ENDP0, EP_CONTROL);
12. SetEPTxStatus(ENDP0, EP_TX_STALL);
13. SetEPRxAddr(ENDP0, ENDP0_RXADDR);
14. SetEPTxAddr(ENDP0, ENDP0_TXADDR);
15. Clear_Status_Out(ENDP0);
16. SetEPRxCount(ENDP0, Device_Property.MaxPacketSize);
17. SetEPRxValid(ENDP0);
18.
19. /* Initialize Endpoint 1 */
20. SetEPType(ENDP1, EP_INTERRUPT);
21. SetEPTxAddr(ENDP1, ENDP1_TXADDR);
22. //修改大小为64
23. SetEPTxStatus(ENDP1, EP_TX_NAK);
24.
25. /* Initialize Endpoint 1 */
26. //SetEPType(ENDP1, EP_INTERRUPT);
27. SetEPTxAddr(ENDP1, ENDP1_RXADDR);
28. //修改大小为64
29. SetEPRxStatus(ENDP1, EP_RX_VALID);
30. /* Set this device to response on default address */
31. SetDeviceAddress(0);
32. bDeviceState = ATTACHED;
33. }</span><span style="font-family:SimSun;font-size:14px;">
34. </span>
在上面中ENDP1_RXADDR是我们新加的,需要我们在usb_conf.h中添加对于它的定义
[cpp] view plain copy
1. <span style="font-family:SimSun;font-size:10px;">/* EP1 */
2. /* tx buffer base address */
3. #define ENDP1_TXADDR (0x100)
4. #define ENDP1_RXADDR (0x140)</span><span style="font-family:SimSun;font-size:14px;">
5. </span>
最后我们编写对于USB输出数据的处理函数,即
[cpp] view plain copy
1. <span style="font-family:SimSun;font-size:10px;">void EP1_OUT_Callback(void)
2. {
3. //kk是个64字节的数组
4.
5. if(kk[0]&&0x01 == 0x01)
6. LED(LED1,1);
7. else
8. LED(LED1,0);
9. if(kk[0]&&0x02 == 0x02)
10. LED(LED2,1);
11. else
12. LED(LED2,0);
13. if(kk[0]&&0x04 == 0x04)
14. LED(LED3,1);
15. else
16. LED(LED3,0);
17. if(kk[0]&&0x08 == 0x08)
18. LED(LED4,1);
19. else
20. LED(LED4,0);
21. SetEPRxValid(ENDP1);
22. }</span><span style="font-family:SimSun;font-size:14px;">
23. </span>
在主函数中我们添加USB IN的相关操作,因为设置的是interrupt端点,所以每过10ms主机就是问询设备是否有数据要发送,我们只需要在stm32的主函数中填装数据即可。具体的while循环中代码是
[cpp] view plain copy
1. <span style="font-family:SimSun;font-size:10px;">if(GetEPTxStatus(ENDP1)==EP_TX_NAK)
2. {
3. if(DataLen !=0)
4. {
5. USB_SIL_Write(ENDP1,kk,DataLen);
6. SetEPTxValid(ENDP1);
7. DataLen=0;
8. }
9. }</span><span style="font-family:SimSun;font-size:14px;">
10. </span>
最后的最后不要忘了在stm32f10x_it.c中添加中断处理
[cpp] view plain copy
1. <span style="font-family:SimSun;font-size:10px;">void USB_LP_CAN1_RX0_IRQHandler(void)
2. {
3. USB_Istr();
4. }
5. void USBWakeUp_IRQHandler(void)
6. {
7. EXTI_ClearITPendingBit(EXTI_Line18);
8. }</span><span style="font-family:SimSun;font-size:14px;">
9. </span>
至此整个移植过程就完成了,程序实现的功能是,通过电脑给USB设备发送数据,USB设备收到数据,然后再把数据发送回电脑,为了检验USB的发送与接收,使用Labview编写了一个简单的上位机。大概就是这个样子,发送哈哈,收到哈哈。
关于上位机就在下一篇博文里写吧
USB的应用中HID类是比较常见的方式。通过修改STM32 USB固件库V4.0的JOYSTICK应用,我们实现一个双向USB通信。
一、移植
使用STM32源程序为点亮LED灯程序。
首先将USB固件库中有用的函数复制到源函数中, 建立LIB文件夹其中放入USB2.0协议函数
建立CFG文件夹放入USB应用函数
将两个文件夹都放到源工程目录下将文件添加进来,设置好,配置好KEIL软件设置。
二、修改文件
1、首先修改platform_config.h函数。
该文件是对于多种芯片对于USB库的支持。我们使用STM32F103ZET6芯片,所以只保留与之相关的ID项,与USB_DISCONNECT线(PG11)的配置。修改之后的头文件如下所示
2、然后修改hw_config.c中Set_System函数
实际上STM32F103zet6不需要对USB端口进行单独的配置,删掉Set_System函数中没用的部分,只对USB_DISCONNECT线(PG11)进行初始化。修改后的函数如下图所示。
3、接下来修改hw_config.c文件中USB_Cable_Config函数因为我使用的是比较老的神舟III开发板,当PG11为高电平时实现上拉,所以修改后的函数为
4、修改USB_Interrupt_Config函数,配置USB_LP_CAN1_RX0_IRQn和USBWakeUp_IRQn中断修改后的函数如下图所示。
5、接下来删掉hw_config.c文件中GPIO_AINConfig函数,没有什么用,只会报错
6、删掉与按键相关的设置,因为我们并没有用到,主要是先清除JoyState和Joystick_Send函数中内容,不用管。
7、最后我们处理USB挂起相关的问题。在USB固件库中提供挂起相关的处理函数,主要有2个函数需要修改。它们是Suspend函数和Enter_LowPowerMode函数。其中,在Suspend函数中注释掉PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI);语句。修改Suspend函数,如下图所示
OK接下来应该就没有什么需要修改的地方了(如果有的话请酌情修改)
三、描述符修改
使用USB HID类进行通信调试。所以要对USB的描述符要做部分修改(usb_desc.c)。下面我们贴程序说明(只对重要修改做说明)
设备描述符
修改idVendor和idProduct的值,为任意其他值,我里我用给的是0x0413和0x5724,修改的时候注意大小端
配置描述符集合
[cpp] view plain copy
1. <span style="font-family:SimSun;font-size:10px;">const uint8_t Joystick_ConfigDescriptor[JOYSTICK_SIZ_CONFIG_DESC] =
2. {
3. /* bLength: Configuration Descriptor size */
4. /* bDescriptorType: Configuration */
5. JOYSTICK_SIZ_CONFIG_DESC,
6. /* wTotalLength: Bytes returned */
7. 0x00,
8. /*bNumInterfaces: 1 interface*/
9. /*bConfigurationValue: Configuration value*/
10. /*iConfiguration: Index of string descriptor describing
11. the configuration*/
12. /*bmAttributes: Self powered */
13. /*MaxPower 100 mA: this current is used for detecting Vbus*/
14.
15. /************** Descriptor of Joystick Mouse interface ****************/
16. /* 09 */
17. /*bLength: Interface Descriptor size*/
18. /*bDescriptorType: Interface descriptor type*/
19. /*bInterfaceNumber: Number of Interface*/
20. /*bAlternateSetting: Alternate setting*/
21. /*bNumEndpoints除端点0外,需要1输入1输出*/
22. /*bInterfaceClass: HID*/
23. /*bInterfaceSubClass : 1=BOOT, 0=no boot选择no boot*/
24. /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse选择none*/
25. /*iInterface: Index of string descriptor*/
26. /******************** Descriptor of Joystick Mouse HID ********************/
27. /* 18 */
28. /*bLength: HID Descriptor size*/
29. /*bDescriptorType: HID*/
30. /*bcdHID: HID Class Spec release number*/
31. 0x01,
32. /*bCountryCode: Hardware target country*/
33. /*bNumDescriptors: Number of HID class descriptors to follow*/
34. /*bDescriptorType*/
35. /*wItemLength: Total length of Report descriptor*/
36. 0x00,
37. /******************** Descriptor of Joystick Mouse endpoint ********************/
38. /* 27 */
39. /*bLength: Endpoint Descriptor size*/
40. /*bDescriptorType:*/
41.
42. /*bEndpointAddress: Endpoint Address (IN)*/
43. /*bmAttributes: Interrupt endpoint*/
44. /*wMaxPacketSize: 64 Byte max修改最大包大小为64字节 */
45. 0x00,
46. /*bInterval: Polling Interval (10 ms)修改轮询间隔为10ms*/
47. /******************** Descriptor of Joystick Mouse endpoint ********************/
48. /* 34 */
49. /*bLength: Endpoint Descriptor size*/
50. /*bDescriptorType:*/
51.
52. /*bEndpointAddress: Endpoint Address (OUT)*/
53. /*bmAttributes: Interrupt endpoint*/
54. /*wMaxPacketSize: 64 Byte max修改最大包大小为64字节 */
55. 0x00,
56. /*bInterval: Polling Interval (10 ms) 修改轮询间隔为10ms */
57. /*41 */
58. "font-family:SimSun;font-size:14px;">
59. </span>
报告描述符
[cpp] view plain copy
1. <span style="font-family:SimSun;font-size:10px;">const uint8_t Joystick_ReportDescriptor[JOYSTICK_SIZ_REPORT_DESC] =
2. {
3. /*Usage Page(User Define)*/
4. /*Usage(User Define)*/
5. /*Collection(application)*/
6. /*Usage Page(1)*/
7. /*Usage Minimum(0)*/
8. /*Usage Maximum(255)*/
9. /*Logical Minimum(0)*/
10. /*Logical Maximum(255)*/
11. /*Report Count(3)*/
12. /*Report Size(1)*/
13. 0x81, 0x02, /*Input(Data,Var,Abs)*/
14. 0x05, 0x02 /*Usage Page(2)*/
15. 0x19, 0x00, /*Usage Minimum(0)*/
16. /*Usage Maximum(255)*/
17. /*Logical Minimum(0)*/
18. /*Logical Maximum(255)*/
19. /*Report Count(64)*/
20. /*Report Size(8)*/
21. 0x91, 0x02, /*Input(Data,Var,Abs)*/
22. 0xc0 /*关集合*/
23. /* Joystick_ReportDescriptor */</span><span style="font-family:SimSun;font-size:14px;">
24. </span>
上面我们修改了数组内容,导致数组大小发生变化,所以根据变化做响应修改,在usb_desc.c中
[cpp] view plain copy
1. <span style="font-family:SimSun;font-size:10px;">#define JOYSTICK_SIZ_CONFIG_DESC 41
2. #define JOYSTICK_SIZ_REPORT_DESC 39</span>
四、修改函数
修改设备RESET函数Joystick_Reset,这个函数是在RESET中断中被调用,用于端口的初始化。因为我们增加了端点1的输出,修改了包大小,所以要做响应的修改。这个函数位于usb_prop.c中,修改后的函数为
[cpp] view plain copy
1. <span style="font-family:SimSun;font-size:10px;">void Joystick_Reset(void)
2. {
3. /* Set Joystick_DEVICE as not configured */
4. pInformation->Current_Configuration = 0;
5. /*the default Interface*/
6.
7. /* Current Feature initialization */
8. pInformation->Current_Feature = Joystick_ConfigDescriptor[7];
9. SetBTABLE(BTABLE_ADDRESS);
10. /* Initialize Endpoint 0 */
11. SetEPType(ENDP0, EP_CONTROL);
12. SetEPTxStatus(ENDP0, EP_TX_STALL);
13. SetEPRxAddr(ENDP0, ENDP0_RXADDR);
14. SetEPTxAddr(ENDP0, ENDP0_TXADDR);
15. Clear_Status_Out(ENDP0);
16. SetEPRxCount(ENDP0, Device_Property.MaxPacketSize);
17. SetEPRxValid(ENDP0);
18.
19. /* Initialize Endpoint 1 */
20. SetEPType(ENDP1, EP_INTERRUPT);
21. SetEPTxAddr(ENDP1, ENDP1_TXADDR);
22. //修改大小为64
23. SetEPTxStatus(ENDP1, EP_TX_NAK);
24.
25. /* Initialize Endpoint 1 */
26. //SetEPType(ENDP1, EP_INTERRUPT);
27. SetEPTxAddr(ENDP1, ENDP1_RXADDR);
28. //修改大小为64
29. SetEPRxStatus(ENDP1, EP_RX_VALID);
30. /* Set this device to response on default address */
31. SetDeviceAddress(0);
32. bDeviceState = ATTACHED;
33. }</span><span style="font-family:SimSun;font-size:14px;">
34. </span>
在上面中ENDP1_RXADDR是我们新加的,需要我们在usb_conf.h中添加对于它的定义
[cpp] view plain copy
1. <span style="font-family:SimSun;font-size:10px;">/* EP1 */
2. /* tx buffer base address */
3. #define ENDP1_TXADDR (0x100)
4. #define ENDP1_RXADDR (0x140)</span><span style="font-family:SimSun;font-size:14px;">
5. </span>
最后我们编写对于USB输出数据的处理函数,即
[cpp] view plain copy
1. <span style="font-family:SimSun;font-size:10px;">void EP1_OUT_Callback(void)
2. {
3. //kk是个64字节的数组
4.
5. if(kk[0]&&0x01 == 0x01)
6. LED(LED1,1);
7. else
8. LED(LED1,0);
9. if(kk[0]&&0x02 == 0x02)
10. LED(LED2,1);
11. else
12. LED(LED2,0);
13. if(kk[0]&&0x04 == 0x04)
14. LED(LED3,1);
15. else
16. LED(LED3,0);
17. if(kk[0]&&0x08 == 0x08)
18. LED(LED4,1);
19. else
20. LED(LED4,0);
21. SetEPRxValid(ENDP1);
22. }</span><span style="font-family:SimSun;font-size:14px;">
23. </span>
在主函数中我们添加USB IN的相关操作,因为设置的是interrupt端点,所以每过10ms主机就是问询设备是否有数据要发送,我们只需要在stm32的主函数中填装数据即可。具体的while循环中代码是
[cpp] view plain copy
1. <span style="font-family:SimSun;font-size:10px;">if(GetEPTxStatus(ENDP1)==EP_TX_NAK)
2. {
3. if(DataLen !=0)
4. {
5. USB_SIL_Write(ENDP1,kk,DataLen);
6. SetEPTxValid(ENDP1);
7. DataLen=0;
8. }
9. }</span><span style="font-family:SimSun;font-size:14px;">
10. </span>
最后的最后不要忘了在stm32f10x_it.c中添加中断处理
[cpp] view plain copy
1. <span style="font-family:SimSun;font-size:10px;">void USB_LP_CAN1_RX0_IRQHandler(void)
2. {
3. USB_Istr();
4. }
5. void USBWakeUp_IRQHandler(void)
6. {
7. EXTI_ClearITPendingBit(EXTI_Line18);
8. }</span><span style="font-family:SimSun;font-size:14px;">
9. </span>
至此整个移植过程就完成了,程序实现的功能是,通过电脑给USB设备发送数据,USB设备收到数据,然后再把数据发送回电脑,为了检验USB的发送与接收,使用Labview编写了一个简单的上位机。大概就是这个样子,发送哈哈,收到哈哈。
关于上位机就在下一篇博文里写吧