打印
[应用相关]

STM32快速实现USB虚拟串口+回环测试+USB转TTL的功能

[复制链接]
965|13
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
labasi|  楼主 | 2021-7-4 16:22 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式

环境:

  • STM32F103RB
  • STM32CUBEIDE1.5.1

1、配置时钟


使用特权

评论回复
沙发
labasi|  楼主 | 2021-7-4 16:22 | 只看该作者
2、开启USB


使用特权

评论回复
板凳
labasi|  楼主 | 2021-7-4 16:22 | 只看该作者
3、开启USB设备:虚拟串口


使用特权

评论回复
地板
labasi|  楼主 | 2021-7-4 16:23 | 只看该作者
4、生成工程



使用特权

评论回复
5
labasi|  楼主 | 2021-7-4 16:24 | 只看该作者
5、修改代码实现回环收发数据测试

在usbd_cdc_if.c文件中新定义一个结构体:

USBD_CDC_LineCodingTypeDef USBD_CDC_LineCoding =
{
        115200,      // 默认波特率
        0X00,        // 1位停止位
        0X00,        // 无奇偶校
        0X08,        // 无流控,8bit数据位
};




找到CDC_Control_FS函数,找到CDC_SET_LINE_CODING和CDC_GET_LINE_CODING分支,添加以下代码:
(CDC_SET_LINE_CODING:你在用串口助手选择波特率时候,STM32就会调用这个分支进行修改USB波特率)
(CDC_GET_LINE_CODING:获取STM32的USB波特率)

    case CDC_SET_LINE_CODING:
            USBD_CDC_LineCoding.bitrate = (pbuf[3] << 24) | (pbuf[2] << 16) | (pbuf[1] << 8) | pbuf[0];
            USBD_CDC_LineCoding.format = pbuf[4];
            USBD_CDC_LineCoding.paritytype = pbuf[5];
            USBD_CDC_LineCoding.datatype = pbuf[6];
    break;

    case CDC_GET_LINE_CODING:
            pbuf[0] = (uint8_t)(USBD_CDC_LineCoding.bitrate);
            pbuf[1] = (uint8_t)(USBD_CDC_LineCoding.bitrate >> 8);
            pbuf[2] = (uint8_t)(USBD_CDC_LineCoding.bitrate >> 16);
            pbuf[3] = (uint8_t)(USBD_CDC_LineCoding.bitrate >> 24);
            pbuf[4] = USBD_CDC_LineCoding.format;
            pbuf[5] = USBD_CDC_LineCoding.paritytype;
            pbuf[6] = USBD_CDC_LineCoding.datatype;
    break;


使用特权

评论回复
6
labasi|  楼主 | 2021-7-4 16:24 | 只看该作者
如下图所示:

使用特权

评论回复
7
labasi|  楼主 | 2021-7-4 16:24 | 只看该作者
找到CDC_Receive_FS函数,这个函数如果USB虚拟串口数据收到就会被调用,我们在这个函数中将收到的数据在发回去,只需要添加CDC_Transmit_FS(Buf, *Len);这一句即可,如下图:

使用特权

评论回复
8
labasi|  楼主 | 2021-7-4 16:24 | 只看该作者
然后编译工程并下载,接上USB之后,设备管理器COM出现一个新的端口:

使用特权

评论回复
9
labasi|  楼主 | 2021-7-4 16:24 | 只看该作者
我们使用串口调试助手给它发数据:

使用特权

评论回复
10
labasi|  楼主 | 2021-7-4 16:25 | 只看该作者
6、实现USB转串口功能

发数据流程:串口调试助手发送数据->STM32的USB数据接收->STM32转发到串口3
收数据流程:STM32的串口3收到数据->转发到USB->STM32的USB发送到串口调试助手

第一步先在这里加入串口3的初始化操作:


使用特权

评论回复
11
labasi|  楼主 | 2021-7-4 16:25 | 只看该作者
贴一下串口3的初始化代码,这里我用到了队列,因为实际测试发现串口3接收数据量比较大的话,那么转发到USB虚拟串口的时候会丢数据,所以这里采用了缓存队列,当串口接收到的数据到达一定数据量之后才做一次转发到USB的操作,并且开启了空闲中断,作用是转发最后一包数据:





/*
* myusart.c
*
*  Created on: Mar 22, 2021
*      Author: hello
*/
#include "myusart.h"
#include "myqueue.h"

// 队列大小,定义为USB_CDC的发送包大小
#define QUEUE_SIZE APP_TX_DATA_SIZE

// 队列数据空间。请使用宏QALIGN4,目的是为了根据队列大小计算实际需要的队列存储空间大小并对齐4字节
uint8_t QueueBuffer[QALIGN4(QUEUE_SIZE)];

// 队列句柄
Queue Uart3QueueHandle = {0};
#define UART3_QUEUE_Handle (&Uart3QueueHandle)

// 串口转发到USB的缓冲,定义为USB包的一半大小
static uint8_t buffer[APP_RX_DATA_SIZE >> 1];

void UART3_Init(const USBD_CDC_LineCodingTypeDef *USBD_CDC_LineCoding)
{
        __HAL_UART_DISABLE_IT(&huart3, UART_IT_RXNE);

        __HAL_UART_DISABLE_IT(&huart3, UART_IT_IDLE);

        HAL_UART_DeInit(&huart3);

        huart3.Instance = USART3;
        huart3.Init.BaudRate = USBD_CDC_LineCoding->bitrate;
        huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;
        huart3.Init.Mode = UART_MODE_TX_RX;
        huart3.Init.OverSampling = UART_OVERSAMPLING_16;

        switch (USBD_CDC_LineCoding->paritytype)
        {
        case 0:
                huart3.Init.Parity = UART_PARITY_NONE;
                break;
        case 1:
                huart3.Init.Parity = UART_PARITY_ODD;
                break;
        case 2:
                huart3.Init.Parity = UART_PARITY_EVEN;
                break;
        default:
                huart3.Init.Parity = UART_PARITY_NONE;
                break;
        }

        switch (USBD_CDC_LineCoding->datatype)
        {
        case 0x07:
                huart3.Init.WordLength = UART_WORDLENGTH_8B;
                break;
        case 0x08:
                if (huart3.Init.Parity == UART_PARITY_NONE)
                {
                        huart3.Init.WordLength = UART_WORDLENGTH_8B;
                }
                else
                {
                        huart3.Init.WordLength = UART_WORDLENGTH_9B;
                }
                break;
        default:
                huart3.Init.WordLength = UART_WORDLENGTH_8B;
                break;
        }

        switch (USBD_CDC_LineCoding->format)
        {
        case 0:
                huart3.Init.StopBits = UART_STOPBITS_1;
                break;
        case 2:
                huart3.Init.StopBits = UART_STOPBITS_2;
                break;
        default:
                huart3.Init.StopBits = UART_STOPBITS_1;
                break;
        }

        HAL_UART_Init(&huart3);

        Queue_Init(UART3_QUEUE_Handle, QUEUE_SIZE, QueueBuffer);

        __HAL_UART_ENABLE_IT(&huart3, UART_IT_RXNE);

        __HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE);
}


使用特权

评论回复
12
labasi|  楼主 | 2021-7-4 16:25 | 只看该作者
void USART3_IRQHandler(void)
{
        static uint32_t nsent = 0;
        static uint8_t dat = 0;

        if (__HAL_UART_GET_FLAG(&huart3, UART_FLAG_RXNE) != RESET)
        {
                __HAL_UART_CLEAR_FLAG(&huart3, UART_FLAG_RXNE);
                dat = huart3.Instance->DR & 0XFF;
                Queue_PutByte(UART3_QUEUE_Handle, dat);                     // 入队一个字节的数据
                if (Queue_GetUsed(UART3_QUEUE_Handle) >= sizeof(buffer))
                {
                        Queue_Read(UART3_QUEUE_Handle, buffer, sizeof(buffer));
                        CDC_Transmit_FS(buffer, sizeof(buffer));                // 转发到USB
                }
        }

        if (__HAL_UART_GET_FLAG(&huart3, UART_FLAG_IDLE) != RESET)
        {
                nsent = Queue_GetUsed(UART3_QUEUE_Handle);
                if (nsent != 0)
                {
                        Queue_Read(UART3_QUEUE_Handle, buffer, nsent);
                        CDC_Transmit_FS(buffer, nsent);               // 转发到USB
                }
                __HAL_UART_CLEAR_IDLEFLAG(&huart3);
        }
}


使用特权

评论回复
13
labasi|  楼主 | 2021-7-4 16:26 | 只看该作者
void UART3_SendData(const void *buf, uint32_t len)
{
        const uint8_t *p = (const uint8_t*) buf;
        while (len--)
        {
                while (__HAL_UART_GET_FLAG(&huart3, UART_FLAG_TXE) != SET);
                huart3.Instance->DR = (uint8_t) (*p++ & 0XFF);
                while (__HAL_UART_GET_FLAG(&huart3, UART_FLAG_TC) != SET);
        }
}

//#ifdef __GNUC__
//#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
//#else
//#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
//#endif
//PUTCHAR_PROTOTYPE
//{
//        while (__HAL_UART_GET_FLAG(&huart3, UART_FLAG_TXE) != SET);
//        huart3.Instance->DR = (uint8_t) (ch & 0XFF);
//        while (__HAL_UART_GET_FLAG(&huart3, UART_FLAG_TC) != SET);
//        return ch;
//}



使用特权

评论回复
14
labasi|  楼主 | 2021-7-4 16:26 | 只看该作者

然后在添加USB转发到串口的操作:

这样就实现了一个类似于USB转TTL模块的功能!



使用特权

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

本版积分规则

52

主题

3398

帖子

2

粉丝