| 
 
| 一、引言 在嵌入式系统开发中,USB虚拟串口通信是一种非常实用的通信方式。通过USB接口模拟串口通信,能够方便地实现与上位机的数据交互,无需额外的串口转USB芯片。STM32系列微控制器具有丰富的外设资源,其中部分型号支持USB功能,可以很方便地实现USB虚拟串口通信。本文将详细介绍基于STM32的USB虚拟串口通信的原理、实现步骤,并给出相应的代码示例。
 
 二、原理概述
 2.1 USB通信基础
 USB(Universal Serial Bus)即通用串行总线,是一种应用广泛的外部总线标准。它具有高速、可靠、支持热插拔等优点。USB通信基于主机 - 设备架构,主机(通常是计算机)负责管理和控制USB总线上的设备。
 
 2.2 虚拟串口通信原理
 USB虚拟串口通信是通过USB接口模拟传统的串口(UART)通信。在设备端(STM32),将数据按照USB协议封装后通过USB接口发送到主机;在主机端,操作系统会将接收到的USB数据解析为串口数据,就像连接了一个真实的串口设备一样。这样,用户可以使用串口调试工具(如串口助手)与STM32进行通信。
 
 2.3 STM32的USB功能
 STM32部分型号集成了USB外设,支持多种USB设备类,其中包括CDC(Communication Device Class)类,该类可以实现USB虚拟串口通信。CDC类定义了一套通信协议,使得设备可以模拟串口的功能,包括数据的发送和接收。
 
 三、硬件连接
 以STM32F103C8T6为例,其USB接口连接方式如下:
 
 USB D+(PA12):数据正线,用于传输USB差分信号的正端。
 USB D-(PA11):数据负线,用于传输USB差分信号的负端。
 VDD(3.3V):电源引脚,为STM32提供工作电压。
 GND:接地引脚。
 四、实现步骤
 4.1 环境搭建
 开发工具:使用Keil MDK作为开发环境,确保已经安装了STM32F1系列的芯片支持包。
 库文件:使用STM32标准外设库,该库提供了丰富的函数和驱动代码,方便进行USB开发。
 4.2 配置USB时钟
 在STM32中,USB外设需要48MHz的时钟源。通常可以使用PLL(锁相环)将系统时钟倍频到48MHz。以下是配置USB时钟的代码示例:
 
 #include "stm32f10x.h"
 
 void RCC_Configuration(void)
 {
 ErrorStatus HSEStartUpStatus;
 
 // 使能HSE
 RCC_HSEConfig(RCC_HSE_ON);
 
 // 等待HSE启动稳定
 HSEStartUpStatus = RCC_WaitForHSEStartUp();
 
 if (HSEStartUpStatus == SUCCESS)
 {
 // 使能预取缓冲区
 FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
 FLASH_SetLatency(FLASH_Latency_2);
 
 // 设置AHB时钟(HCLK)
 RCC_HCLKConfig(RCC_SYSCLK_Div1);
 
 // 设置APB2时钟(PCLK2)
 RCC_PCLK2Config(RCC_HCLK_Div1);
 
 // 设置APB1时钟(PCLK1)
 RCC_PCLK1Config(RCC_HCLK_Div2);
 
 // 设置PLL时钟源和倍频系数
 RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
 
 // 使能PLL
 RCC_PLLCmd(ENABLE);
 
 // 等待PLL锁定
 while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
 
 // 设置系统时钟源为PLL
 RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
 
 // 等待系统时钟源切换完成
 while (RCC_GetSYSCLKSource() != 0x08);
 }
 
 // 使能USB时钟
 RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB, ENABLE);
 }
 
 
 
 4.3 初始化USB设备
 使用STM32标准外设库提供的函数初始化USB设备,使其作为CDC类设备工作。以下是初始化USB设备的代码示例:
 
 #include "stm32f10x.h"
 #include "usb_lib.h"
 #include "usb_desc.h"
 #include "usb_pwr.h"
 
 void USB_Configuration(void)
 {
 // 初始化USB中断向量表
 NVIC_Configuration();
 
 // 初始化USB设备
 USB_Init();
 }
 
 void NVIC_Configuration(void)
 {
 NVIC_InitTypeDef NVIC_InitStructure;
 
 // 使能USB中断
 NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;
 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
 NVIC_Init(&NVIC_InitStructure);
 }
 
 
 
 4.4 实现数据收发功能
 在主函数中,实现数据的发送和接收功能。以下是一个简单的示例代码:
 
 #include "stm32f10x.h"
 #include "usb_lib.h"
 #include "usb_pwr.h"
 #include "usb_desc.h"
 #include "usb_endp.h"
 
 #define BUFFER_SIZE 64
 
 uint8_t ReceiveBuffer[BUFFER_SIZE];
 uint8_t SendBuffer[BUFFER_SIZE] = "Hello, USB Virtual Serial Port!";
 
 int main(void)
 {
 // 配置系统时钟
 RCC_Configuration();
 
 // 初始化USB设备
 USB_Configuration();
 
 while (1)
 {
 // 检查是否有数据接收
 if (ReceiveLength > 0)
 {
 // 处理接收到的数据
 for (int i = 0; i < ReceiveLength; i++)
 {
 // 简单示例:将接收到的数据原样返回
 SendBuffer[i] = ReceiveBuffer[i];
 }
 
 // 发送数据
 UserToPMABufferCopy(SendBuffer, ENDP1_TXADDR, ReceiveLength);
 SetEPTxCount(ENDP1, ReceiveLength);
 SetEPTxValid(ENDP1);
 
 // 清空接收缓冲区
 ReceiveLength = 0;
 }
 }
 }
 
 
 
 4.5 处理USB中断
 在USB中断处理函数中,处理USB数据的接收和发送。以下是USB中断处理函数的示例代码:
 
 #include "stm32f10x.h"
 #include "usb_lib.h"
 #include "usb_pwr.h"
 #include "usb_desc.h"
 #include "usb_endp.h"
 
 extern uint8_t ReceiveBuffer[BUFFER_SIZE];
 extern uint8_t ReceiveLength;
 
 void USB_LP_CAN1_RX0_IRQHandler(void)
 {
 USB_Istr();
 }
 
 void EP1_IN_Callback(void)
 {
 // 处理发送完成事件
 }
 
 void EP3_OUT_Callback(void)
 {
 // 处理数据接收事件
 ReceiveLength = USB_SIL_Read(EP3_OUT, ReceiveBuffer);
 SetEPRxStatus(ENDP3, EP_RX_VALID);
 }
 
 
 
 五、总结
 通过以上步骤,我们可以实现基于STM32的USB虚拟串口通信。在实际应用中,可以根据需求对代码进行扩展,实现更复杂的数据处理和通信功能。同时,需要注意USB通信的稳定性和兼容性,确保在不同的主机环境下都能正常工作。
 
 以上代码仅为示例,实际开发中可能需要根据具体的硬件平台和需求进行调整。在开发过程中,建议参考STM32的官方文档和标准外设库的使用手册,以确保代码的正确性和稳定性。
 ————————————————
 
 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
 
 原文链接:https://blog.csdn.net/duierrorshuobu/article/details/145621630
 
 
 | 
 |