打印
[应用相关]

STM32 -- USB虚拟串口通信

[复制链接]
76|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
xiaoqizi|  楼主 | 2025-2-18 19:07 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
一、引言
在嵌入式系统开发中,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

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

106

主题

4180

帖子

3

粉丝