查看: 2157|回复: 0
收起左侧

第八十五章:CH32V103应用教程——USB Host

[复制链接]

  离线 

  • TA的每日心情
    慵懒
    2021-7-23 17:16
  • 签到天数: 17 天

    [LV.4]

    发表于 2021-5-1 16:30:19 | 显示全部楼层 |阅读模式

    有人预言,RISC-V或将是继Intel和Arm之后的第三大主流处理器体系。欢迎访问全球首家只专注于RISC-V单片机行业应用的中文网站

    您需要 登录 才可以下载或查看,没有帐号?立即注册

    x
    本帖最后由 草帽王子 于 2021-9-10 18:21 编辑

    本章教程主要使用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、下载验证

    将编译好的程序下载到开发板并复位,打开串口调试助手,串口打印如下:
    CH32V CH573单片机芯片-第八十五章:CH32V103应用教程——USB Hostrisc-v单片机中文社区(1)
    当插入鼠标设备并移动鼠标时,串口打印显示如下:
    CH32V CH573单片机芯片-第八十五章:CH32V103应用教程——USB Hostrisc-v单片机中文社区(2)
    CH32V CH573单片机芯片-第八十五章:CH32V103应用教程——USB Hostrisc-v单片机中文社区(3)
    其他设备与之类似,不再一一附图展示。

    84、CH32V USB Host.rar
    CH32V CH573单片机芯片-第八十五章:CH32V103应用教程——USB Hostrisc-v单片机中文社区(4) 84、CH32V USB Host.rar (538.98 KB, 下载次数: 9)
    链接:https://pan.baidu.com/s/1rMHjTFBqqq-jpkWPxOsTVA
    提取码:t9ar
    复制这段内容后打开百度网盘手机App,操作更方便哦







    上一篇:第八十四章:CH32V103应用教程——USB模拟U盘
    下一篇:第八十六章:CH32V103应用教程——模拟SPI驱动OLED
    RISCV作者优文
    全球首家只专注于RISC-V单片机行业应用的中文网站
    回复

    使用道具 举报

    高级模式
    B Color Image Link Quote Code Smilies

    本版积分规则

    关闭

    RISC-V单片机中文网上一条 /2 下一条



    版权及免责声明|RISC-V单片机中文网 |网站地图

    GMT+8, 2024-4-19 11:51 , Processed in 0.914274 second(s), 46 queries .

    快速回复 返回顶部 返回列表