对于大多数单片机而言,外设是用来和外部的各种各样的外部芯片沟通的,本身也不难,万变不离其宗.更多的是单片机上的逻辑,算法实现,但是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; }
看来只是直接内容直接返回回去就行了.