STM32F103 USB实现虚拟串口

STM32F103 USB实现虚拟串口

最近买了一个STM32F103C8T6最小核心板,使用CubeIDE无法识别该芯片,发现该芯片的flash是128Kbytes,ST的标准库是64Kbytes,奇怪啊!也许是国产替代的,国产化太先进了,导致原厂落后了,不认识先进的东西了。
只好又在某宝买了5.5元的STM32F103C6T6最小核心板,CubeID环境下正常工作和调试。感谢万能的某宝!最便宜的东西才能工作正常,好无语啊!

实验环境

STM32F103C6T6A最小系统板, CubeIDE 1.10.1, ST-LINK;在这里插入图片描述

实验目的

实现USB的串口数据传输;

操作步骤

  1. cubeIDE建立工程并生成代码。
  2. 设置晶振RCC在这里插入图片描述
  3. 设置SYS, 调试方式在这里插入图片描述
  4. 设置USB,激活USB在这里插入图片描述
  5. 设置Middleware, USB vitrual port, 在这里插入图片描述
  6. 保存并generate code,进行编译一遍,通常是不能编译通过的。
  7. 修改usb_cdc_if.h 文件
/* USER CODE BEGIN INCLUDE */
 #define USB_REC_LEN   256 //定义USB串口接收字节数
 extern uint8_t USB_RX_BUF[USB_REC_LEN];//接收缓冲
 extern uint16_t USB_RX_STA;//接收标记
 /* USER CODE END INCLUDE */`
  1. 修改usb_cdc_if.c文件 在这里插入图片描述
/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
 uint8_t USB_RX_BUF[USB_REC_LEN];//接收缓冲,最大USB_REC_LEN个字节.
 uint16_t USB_RX_STA=0;//接收状态标记(接收到的有效字节数量)
/* USER CODE END PV */

修改接收函数代码

static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
	if(*Len<USB_REC_LEN)//判断收到数据量是否小于寄存器上限
	{
	   uint16_t i;
	   USB_RX_STA = *Len;//将数据量值放入标志位
	   for(i=0;i<*Len;i++)//循环
		   USB_RX_BUF[i] = Buf[i];//将数据内容放入数据寄存器
	}
  USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);  //将接收数组buff清空
  USBD_CDC_ReceivePacket(&hUsbDeviceFS);  //再次虚拟串口接收
  return (USBD_OK);
  /* USER CODE END 6 */
}

修改发送函数代码

uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len)
{
  uint8_t result = USBD_OK;
  /* USER CODE BEGIN 7 */
  uint32_t TimeStart = HAL_GetTick();
  USBD_CDC_HandleTypeDef *hcdc =  (USBD_CDC_HandleTypeDef*)hUsbDeviceFS.pClassData;
  if(hcdc == 0) return  USBD_FAIL;  //避免未插入USB设备,出现死机的情况。
  //if (hcdc->TxState != 0) return  USBD_BUSY;
  while(hcdc->TxState)
  {
     if(HAL_GetTick()-TimeStart > 10)
    	 return USBD_BUSY;
     else
    	 break;
  }
  USBD_CDC_SetTxBuffer(&hUsbDeviceFS, Buf,  Len);
  result =  USBD_CDC_TransmitPacket(&hUsbDeviceFS);
  TimeStart = HAL_GetTick();
  while(hcdc->TxState)
	{
		if(HAL_GetTick()-TimeStart > 10)
		return USBD_BUSY;
	}
  /* USER CODE END 7 */
  return result;
}

添加输出函数

#include <stdarg.h>
void USB_printf(const char *format, ...)//USB模拟串口的打印函数
{
    va_list args;
    uint32_t length;
    va_start(args, format);
    length = vsnprintf((char  *)UserTxBufferFS, APP_TX_DATA_SIZE, (char  *)format, args);
    va_end(args);
    CDC_Transmit_FS(UserTxBufferFS, length);
}
  1. 修改main.c
    添加include文件
	/* USER CODE BEGIN Includes */
#include "../../USB_DEVICE/App/usbd_cdc_if.h"
    /* USER CODE END Includes */ 

修改main函数的while循环

  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		//USB模拟串口的查寻接收处理
		if(USB_RX_STA!=0)//判断是否有
		{
			//USB_printf("USB_RX:");//向USB模拟串口发字符串
			CDC_Transmit_FS(USB_RX_BUF,USB_RX_STA);//USB串口:将接收的数据发回给电脑端
			//USB_printf("\r\n");//向USB模拟串口发(回车)
			USB_RX_STA=0;//数据标志位清0
			memset(USB_RX_BUF,0,sizeof(USB_RX_BUF));//USB串口数据寄存器清0
		}
  }
  /* USER CODE END 3 */
  1. 修改堆栈和堆;在这里插入图片描述

11.修改编译优化在这里插入图片描述

设置编译优化: Properties for F10****/ C/C++ Build / Settings / MCU GCC Compiler / Optimization \ Optimization level = “Optimize for size(-Os)”
否则会出现错误如下:

\tools\arm-none-eabi\bin\ld.exe: F103C6T6Atest.elf section .text' will not fit in region FLASH’ \tools\arm-none-eabi\bin\ld.exe: region
`FLASH’ overflowed by 1656 bytes collect2.exe: error: ld returned 1
exit status make: *** [makefile:68: F103C6T6Atest.elf] Error 1 “make
-j8 all” terminated with exit code 2. Build might be incomplete.

运行测试

1.设备管理器可以看到串口;
2.使用串口工具,发送接收数据正常。
在这里插入图片描述
CubeIDE1.10.1 环境下的源代码:https://download.youkuaiyun.com/download/qq_23313467/87824117

注意事项
tip1:STM32F103C6T6的存储容量比较小,实现USB的虚拟串口基本上用完了内存和flash,如果再添加别的复杂功能,估计存储就不够了。参看了一下生成的hex文件,已经达到38k了!

tip2:程序重启后,需要硬件拔插一下USB接口,PC才能使用串口。加入下面代码可以避免这种情况。

/*USB 重新枚举函数
 * 解决问题:每次下载完程序后都要重新拔插一次USB PC才能识别串口,这是由于芯片在下载完程序后没有重新枚举所导致的。需要在MCU上电后对USB进行关闭,PC才能重新枚举识别。
 * 解决方法为将USB DP(PA12)引脚拉低一段时间后即可*/
void USB_Reset(void)
{
  	GPIO_InitTypeDef GPIO_InitStruct = {0};
	__HAL_RCC_GPIOA_CLK_ENABLE();
	GPIO_InitStruct.Pin = GPIO_PIN_12;
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
	HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_12,GPIO_PIN_RESET);
	HAL_Delay(100);
	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_12,GPIO_PIN_SET);
}

参考链接

### STM32 使用虚拟串口接收数据的方法 #### 初始化配置 在使用USB虚拟串口之前,需要先初始化STM32USB外设以及相应的中断设置。这通常涉及到启用时钟、配置GPIO引脚为复用功能,并启动USB设备模式。 对于具体的实现细节,在代码层面可以参照如下步骤: 1. **使能USB时钟** 需要在RCC模块中开启USB接口所需的时钟源。 2. **端点配置** 设置用于传输数据的具体端点(Endpoint),一般情况下会有一个IN端点和一个OUT端点分别用来处理发送与接收操作。 3. **中断服务程序注册** 注册并编写对应的中断服务例程来响应来自PC机的数据包到达事件。 ```c // 启动 USB 设备模式 USBD_Init(&hUsbDeviceFS, &VCP_Desc, DEVICE_FS); // 安装类驱动 USBD_RegisterClass(&hUsbDeviceFS, USBD_CDC_CLASS); ``` #### 数据接收逻辑设计 当接收到新的字符流时,系统会产生一次中断请求IRQ,此时进入预定义好的ISR函数内执行特定的任务——读取缓冲区内的新到来的信息片段直至整个消息被完全获取为止;期间还需要考虑防止溢出等问题的发生[^1]。 具体来说就是利用`HAL_UART_Receive_IT()` 函数来进行异步方式下的数据采集工作,它允许应用程序指定回调机制以便于后续进一步加工这些原始二进制序列前后的边界界定等工作。 ```c static void MX_USB_DEVICE_Init(void){ // ...其他必要的初始化... HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1); } void CDC_DataReceived(uint8_t *pData, uint32_t Length){ // 处理接收到的数据 } ``` #### 错误检测与超时管理 为了避免长时间等待而造成死锁现象发生,可以在每次尝试从硬件层面上拉取下一个可用字节的同时设定最大时限参数给定合理的范围值作为保护措施之一。如果超过此期限仍未能成功,则认为此次通讯失败并采取相应补救手段比如重试或者报告异常情况等动作[^2]。 ```c if(HAL_OK != HAL_UART_Receive(&huart1, aData, Size, Timeout)){ Error_Handler(); } ```
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值