【stm32@USB】应用-1:实现USB虚拟串口(CDC_VPC)
本文转载自博客园博主:Couvrir洪荒猛兽
原文章链接:https://www.cnblogs.com/couvrir/p/17461078.html
MCU:STM32F103VET6
开发板:野火指南者
开发环境:STM32CubeMX+MDK5
USB虚拟串口,简称VCP,是Virtual COM Port的简写,它是利用 USB的 CDC类来实现的一种通信接口。我们可以利用STM32自带的USB功能,来实现一个USB虚拟串口,从而通过USB,实现电脑与STM32的数据互传。
上位机无需编写专门的USB程序,只需要一个下载虚拟串口驱动程序+串口调试助手即可调试,非常实用。
(虚拟串口启动在Win7、Win8机型PC中需要到ST官网下载。win10及以上机型在本地已有驱动,无需安装。)

STM32 官方 虚拟串口驱动
实现USB的虚拟串口不需要去理解USB的底层驱动,只需要STM32CubeMX去配置生成工程即可。在野火的指南者中,是没有这一类的视频和示例的,博主使用这款开发板实现USB虚拟串口。
首先需要打开STM32CubeMX工具。输入开发板MCU对应型号,找到开发板对应封装的MCU型号,双击打开(图中第三)。

此时,双击完后会关闭此界面,然后打开一个新界面。

然后,我们开始基本配置。

现在我们选择一个LED作为系统LED,该步骤可以忽略,只是本人喜欢这样子。以硬件原理图的绿灯为例子。

基本配置除了时钟树外,基本上已经配置好了。
现在来配置USB_Device。STM32F1系列USB只支持USB_Device。

选中USB类型后,还需要细化其中的类型。

一切配置都是基于硬件原理图的。硬件配置除常规配置外,还是需要看硬件原理图的。在硬件原理图中,可以看到只有PD6拉低时,USB才使能。(针对野火指南者开发板)

现在配置时钟树

配置完成,完善工程,生成工程。

到此,STM32CubeMX工具的使用结束!可以发现在桌面已经生成了USB_VPC工程。
USB虚拟串口还需要装驱动才能被是识别到,在Win7、Win8机型PC中需要到ST官网下载。win10及以上机型在本地已有驱动,无需安装。
使用MDK5打开USB_VPC工程打开。点击魔法棒,勾选微库。选择对应的下载器,勾选下载完复位允许。USB线一端接开发板USB_Device,一端接PC。

现在可以开始实验了,实现VPC的发送与回传,并重定向printf函数。
在此之前,简单描述一下生成的USB文件以及重要函数。

然后再插播一条,看帖子说是,刚下载完程序时,是识别不出端口的。需要在上电的情况下从PC那拔插一次USB线。然后可以使用一个函数解决这个问题。可以在gpio.c中写入函数,然后记得在头文件声明。使用要在MX_USB_DEVICE_Iint()之前。
(我没遇到这个问题,但是我还是放到工程了,但是我没用这函数。)
1 /* USER CODE BEGIN 2 */
2 /*USB 重新枚举函数*/
3 void USB_Reset(void)
4 {
5 GPIO_InitTypeDef GPIO_InitStruct = {0};
6
7 __HAL_RCC_GPIOA_CLK_ENABLE();
8
9 GPIO_InitStruct.Pin = GPIO_PIN_12;
10 GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
11 GPIO_InitStruct.Pull = GPIO_NOPULL;
12 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
13 HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
14
15 HAL_GPIO_WritePin(GPIOA,GPIO_PIN_12,GPIO_PIN_RESET);
16 HAL_Delay(100);
17 HAL_GPIO_WritePin(GPIOA,GPIO_PIN_12,GPIO_PIN_SET);
18 }
19 /* USER CODE END 2 */
实验环节:发送与回传
在main.c中(截取片段,修改部分)
1 /* Private includes ----------------------------------------------------------*/
2 /* USER CODE BEGIN Includes */
3 #include "usbd_cdc_if.h"
4 /* USER CODE END Includes */
5
6 /* Private typedef -----------------------------------------------------------*/
7 /* USER CODE BEGIN PTD */
8
9 /* USER CODE END PTD */
10
11 /* Private define ------------------------------------------------------------*/
12 /* USER CODE BEGIN PD */
13 /* USER CODE END PD */
14
15 /* Private macro -------------------------------------------------------------*/
16 /* USER CODE BEGIN PM */
17
18 /* USER CODE END PM */
19
20 /* Private variables ---------------------------------------------------------*/
21
22 /* USER CODE BEGIN PV */
23
24 /* USER CODE END PV */
25
26 /* Private function prototypes -----------------------------------------------*/
27 void SystemClock_Config(void);
28 /* USER CODE BEGIN PFP */
29
30 /* USER CODE END PFP */
31
32 /* Private user code ---------------------------------------------------------*/
33 /* USER CODE BEGIN 0 */
34
35 /* USER CODE END 0 */
36
37 /**
38 * @brief The application entry point.
39 * @retval int
40 */
41 int main(void)
42 {
43 /* USER CODE BEGIN 1 */
44 char str[] = "Hello World!\r\n";
45 /* USER CODE END 1 */
46
47 /* MCU Configuration--------------------------------------------------------*/
48
49 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
50 HAL_Init();
51
52 /* USER CODE BEGIN Init */
53
54 /* USER CODE END Init */
55
56 /* Configure the system clock */
57 SystemClock_Config();
58
59 /* USER CODE BEGIN SysInit */
60 // USB_Reset();
61 /* USER CODE END SysInit */
62
63 /* Initialize all configured peripherals */
64 MX_GPIO_Init();
65 MX_USB_DEVICE_Init();
66 /* USER CODE BEGIN 2 */
67
68 /* USER CODE END 2 */
69
70 /* Infinite loop */
71 /* USER CODE BEGIN WHILE */
72 while (1)
73 {
74 /* USER CODE END WHILE */
75 CDC_Transmit_FS((uint8_t*)str, 14);
76 HAL_Delay(2000);
77 /* USER CODE BEGIN 3 */
78 }
79 /* USER CODE END 3 */
80 }
在usbd_cdc_if.c中(截取片段,修改部分)
1 /**
2 * @brief Data received over USB OUT endpoint are sent over CDC interface
3 * through this function.
4 *
5 * @note
6 * This function will issue a NAK packet on any OUT packet received on
7 * USB endpoint until exiting this function. If you exit this function
8 * before transfer is complete on CDC interface (ie. using DMA controller)
9 * it will result in receiving more data while previous ones are still
10 * not sent.
11 *
12 * @param Buf: Buffer of data to be received
13 * @param Len: Number of data received (in bytes)
14 * @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
15 */
16 static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
17 {
18 /* USER CODE BEGIN 6 */
19 CDC_Transmit_FS(Buf, *Len);
20
21 USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
22 USBD_CDC_ReceivePacket(&hUsbDeviceFS);
23 return (USBD_OK);
24 /* USER CODE END 6 */
25 }
实验结果(波特率随意选)

实验环节:打印重定向
在usbd_cdc_if.c中(截取片段,修改部分),声明在usbd_cdc_if.h文件。
1 /* USER CODE BEGIN PRIVATE_FUNCTIONS_IMPLEMENTATION */
2 #include "stdarg.h"
3 #include "stdio.h"
4
5 uint8_t usbtemp[64];
6 void usbvcom_printf(const char *format,...)
7 {
8 uint16_t len;
9 va_list args;
10
11 va_start(args, format);
12 len = vsnprintf((char *)usbtemp, sizeof(usbtemp)+1, (char *)format, args);
13 va_end(args);
14
15 CDC_Transmit_HS(usbtemp, len);
16 }
17
18 /* USER CODE END PRIVATE_FUNCTIONS_IMPLEMENTATION */
然后就可以像printf那样使用了,实测过是正常的!
扩展:什么是CDC类?
在USB标准子类中,有一类称之为CDC类,可以实现虚拟串口通信的协议,而且由于大部分的操作系统(Windows和Linux)都带有支持CDC类的设备驱动程序,可以自动识别CDC类的设备,这样不仅免去了写专用设备驱动的负担,同时简化了设备驱动的安装
USB的CDC类是USB通信设备类(Communication Device Class)的简称。CDC类是USB组织定义的一类专门给各种通信设备(电信通信设备和中速网络通信设备)使用的USB子类。根据CDC类所针对通信设备的不同,CDC类又被分成以下不同的模型:USB传统纯电话业务(POTS)模型,USB ISDN模型和USB网络模型。其中,USB传统纯电话业务模型,有可分为直接线控制模型(Direct Line Control Model)、抽象控制模型(Abstract Control Model)和USB电话模型(USB Telephone Model),本文所讨论的虚拟串口就属于USB传统纯电话业务模型下的抽象控制模型。
参考资料:
1. 虚拟串口驱动:https://www.elecfans.com/soft/Mec/2023/202310092263644.html


浙公网安备 33010602011771号