IMXRT学习记录 – USB初探索(普通设备)

对于大多数单片机而言,外设是用来和外部的各种各样的外部芯片沟通的,本身也不难,万变不离其宗.更多的是单片机上的逻辑,算法实现,但是USB,以太网之类的外设就复杂多了.

因为USB IP相对复杂,就会有人考虑买成熟IP,也有人按照标准来实现,反正各家人都有各家人的实现方式,但是,USB始终也不能离开协议来讲,描述符通信方式等等.

在IMXRT中,USB例子的main文件全是宏定义?看来是很多种实现方式,被他揉一个文件里了,Bare Metal凡是并不关心,因为大多数复杂的程序都上了RTOS之类的,BM实在是有点考验人,我的能力驾驭不来.

在IMXRT中,可以让USB由单独任务来管理,也可以并到一个任务里管理,受USB_DEVICE_CONFIG_USE_TASK宏定义影响,主要管理一些USB可选功能.初始化交由USB_DeviceApplicationInit来负责.如果要最小化地研究USB的实现,只需要管初始化和中断就可以了.

不要被大量宏定义吓到,实际上只有USB_DeviceClockInit->USB_DeviceClassInit->USB_DeviceIsrEnable->USB_DeviceRun

static void USB_DeviceApplicationInit(void)
{
    USB_DeviceClockInit();

    /* Set HID mouse to default state */
    g_UsbDeviceHidMouse.speed = USB_SPEED_FULL;
    g_UsbDeviceHidMouse.attach = 0U;
    g_UsbDeviceHidMouse.hidHandle = (class_handle_t)NULL;
    g_UsbDeviceHidMouse.deviceHandle = NULL;
    g_UsbDeviceHidMouse.buffer = s_MouseBuffer;

    /* Initialize the usb stack and class drivers */
    if (kStatus_USB_Success !=
        USB_DeviceClassInit(CONTROLLER_ID, &g_UsbDeviceHidConfigList, &g_UsbDeviceHidMouse.deviceHandle))
    {
        usb_echo("USB device mouse failed\r\n");
        return;
    }
    else
    {
        usb_echo("USB device HID mouse demo\r\n");
        /* Get the HID mouse class handle */
        g_UsbDeviceHidMouse.hidHandle = g_UsbDeviceHidConfigList.config->classHandle;
    }

    USB_DeviceIsrEnable();

    /* Start USB device HID mouse */
    USB_DeviceRun(g_UsbDeviceHidMouse.deviceHandle);
}

由于我用的芯片比较垃圾(低端),只有一个USB,也就只有1个EHCI,所以除了USB_DeviceClassInit外,其他应该都可以全抄,如果有不同的配置,只需要修改引脚和时钟也是很简单的.所以重点是USB_DeviceClassInit和USB的中断处理.

可见初始化需要传入g_UsbDeviceHidConfigList和deviceHandle,其中deviceHandle已知是被返回用来储存Handle的,展开g_UsbDeviceHidConfigList大概如下(未完全展开):

static usb_status_t USB_DeviceCallback(usb_device_handle handle, uint32_t event, void *param);
static usb_status_t USB_DeviceHidMouseCallback(class_handle_t handle, uint32_t event, void *param);

usb_device_interfaces_struct_t g_UsbDeviceHidMouseInterfaces[USB_HID_MOUSE_INTERFACE_COUNT] = {
    {
        USB_HID_MOUSE_CLASS,           /* HID mouse class code */
        USB_HID_MOUSE_SUBCLASS,        /* HID mouse subclass code */
        USB_HID_MOUSE_PROTOCOL,        /* HID mouse protocol code */
        USB_HID_MOUSE_INTERFACE_INDEX, /* The interface number of the HID mouse */
        g_UsbDeviceHidMouseInterface,  /* Interfaces handle */
        sizeof(g_UsbDeviceHidMouseInterface) / sizeof(usb_device_interfaces_struct_t),
    },
};

usb_device_interface_list_t g_UsbDeviceHidMouseInterfaceList[USB_DEVICE_CONFIGURATION_COUNT] = {
    {
        USB_HID_MOUSE_INTERFACE_COUNT, /* The interface count of the HID mouse */
        g_UsbDeviceHidMouseInterfaces, /* The interfaces handle */
    },
};

usb_device_class_struct_t g_UsbDeviceHidMouseConfig = {
    g_UsbDeviceHidMouseInterfaceList, /* The interface list of the HID mouse */
    kUSB_DeviceClassTypeHid,          /* The HID class type */
    USB_DEVICE_CONFIGURATION_COUNT,   /* The configuration count */
};

/* Set class configurations */
usb_device_class_config_struct_t g_UsbDeviceHidConfig[1] = {{
    USB_DeviceHidMouseCallback, /* HID mouse class callback pointer */
    (class_handle_t)NULL,       /* The HID class handle, This field is set by USB_DeviceClassInit */
    &g_UsbDeviceHidMouseConfig, /* The HID mouse configuration, including class code, subcode, and protocol, class type,
                           transfer type, endpoint address, max packet size, etc.*/
}};

/* Set class configuration list */
usb_device_class_config_list_struct_t g_UsbDeviceHidConfigList = {
    g_UsbDeviceHidConfig, /* Class configurations */
    USB_DeviceCallback,   /* Device callback pointer */
    1U,                   /* Class count */
};

可见注册了两个回调,回调用于通信时的处理,暂时也不看.重点落在g_UsbDeviceHidMouseConfig,这里其实指向的是我们USB一定要用到的东西,描述符和端点分配.

USB_DeviceHidMouseCallback处理针对传入这个端点的各类REQUEST,包括Set / Get Report,Set / Get Idle,Set / Get Protocol,Send / Get Response,RequestReportBuffer都全部要回调这个函数,而usb_device_hid_mouse例子中,就等kUSB_DeviceHidEventSendResponse事件来临,进行鼠标的信息上报.上报使用USB_DeviceHidSend函数主动发送.在我们实现自己的逻辑时候,应该先准备数据,等待受到kUSB_DeviceHidEventSendResponse再上报,或者控制至足够间隔(牺牲带宽)

USB_DeviceCallback只负责和Device相关的回调,而关于数据就不在这里处理.可以处理的事件有很多,还有部分是可选事件,比如ATTACH/DETACH之类的,我们能正确报告描述符,设置速度等等,全靠这个实现.但是,大多数的外设初始化过程并没太大区别,所以这部分也变得很简单.当然多个设备要在这里分别枚举.这里举例kUSB_DeviceEventGetHidDescriptor来说,他会直接调起USB_DeviceGetHidDescriptor,如果这里涉及多个HID设备,要分别设计不同的USB_DeviceGetHidDescriptor,这样给USB提供更多的灵活性.

USB_DMA_INIT_DATA_ALIGN(USB_DATA_ALIGN_SIZE)
uint8_t g_UsbDeviceConfigurationDescriptor[] = {
    USB_DESCRIPTOR_LENGTH_CONFIGURE, /* Size of this descriptor in bytes */
    USB_DESCRIPTOR_TYPE_CONFIGURE,   /* CONFIGURATION Descriptor Type */
    USB_SHORT_GET_LOW(USB_DESCRIPTOR_LENGTH_CONFIGURE + USB_DESCRIPTOR_LENGTH_INTERFACE + USB_DESCRIPTOR_LENGTH_HID +
                      USB_DESCRIPTOR_LENGTH_ENDPOINT),
    USB_SHORT_GET_HIGH(USB_DESCRIPTOR_LENGTH_CONFIGURE + USB_DESCRIPTOR_LENGTH_INTERFACE + USB_DESCRIPTOR_LENGTH_HID +
                       USB_DESCRIPTOR_LENGTH_ENDPOINT), /* Total length of data returned for this configuration. */
    USB_HID_MOUSE_INTERFACE_COUNT,                      /* Number of interfaces supported by this configuration */
    USB_HID_MOUSE_CONFIGURE_INDEX,                      /* Value to use as an argument to the
                                                             SetConfiguration() request to select this configuration */
    0x00U,                                              /* Index of string descriptor describing this configuration */
    (USB_DESCRIPTOR_CONFIGURE_ATTRIBUTE_D7_MASK) |
        (USB_DEVICE_CONFIG_SELF_POWER << USB_DESCRIPTOR_CONFIGURE_ATTRIBUTE_SELF_POWERED_SHIFT) |
        (USB_DEVICE_CONFIG_REMOTE_WAKEUP << USB_DESCRIPTOR_CONFIGURE_ATTRIBUTE_REMOTE_WAKEUP_SHIFT),
    /* Configuration characteristics
         D7: Reserved (set to one)
         D6: Self-powered
         D5: Remote Wakeup
         D4...0: Reserved (reset to zero)
    */
    USB_DEVICE_MAX_POWER,            /* Maximum power consumption of the USB
                                      * device from the bus in this specific
                                      * configuration when the device is fully
                                      * operational. Expressed in 2 mA units
                                      *  (i.e., 50 = 100 mA).
                                      */
    USB_DESCRIPTOR_LENGTH_INTERFACE, /* Size of this descriptor in bytes */
    USB_DESCRIPTOR_TYPE_INTERFACE,   /* INTERFACE Descriptor Type */
    USB_HID_MOUSE_INTERFACE_INDEX,   /* Number of this interface. */
    0x00U,                           /* Value used to select this alternate setting
                                        for the interface identified in the prior field */
    USB_HID_MOUSE_ENDPOINT_COUNT,    /* Number of endpoints used by this
                                          interface (excluding endpoint zero). */
    USB_HID_MOUSE_CLASS,             /* Class code (assigned by the USB-IF). */
    USB_HID_MOUSE_SUBCLASS,          /* Subclass code (assigned by the USB-IF). */
    USB_HID_MOUSE_PROTOCOL,          /* Protocol code (assigned by the USB). */
    0x00U,                           /* Index of string descriptor describing this interface */

    USB_DESCRIPTOR_LENGTH_HID,      /* Numeric expression that is the total size of the
                                       HID descriptor. */
    USB_DESCRIPTOR_TYPE_HID,        /* Constant name specifying type of HID
                                       descriptor. */
    0x00U, 0x01U,                   /* Numeric expression identifying the HID Class
                                       Specification release. */
    0x00U,                          /* Numeric expression identifying country code of
                                       the localized hardware */
    0x01U,                          /* Numeric expression specifying the number of
                                       class descriptors(at least one report descriptor) */
    USB_DESCRIPTOR_TYPE_HID_REPORT, /* Constant name identifying type of class descriptor. */
    USB_SHORT_GET_LOW(USB_DESCRIPTOR_LENGTH_HID_MOUSE_REPORT),
    USB_SHORT_GET_HIGH(USB_DESCRIPTOR_LENGTH_HID_MOUSE_REPORT),
    /* Numeric expression that is the total size of the
       Report descriptor. */
    USB_DESCRIPTOR_LENGTH_ENDPOINT, /* Size of this descriptor in bytes */
    USB_DESCRIPTOR_TYPE_ENDPOINT,   /* ENDPOINT Descriptor Type */
    USB_HID_MOUSE_ENDPOINT_IN | (USB_IN << USB_DESCRIPTOR_ENDPOINT_ADDRESS_DIRECTION_SHIFT), /* The address of the endpoint on the USB device described by this descriptor. */ USB_ENDPOINT_INTERRUPT, /* This field describes the endpoint's attributes */ USB_SHORT_GET_LOW(FS_HID_MOUSE_INTERRUPT_IN_PACKET_SIZE), USB_SHORT_GET_HIGH(FS_HID_MOUSE_INTERRUPT_IN_PACKET_SIZE), /* Maximum packet size this endpoint is capable of sending or receiving when this configuration is selected. */ FS_HID_MOUSE_INTERRUPT_IN_INTERVAL, /* Interval for polling endpoint for data transfers. */ }; /* Get hid descriptor request */ usb_status_t USB_DeviceGetHidDescriptor(usb_device_handle handle, usb_device_get_hid_descriptor_struct_t *hidDescriptor) { if (USB_HID_MOUSE_INTERFACE_INDEX == hidDescriptor->interfaceNumber)
    {
        hidDescriptor->buffer =
            &g_UsbDeviceConfigurationDescriptor[USB_DESCRIPTOR_LENGTH_CONFIGURE + USB_DESCRIPTOR_LENGTH_INTERFACE];
        hidDescriptor->length = USB_DESCRIPTOR_LENGTH_HID;
    }
    else
    {
        return kStatus_USB_InvalidRequest;
    }
    return kStatus_USB_Success;
}

看来只是直接内容直接返回回去就行了.

发表评论

电子邮件地址不会被公开。 必填项已用*标注