STM32CubeMx+HAL库实现串口中断收发数据(STM32F103RCT6)新手小白必看的保姆级教程

发布时间:2024年01月21日

(一)实验现象?

串口发送0,RX显示 receive 0 LED0 OFF! , LED0灭;

串口发送1,RX显示 receive 1?LED0 ON! , LED0亮;

串口发送2,RX显示 receive 2?LED0 ON! , LED1亮;

串口发送3,RX显示 receive 3?LED0 OFF! , LED1灭;

笔者使用的是正点原子的?MiniSTM32F103RCT6开发板,其他开发板配置原理同样如此(完整版程序详见文末链接)。


?补充说明:(可先看后面的配置和逻辑代码编写)

注意:串口发送的时候没有勾选“16进制发送”,也就意味着是以字符0,字符1,字符2,字符3发送的,所以对应代码里面的判断也是字符;如果勾选“16进制发送”,那发送的数据就是16进制数,对应代码里面的判断就是数字0,1,2,3。视频中没有勾选16进制,所以代码里写的switch-case语句判断的是字符0-3。请大家注意!!!!!!


(二)STM32CubeMX配置

1.时钟配置(RCC)

RCC:Reset and Clock Control? 复位和时钟控制

图一? 时钟选择

时钟通过锁相环(PLL)来实现倍频,STM32默认时钟都是关闭的,要使用一个外设,首先要使能这个外设的时钟?。时钟相当于单片机的心脏,不可或缺!?

__HAL_RCC_PPP_CLK_ENABLE(); //其中PPP代表要使能的外设
图二? 时钟树配置

2.串口配置?

图三 串口配置

模式选择:

?? ?Asynchronous:?? ???异步通信
?? ?Synchronous:?? ??? ?同步通信
?? ?Single Wire (Half-Duplex):?? ?单线/半双工
?? ?Multiprocessor Communication:?? ?多处理器

图四 配置说明

图五 串口中断配置

3.GPIO配置

查看正点原子 MiniSTM32F103RCT6板 原理图,目的是配置GPIO引脚。

PA8----LED0----配置为默认低电平(导通,LED0亮);PD2----LED1-----配置为默认高电平(不导通,LED1灭)?

图六 LED灯原理图

PA8和PD2都要进行配置?

图七 GPIO引脚配置

?4.调试接口配置

在实际项目中SWD协议(可仿真可调试)使用的比较多,SWD与JTAG相比,速度更快,占用的引脚更少,推荐大家配置成SWD协议。

最终的引脚图如下?

5.工程建立?

6.生成代码

最后点击右上角的?GENERATE CODE ,生成代码。


(三)逻辑代码编写?

首先我们需要定义一个整形 rx_data[1] 的数组,用来存放用户从串口输入的1字节数据;再定义四个字符型数组用来存放串口接收后返回的提示;

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
uint8_t rx_data[1] = {0};    // 定义串口待接收数据缓冲区
char rx_data1[] = "receive 0 LED0 OFF!"; 
char rx_data2[] = "receive 1 LED0 ON!"; 
char rx_data3[] = "receive 2 LED1 ON!"; 
char rx_data4[] = "receive 3 LED1 OFF!"; 
/* USER CODE END 0 */

接着我们介绍几个重要的函数?

汇总:

HAL_UART_Transmit_IT():串口中断模式发送

HAL_UART_TxCpltCallback():发送回调函数
HAL_UART_Receive_IT(): 串口中断模式接收

HAL_UART_RxCpltCallback():接收回调函数

1.发送中断

HAL_UART_Transmit_IT() ;该函数通过使用中断机制来实现异步传输,它会启动UART传输中断,并在传输完成后调用用户定义的回调函数。这个函数会将待发送的数据以中断方式发送,而不会阻塞程序的执行,允许系统在数据发送的同时执行其他任务。

HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);

参数说明:

  • huart: 指向 UART_HandleTypeDef 结构体的指针,该结构体包含有关UART外设的配置和状态信息。
  • pData: 指向待发送数据的缓冲区的指针。
  • Size: 要发送的数据的字节数。

返回值:

  • HAL_OK: 函数执行成功。

?发送回调函数:

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);
说明:
    当通过HAL_UART_Transmit_IT()函数启动异步数据传输后,当所有数据成功发送完毕时,将触发该回调函数。
    用户可以在这个回调函数中实现自定义的操作,比如准备下一组要发送的数据、触发其他任务等。

?2.接收中断

HAL_UART_Receive_IT() ;函数用于异步接收数据,它会启动UART接收中断,并在接收到指定数量的数据后调用用户定义的回调函数该函数的使用方式是将待接收数据的缓冲区和缓冲区大小传递给函数,然后函数将启动接收过程。当接收完成时,通过中断机制调用用户定义的回调函数,该回调函数中可以处理接收到的数据。

HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);

参数说明:

  • huart: 指向 UART_HandleTypeDef 结构体的指针,该结构体包含有关UART外设的配置和状态信息。
  • pData: 指向用于存储接收数据的缓冲区的指针。
  • Size: 要接收的数据的字节数。

返回值:

  • HAL_OK: 函数执行成功。

接收回调函数:?

?void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
说明:
    在使用HAL_UART_Receive_IT()函数启动异步接收时,当接收到指定数量的数据时,会触发该回调函数。
    用户可以在这个回调函数中实现自定义的数据处理逻辑,比如解析接收到的数据、执行特定的操作等。

?我们需要自己编写接收中断回调函数:

sizeof是一个计算数据类型所占空间大小单目运算符,在计算字符串的空间大小时,包含了结束符\0的位置;而strlen是一个计算字符串长度函数,使用时需要引用头文件#include <string.h>,不包含\0,即计算\0之前的字符串长度。

sizeof() 将返回数组能容纳的字节数,而不是数组中存储的有效数据的长度。

strlen() 用于计算以 null 字节('\0')结尾的字符串的长度。这个函数会从数组的起始位置开始查找,直到遇到 '\0'为止。

在串口通信中,通常使用 sizeof() 来指定接收数据的缓冲区大小,因为接收的数据可能包含二进制数据,而不一定是以 null 字节结尾的字符串。

在串口通信中,如果发送的是字符串,通常使用 strlen() 是合适的。

/* USER CODE BEGIN 4 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  if(huart->Instance == USART1)    //检查是否为USART1串口
  {
    // 在UART接收完成回调函数中,根据接收到的数据进行不同的处理
    switch(rx_data[0])
    {
        case 0:
            // 如果接收到的数据为 0,则发送 rx_data1 并设置 GPIOA_PIN_8为高电平
            HAL_UART_Transmit_IT(&huart1, (uint8_t*)rx_data1, strlen(rx_data1));
            HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET);
            break;
        case 1:
            // 如果接收到的数据为 1,则发送 rx_data2 并清除 GPIOA_PIN_8 为低电平
            HAL_UART_Transmit_IT(&huart1, (uint8_t*)rx_data2, strlen(rx_data2));
            HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET);
            break;
        case 2:
            // 如果接收到的数据为 2,则发送 rx_data3 并清除 GPIOD_PIN_2 为低电平
            HAL_UART_Transmit_IT(&huart1, (uint8_t*)rx_data3, strlen(rx_data3));
            HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
            break;
        case 3:
            // 如果接收到的数据为 3,则发送 rx_data4 并设置 GPIOD_PIN_2 为高电平
            HAL_UART_Transmit_IT(&huart1, (uint8_t*)rx_data4, strlen(rx_data4));
            HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
            break;
        default:
            // 如果接收到的数据不在上述情况中,不执行任何操作
            break;
    }
    // 启动下一次异步接收
    HAL_UART_Receive_IT(&huart1, (uint8_t*)rx_data, sizeof(rx_data));
  }
}
/* USER CODE END 4 */

在开始的时候我们需要先启动一次异步接收?HAL_UART_Receive_IT(&huart1, rx_data, 1);?

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  HAL_UART_Receive_IT(&huart1, rx_data, 1);// 启动异步接收
  /* USER CODE END 2 */

(四)总结?

基本流程如下:

  1. 用户通过串口助手发送 0/1/2/3 一字节的数据,数据保留到rx_data缓冲区;
  2. 通过 HAL_UART_Receive_IT(&huart1, rx_data, 1),启动接收来自rx_data缓冲区的1字节数据,当收到1字节的数据时,UART 接收中断会被触发。
  3. 在中断回调处理函数 HAL_UART_RxCpltCallback 中,处理接收到的数据,开始switch-case判断,根据用户发送的数据执行对应的代码,比如接收到的数据为 0,则发送 rx_data1,在串口助手端口就会显示 RX:receive 0 LED0 OFF! ,并设置 GPIOA_PIN_8为高电平,即LED0灭;之后再次调用 HAL_UART_Receive_IT 来启动下一次接收。
  4. 如此反复,用户可以不断的从串口发送1字节数据,来控制LED0和LED1的亮灭。

到这里 STM32CubeMx 的所有配置及 Keil5 代码编写就结束了!!

完结!!!撒花!!? ? ? *★,°*:.☆( ̄▽ ̄)/$:*.°★* 。

抓住共性可由类似知识点推之其余,达到事半功倍的效果;识别个性可以扬长避短,用在合适场合。


工程源码及串口助手

链接:https://pan.baidu.com/s/1uX5_8mGaGZ4387PAf5HLZw?pwd=4cix?
提取码:4cix


本篇完。

本人博客仅代表个人见解方便记录成长笔记。

若有不足,请指出,感谢您的阅读!

文章来源:https://blog.csdn.net/weixin_54015326/article/details/135683705
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。