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