[RISC-V MCU 应用开发] 第八十五章、CH32V103应用教程——USB Host

[复制链接]
 楼主| RISCVLAR 发表于 2021-3-20 13:24 | 显示全部楼层 |阅读模式
os, ST, USB, ev, TE, ui
本帖最后由 RISCVLAR 于 2021-3-20 13:24 编辑

CH32V103应用教程——USB Host

本章教程主要使用CH32V103 USB用作主机模式,程序仅供参考。

1、USB简介及相关函数介绍
CH32V103芯片内嵌USB主从控制器及收发器,支持USB Host主机功能和USB Device设备功能。

在 USB 主机模式下,芯片提供了一组双向主机端点,包括一个发送端点 OUT 和一个接收端点 IN,一个数据包的最大长度是 1023 字节(V103),支持控制传输、中断传输、批量传输和实时/同步传输。

主机端点发起的每一个 USB 事务,在处理结束后总是自动设置RB_UIF_TRANSFER 中断标志。应用程序可以直接查询或在 USB 中断服务程序中查询并分析中断标志寄存器 R8_USB_INT_FG,根据各中断标志分别进行相应的处理;并且,如果 RB_UIF_TRANSFER 有效,那么还需要继续分析 USB 中断状态寄存器 R8_USB_INT_ST,根据当前 USB 传输事务的应答 PID 标识 MASK_UIS_H_RES 进行相应的处理。

如果事先设定了主机接收端点的 IN 事务的同步触发位(RB_UH_R_TOG),那么可以通过RB_U_TOG_OK 或者 RB_UIS_TOG_OK 判断当前所接收到的数据包的同步触发位是否与主机接收端点的同步触发位匹配,如果数据同步,则数据有效;如果数据不同步,则数据应该被丢弃。每次处理完USB 发送或者接收中断后,都应该正确修改相应主机端点的同步触发位,用于同步下次所发送的数据包和检测下次所接收的数据包是否同步;另外,通过设置 RB_UH_T_AUTO_TOG 和 RB_UH_R_AUTO_TOG可以实现在发送成功或接收成功后自动翻转相应的同步触发位。

USB 主机令牌设置寄存器 R8_UH_EP_PID 用于设置被操作的目标设备的端点号和本次 USB 传输事务的令牌 PID 包标识。SETUP 令牌和 OUT 令牌所对应的数据由主机发送端点提供,准备发送的数据在R16_UH_TX_DMA 缓冲区中,准备发送的数据长度设置在 R16_UH_TX_LEN 中;IN 令牌所对应数据由目标设备返回给主机接收端点,接收到数据存放 R16_UH_RX_DMA 缓冲区中,接收到的数据长度存放在R8_USB_RX_LEN 中。

关于USB主机模式具体信息以及寄存器相关描述,可参考CH32V103应用手册。

2、硬件设计
本章教程主要进行USB主机模式实验,仅需用到开发板USB口。

3软件设计
本章程序全在主函数中进行,具体程序如下:
main.c文件
  1. /********************************** (C) COPYRIGHT *******************************
  2. * File Name          : main.c
  3. * Author             : WCH
  4. * Version            : V1.0.0
  5. * Date               : 2019/10/15
  6. * Description        : Main program body.
  7. *******************************************************************************/

  8. /*
  9. *@Note
  10.   USB设备的简易枚举过程例程:
  11.   USBHDM(PB6)、USBHDP(PB7)。
  12. */

  13. #include "debug.h"
  14. #include "string.h"
  15. #include "ch32v10x_conf.h"

  16. /* Global Variable */
  17. __attribute__ ((aligned(4))) UINT8  RxBuffer[MAX_PACKET_SIZE] ;      // IN, must even address
  18. __attribute__ ((aligned(4))) UINT8  TxBuffer[MAX_PACKET_SIZE] ;      // OUT, must even address
  19. __attribute__ ((aligned(4))) UINT8  Com_Buffer[128];

  20. _DevOnHubPort  DevOnHubPort[HUB_MAX_PORTS];
  21. _RootHubDev  ThisUsbDev;

  22. /*******************************************************************************
  23. * Function Name  : Set_USBConfig
  24. * Description    : Set USB clock.
  25. * Input          : None
  26. * Return         : None
  27. *******************************************************************************/
  28. void USBHD_ClockCmd(UINT32 RCC_USBCLKSource,FunctionalState NewState)
  29. {
  30.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, NewState);
  31.     EXTEN->EXTEN_CTR |= EXTEN_USBHD_IO_EN;
  32.     RCC_USBCLKConfig(RCC_USBCLKSource);             //USBclk=PLLclk/1.5=48Mhz
  33.     RCC_AHBPeriphClockCmd(RCC_AHBPeriph_USBHD,NewState);
  34. }

  35. /*******************************************************************************
  36. * Function Name  : main
  37. * Description    : Main program.
  38. * Input          : None
  39. * Return         : None
  40. *******************************************************************************/
  41. int main()
  42. {
  43.     UINT8   s;
  44.     UINT8   i, k, len, endp;
  45.     UINT16  loc;

  46.     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  47.     Delay_Init();
  48.     USART_Printf_Init(115200);

  49.     printf("SystemClk:%d\r\n",SystemCoreClock);
  50.     printf("USBHD HOST Test\r\n");

  51.     USBHD_ClockCmd(RCC_USBCLKSource_PLLCLK_1Div5,ENABLE);

  52.     pHOST_RX_RAM_Addr = RxBuffer;
  53.     pHOST_TX_RAM_Addr = TxBuffer;

  54.     USB_HostInit();  //初始化USB主机模式

  55.     printf( "Wait Device In\n" );

  56.     while(1)
  57.     {
  58.         s = ERR_SUCCESS;

  59.         //RB_UIF_DETECT:USB主机模式下USB设备连接或断开事件中断标志位
  60.         if ( R8_USB_INT_FG & RB_UIF_DETECT )
  61.         {
  62.             R8_USB_INT_FG = RB_UIF_DETECT ;      //检测到USB设备连接或断开触发

  63.             s = AnalyzeRootHub( );               //分析跟集线器状态

  64.             if ( s == ERR_USB_CONNECT )          //如果检测到设备连接
  65.             {
  66.                 printf( "New Device In\r\n" );
  67.                 FoundNewDev = 1;
  68.             }
  69.             if( s == ERR_USB_DISCON )            //如果检测到设备断开连接
  70.             {
  71.                 printf( "Device Out\r\n" );
  72.             }
  73.         }

  74.         if ( FoundNewDev || s == ERR_USB_CONNECT )     //如果检测到有USB设备连接
  75.         {
  76.             FoundNewDev = 0;
  77.             s = EnumAllRootDevice( );                  //枚举所有ROOT-HUB端口的USB设备
  78.             if ( s == ERR_SUCCESS )                    //如果枚举成功
  79.             {
  80.                 printf( "Device Enum Succeed\r\n" );
  81.             }
  82.             else                                       //如果枚举失败
  83.             {
  84.                 printf( "Device Enum Failed\r\n" );
  85.                 printf( "EnumAllRootDev err = %02X\n", (UINT16)s );
  86.             }
  87.         }

  88.         //如果CH32V103下端连接的是HUB,则先枚举HUB
  89.         s = EnumAllHubPort( );                                     //枚举所有ROOT-HUB端口下外部HUB后的二级USB设备
  90.         if ( s != ERR_SUCCESS )                                    //可能是HUB断开了
  91.         {
  92.             printf( "EnumAllHubPort err = %02X\n", (UINT16)s );
  93.         }

  94.         //如果设备是鼠标
  95.         loc = SearchTypeDevice( DEV_TYPE_MOUSE );      // 在ROOT-HUB以及外部HUB各端口上搜索指定类型的设备所在的端口号

  96.         //printf("loc=%d\n",loc);

  97.         if ( loc != 0xFFFF )                           // 找到了,如果有两个MOUSE如何处理?
  98.         {
  99.             //printf( "Query Mouse @%04X\n", loc );

  100.             i = (UINT8)( loc >> 8 );

  101.             len = (UINT8)loc;

  102.             //printf("len=%d\n",len);

  103.             SelectHubPort( len );                                            // 选择操作指定的ROOT-HUB端口,设置当前USB速度以及被操作设备的USB地址

  104.             //printf("len1=%d\n",len);

  105.             endp = len ? DevOnHubPort[len-1].GpVar[0] : ThisUsbDev.GpVar[0]; // 中断端点的地址,位7用于同步标志位

  106.             //printf("endp=%d\n",endp);

  107.             if ( endp & USB_ENDP_ADDR_MASK )                                 // 端点有效
  108.             {

  109.                 s = USBHostTransact( USB_PID_IN << 4 | (endp & 0x7F), endp & 0x80 ? RB_UH_R_TOG | RB_UH_T_TOG : 0, 0 );// CH32传输事务,获取数据,NAK不重试

  110.                 if ( s == ERR_SUCCESS )
  111.                 {
  112.                     endp ^= 0x80;                                            // 同步标志翻转

  113.                     if ( len ) DevOnHubPort[len-1].GpVar[0] = endp;          // 保存同步标志位

  114.                     else ThisUsbDev.GpVar[0] = endp;

  115.                     len = R8_USB_RX_LEN;                                     // 接收到的数据长度

  116.                     if ( len )
  117.                     {
  118.                         printf("Mouse data: ");

  119.                         for ( i = 0; i < len; i ++ )
  120.                         {
  121.                             printf("x%02X ",(UINT16)(RxBuffer[i]) );
  122.                         }

  123.                         printf("\n");
  124.                     }
  125.                 }
  126.                 else if ( s != ( USB_PID_NAK | ERR_USB_TRANSFER ) )
  127.                 {
  128.                     printf("Mouse error %02x\n",(UINT16)s);                  // 可能是断开了
  129.                 }
  130.             }
  131.             else
  132.             {
  133.                 printf("Mouse no interrupt endpoint\n");
  134.             }
  135.             SetUsbSpeed( 1 );                                                // 默认为全速
  136.         }

  137.         //如果设备是键盘
  138.         loc = SearchTypeDevice( DEV_TYPE_KEYBOARD );                         // 在ROOT-HUB以及外部HUB各端口上搜索指定类型的设备所在的端口号
  139.         if ( loc != 0xFFFF )                                                 // 找到了,如果有两个KeyBoard如何处理?
  140.         {
  141.             //printf( "Query Keyboard @%04X\n", loc );

  142.             i = (UINT8)( loc >> 8 );

  143.             len = (UINT8)loc;

  144.             SelectHubPort( len );                                            // 选择操作指定的ROOT-HUB端口,设置当前USB速度以及被操作设备的USB地址

  145.             endp = len ? DevOnHubPort[len-1].GpVar[0] : ThisUsbDev.GpVar[0]; // 中断端点的地址,位7用于同步标志位

  146.             //printf("%02X  ",endp);
  147.             if ( endp & USB_ENDP_ADDR_MASK )                                 // 端点有效
  148.             {
  149.                 s = USBHostTransact( USB_PID_IN << 4 | (endp & 0x7F), endp & 0x80 ? RB_UH_R_TOG | RB_UH_T_TOG : 0, 0 );// CH32传输事务,获取数据,NAK不重试
  150.                 if ( s == ERR_SUCCESS )
  151.                 {
  152.                     endp ^= 0x80;                                            // 同步标志翻转

  153.                     if ( len ) DevOnHubPort[len-1].GpVar[0] = endp;          // 保存同步标志位

  154.                     else ThisUsbDev.GpVar[0] = endp;

  155.                     len = R8_USB_RX_LEN;                                     // 接收到的数据长度

  156.                     if ( len )
  157.                     {
  158.                         SETorOFFNumLock(RxBuffer);
  159.                         printf("keyboard data: ");
  160.                         for ( i = 0; i < len; i ++ )
  161.                         {
  162.                             printf("x%02X ",(UINT16)(RxBuffer[i]) );
  163.                         }
  164.                         printf("\n");
  165.                     }
  166.                 }
  167.                 else if ( s != ( USB_PID_NAK | ERR_USB_TRANSFER ) )
  168.                 {
  169.                     printf("keyboard error %02x\n",(UINT16)s);               // 可能是断开了
  170.                 }
  171.             }
  172.             else
  173.             {
  174.                 printf("keyboard no interrupt endpoint\n");
  175.             }
  176.             SetUsbSpeed( 1 );                                                // 默认为全速
  177.         }


  178. //      //操作USB打印机
  179. //      if(GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_1) == 0)              //PA1为低,开始打印
  180. //      {
  181. //          memset(TxBuffer,0,sizeof(TxBuffer));
  182. //
  183. //          TxBuffer[0]=0x1B;TxBuffer[1]=0x40;TxBuffer[2]=0x1D;TxBuffer[3]=0x55;TxBuffer[4]=0x42;TxBuffer[5]=0x02;TxBuffer[6]=0x18;TxBuffer[7]=0x1D;
  184. //          TxBuffer[8]=0x76;TxBuffer[9]=0x30;TxBuffer[10]=0x00;TxBuffer[11]=0x30;TxBuffer[12]=0x00;TxBuffer[13]=0x18;

  185.             loc = SearchTypeDevice( USB_DEV_CLASS_PRINTER );                 // 在ROOT-HUB以及外部HUB各端口上搜索指定类型的设备所在的端口号

  186.             if ( loc != 0xFFFF )                                             // 找到了,如果有两个打印机如何处理?
  187.             {
  188.                 //printf( "Query Printer @%04X\n", loc );

  189.                 i = (UINT8)( loc >> 8 );

  190.                 len = (UINT8)loc;

  191.                 SelectHubPort( len );                                             // 选择操作指定的ROOT-HUB端口,设置当前USB速度以及被操作设备的USB地址

  192.                 endp = len ? DevOnHubPort[len-1].GpVar[0] : ThisUsbDev.GpVar[0];  // 端点的地址,位7用于同步标志位

  193.                 //printf("%02X  ",endp);

  194.                 if ( endp & USB_ENDP_ADDR_MASK )                                  // 端点有效
  195.                 {

  196.                     R8_UH_TX_LEN = 64;                                            // 默认无数据故状态阶段为IN

  197.                     s = USBHostTransact( USB_PID_OUT << 4 | (endp & 0x7F), endp & 0x80 ? RB_UH_R_TOG | RB_UH_T_TOG : 0, 0xffff );// CH554传输事务,获取数据,NAK重试

  198.                     if ( s == ERR_SUCCESS )
  199.                     {
  200.                         endp ^= 0x80;                                             // 同步标志翻转

  201.                         memset(TxBuffer,0,sizeof(TxBuffer));

  202.                         R8_UH_TX_LEN = 64;                                        // 默认无数据故状态阶段为IN

  203.                         s = USBHostTransact( USB_PID_OUT << 4 | (endp & 0x7F), endp & 0x80 ? RB_UH_R_TOG | RB_UH_T_TOG : 0, 0xffff );// CH554传输事务,获取数据,NAK重试

  204.                     }
  205.                     else if ( s != ( USB_PID_NAK | ERR_USB_TRANSFER ) )

  206.                     printf("Printer error %02x\n",(UINT16)s); // 可能是断开了
  207.                 }
  208.             }
  209. //      }

  210.         //操作HID复合设备
  211.         loc = SearchTypeDevice( USB_DEV_CLASS_HID );                              // 在ROOT-HUB以及外部HUB各端口上搜索指定类型的设备所在的端口号

  212.         if ( loc != 0xFFFF )                                                      // 找到了
  213.         {
  214.             //printf( "Query USB_DEV_CLASS_HID @%04X\n", loc );

  215.             loc = (UINT8)loc;

  216.             for(k=0;k!=4;k++)
  217.             {
  218.                 //端点是否有效?
  219.                 endp = loc ? DevOnHubPort[loc-1].GpVar[k] : ThisUsbDev.GpVar[k];  // 中断端点的地址,位7用于同步标志位

  220.                 if ( (endp & USB_ENDP_ADDR_MASK) == 0 ) break;

  221.                 //printf("endp: %02X\n",(UINT16)endp);

  222.                 SelectHubPort( loc );                                             // 选择操作指定的ROOT-HUB端口,设置当前USB速度以及被操作设备的USB地址

  223.                 s = USBHostTransact( USB_PID_IN << 4 | (endp & 0x7F), endp & 0x80 ? RB_UH_R_TOG | RB_UH_T_TOG : 0, 0 );// CH554传输事务,获取数据,NAK不重试
  224.                 if ( s == ERR_SUCCESS )
  225.                 {
  226.                     endp ^= 0x80;                                                 // 同步标志翻转

  227.                     if ( loc ) DevOnHubPort[loc-1].GpVar[k] = endp;               // 保存同步标志位

  228.                     else ThisUsbDev.GpVar[k] = endp;

  229.                     len = R8_USB_RX_LEN;                                          // 接收到的数据长度

  230.                     if ( len )
  231.                     {
  232.                         printf("keyboard data: ");
  233.                         for ( i = 0; i < len; i ++ )
  234.                       {
  235.                             printf("x%02X ",(UINT16)(RxBuffer[i]) );
  236.                         }
  237.                         printf("\n");
  238.                     }
  239.                 }
  240.                 else if ( s != ( USB_PID_NAK | ERR_USB_TRANSFER ) )
  241.                 {
  242.                     printf("keyboard error %02x\n",(UINT16)s);                    // 可能是断开了
  243.                 }

  244.             }
  245.             SetUsbSpeed( 1 );                                                     // 默认为全速
  246.         }
  247.     }
  248. }
main.c文件主要进行插入设备的检测、识别以及数据的传输,具体可见程序和注释。

4下载验证
将编译好的程序下载到开发板并复位,打开串口调试助手,串口打印如下:
图片1.png
当插入鼠标设备并移动鼠标时,串口打印显示如下:
图片2.png
图片3.png
其他设备与之类似,不再一一附图展示。

   

84、CH32V USB Host.rar

538.98 KB, 下载次数: 271

评论

[url=home.php?mod=space&uid=3431082]@洛川飞鱼[/url] :找到原因了 20.2.2 功能配置 1)GPIO 端口 一旦使能了 USBD 模块,作为 UDP 和 UPM 的 GPIO 口会自动连接到内部 USB 收发器,而断开其 GPIO 外设的端口设置。所以推荐 GPIO 口配置为推挽方式输出低电平,防止在未开启 USBD 功能前,出现端 口不确定状态或者连接 PC 主机时,提前通知有 USB 设备接入。  发表于 2023-5-10 11:55
请教一下,哪里设置的IO口,没找到,看注释写的是PB的口,我这板子是PA的口,直接烧录竟然跑得通?  发表于 2023-5-9 17:36
参考做扫码枪上位机,注释细致,很受用,感谢  发表于 2023-5-9 17:31
huangyu_ninbo 发表于 2022-1-13 15:43 | 显示全部楼层
  USBHDM(PB6)、USBHDP(PB7)。
-----------------------------------------------------
这个DEMO是配合103的吧。但是103只有1个USB,口线是 USBHDM(PA11)、USBHDP(PA12)。
笔误》还是其他的MCU的demo没有变更过来?
huangyu_ninbo 发表于 2022-1-13 16:41 | 显示全部楼层
这个demo是官网的嘛?我发现官网的demo里面内容没有那么多。其中的官网.h文件内容少很多。求大神指点。
huangyu_ninbo 发表于 2022-1-13 16:44 | 显示全部楼层
ch32v10x_usb_host.h 和 ch32v10x_usb_host.c 官网的写的很少。
 楼主| RISCVLAR 发表于 2022-1-13 19:15 | 显示全部楼层
huangyu_ninbo 发表于 2022-1-13 16:41
这个demo是官网的嘛?我发现官网的demo里面内容没有那么多。其中的官网.h文件内容少很多。求大神指点。 ...

非官方例程例程,移植例程,仅供参考
GSDDDD 发表于 2022-1-17 13:09 | 显示全部楼层
上传的附件打不开
CHV.JPG
sesefadou 发表于 2022-12-3 12:11 | 显示全部楼层
这个需要设置驱动程序的吗?              
sheflynn 发表于 2022-12-5 21:22 | 显示全部楼层
USB Host最大传输字节是多少的?
您需要登录后才可以回帖 登录 | 注册

本版积分规则

133

主题

296

帖子

44

粉丝
快速回复 在线客服 返回列表 返回顶部

133

主题

296

帖子

44

粉丝
快速回复 在线客服 返回列表 返回顶部