1526|1

35

帖子

0

资源

一粒金砂(中级)

【沁恒RISC-V内核 CH582】CH582 USB HOST 代码解读 [复制链接]

本帖最后由 pomin 于 2022-4-3 14:24 编辑

CH582 USB HOST 代码解读

CH582这款MCU的特点在蓝牙上面,所以准备做一个有线键盘转蓝牙键盘的小东西,看到官方给的例程里面也有USB HOST的例程,所以就研读一下。

官方的有main函数的代码如下:


#include "CH58x_common.h"

__attribute__((aligned(4))) uint8_t RxBuffer[MAX_PACKET_SIZE]; // IN, must even address
__attribute__((aligned(4))) uint8_t TxBuffer[MAX_PACKET_SIZE]; // OUT, must even address

/*********************************************************************
 * @fn      main
 *
 * @brief 主函数
 *
 * @return none
 */
int main()
{
    uint8_t i, s, k, len, endp;
    uint16_t  loc;

    SetSysClock(CLK_SOURCE_PLL_60MHz);
    DelayMs(5);
    /* 开启电压监控 */
    PowerMonitor(ENABLE, HALevel_2V1);

    GPIOA_SetBits(GPIO_Pin_9);
    GPIOA_ModeCfg(GPIO_Pin_8, GPIO_ModeIN_PU);
    GPIOA_ModeCfg(GPIO_Pin_9, GPIO_ModeOut_PP_5mA);
    UART1_DefInit();
    PRINT("Start @ChipID=%02X\n", R8_CHIP_ID);

    pU2HOST_RX_RAM_Addr = RxBuffer;
    pU2HOST_TX_RAM_Addr = TxBuffer;
    USB2_HostInit();
    PRINT("Wait Device In\n");
    while(1)
    {
        s = ERR_SUCCESS;
        if(R8_USB2_INT_FG & RB_UIF_DETECT)
        { // 如果有USB主机检测中断则处理
            R8_USB2_INT_FG = RB_UIF_DETECT;
            s = AnalyzeRootU2Hub();
            if(s == ERR_USB_CONNECT)
                FoundNewU2Dev = 1;
        }

        if(FoundNewU2Dev || s == ERR_USB_CONNECT)
        { // 有新的USB设备插入
            FoundNewU2Dev = 0;
            mDelaymS(200);          // 由于USB设备刚插入尚未稳定,故等待USB设备数百毫秒,消除插拔抖动
            s = InitRootU2Device(); // 初始化USB设备
            if(s != ERR_SUCCESS)
            {
                PRINT("EnumAllRootDev err = %02X\n", (uint16_t)s);
            }
        }

        /* 如果下端连接的是HUB,则先枚举HUB */
        s = EnumAllU2HubPort(); // 枚举所有ROOT-HUB端口下外部HUB后的二级USB设备
        if(s != ERR_SUCCESS)
        { // 可能是HUB断开了
            PRINT("EnumAllHubPort err = %02X\n", (uint16_t)s);
        }

        /* 如果设备是鼠标 */
        loc = U2SearchTypeDevice(DEV_TYPE_MOUSE); // 在ROOT-HUB以及外部HUB各端口上搜索指定类型的设备所在的端口号
        if(loc != 0xFFFF)
        { // 找到了,如果有两个MOUSE如何处理?
            i = (uint8_t)(loc >> 8);
            len = (uint8_t)loc;
            SelectU2HubPort(len);                                                 // 选择操作指定的ROOT-HUB端口,设置当前USB速度以及被操作设备的USB地址
            endp = len ? DevOnU2HubPort[len - 1].GpVar[0] : ThisUsb2Dev.GpVar[0]; // 中断端点的地址,位7用于同步标志位
            if(endp & USB_ENDP_ADDR_MASK)
            {                                                                                                        // 端点有效
                s = USB2HostTransact(USB_PID_IN << 4 | endp & 0x7F, endp & 0x80 ? RB_UH_R_TOG | RB_UH_T_TOG : 0, 0); // 传输事务,获取数据,NAK不重试
                if(s == ERR_SUCCESS)
                {
                    endp ^= 0x80; // 同步标志翻转
                    if(len)
                        DevOnU2HubPort[len - 1].GpVar[0] = endp; // 保存同步标志位
                    else
                        ThisUsb2Dev.GpVar[0] = endp;
                    len = R8_USB2_RX_LEN; // 接收到的数据长度
                    if(len)
                    {
                        PRINT("Mouse data: ");
                        for(i = 0; i < len; i++)
                        {
                            PRINT("x%02X ", (uint16_t)(RxBuffer));
                        }
                        PRINT("\n");
                    }
                }
                else if(s != (USB_PID_NAK | ERR_USB_TRANSFER))
                {
                    PRINT("Mouse error %02x\n", (uint16_t)s); // 可能是断开了
                }
            }
            else
            {
                PRINT("Mouse no interrupt endpoint\n");
            }
            SetUsb2Speed(1); // 默认为全速
        }

        /* 如果设备是键盘 */
        loc = U2SearchTypeDevice(DEV_TYPE_KEYBOARD); // 在ROOT-HUB以及外部HUB各端口上搜索指定类型的设备所在的端口号
        if(loc != 0xFFFF)
        { // 找到了,如果有两个KeyBoard如何处理?
            i = (uint8_t)(loc >> 8);
            len = (uint8_t)loc;
            SelectU2HubPort(len);                                                 // 选择操作指定的ROOT-HUB端口,设置当前USB速度以及被操作设备的USB地址
            endp = len ? DevOnU2HubPort[len - 1].GpVar[0] : ThisUsb2Dev.GpVar[0]; // 中断端点的地址,位7用于同步标志位
            if(endp & USB_ENDP_ADDR_MASK)
            {                                                                                                        // 端点有效
                s = USB2HostTransact(USB_PID_IN << 4 | endp & 0x7F, endp & 0x80 ? RB_UH_R_TOG | RB_UH_T_TOG : 0, 0); // 传输事务,获取数据,NAK不重试
                if(s == ERR_SUCCESS)
                {
                    endp ^= 0x80; // 同步标志翻转
                    if(len)
                        DevOnU2HubPort[len - 1].GpVar[0] = endp; // 保存同步标志位
                    else
                        ThisUsb2Dev.GpVar[0] = endp;
                    len = R8_USB2_RX_LEN; // 接收到的数据长度
                    if(len)
                    {
                        U2SETorOFFNumLock(RxBuffer);
                        PRINT("keyboard data: ");
                        for(i = 0; i < len; i++)
                        {
                            PRINT("x%02X ", (uint16_t)(RxBuffer));
                        }
                        PRINT("\n");
                    }
                }
                else if(s != (USB_PID_NAK | ERR_USB_TRANSFER))
                {
                    PRINT("keyboard error %02x\n", (uint16_t)s); // 可能是断开了
                }
            }
            else
            {
                PRINT("keyboard no interrupt endpoint\n");
            }
            SetUsb2Speed(1); // 默认为全速
        }
    }
}

硬件连接

2022.03.31_17.01.44.jpg


/*********************************************************************
 * @fn      InitRootU2Device
 *
 * @brief   初始化指定ROOT-HUB端口的USB设备
 *
 * @param   none
 *
 * @return  错误码
 */
uint8_t InitRootU2Device(void)
{
    uint8_t i, s;
    uint8_t cfg, dv_cls, if_cls;

    PRINT("Reset U2 host port\n");
    ResetRootU2HubPort(); // 检测到设备后,复位相应端口的USB总线
    for(i = 0, s = 0; i < 100; i++)
    { // 等待USB设备复位后重新连接,100mS超时
        mDelaymS(1);
        if(EnableRootU2HubPort() == ERR_SUCCESS)
        { // 使能端口
            i = 0;
            s++;
            if(s > 100)
                break; // 已经稳定连接100mS
        }
    }
    if(i)
    { // 复位后设备没有连接
        DisableRootU2HubPort();
        PRINT("Disable U2 host port because of disconnect\n");
        return (ERR_USB_DISCON);
    }
    SetUsb2Speed(ThisUsb2Dev.DeviceSpeed); // 设置当前USB速度

    PRINT("GetU2DevDescr: ");
    s = CtrlGetU2DeviceDescr(); // 获取设备描述符
    if(s == ERR_SUCCESS)
    {
        for(i = 0; i < ((PUSB_SETUP_REQ)SetupGetU2DevDescr)->wLength; i++)
            PRINT("x%02X ", (uint16_t)(U2Com_Buffer));
        PRINT("\n");

        ThisUsb2Dev.DeviceVID = ((PUSB_DEV_DESCR)U2Com_Buffer)->idVendor; //保存VID PID信息
        ThisUsb2Dev.DevicePID = ((PUSB_DEV_DESCR)U2Com_Buffer)->idProduct;
        dv_cls = ((PUSB_DEV_DESCR)U2Com_Buffer)->bDeviceClass;

        s = CtrlSetUsb2Address(((PUSB_SETUP_REQ)SetupSetUsb2Addr)->wValue);
        if(s == ERR_SUCCESS)
        {
            ThisUsb2Dev.DeviceAddress = ((PUSB_SETUP_REQ)SetupSetUsb2Addr)->wValue; // 保存USB地址

            PRINT("GetU2CfgDescr: ");
            s = CtrlGetU2ConfigDescr();
            if(s == ERR_SUCCESS)
            {
                for(i = 0; i < ((PUSB_CFG_DESCR)U2Com_Buffer)->wTotalLength; i++)
                {
                    PRINT("x%02X ", (uint16_t)(U2Com_Buffer));
                }
                PRINT("\n");
                /* 分析配置描述符,获取端点数据/各端点地址/各端点大小等,更新变量endp_addr和endp_size等 */
                cfg = ((PUSB_CFG_DESCR)U2Com_Buffer)->bConfigurationValue;
                if_cls = ((PUSB_CFG_DESCR_LONG)U2Com_Buffer)->itf_descr.bInterfaceClass; // 接口类代码

                if((dv_cls == 0x00) && (if_cls == USB_DEV_CLASS_STORAGE))
                { // 是USB存储类设备,基本上确认是U盘
#ifdef FOR_ROOT_UDISK_ONLY
                    CHRV3DiskStatus = DISK_USB_ADDR;
                    return (ERR_SUCCESS);
                }
                else
                    return (ERR_USB_UNSUPPORT);
#else
                    s = CtrlSetUsb2Config(cfg); // 设置USB设备配置
                    if(s == ERR_SUCCESS)
                    {
                        ThisUsb2Dev.DeviceStatus = ROOT_DEV_SUCCESS;
                        ThisUsb2Dev.DeviceType = USB_DEV_CLASS_STORAGE;
                        PRINT("U2 USB-Disk Ready\n");
                        SetUsb2Speed(1); // 默认为全速
                        return (ERR_SUCCESS);
                    }
                }
                else if((dv_cls == 0x00) && (if_cls == USB_DEV_CLASS_PRINTER) && ((PUSB_CFG_DESCR_LONG)U2Com_Buffer)->itf_descr.bInterfaceSubClass == 0x01)
                {                               // 是打印机类设备
                    s = CtrlSetUsb2Config(cfg); // 设置USB设备配置
                    if(s == ERR_SUCCESS)
                    {
                        //	需保存端点信息以便主程序进行USB传输
                        ThisUsb2Dev.DeviceStatus = ROOT_DEV_SUCCESS;
                        ThisUsb2Dev.DeviceType = USB_DEV_CLASS_PRINTER;
                        PRINT("U2 USB-Print Ready\n");
                        SetUsb2Speed(1); // 默认为全速
                        return (ERR_SUCCESS);
                    }
                }
                else if((dv_cls == 0x00) && (if_cls == USB_DEV_CLASS_HID) && ((PUSB_CFG_DESCR_LONG)U2Com_Buffer)->itf_descr.bInterfaceSubClass <= 0x01)
                { // 是HID类设备,键盘/鼠标等
                    //  从描述符中分析出HID中断端点的地址
                    s = AnalyzeU2HidIntEndp(U2Com_Buffer, 0); // 从描述符中分析出HID中断端点的地址
                    PRINT("AnalyzeU2HidIntEndp %02x\n", (uint16_t)s);
                    //  保存中断端点的地址,位7用于同步标志位,清0
                    if_cls = ((PUSB_CFG_DESCR_LONG)U2Com_Buffer)->itf_descr.bInterfaceProtocol;
                    s = CtrlSetUsb2Config(cfg); // 设置USB设备配置
                    if(s == ERR_SUCCESS)
                    {
                        //	    					Set_Idle( );
                        //	需保存端点信息以便主程序进行USB传输
                        ThisUsb2Dev.DeviceStatus = ROOT_DEV_SUCCESS;
                        if(if_cls == 1)
                        {
                            ThisUsb2Dev.DeviceType = DEV_TYPE_KEYBOARD;
                            //	进一步初始化,例如设备键盘指示灯LED等
                            PRINT("U2 USB-Keyboard Ready\n");
                            SetUsb2Speed(1); // 默认为全速
                            return (ERR_SUCCESS);
                        }
                        else if(if_cls == 2)
                        {
                            ThisUsb2Dev.DeviceType = DEV_TYPE_MOUSE;
                            //	为了以后查询鼠标状态,应该分析描述符,取得中断端口的地址,长度等信息
                            PRINT("U2 USB-Mouse Ready\n");
                            SetUsb2Speed(1); // 默认为全速
                            return (ERR_SUCCESS);
                        }
                        s = ERR_USB_UNSUPPORT;
                    }
                }
                else if(dv_cls == USB_DEV_CLASS_HUB)
                { // 是HUB类设备,集线器等
                    s = CtrlGetU2HubDescr();
                    if(s == ERR_SUCCESS)
                    {
                        PRINT("Max Port:%02X ", (((PXUSB_HUB_DESCR)U2Com_Buffer)->bNbrPorts));
                        ThisUsb2Dev.GpHUBPortNum = ((PXUSB_HUB_DESCR)U2Com_Buffer)->bNbrPorts; // 保存HUB的端口数量
                        if(ThisUsb2Dev.GpHUBPortNum > HUB_MAX_PORTS)
                        {
                            ThisUsb2Dev.GpHUBPortNum = HUB_MAX_PORTS; // 因为定义结构DevOnHubPort时人为假定每个HUB不超过HUB_MAX_PORTS个端口
                        }
                        s = CtrlSetUsb2Config(cfg); // 设置USB设备配置
                        if(s == ERR_SUCCESS)
                        {
                            ThisUsb2Dev.DeviceStatus = ROOT_DEV_SUCCESS;
                            ThisUsb2Dev.DeviceType = USB_DEV_CLASS_HUB;
                            //需保存端点信息以便主程序进行USB传输,本来中断端点可用于HUB事件通知,但本程序使用查询状态控制传输代替
                            //给HUB各端口上电,查询各端口状态,初始化有设备连接的HUB端口,初始化设备
                            for(i = 1; i <= ThisUsb2Dev.GpHUBPortNum; i++) // 给HUB各端口都上电
                            {
                                DevOnU2HubPort[i - 1].DeviceStatus = ROOT_DEV_DISCONNECT; // 清外部HUB端口上设备的状态
                                s = U2HubSetPortFeature(i, HUB_PORT_POWER);
                                if(s != ERR_SUCCESS)
                                {
                                    PRINT("Ext-HUB Port_%1d# power on error\n", (uint16_t)i); // 端口上电失败
                                }
                            }
                            PRINT("U2 USB-HUB Ready\n");
                            SetUsb2Speed(1); // 默认为全速
                            return (ERR_SUCCESS);
                        }
                    }
                }
                else
                {                               // 可以进一步分析
                    s = CtrlSetUsb2Config(cfg); // 设置USB设备配置
                    if(s == ERR_SUCCESS)
                    {
                        //	需保存端点信息以便主程序进行USB传输
                        ThisUsb2Dev.DeviceStatus = ROOT_DEV_SUCCESS;
                        ThisUsb2Dev.DeviceType = DEV_TYPE_UNKNOW;
                        SetUsb2Speed(1);      // 默认为全速
                        return (ERR_SUCCESS); /* 未知设备初始化成功 */
                    }
                }
#endif
            }
        }
    }

    PRINT("InitRootU2Dev Err = %02X\n", (uint16_t)s);
#ifdef FOR_ROOT_UDISK_ONLY
    CHRV3DiskStatus = DISK_CONNECT;
#else
    ThisUsb2Dev.DeviceStatus = ROOT_DEV_FAILED;
#endif
    SetUsb2Speed(1); // 默认为全速
    return (s);
}

从USB库的代码可以看到支持的有HID键鼠、USB-HUB、打印机等常见的USB设备。用之前做的USB拓展坞,上面有SL2.1A的分线器芯片。编译烧录:

2022.03.31_19.54.31.png

上电打开串口终端查看:

QQ录屏20220331172243.gif

实物连接:

IMG_20220331_192833.jpg

可以看到接了三台设备(HUB、键盘、鼠标)后仍然反应速度很快。

 


回复

7106

帖子

0

资源

五彩晶圆(中级)

测试的效果还行


回复
您需要登录后才可以回帖 登录 | 注册

相关帖子
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
    推荐帖子
    LTC1867芯片单极模式只能采样0~2V

    本帖最后由 清风烈酒 于 2018-1-30 12:58 编辑 在stm32f107基础上使用LTC1867芯片采样,设置单极性后只能采样要0~2V的电压, ...

    微软即将推出的HoloLens v2可能已经泄露

    微软并没有承认他们正在开发HoloLens v2,并且明白了2代产品采用改良的全息处置单元和改良的基于Kinect的深度感应单元。如今,行 ...

    RAM、ROM、SRAM、DRAM、SSRAM、SDRAM、FLASH、EEPROM

    RAM(Random Access Memory) 随机存储器。存储单元的内容可按需随意取出或存入,且存取的速度与存储单元的位置无关的存储器。这种 ...

    电机型号参数大全,再也不怕看不懂电机型号了

    电动机型号是便于使用、设计、制造等部门进行业务联系和简化技术文件中产品名称、规格、型式等叙述而引用的一种代号。防爆云平台 ...

    PCB走线角度选择到底该不该90°?

    现在但凡打开SoC原厂的pcbLayout Guide,都会提及到高速信号的走线的拐角角度问题,都会说高速信号不要以直角走线,要以45度角走 ...

    STM 的Arduion 无法下载固件问题?

    用Arduion 来编程STM32 非常方便,目前可以支持很多ST 官方的板子,如下图所示,但在下载的时候,基本没有速度,感觉是服务 ...

    关闭
    站长推荐上一条 1/7 下一条

    About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

    站点相关: 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

    北京市海淀区知春路23号集成电路设计园量子银座1305 电话:(010)82350740 邮编:100191

    电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2022 EEWORLD.com.cn, Inc. All rights reserved
    快速回复 返回顶部 返回列表