USB 实现串口设备
1. 硬件连接
软件版本: stm32cubmx:6.2 keil 5
硬件:
- stm32F103C8T6最小系统板
- 下载器
- USB线
从bilibili 工房子购买开发版 https://gf.bilibili.com/item/detail/1106139112
2. CubeMX生成代码
选择芯片型号,我这边是C8t6
debug选择 SW模式
配置外部时钟输入 配置USB device的功能
将USB的模式配置成COM口的功能
时钟树配置,如果前面没有配置外部晶振输入,这边无法配置成功。正常的话自动会配置成功
工程配置,配置成对应keil的版本
框出来的位置有时候因为尺寸太小会出错,有人说默认的也可以,反正我改了,如下图
3. 编写代码
实现串口输出ABCD,需要更改的部分
//1.添加头文件
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usb_device.h"
#include "gpio.h"
#include "usbd_cdc_if.h"
更改main函数
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void USB_Disconnected(void) {
__HAL_RCC_USB_FORCE_RESET();
HAL_Delay(200);
__HAL_RCC_USB_RELEASE_RESET();
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_Initure.Pin = GPIO_PIN_11 | GPIO_PIN_12;
GPIO_Initure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_Initure.Pull = GPIO_PULLDOWN;
GPIO_Initure.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_Initure);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_RESET);
HAL_Delay(300);
}
/* USER CODE END 0 */
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 卸载操作,取消按复位键后无法识别的问题
USB_Disconnected();
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USB_DEVICE_Init();
/* USER CODE BEGIN 2 */
//2.添加数组
unsigned char buff[10] = {"abcd\n\r"};
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
//3. 在while循环中添加如下代码
HAL_Delay(1000);
CDC_Transmit_FS(buff,sizeof(buff)); //USB 串口发送数据
}
/* USER CODE END 3 */
}
串口助手
读取输入的值和上一次是否相同,从而输出不同的值 首先需要将main函数恢复成初始的状态 更改下面的函数 CDC_Receive_FS();这个函数在usbd_cdc_if.c的文件下面的位置 初始代码
static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
/* USER CODE BEGIN 6 */
USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
USBD_CDC_ReceivePacket(&hUsbDeviceFS);
return (USBD_OK);
/* USER CODE END 6 */
}
将代码改成如下的样子
uint8_t a=0,a1=1;
unsigned char buff1[4] = {"abcd"};
static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
a = *Buf;
if(a == a1)
CDC_Transmit_FS(Buf,*Len);//自收自发
else
{
CDC_Transmit_FS(buff1,sizeof(buff1));
a1 = a;
}
/* USER CODE BEGIN 6 */
USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
USBD_CDC_ReceivePacket(&hUsbDeviceFS);
//CDC_Transmit_FS(Buf,*Len);//自收自发
return (USBD_OK);
/* USER CODE END 6 */
}
会对比此次的和上一次的差异,如果一样则输出 接收到的值,如果不一样则输出abcd
PS:有一个BUG,数字大了后识别会出错,只能识别发送的第一个数据的差异
3.4 串口定向printf
参考文档: 文件的配置教程 https://www.jianshu.com/p/579783d28044
1.驱动下载链接 https://www.st.com/content/st_com/en/products/development-tools/software-development-tools/stm32-software-development-tools/stm32-utilities/stsw-stm32102.html 2. 代码操作 https://blog.csdn.net/u010779035/article/details/104369515
假装的Print—使用sprintf 这个函数将数据处理到串口发送的数组中,串口通过设置中断或者定时发送,进行数据的发送,但需要占用比较大的数据资源。实时性不确定。
3.5 串口接收的数据存到数组中
如下代码需要在main函数中进行更改
定义初始化函数
/* USER CODE BEGIN PTD */
uint8_t uart_number[64]="nihao\n\r";
/* USER CODE END PTD */
如下使用串口发送函数,将数组 uart_number 发送出去
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
CDC_Transmit_FS(uart_number,sizeof(uart_number)); //USB 串口发送数据
HAL_Delay(1000);
}
/* USER CODE END 3 */
CDC_Receive_FS();这个函数在usbd_cdc_if.c的文件下面的位置
extern uint8_t uart_number[64];
uint8_t* aa;
static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
uint32_t i;
/* USER CODE BEGIN 6 */
//会对比此次的和上一次的差异,如果一样则输出 接收到的值,如果不一样则输出abcd
//最多11个汉字 64个16进制数
memset(uart_number, 0, sizeof uart_number); //清除数组uart_number
//循环函数,将接收到的数字,存到数组中
aa = Buf;
for(i = 0;i < *Len;i++)
{
uart_number[i] = *aa;
aa++;
}
USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
USBD_CDC_ReceivePacket(&hUsbDeviceFS);
return (USBD_OK);
/* USER CODE END 6 */
}
4. 后记
另外PS一个bug: 可能由于数组的问题,在接收数据太大后会出现溢出?//最多11个汉字 64个16进制数 能够满足基本的使用,但是不推荐太长的数据,或者控制单次发送的数据长度,分多次发送。
回答:只能接收64个字节是因为CDC_Receive_FS里面接收一包数据最长只有64字节,超过后要重新设置USBD_CDC_SetRxBuffer的接收pbuff地址
#bug讨论#"有一个BUG,数字大了后识别会出错,只能识别发送的第一个数据的差异" *Buf就是usb接收缓存字符串,字符串不能直接等号比较,需要使用字符串比较函数。
解决BUG:关于在按下reset之后,串口可以识别,但是无法打开的问题 在USB初始化前强制USB时钟复位
void USB_Disconnected(void) {
__HAL_RCC_USB_FORCE_RESET();
HAL_Delay(200);
__HAL_RCC_USB_RELEASE_RESET();
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_Initure.Pin = GPIO_PIN_11 | GPIO_PIN_12;
GPIO_Initure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_Initure.Pull = GPIO_PULLDOWN;
GPIO_Initure.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_Initure);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_RESET);
HAL_Delay(300);
}
再USB初始化之前,执行一下这些就行了,相当于告诉电脑断开链。这样按下复位键后会自动断开连接,软件上重新选择com口和重新打开旧可以正常识别。
PS:这样的方式就使得USB虚拟的串口,会有一段空白的阶段,使得其无法输出初始化状态下的串口信息。有着一丝的鸡肋?
上面的程序放在如下的main.c 文件中
/* USER CODE BEGIN SysInit */
//注销USB
USB_Disconnected();
/* USER CODE END SysInit */