【STM32 HAL库】串口通信与CubeMX配置

【STM32 HAL库】串口通信与CubeMX配置

  • 前言
  • 串口通信概述
  • USART
  • 硬件连接原理
  • 轮询模式
  • 理论
  • 应用
  • CubeMX配置
  • Keil5代码
  • 发送
  • 接收
  • 中断模式
  • 理论
  • 应用
  • CubeMX配置
  • Keil5代码
  • 发送
  • 接收
  • DMA模式
  • 理论
  • 应用
  • CubeMX配置
  • Keil5代码
  • 发送
  • 接收
  • 串口接收不定长数据
  • 串口重定向为printf
  • 原理
  • 应用
  • CubeMX配置
  • Keil5配置
  • 前言

    本文为笔者学习串口通信知识的总结与复盘,基于keysking的系列视频,欢迎大家指正文中错误

    串口通信概述

    USART

    Universal Synchronous / Asynchronous Receiver & Transmitter
    = USART 通用同步或异步接收器和发送器
    故UART为通用异步接收器和发送器

    硬件连接原理

    STM32:一般由芯片引脚引出的为TTL电平或RS232电平,通信协议为UART或USART协议
    PC:一般为USB电平标准,通信协议为USB协议
    因此,STM32与PC端进行通信时,必须克服“语言”的障碍,需要“翻译官”为他们统一“语言”,一般需要USB转TTL串口模块充当STM32与PC通信的“翻译官”“桥梁”

    注意,一些开发板有板载USB转TTL串口模块、CH340芯片,就不用外接USB转TTL串口模块了

    轮询模式

    理论

    应用

    CubeMX配置

    Keil5代码

    发送

    串口发送数据

    HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
    

    注意:串口的阻塞式发送会导致程序的阻塞,只有当数据全部发送完时,程序才会继续向下执行

    接收

    串口接收数据

    HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
    

    注意:串口的阻塞式接收会导致程序的阻塞,只有当接收到数据时,程序才会继续向下执行。比如用蓝牙调试助手控制小车的运动,阻塞式接收会导致当没有接收到数据时,程序的卡死,小车的无响应,不推荐使用阻塞式接收!!!

    参数详解

    HAL_UART_Transmit:HAL库以UART协议发送(Transmit)
    HAL_UART_Receive:HAL库以UART协议接收(Receive)
    UART_HandleTypeDef *huart:指向UART_HandleTypeDef结构体的指针(如&huart1,也即发送数据或接收数据的串口
    uint8_t *pData:指向要发送数据或接收数据的指针(如(uint8_t *)message
    uint16_t Size:发送的数据或接收的数据的字节长度(如2或strlen(message)
    Timeout:最大发送或接收时间(如100或HAL_MAX_DELAY
    

    中断模式

    理论

    中断模式详解
    当开启串口的中断模式(HAL_UART_Transmit_IT())发送数据后,串口开始发送数据。若发送数据寄存器为空 –> 触发串口中断 –> 进入串口中断处理函数USARTx_IRQHandler –> 判断中断触发源 –> 进入”发送数据寄存器空中断“的中断回调函数 –> 叫回CPU进行数据搬运(内存数据 –> 发送数据寄存器)

    又因为,中断模式的收发为非阻塞,所以为了避免在处理数据时还没有完全接收数据,我们不应在串口中断处理函数中写具体代码,而应该在中断回调函数中实现代码

    如UART发送数据完成中断回调函数

    __weak void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
    

    保证了在完全接收数据后,再处理数据

    补充一下

    因为每个USART只有一个中断向量,但有很多能触发USART中断处理函数的中断源(比如“接收数据寄存器非空”中断 “发送完成”中断 都能触发中断从而进入USART的中断处理函数)
    这些中断源都由中断向量指向唯一的中断处理函数,所以中断处理函数中,根据不同的中断源,内置了不同的中断回调函数(比如:中断源为“发送完成” 那么对应进入”发送完成“中断的中断回调函数)

    应用

    CubeMX配置

    先正常配置

    再使能中断

    Keil5代码

    发送

    串口发送数据

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

    IT模式下“串口发送数据完成”中断回调函数

    HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart); 
    
    接收

    串口接收数据

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

    IT模式下“串口接收数据完成”中断回调函数

    void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart); 
    

    示例

    //程序开始:开启IT模式串口接收数据(注意不要在循环中开启,否则会导致数据未接收完全,就重新开启,导致数据丢失,在setup初始化部分开启一次即可
    HAL_UART_Receive_IT(&huart2, receiveData, sizeof(receiveData));
    
    //重定义回调函数
    void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
    {
    //好习惯,先判断下中断是否来自huart2,因为其他usart触发中断也会调用次回调函数
    	if(huart == &huart2)
    		{
    		/*此处为逻辑代码*/
    		//最后注意重新打开串口IT模式接收,如果不重新开启,在后续数据传入时,串口将不再接收数据
    		HAL_UART_Receive_IT(&huart2, receiveData, sizeof(receiveData));
    		}
    } 
    

    DMA模式

    理论

    什么是DMA?
    DMA = Direct Memory Access 直接内存访问
    充当CPU的小助手,帮助在内存变量与接收/发送数据寄存器之间搬运数据,可以提高传输速度并减少CPU负担

    应用

    CubeMX配置

    先配置USART参数

    再使能USART中断
    因为DMA模式的UART通信也需要调用中断回调等中断相关函数

    最后配置DMA模式 默认参数即可

    Keil5代码

    发送

    串口发送数据

    HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
    
    接收

    串口接收数据

    HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
    

    DMA模式下 “串口接收数据完成”中断回调函数

    void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart,uint16_t Size);
    

    串口接收不定长数据

    利用串口空闲(idle)中断:
    当RX引脚无后续数据传入时触发该中断,也即RX从忙碌转为空闲时触发,也即一帧数据包接收完成时触发

    CubeMX配置同DMA模式
    Keil5代码

  • 串口接收函数
  • HAL_UARTEx_ReceiveToIdle_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
    
    Ex = 扩展   Receive = 接收   Idle = 空闲中断
    *huart = 接收串口的指针地址 例:&huart2
    *pData = 用来接收数据的变量的指针地址 例:receiveData
    Size = 一次能接收的最大数据长度,一般填写接收数据变量的长度,防止数组越界 例:sizeof(receiveData)
    
  • DMA模式下“串口接收数据完成”中断回调函数
  • void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart,uint16_t Size)
    
  • 整体
  • //程序开始:开启DMA模式串口接收数据空闲中断,关闭DMA传输过半中断
    HAL_UARTEx_ReceiveToIdle_DMA(&huart2, receiveData, sizeof(receiveData));
    __HAL_DMA_DISABLE_IT(&hdma_usart2_rx,DMA_IT_HT);
    
    //重定义回调函数
    void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart,uint16_t Size){
    //养成好习惯,先判断下中断是否来自huart2,因为其他usart触发的DMA模式接收数据完成中断也使用的是该中断回调函数
    	if(huart == &huart2)
    		{
    		/*此处为逻辑代码*/
    		//最后注意重新打开串口DMA模式接收,否则在后续数据传入时,DMA会不再搬运数据
    		HAL_UARTEx_ReceiveToIdle_DMA(&huart2, receiveData, sizeof(receiveData));
    		//关闭”DMA传输过半中断”,防止接收数据长度大于一半的接收数据最大长度时 导致的再进入该中断回调函数
    		//注意 该关闭“DMA传输过半中断”在程序开头也要有 防止第一次接收的数据大于一半最大接收数据上限
    		__HAL_DMA_DISABLE_IT(&hdma_usart2_rx,DMA_IT_HT);
    		}
    } 
    

    补充
    USART勾选NVIC使能并添加了DMA的通道,不影响串口的基本的轮询发送数据的功能,勾选NVIC与添加DMA通道只是扩展了串口的功能,并不限制原有的基本功能

    串口重定向为printf

    原理

    为了能使用printf()函数来进行数据的交互,就要进行函数的”重映射“,即重新定义c库函数printf()中调用的fputc()函数
    fputc为__weak弱定义,方便我们修改重定义以此调用printf

    应用

    CubeMX配置

    与轮询模式配置一致
    因为中断模式与DMA模式收发数据异常

    Keil5配置

    勾选 “Use MicroLIB”

    添加头文件

    重写fputc()函数

    int fputc(int ch,FILE *f)
    {
    	uint8_t temp[1] = {ch};
    	
    	//采用轮询方式发送1字节数据
    	HAL_UART_Transmit(&huart3,temp,1,2);
    	return ch;
    }
    

    最后,你就能调用 printf() 函数了!!!

    作者:Abaaba+

    物联沃分享整理
    物联沃-IOTWORD物联网 » 【STM32 HAL库】串口通信与CubeMX配置

    发表回复