前言
STM32单片机一般都有USB接口,用于实现各种类型的USB通信,典型的USB转串口功能,接下来就介绍一下,USB转串口的使用,先以自发自收回环测试为例,演示USB转串口的使用,然后以此为基础,修改为USB转TTL串口,其中主要代码使用STM32CubeMX生成。
开发环境
- STM32F103RB
- STM32CubeMX
- STM32CUBEIDE
STM32CubeMx配置
新建工程
在STM32CubeMx中新建基于STM32F103RB的Project。
配置时钟
时钟的配置如下图所示:
开启USB
-
首先,使能USB Device,如下图:
-
开启USB设备:虚拟串口,如下图:
-
生成工程
修改代码实现回环收发数据测试
在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;
如下图所示:
找到CDC_Receive_FS函数,这个函数如果USB虚拟串口数据收到就会被调用,我们在这个函数中将收到的数据在发回去,只需要添加CDC_Transmit_FS(Buf, *Len);这一句即可,如下图:
然后编译工程并下载,接上USB之后,设备管理器COM出现一个新的端口:
我们使用串口调试助手给它发数据:
实现USB转串口功能
发数据流程:串口调试助手发送数据->STM32的USB数据接收->STM32转发到串口3
收数据流程:STM32的串口3收到数据->转发到USB->STM32的USB发送到串口调试助手
第一步先在这里加入串口3的初始化操作:
贴一下串口3的初始化代码,这里我用到了队列,因为实际测试发现串口3接收数据量比较大的话,那么转发到USB虚拟串口的时候会丢数据,所以这里采用了缓存队列,当串口接收到的数据到达一定数据量之后才做一次转发到USB的操作,并且开启了空闲中断,作用是转发最后一包数据:
关于队列的使用可以查看我的另一篇博客:C/C++语言实现的一个缓存队列
/*
* 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);
}
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);
}
}
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;
//}
然后在添加USB转发到串口的操作:
这样就实现了一个USB转TTL串口的功能!
原文链接(转载有改动,感谢原作者):https://blog.51cto.com/u_15950551/6032374
评论区