MCU:STM32F429ZIT6
开发环境:STM32CubeMX+MDK5

实现USB的虚拟串口不需要去理解USB的底层驱动,只需要STM32CubeMX去配置生成工程即可。

首先需要打开STM32CubeMX工具。输入开发板MCU对应型号,找到开发板对应封装的MCU型号,双击打开(图中第三)。

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

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

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

基本配置除了时钟树外,基本上已经配置好了。

现在配置USB类型。查看硬件原理图。

现在配置时钟树

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

到此,STM32CubeMX工具的使用结束!可以发现在桌面已经生成了vcomProgram工程。

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


现在可以开始实验了,实现VPC的发送与回传,并重定向printf函数。

然后再插播一条,看帖子说是,刚下载完程序时,是识别不出端口的。需要在上电的情况下从PC那拔插一次USB线。然后可以使用一个函数解决这个问题。可以在gpio.c中写入函数,然后记得在头文件声明。使用要在MX_USB_DEVICE_Iint()之前。

(我没遇到这个问题,但是我还是放到工程了,但是我没用这函数。)

/* USER CODE BEGIN 2 */
/*USB 重新枚举函数*/
void USB_Reset(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    __HAL_RCC_GPIOA_CLK_ENABLE();

    GPIO_InitStruct.Pin = GPIO_PIN_12;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    HAL_GPIO_WritePin(GPIOA,GPIO_PIN_12,GPIO_PIN_RESET);
    HAL_Delay(100);
    HAL_GPIO_WritePin(GPIOA,GPIO_PIN_12,GPIO_PIN_SET);
}
/* USER CODE END 2 */

实验环节:发送与回传

在main.c中(截取片段,修改部分)

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "usbd_cdc_if.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */
//    USB_Reset();
  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  MX_USB_DEVICE_Init();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
     HAL_GPIO_TogglePin(Sys_led_GPIO_Port, Sys_led_Pin);
     CDC_Transmit_HS((uint8_t *)"hello\r\n",7);
      HAL_Delay(2000);
  }
  /* USER CODE END 3 */
}

在usbd_cdc_if.c中(截取片段,修改部分)

/**
  * @brief Data received over USB OUT endpoint are sent over CDC interface
  *         through this function.
  *
  *         @note
  *         This function will issue a NAK packet on any OUT packet received on
  *         USB endpoint until exiting this function. If you exit this function
  *         before transfer is complete on CDC interface (ie. using DMA controller)
  *         it will result in receiving more data while previous ones are still
  *         not sent.
  *
  * @param  Buf: Buffer of data to be received
  * @param  Len: Number of data received (in bytes)
  * @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAILL
  */
static int8_t CDC_Receive_HS(uint8_t* Buf, uint32_t *Len)
{
  /* USER CODE BEGIN 11 */
    CDC_Transmit_HS(Buf, *Len);

  USBD_CDC_SetRxBuffer(&hUsbDeviceHS, &Buf[0]);
  USBD_CDC_ReceivePacket(&hUsbDeviceHS);
  return (USBD_OK);
  /* USER CODE END 11 */
}

实验结果(波特率随意选)

实验环节:打印重定向

在usbd_cdc_if.c中(截取片段,修改部分),声明在usbd_cdc_if.h文件。

/* USER CODE BEGIN PRIVATE_FUNCTIONS_IMPLEMENTATION */
#include "stdarg.h"
#include "stdio.h"

uint8_t usbtemp[64];
void usbvcom_printf(const char *format,...)
{
    uint16_t     len;
    va_list     args;

    va_start(args, format);
    len = vsnprintf((char *)usbtemp, sizeof(usbtemp)+1, (char *)format, args);
    va_end(args);

    CDC_Transmit_HS(usbtemp, len);
}

/* USER CODE END PRIVATE_FUNCTIONS_IMPLEMENTATION */

然后就可以像printf那样使用了,实测过是正常的!

时代越来越好,开发效率越来越高,希望能帮助到你!!!

还有就是,开源万岁。