一、实验引言

串口通信是一种经典的异步串行通信技术,它硬件结构简单、稳定性高,在嵌入式系统调试、工业设备通信、数据采集等领域一直有着重要地位。本次实验用两台笔记本电脑、CH340 模块和杜邦线搭建串口连接,借助串口助手传输大文件,像图片、视频、压缩包这类,主要有两个目标:一是验证文件大小、波特率和传输时间之间的理论关系,弄清楚串口通信的性能瓶颈;二是研究硬件连接,比如电源、地线对串口通信的影响,明白串口通信对硬件的依赖本质。通过这个实验,能把串口协议的理论知识和实际操作结合起来,深入理解异步通信中 “信号 - 数据 - 完整性” 的逻辑链条。

二、实验工作流程

2.1 实验准备

首先说硬件方面,需要准备 2 台笔记本电脑,一台作为发送端,存放源文件,另一台作为接收端,也就是目标设备;2 个基于 CH340 芯片的 USB/TTL 转 RS-232 模块,用来实现 USB 信号和串口信号的转换,电脑的 USB 信号经过模块能变成 TTL/RS-232 电平,反过来也能转换;4 根公对母的杜邦线,用来连接模块的 TX、RX、GND 引脚,另外还需要 1 根杜邦线给模块供电,不过有些模块能通过 USB 直接供电,这根线就可以不用了;还有 3 类待传输的大文件,比如 10MB 的 JPG 图片、100MB 的 MP4 视频、50MB 的 ZIP 压缩包,涵盖不同类型的文件,这样实验结果更有代表性。软件方面,要在两台电脑上都安装支持文件传输功能的串口助手,比如 SSCOM ;还要提前安装 CH340 芯片的驱动程序,确保电脑能把模块识别成 COM 口,这一步可以在设备管理器里查看是否安装成功;再说说 CH340 模块的关键硬件原理,它的核心功能是信号转换和电平适配。当电脑通过 USB 发送数据包时,CH340 芯片会把这些数据包解析成 TTL 电平的串口信号,从 TX 引脚输出,这是 USB 转串口的过程;而当模块的 RX 引脚接收到串口信号时,CH340 又能把它转换成 USB 数据包,上传到电脑,这是串口转 USB 的过程。有些 CH340 模块还集成了 MAX232 芯片,能把 TTL 电平(0~0.8V 是低电平,2.4~5V 是高电平)转换成 RS-232 电平(+3~+15V 是低电平,-3~-15V 是高电平),这样就能适配不同电平需求的设备了。

2.2 硬件连接

串口通信关键在于交叉连接和共地,这里以 CH340 模块仅输出 TTL 电平(没有 MAX232 芯片)为例来说明具体的连接方式。发送端模块的 TX 引脚,也就是发送引脚,要连接到接收端模块的 RX 引脚,也就是接收引脚,因为发送端输出的信号需要传到接收端的输入引脚,这就是 TX-RX 交叉连接;同样,接收端模块的 RX 引脚要连接到发送端模块的 TX 引脚,这样才能完成双向通信的路径。然后,发送端模块的 GND 引脚,也就是地引脚,必须和接收端模块的 GND 引脚连接起来,只有这样才能确保两台模块的电平参考点一致,否则信号就没办法正确识别。至于模块的电源引脚 VCC,多数模块可以通过 USB 线直接从电脑取电,不需要额外用杜邦线连接电源,如果模块不能通过 USB 取电,再用杜邦线连接到合适的电源,比如 5V 或 3.3V 电源。简单来说,整个连接路径就是发送端电脑 USB 连接到 CH340 模块 1 的 USB 口,模块 1 的 TX、RX、GND 引脚通过杜邦线分别连接到模块 2 的 RX、TX、GND 引脚,模块 2 的 USB 口再连接到接收端电脑 USB。

2.3 软件配置

软件配置需要在两台电脑上同步操作。第一步是识别 COM 口,把 CH340 模块分别插入两台电脑,然后打开设备管理器,找到 “端口(COM 和 LPT)” 选项,记录下发送端模块对应的 COM 号,比如 COM3,还有接收端模块对应的 COM 号,比如 COM5。

第二步是设置串口助手参数,发送端和接收端的参数必须完全一致,不然没办法通信。波特率可以选择常用的数值,像 9600、115200、921600,实验过程中需要测试不同的波特率;数据位选择 8 位,这是串口通信的默认设置,也就是 1 字节数据;停止位选择 1 位,用来标识 1 个字节的结束;校验位选择无,这样能减少额外的开销,更适合文件传输;流控也选择无,因为这次实验是简单的点对点通信,不需要硬件流控。

第三步是接收端准备工作,打开接收端电脑上的 SSCOM,找到 “文件” 选项,点击 “接收文件”,然后选择文件保存的路径,最后点击 “开始接收”,等待发送端发送数据。

2.4 文件传输与完整性验证

发送端操作时,打开 SSCOM,在 “文件” 选项里选择 “发送文件”,找到要传输的大文件,比如 100MB 的 MP4 视频,点击 “开始发送”。在传输过程中,串口助手会显示已发送的字节数和传输进度,这时候要记录下传输开始的时间和结束的时间。

接收端在接收完成后,关闭 “接收文件” 功能,找到保存好的文件。然后用 MD5 校验工具,分别计算发送端原文件和接收端新文件的 MD5 值,如果两个 MD5 值一致,就说明文件传输完整;如果不一致,就要检查硬件连接是否有问题,或者波特率设置是否正确。

为了全面验证实验结果,还需要进行变量控制测试。一方面,固定文件大小,比如用 10MB 的图片,分别测试不同波特率下的传输时间,像 9600、115200、921600 这几种常用波特率;另一方面,固定波特率,比如 115200,分别传输不同大小的文件,比如 10MB、50MB、100MB 的文件,记录下每次的传输时间,这样能更清楚地看到文件大小和波特率对传输时间的影响。

三、关键问题解答

3.1 文件大小、波特率与传输时间的关系

先从理论关系来看,串口通信是异步通信,每个字节(8 位数据)需要额外添加 1 位起始位和 1 位停止位,而且这是在没有校验位的情况下,也就是说 1 个字节对应 10 位信号。所以串口的有效传输速率要考虑这些额外位的开销。波特率指的是单位时间内传输的位数,单位是 bit/s,比如 115200 波特率就是每秒传输 115200 位。那么有效字节传输速率就是波特率除以 10,因为每 10 位对应 1 个字节。理论传输时间的计算方式就是文件大小(以字节为单位)乘以 10,再除以波特率(以 bit/s 为单位)。

再看预估时间和实际传输时间的对比,实际传输时间通常比理论时间长 10% 到 20%,这主要有几个原因。首先是软件开销,串口助手在读取文件、写入文件以及处理数据缓存的时候都需要时间;其次是帧间隙,在传输相邻字节的时候,可能会因为串口芯片或者驱动的原因出现微小的间隙;还有就是干扰丢包,如果硬件连接松动,或者周围环境有干扰,就可能出现数据丢包的情况,有些串口助手支持自动重传,这也会增加传输时间。

从实验结果能得出这样的结论:文件大小和传输时间成正比例关系,文件越大,传输时间越长;波特率和传输时间成反比例关系,波特率越高,传输时间越短。而且波特率越高,实际传输时间和理论传输时间的偏差就越小,这是因为在高波特率下,软件开销在总传输时间里所占的比例降低了。

3.2 只接 TX-RX,不接电源线或 GND 的传输可行性

先看不接电源线的情况,这种情况下绝对没办法工作。因为 CH340 模块是有源设备,需要外部供电,一般是 5V 或者 3.3V,只有供电了芯片才能启动工作。如果不接电源线,模块没有供电,CH340 芯片就没办法解析 USB 信号,也不能生成 TTL 电平的 TX 和 RX 信号。这时候在电脑的设备管理器里也没办法识别到 COM 口,串口助手自然就没办法建立连接,文件传输从一开始就没办法启动。

再看不接 GND,只接 TX-RX 的情况,这种情况也不能正常工作。串口通信的核心是识别电平差,而 GND 是电平的参考基准,不接 GND 的话,信号就没办法正确解码。具体来说,发送端 TX 引脚输出的高电平,比如 5V,或者低电平,比如 0V,都是以自身模块的 GND 为参考的。如果接收端模块没有连接 GND,它的 RX 引脚的电平参考点要么是悬空的,要么和发送端的 GND 存在电位差,比如 2V。这样一来,发送端的高电平 5V,相对于接收端的 GND 可能就只有 3V 了,因为存在 2V 的电位差,接收端的串口芯片就可能把这个 3V 误判成低电平。结果就是信号识别混乱,传输的数据会出现大量错误,接收端得到的文件会损坏,通过 MD5 校验能发现 MD5 值不一致,严重的时候甚至完全没办法接收数据。而且在极端情况下,如果两端的电位差过大,还有可能损坏模块的 TX 和 RX 引脚,虽然 CH340 芯片有一定的过压保护,但还是有损坏的风险。

所以从实验结果能得出,电源线是给模块提供工作能量的,GND 是给信号提供参考基准的,两者都是串口通信必不可少的条件,只接 TX-RX 是没办法建立有效通信的。

四、串口通信接收与发送数据(hal库方式实现)

  1. 安装 STM32CubeMX
    • 前往 ST 官网下载 STM32CubeMX 安装包,按照提示逐步安装,安装完成后打开软件。
  2. 创建工程
    • 在 STM32CubeMX 中,选择对应的 STM32 芯片型号,进入工程配置界面。
  3. 配置 USART1
    • 在 “Pinout & Configuration” 选项卡中,找到 USART1,将其模式设置为 “异步(Asynchronous)”。
    • 然后在参数设置里,将波特率设为 115200,停止位设为 1 位,校验位选择 “无(None)”。
  4. 生成代码
    • 点击 “Project Manager”,设置工程名称、保存路径,选择工具链为 Keil MDK - ARM,然后点击 “GENERATE CODE” 生成工程代码。
  5. Keil 工程配置
    • 打开 Keil 软件,导入生成的工程。检查工程中的 HAL 库文件是否正确添加,确保编译环境配置无误。

2、串口发送数据给上位机

STM32系统给上位机(win11)发送“hello windows!”,win11采用“串口助手”sscom工具接收。

当配置完cubemx完之后,点开keil点击main函数来编辑代码

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2025 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32f1xx_hal.h"
#include "stm32f1xx_hal_uart.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart1;

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
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 */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
    
    // 输出 "hello windows!"
     uint8_t sendData[] = "hello windows!";
    // 调用 HAL_UART_Transmit 发送数据,超时时间设置为 1000ms
    HAL_UART_Transmit(&huart1, sendData, sizeof(sendData) - 1, 1000);
  /* USER CODE END 2 */

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

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief USART1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_USART1_UART_Init(void)
{

  /* USER CODE BEGIN USART1_Init 0 */

  /* USER CODE END USART1_Init 0 */

  /* USER CODE BEGIN USART1_Init 1 */

  /* USER CODE END USART1_Init 1 */
  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART1_Init 2 */

  /* USER CODE END USART1_Init 2 */

}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  /* USER CODE BEGIN MX_GPIO_Init_1 */

  /* USER CODE END MX_GPIO_Init_1 */

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOA_CLK_ENABLE();

  /* USER CODE BEGIN MX_GPIO_Init_2 */

  /* USER CODE END MX_GPIO_Init_2 */
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

接入sscom查看实验结果:

3、串口发送接收数据

当上位机给stm32发送一个字符“#”后,stm32暂停发送“hello windows!”;发送一个字符“#”后,stm32继续发送;

当配置完cubemx完之后,点开keil点击main函数来编辑代码

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2025 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32f1xx_hal.h"
#include "stm32f1xx_hal_uart.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart1;

/* USER CODE BEGIN PV */
uint8_t sendData[] = "hello windows!";  // 要发送的字符串
uint8_t sendEnable = 0;                  // 发送使能标志:0-停止发送,1-开始发送
uint8_t rxBuffer;                        // 接收缓冲区
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
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 */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  // 初始发送一次提示信息
  uint8_t initMsg[] = "请发送#控制消息发送/停止\r\n";
  HAL_UART_Transmit(&huart1, initMsg, sizeof(initMsg) - 1, 1000);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* 接收上位机数据(轮询方式,超时10ms) */
    if (HAL_UART_Receive(&huart1, &rxBuffer, 1, 10) == HAL_OK)
    {
      /* 判断是否收到控制字符'#' */
      if (rxBuffer == '#')
      {
        sendEnable = !sendEnable;  // 翻转发送使能状态
        
        /* 发送状态反馈 */
        if (sendEnable)
        {
          uint8_t startMsg[] = "已开启发送\r\n";
          HAL_UART_Transmit(&huart1, startMsg, sizeof(startMsg) - 1, 1000);
        }
        else
        {
          uint8_t stopMsg[] = "已停止发送\r\n";
          HAL_UART_Transmit(&huart1, stopMsg, sizeof(stopMsg) - 1, 1000);
        }
      }
    }

    /* 如果使能则周期性发送消息 */
    if (sendEnable)
    {
      HAL_UART_Transmit(&huart1, sendData, sizeof(sendData) - 1, 1000);
      HAL_UART_Transmit(&huart1, (uint8_t*)"\r\n", 2, 1000);  // 换行
      HAL_Delay(1000);  // 1秒发送一次,可根据需要调整
    }

    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief USART1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_USART1_UART_Init(void)
{

  /* USER CODE BEGIN USART1_Init 0 */

  /* USER CODE END USART1_Init 0 */

  /* USER CODE BEGIN USART1_Init 1 */

  /* USER CODE END USART1_Init 1 */
  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART1_Init 2 */

  /* USER CODE END USART1_Init 2 */

}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  /* USER CODE BEGIN MX_GPIO_Init_1 */

  /* USER CODE END MX_GPIO_Init_1 */

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOA_CLK_ENABLE();

  /* USER CODE BEGIN MX_GPIO_Init_2 */

  /* USER CODE END MX_GPIO_Init_2 */
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

同样的,打开sscom,打开com端口查看代码运行结果:

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐