基于STM32F407的USB CDC通信实现
一、系统概述
1.1 USB CDC(Communication Device Class)介绍
USB CDC类允许将USB设备模拟成串口设备,实现与PC的虚拟串口通信
优点:即插即用、高速传输、免驱动(Windows 10自动识别)
1.2 硬件连接
// STM32F407 USB OTG FS连接
// PA11: USB_DM (Data Minus)
// PA12: USB_DP (Data Plus)
// VBUS: 5V电源检测(可选)
二、完整工程代码
2.1 工程结构
STM32F407_USB_CDC/
├── Core/
│ ├── Inc/
│ │ ├── main.h
│ │ ├── usbd_cdc_if.h
│ │ ├── usb_device.h
│ │ └── ...
│ ├── Src/
│ │ ├── main.c
│ │ ├── usbd_cdc_if.c
│ │ ├── usb_device.c
│ │ └── ...
│ └── Startup/
├── Drivers/
├── Middlewares/
│ └── ST/
│ ├── STM32_USB_Device_Library/
│ └── STM32_USB_Host_Library/
└── MDK-ARM/
2.2 主程序文件
// main.c
#include "main.h"
#include "usb_device.h"
#include "usbd_cdc_if.h"
#include <stdio.h>
#include <string.h>
// 全局变量
PCD_HandleTypeDef hpcd_USB_OTG_FS;
UART_HandleTypeDef huart2; // 调试串口
TIM_HandleTypeDef htim2; // 定时器
// 环形缓冲区
#define USB_RX_BUFFER_SIZE 1024
uint8_t usb_rx_buffer[USB_RX_BUFFER_SIZE];
uint16_t usb_rx_write_index = 0;
uint16_t usb_rx_read_index = 0;
uint16_t usb_rx_count = 0;
// 调试信息缓冲区
char debug_buffer[128];
// 系统状态
typedef enum {
USB_NOT_READY = 0,
USB_READY,
USB_BUSY
} USB_Status;
USB_Status usb_status = USB_NOT_READY;
volatile uint8_t usb_connected = 0;
// 函数声明
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
static void MX_USB_OTG_FS_PCD_Init(void);
static void MX_TIM2_Init(void);
void Error_Handler(void);
void USB_Transmit(const char* data, uint16_t len);
void USB_Printf(const char* format, ...);
void Process_USB_Data(uint8_t* data, uint16_t len);
void LED_Indicator(void);
int main(void) {
// HAL库初始化
HAL_Init();
// 系统时钟配置
SystemClock_Config();
// 外设初始化
MX_GPIO_Init();
MX_USART2_UART_Init();
MX_USB_OTG_FS_PCD_Init();
MX_TIM2_Init();
// USB设备初始化
MX_USB_DEVICE_Init();
// 启动定时器
HAL_TIM_Base_Start_IT(&htim2);
// 开机信息
printf("STM32F407 USB CDC Demo Started\r\n");
printf("System Clock: %ld Hz\r\n", HAL_RCC_GetSysClockFreq());
printf("USB CDC Virtual COM Port Ready\r\n");
// 主循环
while (1) {
// 1. USB数据接收处理
if (usb_rx_count > 0) {
uint8_t data;
data = usb_rx_buffer[usb_rx_read_index];
usb_rx_read_index = (usb_rx_read_index + 1) % USB_RX_BUFFER_SIZE;
usb_rx_count--;
// 处理接收到的数据
static uint8_t cmd_buffer[64];
static uint8_t cmd_index = 0;
if (data == '\r' || data == '\n') {
if (cmd_index > 0) {
cmd_buffer[cmd_index] = '\0';
Process_USB_Data(cmd_buffer, cmd_index);
cmd_index = 0;
}
} else if (cmd_index < sizeof(cmd_buffer) - 1) {
cmd_buffer[cmd_index++] = data;
}
}
// 2. LED状态指示
LED_Indicator();
// 3. 低功耗处理
if (!usb_connected) {
HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);
}
}
}
// USB数据接收回调函数
void CDC_RxCallback(uint8_t* Buf, uint32_t *Len) {
for (uint32_t i = 0; i < *Len; i++) {
uint16_t next_index = (usb_rx_write_index + 1) % USB_RX_BUFFER_SIZE;
if (next_index != usb_rx_read_index) { // 缓冲区未满
usb_rx_buffer[usb_rx_write_index] = Buf[i];
usb_rx_write_index = next_index;
usb_rx_count++;
} else {
// 缓冲区满,丢弃数据
break;
}
}
// 准备接收下一包数据
CDC_Receive_FS(Buf, USB_RX_BUFFER_SIZE);
}
// 处理USB接收数据
void Process_USB_Data(uint8_t* data, uint16_t len) {
char response[128];
printf("Received: %s\r\n", data);
// 命令解析
if (strcmp((char*)data, "LED ON") == 0) {
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, GPIO_PIN_SET);
sprintf(response, "LED ON OK\r\n");
} else if (strcmp((char*)data, "LED OFF") == 0) {
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, GPIO_PIN_RESET);
sprintf(response, "LED OFF OK\r\n");
} else if (strcmp((char*)data, "GET TEMP") == 0) {
// 模拟温度读取
float temp = 25.5f;
sprintf(response, "Temperature: %.1f C\r\n", temp);
} else if (strcmp((char*)data, "GET VOLT") == 0) {
// 模拟电压读取
float voltage = 3.3f;
sprintf(response, "Voltage: %.2f V\r\n", voltage);
} else if (strcmp((char*)data, "HELP") == 0) {
sprintf(response, "Available Commands:\r\n"
"LED ON/OFF - Control LED\r\n"
"GET TEMP - Get temperature\r\n"
"GET VOLT - Get voltage\r\n"
"HELP - Show this help\r\n");
} else {
sprintf(response, "Unknown command: %s\r\n", data);
}
// 发送响应
USB_Transmit(response, strlen(response));
}
// USB数据发送函数
void USB_Transmit(const char* data, uint16_t len) {
if (usb_status != USB_READY) {
return;
}
uint8_t status = CDC_Transmit_FS((uint8_t*)data, len);
if (status != USBD_OK) {
usb_status = USB_BUSY;
} else {
usb_status = USB_READY;
}
}
// USB格式化输出
void USB_Printf(const char* format, ...) {
char buffer[256];
va_list args;
va_start(args, format);
int len = vsnprintf(buffer, sizeof(buffer), format, args);
va_end(args);
if (len > 0) {
USB_Transmit(buffer, len);
}
}
// LED状态指示
void LED_Indicator(void) {
static uint32_t last_toggle = 0;
uint32_t current_time = HAL_GetTick();
if (usb_connected) {
// USB连接时,LED常亮
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_13, GPIO_PIN_SET);
} else {
// USB未连接时,LED闪烁
if (current_time - last_toggle > 500) { // 500ms闪烁
HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_13);
last_toggle = current_time;
}
}
}
// 定时器中断回调
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
if (htim->Instance == TIM2) {
static uint32_t counter = 0;
counter++;
// 每5秒发送一次状态信息
if (usb_connected && (counter % 500 == 0)) {
USB_Printf("System Status: Time=%lus\r\n", counter/100);
}
}
}
// USB连接状态回调
void USB_ConnectCallback(uint8_t state) {
usb_connected = state;
if (state) {
printf("USB Connected\r\n");
USB_Printf("STM32F407 USB CDC Ready\r\n");
} else {
printf("USB Disconnected\r\n");
}
}
// 系统时钟配置
void SystemClock_Config(void) {
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
// 配置HSE
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 8;
RCC_OscInitStruct.PLL.PLLN = 336;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 7;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
Error_Handler();
}
// 配置系统时钟
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK) {
Error_Handler();
}
// 配置外设时钟
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_USART2|RCC_PERIPHCLK_CLK48;
PeriphClkInitStruct.Usart2ClockSelection = RCC_USART2CLKSOURCE_PCLK1;
PeriphClkInitStruct.Clk48ClockSelection = RCC_CLK48CLKSOURCE_PLLQ;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) {
Error_Handler();
}
}
// USB OTG FS初始化
static void MX_USB_OTG_FS_PCD_Init(void) {
hpcd_USB_OTG_FS.Instance = USB_OTG_FS;
hpcd_USB_OTG_FS.Init.dev_endpoints = 4;
hpcd_USB_OTG_FS.Init.speed = PCD_SPEED_FULL;
hpcd_USB_OTG_FS.Init.dma_enable = DISABLE;
hpcd_USB_OTG_FS.Init.phy_itface = PCD_PHY_EMBEDDED;
hpcd_USB_OTG_FS.Init.Sof_enable = DISABLE;
hpcd_USB_OTG_FS.Init.low_power_enable = DISABLE;
hpcd_USB_OTG_FS.Init.lpm_enable = DISABLE;
hpcd_USB_OTG_FS.Init.vbus_sensing_enable = DISABLE;
hpcd_USB_OTG_FS.Init.use_dedicated_ep1 = DISABLE;
if (HAL_PCD_Init(&hpcd_USB_OTG_FS) != HAL_OK) {
Error_Handler();
}
}
// 串口初始化(调试用)
static void MX_USART2_UART_Init(void) {
huart2.Instance = USART2;
huart2.Init.BaudRate = 115200;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart2) != HAL_OK) {
Error_Handler();
}
}
// 定时器初始化
static void MX_TIM2_Init(void) {
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
htim2.Instance = TIM2;
htim2.Init.Prescaler = 8400 - 1; // 84MHz/8400 = 10kHz
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 10 - 1; // 10kHz/10 = 1kHz (1ms)
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_Base_Init(&htim2) != HAL_OK) {
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK) {
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK) {
Error_Handler();
}
}
// GPIO初始化
static void MX_GPIO_Init(void) {
__HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct = {0};
// LED引脚配置
GPIO_InitStruct.Pin = GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
// USB引脚配置
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);
}
// 错误处理
void Error_Handler(void) {
while (1) {
HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_14);
HAL_Delay(100);
}
}
// 重定向printf到串口
int __io_putchar(int ch) {
HAL_UART_Transmit(&huart2, (uint8_t*)&ch, 1, HAL_MAX_DELAY);
return ch;
}
2.3 USB CDC接口实现
// usbd_cdc_if.c
#include "usbd_cdc_if.h"
// 全局变量
USBD_HandleTypeDef *hUsbDeviceFS = NULL;
static int8_t CDC_Init_FS(void);
static int8_t CDC_DeInit_FS(void);
static int8_t CDC_Control_FS(uint8_t cmd, uint8_t* pbuf, uint16_t length);
static int8_t CDC_Receive_FS(uint8_t* pbuf, uint32_t *Len);
// USB CDC接口描述符
USBD_CDC_ItfTypeDef USBD_Interface_fops_FS = {
CDC_Init_FS,
CDC_DeInit_FS,
CDC_Control_FS,
CDC_Receive_FS
};
// 接收缓冲区
uint8_t UserRxBufferFS[APP_RX_DATA_SIZE];
uint8_t UserTxBufferFS[APP_TX_DATA_SIZE];
// CDC初始化
static int8_t CDC_Init_FS(void) {
// 设置Rx缓冲区
USBD_CDC_SetRxBuffer(hUsbDeviceFS, UserRxBufferFS);
// 启动接收
USBD_CDC_ReceivePacket(hUsbDeviceFS);
return (USBD_OK);
}
// CDC反初始化
static int8_t CDC_DeInit_FS(void) {
return (USBD_OK);
}
// CDC控制命令处理
static int8_t CDC_Control_FS(uint8_t cmd, uint8_t* pbuf, uint16_t length) {
switch (cmd) {
case CDC_SEND_ENCAPSULATED_COMMAND:
break;
case CDC_GET_ENCAPSULATED_RESPONSE:
break;
case CDC_SET_COMM_FEATURE:
break;
case CDC_GET_COMM_FEATURE:
break;
case CDC_CLEAR_COMM_FEATURE:
break;
case CDC_SET_LINE_CODING:
// 设置串口参数(波特率、数据位、停止位、校验位)
// 这些参数存储在pbuf中,但虚拟串口通常不需要处理
break;
case CDC_GET_LINE_CODING:
// 返回当前串口参数
// 设置默认参数:115200,8,N,1
pbuf[0] = 0x00; // 波特率 115200
pbuf[1] = 0xC2;
pbuf[2] = 0x01;
pbuf[3] = 0x00;
pbuf[4] = 0x00; // 1停止位
pbuf[5] = 0x00; // 无校验
pbuf[6] = 0x08; // 8数据位
break;
case CDC_SET_CONTROL_LINE_STATE:
// 处理DTR/RTS信号
// 可以用来检测USB连接状态
if (pbuf[0] & 0x01) { // DTR置位
extern void USB_ConnectCallback(uint8_t state);
USB_ConnectCallback(1);
} else { // DTR复位
extern void USB_ConnectCallback(uint8_t state);
USB_ConnectCallback(0);
}
break;
case CDC_SEND_BREAK:
break;
default:
break;
}
return (USBD_OK);
}
// 数据接收回调
static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len) {
// 调用用户回调函数
extern void CDC_RxCallback(uint8_t* Buf, uint32_t *Len);
CDC_RxCallback(Buf, Len);
// 准备接收下一包数据
USBD_CDC_SetRxBuffer(hUsbDeviceFS, Buf);
USBD_CDC_ReceivePacket(hUsbDeviceFS);
return (USBD_OK);
}
// 数据发送函数
uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len) {
uint8_t result = USBD_OK;
// 设置Tx缓冲区
USBD_CDC_SetTxBuffer(hUsbDeviceFS, Buf, Len);
// 发送数据
result = USBD_CDC_TransmitPacket(hUsbDeviceFS);
return result;
}
2.4 USB设备配置
// usbd_conf.c
#include "usbd_conf.h"
PCD_HandleTypeDef hpcd_USB_OTG_FS;
void Error_Handler(void);
// USB设备初始化回调
void HAL_PCD_MspInit(PCD_HandleTypeDef* pcdHandle) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(pcdHandle->Instance == USB_OTG_FS) {
// 使能USB OTG FS时钟
__HAL_RCC_USB_OTG_FS_CLK_ENABLE();
// 配置USB GPIO
// PA11 - USB_DM
// PA12 - USB_DP
__HAL_RCC_GPIOA_CLK_ENABLE();
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);
// 使能USB中断
HAL_NVIC_SetPriority(OTG_FS_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(OTG_FS_IRQn);
}
}
// USB设备反初始化回调
void HAL_PCD_MspDeInit(PCD_HandleTypeDef* pcdHandle) {
if(pcdHandle->Instance == USB_OTG_FS) {
// 禁用USB时钟
__HAL_RCC_USB_OTG_FS_CLK_DISABLE();
// 禁用USB GPIO
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_11 | GPIO_PIN_12);
// 禁用USB中断
HAL_NVIC_DisableIRQ(OTG_FS_IRQn);
}
}
// USB OTG FS中断处理
void OTG_FS_IRQHandler(void) {
HAL_PCD_IRQHandler(&hpcd_USB_OTG_FS);
}
2.5 USB描述符
// usbd_desc.c
#include "usbd_desc.h"
// USB设备描述符
uint8_t *USBD_FS_DeviceDescriptor(USBD_SpeedTypeDef speed, uint16_t *length) {
static uint8_t dev_desc[USB_LEN_DEV_DESC] = {
0x12, // bLength
0x01, // bDescriptorType (Device)
0x00, 0x02, // bcdUSB 2.00
0x02, // bDeviceClass (CDC)
0x00, // bDeviceSubClass
0x00, // bDeviceProtocol
0x40, // bMaxPacketSize0
0x83, 0x04, // idVendor (0x0483)
0x40, 0x57, // idProduct (0x5740)
0x00, 0x02, // bcdDevice 2.00
0x01, // iManufacturer
0x02, // iProduct
0x03, // iSerialNumber
0x01 // bNumConfigurations
};
*length = sizeof(dev_desc);
return dev_desc;
}
// USB配置描述符
uint8_t *USBD_FS_ConfigDescriptor(USBD_SpeedTypeDef speed, uint16_t *length) {
static uint8_t config_desc[USB_LEN_CONFIG_DESC] = {
// 配置描述符
0x09, // bLength
0x02, // bDescriptorType (Configuration)
0x43, 0x00, // wTotalLength 67
0x02, // bNumInterfaces
0x01, // bConfigurationValue
0x00, // iConfiguration
0xC0, // bmAttributes (Self Powered)
0x32, // MaxPower 100mA
// 接口0描述符 (通信接口)
0x09, // bLength
0x04, // bDescriptorType (Interface)
0x00, // bInterfaceNumber
0x00, // bAlternateSetting
0x01, // bNumEndpoints
0x02, // bInterfaceClass (CDC)
0x02, // bInterfaceSubClass (ACM)
0x01, // bInterfaceProtocol (AT)
0x00, // iInterface
// 功能描述符
0x05, // bLength
0x24, // bDescriptorType (CS_INTERFACE)
0x00, // bDescriptorSubtype (Header)
0x10, 0x01, // bcdCDC 1.10
0x04, // bLength
0x24, // bDescriptorType
0x02, // bDescriptorSubtype (ACM)
0x02, // bmCapabilities
0x05, // bLength
0x24, // bDescriptorType
0x06, // bDescriptorSubtype (Union)
0x00, // bMasterInterface
0x01, // bSlaveInterface
0x05, // bLength
0x24, // bDescriptorType
0x01, // bDescriptorSubtype (Call Management)
0x00, // bmCapabilities
0x01, // bDataInterface
// 端点描述符 (通知端点)
0x07, // bLength
0x05, // bDescriptorType (Endpoint)
0x81, // bEndpointAddress (IN)
0x03, // bmAttributes (Interrupt)
0x08, 0x00, // wMaxPacketSize
0xFF, // bInterval
// 接口1描述符 (数据接口)
0x09, // bLength
0x04, // bDescriptorType (Interface)
0x01, // bInterfaceNumber
0x00, // bAlternateSetting
0x02, // bNumEndpoints
0x0A, // bInterfaceClass (CDC Data)
0x00, // bInterfaceSubClass
0x00, // bInterfaceProtocol
0x00, // iInterface
// 端点描述符 (数据输出端点)
0x07, // bLength
0x05, // bDescriptorType (Endpoint)
0x02, // bEndpointAddress (OUT)
0x02, // bmAttributes (Bulk)
0x40, 0x00, // wMaxPacketSize
0x00, // bInterval
// 端点描述符 (数据输入端点)
0x07, // bLength
0x05, // bDescriptorType (Endpoint)
0x82, // bEndpointAddress (IN)
0x02, // bmAttributes (Bulk)
0x40, 0x00, // wMaxPacketSize
0x00 // bInterval
};
*length = sizeof(config_desc);
return config_desc;
}
// 字符串描述符
uint8_t *USBD_FS_StringStrDescriptor(USBD_SpeedTypeDef speed, uint8_t idx, uint16_t *length) {
static uint8_t string_desc[256];
if (idx == 0) {
// 语言ID
string_desc[0] = 0x04;
string_desc[1] = 0x03;
string_desc[2] = 0x09;
string_desc[3] = 0x04;
*length = 4;
} else if (idx == 1) {
// 厂商字符串
const char manufacturer[] = "STMicroelectronics";
string_desc[0] = 2 + strlen(manufacturer) * 2;
string_desc[1] = 0x03;
for (int i = 0; i < strlen(manufacturer); i++) {
string_desc[2 + i * 2] = manufacturer[i];
string_desc[3 + i * 2] = 0;
}
*length = string_desc[0];
} else if (idx == 2) {
// 产品字符串
const char product[] = "STM32 Virtual COM Port";
string_desc[0] = 2 + strlen(product) * 2;
string_desc[1] = 0x03;
for (int i = 0; i < strlen(product); i++) {
string_desc[2 + i * 2] = product[i];
string_desc[3 + i * 2] = 0;
}
*length = string_desc[0];
} else if (idx == 3) {
// 序列号
const char serial[] = "00000000001A";
string_desc[0] = 2 + strlen(serial) * 2;
string_desc[1] = 0x03;
for (int i = 0; i < strlen(serial); i++) {
string_desc[2 + i * 2] = serial[i];
string_desc[3 + i * 2] = 0;
}
*length = string_desc[0];
} else {
return NULL;
}
return string_desc;
}
三、PC端测试程序(Python)
# usb_cdc_test.py
import serial
import time
import threading
import sys
class USB_CDC_Test:
def __init__(self, port='COM3', baudrate=115200):
self.port = port
self.baudrate = baudrate
self.serial = None
self.running = False
self.receive_thread = None
def connect(self):
"""连接串口"""
try:
self.serial = serial.Serial(
port=self.port,
baudrate=self.baudrate,
bytesize=serial.EIGHTBITS,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
timeout=1
)
if self.serial.is_open:
print(f"Connected to {self.port}")
return True
else:
print(f"Failed to connect to {self.port}")
return False
except serial.SerialException as e:
print(f"Serial error: {e}")
return False
def disconnect(self):
"""断开连接"""
if self.serial and self.serial.is_open:
self.serial.close()
print("Disconnected")
def start_receive_thread(self):
"""启动接收线程"""
self.running = True
self.receive_thread = threading.Thread(target=self.receive_data)
self.receive_thread.daemon = True
self.receive_thread.start()
def stop_receive_thread(self):
"""停止接收线程"""
self.running = False
if self.receive_thread:
self.receive_thread.join(timeout=1)
def receive_data(self):
"""接收数据线程"""
while self.running and self.serial and self.serial.is_open:
try:
if self.serial.in_waiting > 0:
data = self.serial.read(self.serial.in_waiting)
if data:
self.process_received_data(data)
time.sleep(0.01)
except Exception as e:
print(f"Receive error: {e}")
break
def process_received_data(self, data):
"""处理接收到的数据"""
try:
text = data.decode('utf-8', errors='ignore')
print(f"Received: {text}", end='')
except Exception as e:
print(f"Decode error: {e}")
def send_command(self, command):
"""发送命令"""
if self.serial and self.serial.is_open:
try:
cmd = command + '\r\n'
self.serial.write(cmd.encode())
print(f"Sent: {command}")
except Exception as e:
print(f"Send error: {e}")
else:
print("Serial port not open")
def interactive_mode(self):
"""交互模式"""
print("\n=== STM32 USB CDC Test ===")
print("Commands:")
print(" led on - Turn LED ON")
print(" led off - Turn LED OFF")
print(" get temp - Get temperature")
print(" get volt - Get voltage")
print(" help - Show help")
print(" exit - Exit program")
print("===========================\n")
self.start_receive_thread()
while True:
try:
cmd = input("Enter command: ").strip().lower()
if cmd == 'exit':
break
elif cmd == 'help':
self.send_command("HELP")
elif cmd == 'led on':
self.send_command("LED ON")
elif cmd == 'led off':
self.send_command("LED OFF")
elif cmd == 'get temp':
self.send_command("GET TEMP")
elif cmd == 'get volt':
self.send_command("GET VOLT")
else:
print("Unknown command")
except KeyboardInterrupt:
print("\nExiting...")
break
except Exception as e:
print(f"Error: {e}")
self.stop_receive_thread()
self.disconnect()
def auto_test(self):
"""自动测试模式"""
if not self.connect():
return
self.start_receive_thread()
time.sleep(2) # 等待连接稳定
test_commands = [
"HELP",
"LED ON",
"GET TEMP",
"GET VOLT",
"LED OFF"
]
for cmd in test_commands:
print(f"\n>>> Testing command: {cmd}")
self.send_command(cmd)
time.sleep(1) # 等待响应
time.sleep(2)
self.stop_receive_thread()
self.disconnect()
def main():
"""主函数"""
# 检测可用串口
import serial.tools.list_ports
ports = list(serial.tools.list_ports.comports())
if not ports:
print("No COM ports found!")
return
print("Available COM ports:")
for i, port in enumerate(ports):
print(f" {i+1}. {port.device} - {port.description}")
# 选择端口
try:
choice = int(input("\nSelect port number (0 for auto test): "))
if choice == 0:
# 自动测试所有端口
for port in ports:
print(f"\nTrying {port.device}...")
test = USB_CDC_Test(port.device)
test.auto_test()
elif 1 <= choice <= len(ports):
port_name = ports[choice-1].device
test = USB_CDC_Test(port_name)
mode = input("Select mode (1: Interactive, 2: Auto test): ")
if mode == '1':
if test.connect():
test.interactive_mode()
elif mode == '2':
test.auto_test()
else:
print("Invalid mode")
else:
print("Invalid choice")
except ValueError:
print("Invalid input")
except Exception as e:
print(f"Error: {e}")
if __name__ == "__main__":
main()
四、Keil5工程配置
4.1 工程设置
Target Options:
- Device: STM32F407VGTx
- Target:
- Xtal: 8.0MHz
- Use MicroLIB: ✓
- Optimization: Level 2 (-O2)
C/C++:
- Define: USE_HAL_DRIVER, STM32F407xx, USE_USB_OTG_FS
- Include Paths: Add all required paths
4.2 链接器设置
// STM32F407VGTx_FLASH.ld
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 1024K
}
五、USB描述符工具
# usb_descriptor_generator.py
def generate_cdc_descriptor(vid=0x0483, pid=0x5740):
"""生成CDC描述符"""
desc = {
'device': [
0x12, 0x01, 0x00, 0x02, 0x02, 0x00, 0x00, 0x40,
vid & 0xFF, vid >> 8,
pid & 0xFF, pid >> 8,
0x00, 0x02, 0x01, 0x02, 0x03, 0x01
],
'config': [
# 配置描述符
0x09, 0x02, 0x43, 0x00, 0x02, 0x01, 0x00, 0xC0, 0x32,
# 接口0
0x09, 0x04, 0x00, 0x00, 0x01, 0x02, 0x02, 0x01, 0x00,
# 功能描述符
0x05, 0x24, 0x00, 0x10, 0x01,
0x04, 0x24, 0x02, 0x02,
0x05, 0x24, 0x06, 0x00, 0x01,
0x05, 0x24, 0x01, 0x00, 0x01,
# 端点(通知)
0x07, 0x05, 0x81, 0x03, 0x08, 0x00, 0xFF,
# 接口1
0x09, 0x04, 0x01, 0x00, 0x02, 0x0A, 0x00, 0x00, 0x00,
# 端点(OUT)
0x07, 0x05, 0x02, 0x02, 0x40, 0x00, 0x00,
# 端点(IN)
0x07, 0x05, 0x82, 0x02, 0x40, 0x00, 0x00
]
}
return desc
def print_descriptor_c_code(desc):
"""输出C语言格式的描述符"""
print("// USB Device Descriptor")
print(f"static uint8_t dev_desc[{len(desc['device'])}] = {{")
for i, byte in enumerate(desc['device']):
if i % 8 == 0:
print(" ", end="")
print(f"0x{byte:02X},", end=" ")
if i % 8 == 7:
print()
print("};\n")
print("// USB Configuration Descriptor")
print(f"static uint8_t config_desc[{len(desc['config'])}] = {{")
for i, byte in enumerate(desc['config']):
if i % 8 == 0:
print(" ", end="")
print(f"0x{byte:02X},", end=" ")
if i % 8 == 7:
print()
print("};")
参考代码 基于STM32F407开发的USB的CDC模式与电脑进行通信 www.youwenfan.com/contentcnu/70387.html
六、高级功能扩展
6.1 大容量数据传输
// usb_cdc_bulk.c
#include "usb_cdc_bulk.h"
#define BULK_BUFFER_SIZE 4096
#define PACKET_SIZE 64
typedef struct {
uint8_t data[BULK_BUFFER_SIZE];
uint32_t length;
uint32_t transferred;
uint8_t in_progress;
} BulkTransfer;
BulkTransfer bulk_tx, bulk_rx;
// 大容量数据发送
uint8_t USB_Bulk_Transmit(uint8_t* data, uint32_t length) {
if (bulk_tx.in_progress || length > BULK_BUFFER_SIZE) {
return 0;
}
memcpy(bulk_tx.data, data, length);
bulk_tx.length = length;
bulk_tx.transferred = 0;
bulk_tx.in_progress = 1;
// 开始传输
USB_Bulk_Transmit_Next();
return 1;
}
// 传输下一包
void USB_Bulk_Transmit_Next(void) {
if (!bulk_tx.in_progress) return;
uint32_t remaining = bulk_tx.length - bulk_tx.transferred;
uint32_t to_send = (remaining > PACKET_SIZE) ? PACKET_SIZE : remaining;
if (to_send > 0) {
CDC_Transmit_FS(&bulk_tx.data[bulk_tx.transferred], to_send);
bulk_tx.transferred += to_send;
} else {
bulk_tx.in_progress = 0;
}
}
// 传输完成回调
void CDC_TxCompleteCallback(void) {
if (bulk_tx.in_progress) {
USB_Bulk_Transmit_Next();
}
}
6.2 数据流控制
// usb_cdc_flow.c
#include "usb_cdc_flow.h"
typedef struct {
uint32_t baud_rate;
uint8_t stop_bits;
uint8_t parity;
uint8_t data_bits;
} LineCoding;
LineCoding line_coding = {115200, 0, 0, 8};
volatile uint8_t dtr_state = 0;
volatile uint8_t rts_state = 0;
// 设置流控制
void USB_SetFlowControl(uint8_t enable) {
if (enable) {
// 硬件流控制
// 等待CTS信号
} else {
// 软件流控制
}
}
// 获取连接状态
uint8_t USB_IsConnected(void) {
return dtr_state; // DTR表示连接状态
}
// 设置波特率
void USB_SetBaudRate(uint32_t baud) {
line_coding.baud_rate = baud;
// 重新配置串口(如果需要)
}
七、故障排除
7.1 常见问题解决
// 1. USB不识别
// 检查项:
// - 电源是否正常(5V)
// - USB线是否完好
// - PA11/PA12连接是否正确
// - 上拉电阻是否连接
// 2. 数据传输不稳定
// 解决方案:
// - 增加USB缓冲区大小
// - 添加数据校验
// - 降低传输速率
7.2 调试信息输出
// debug_usb.c
#include "debug_usb.h"
void USB_Debug_PrintStatus(void) {
char status[256];
sprintf(status,
"USB Status:\r\n"
" Connected: %s\r\n"
" DTR: %s\r\n"
" RTS: %s\r\n"
" Baud: %lu\r\n"
" Buffer Free: %d/%d\r\n",
usb_connected ? "Yes" : "No",
dtr_state ? "High" : "Low",
rts_state ? "High" : "Low",
line_coding.baud_rate,
USB_RX_BUFFER_SIZE - usb_rx_count,
USB_RX_BUFFER_SIZE);
USB_Transmit(status, strlen(status));
}
八、性能优化
8.1 零拷贝传输
// usb_cdc_zero_copy.c
typedef struct {
uint8_t* buffer;
uint32_t size;
uint8_t ready;
} ZeroCopyBuffer;
ZeroCopyBuffer zcb_tx, zcb_rx;
// 零拷贝发送
uint8_t* USB_GetTxBuffer(uint32_t size) {
if (zcb_tx.ready || size > USB_TX_BUFFER_SIZE) {
return NULL;
}
zcb_tx.buffer = usb_tx_buffer;
zcb_tx.size = size;
zcb_tx.ready = 1;
return zcb_tx.buffer;
}
// 提交发送
void USB_CommitTxBuffer(void) {
if (zcb_tx.ready) {
CDC_Transmit_FS(zcb_tx.buffer, zcb_tx.size);
zcb_tx.ready = 0;
}
}
浙公网安备 33010602011771号