STM32片上驱动详解:USB驱动实践指南
一、USB概述
USB全称Universal Serial Bus(通用串行总线); USB是一种支持热插拔的总线接口,使用差分线(D-和D+)来传输数据,USB支持两种供电模式:总线供电和自供电,总线供电就是由USB接口为外部设备供电,在USB2.0下,总线供电最大可以提供500mA的电流。 |
|
USB电气特性:
ID=1:OTG设备工作在从机模式; ID=0:OTG设备工作在主机模式,比如U盘会将ID线拉低,上,需要插到另一端的USB A插座上。 |
USB拓补结构:
|
|
二、USB虚拟串口
CDC (Communication Device Class):用于创建虚拟串行端口,适合用于串口通信的替代。 |
![]() ![]() |
1、CubeMX驱动生成
使能USB_FS: 配置USB_DEVICE: 配置时钟: |
2、使用USB设备
![]() |
确保HAL库hal_conf.h功能模块使能(CubeMX会自动解除注释): #define HAL_PCD_MODULE_ENABLED 注释cubemx/Inc/usb_device.h报错信息: //#include "usbd_def.h" 添加drivers/drv_usbd.c宏定义: #define EP_MPS_64 0U 添加usbd_conf.c硬件驱动到board.c: void HAL_PCD_MspInit(PCD_HandleTypeDef* pcdHandle) { GPIO_InitTypeDef GPIO_InitStruct = {0}; if(pcdHandle->Instance==USB_OTG_FS) { /* USER CODE BEGIN USB_OTG_FS_MspInit 0 */
/* USER CODE END USB_OTG_FS_MspInit 0 */
__HAL_RCC_GPIOA_CLK_ENABLE(); /**USB_OTG_FS GPIO Configuration PA11 ——> USB_OTG_FS_DM PA12 ——> USB_OTG_FS_DP */ GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_12; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF10_OTG_FS; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* Peripheral clock enable */ __HAL_RCC_USB_OTG_FS_CLK_ENABLE();
/* Peripheral interrupt init */ HAL_NVIC_SetPriority(OTG_FS_IRQn, 0, 0); HAL_NVIC_EnableIRQ(OTG_FS_IRQn); /* USER CODE BEGIN USB_OTG_FS_MspInit 1 */
/* USER CODE END USB_OTG_FS_MspInit 1 */ } } 修改board.h: #define BSP_USING_USBDEVICE |
list device可以看到usbd和vcom就大功告成: |
3、示例代码
#include <rtthread.h>
#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
static struct rt_semaphore rx_sem;
static rt_device_t serial;
static rt_err_t uart_input(rt_device_t dev, rt_size_t size)
{
// 串口接收到数据后产生中断,调用此回调函数,然后发送接收信号量
rt_sem_release(&rx_sem);
return RT_EOK;
}
static void serial_thread_entry(void *parameter)
{
char ch;
LOG_D("uart_rx_thread_entry runing..\n");
while (1) {
// 从串口读取一个字节的数据,没有读取到则等待接收信号量
while (rt_device_read(serial, -1, &ch, 1) != 1) {
// 阻塞等待接收信号量,等到信号量后再次读取数据
rt_sem_take(&rx_sem, RT_WAITING_FOREVER);
}
rt_kprintf("%c",ch);
// 读取到的数据通过串口错位输出
rt_device_write(serial, 0, &ch, 1);
}
}
static int serial_init()
{
rt_err_t ret = RT_EOK;
// 查找串口设备
serial = rt_device_find("vcom");
if (serial == RT_NULL) {
rt_kprintf("can't find vcom device!\n");
return RT_ERROR;
}
// 初始化串口设备
ret = rt_device_init(serial);
if (serial == RT_NULL) {
rt_kprintf("can't initialize vcom device!\n");
return RT_ERROR;
}
// 打开设备,可读写,中断接收
ret = rt_device_open(serial, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX);
// 初始化信号量
rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO);
// 设置接收回调函数
rt_device_set_rx_indicate(serial, uart_input);
// 创建接收线程
rt_thread_t thread = rt_thread_create("serial", serial_thread_entry, RT_NULL, 1024, 25, 10);
if (thread == RT_NULL) {
LOG_E("rt_thread_create failed...\n");
}
rt_thread_startup(thread);
return RT_EOK;
}
int main(void)
{
serial_init();
while (1) {
LOG_D("Hello RT-Thread!");
rt_thread_mdelay(1000);
}
return RT_EOK;
}
作者:Xiangfu DING