27

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

 3 years ago
source link: https://www.taterli.com/7107/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

对于大多数单片机而言,外设是用来和外部的各种各样的外部芯片沟通的,本身也不难,万变不离其宗.更多的是单片机上的逻辑,算法实现,但是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一定要用到的东西,描述符和端点分配.

A7jIJzQ.png!web

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;
}

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK