STM32F429第十五篇之串口实验详解二
发布日期:2021-05-14 11:31:29 浏览次数:23 分类:精选文章

本文共 8971 字,大约阅读时间需要 29 分钟。

STM32 HAL库优化与串口通信改进

本文主要讨论了对STM32 HAL库中的串口通信进行优化,以及对系统文件夹中的程序进行简化的实现。通过构建一个新的工程,简化了串口工作流程,并对正点原子原有的system文件夹中的程序进行了优化。最终实现了一个高效的串口通信方案,并对相关系统配置进行了详细说明。

文章目录

  • 1. 系统初始化与配置
  • 2. 串口通信实现
  • 3. 流水灯控制
  • 4. 系统文件夹优化
  • 5. 相关库文件实现

前言

通过上一篇博客已经详细梳理了串口在HAL库中的工作过程,但该过程流程复杂且实时性较差。本文通过构建一个新的工程,简化了串口工作流程,并对正点原子原有的system文件夹中的程序进行了优化。完整的工程文件可在GitHub上找到。

主函数实现

主函数的实现流程如下:

  • 系统初始化:调用HAL_Init(), SystemClock_Config(), GpioInit(), UartInit()完成系统和串口的基本配置
  • 串口初始化消息输出:通过重新定义printf函数实现串口输出
  • 数据接收与发送:接收数据后直接控制寄存器实现快速发送
  • 流水灯控制:通过位带操作控制流水灯的开关状态
  • system文件夹优化

    本文对system文件夹中的程序进行了以下优化:

  • 忽略系统情况:避免对系统本身进行复杂处理
  • 头文件管理:尽量减少头文件的包含,提高可移植性
  • 程序简洁化:通过位带操作和宏定义优化代码结构
  • sys库实现

    sys库包含了系统初始化和位带操作的实现。

    /**  ******************************************************************************  * @file    sys.h  * @author  zhy  * @version 1.0  * @date    2021-01-22  * @brief   系统初始化相关的头文件  ******************************************************************************  */#ifndef _SYS_H#define _SYS_H#include   
    typedef struct{ unsigned int bit0 : 1; unsigned int bit1 : 1; unsigned int bit2 : 1; unsigned int bit3 : 1; unsigned int bit4 : 1; unsigned int bit5 : 1; unsigned int bit6 : 1; unsigned int bit7 : 1; unsigned int bit8 : 1; unsigned int bit9 : 1; unsigned int bit10 : 1; unsigned int bit11 : 1; unsigned int bit12 : 1; unsigned int bit13 : 1; unsigned int bit14 : 1; unsigned int bit15 : 1; unsigned int rsv : 16;} BitBand __attribute__((bitband));
    extern BitBand PAin;extern BitBand PBin;extern BitBand PCin;extern BitBand PDin;extern BitBand PEin;extern BitBand PFin;extern BitBand PGin;extern BitBand PHin;extern BitBand PIin;extern BitBand PAout;extern BitBand PBout;extern BitBand PCout;extern BitBand PDout;extern BitBand PEout;extern BitBand PFout;extern BitBand PGout;extern BitBand PHout;extern BitBand PIout;extern void SystemClock_Config(void); //时钟系统配置

    延迟实现

    延迟实现采用时钟窃取法,确保延时函数的高效性。

    /**  ******************************************************************************  * @name    delay.h  * @author  zhy  * @version 1.0  * @date    2020.11.11  * @brief   提供延时函数的对外接口。  ******************************************************************************  */#ifndef __DELAY_H#define __DELAY_H#include   
    extern void delay_us(uint32_t nus );extern void delay_ms( uint16_t nms );#endif
    /**  ******************************************************************************  * @name    delay.c  * @author  zhy  * @version 1.0  * @date    2020.11.11  * @brief   通过时钟窃取法,得到延时时钟的功能。  ******************************************************************************  */#include "stm32f4xx.h" //包含该头文件不会出现引用错误#include "delay.h"/**  * @brief   延时函数微秒  * @note    None  * @param   nus:延时的微秒数,0~2^32/fac_us  * @retval  None  */void delay_us(uint32_t nus){       uint32_t fac_us = HAL_RCC_GetSysClockFreq() / 1e6; //us延时倍乘数    uint32_t ticks;                                    //存储需要节拍数    uint32_t told, tnow, tcnt = 0;                     //told:上一次存储的时间,tnow:当前时间;tcnt:累计的时间差    uint32_t reload = SysTick->LOAD;                   //读取sysTick装载值    ticks = nus * fac_us;                              //需要的节拍数    told = SysTick->VAL;                               //读取当前计数值    while (1)    {           tnow = SysTick->VAL;        if (tnow != told)        {               /*获得上一次记录的时间和当前时间之间的差值*/            if (tnow < told)                tcnt += told - tnow;            else                tcnt += reload - tnow + told;            /*将当前时间记录下来*/            told = tnow;            if (tcnt >= ticks)                break; //时间超过/等于要延迟的时间,则退出.        }    }}/**  * @brief   延时函数毫秒  * @note    None  * @param   nms:延时的毫秒数  * @retval  None  */void delay_ms(uint16_t nms){       uint32_t i;    for (i = 0; i < nms; i++)        delay_us(1000);}

    队列实现

    队列实现采用环形结构,支持高效的数据接收和发送。

    /**  ******************************************************************************  * @file    queue.h  * @author  zhy  * @version 1.0  * @date    2021-01-25  * @brief   与队列相关的结构体  ******************************************************************************  */#ifndef __QUEUE_H__#define __QUEUE_H__#include   
    typedef struct Queue{ uint8_t mem[256]; //255个长度的队列 uint8_t *front; //队首 uint8_t *rear; //队尾} Queue;
    /** * @brief 判断队列是否为空 * @note 注意括号的使用 * @param q:队列指针 * @retval 0:队列非空,1:队列空 */#define isQueueEmpty(q) ((q)->front == (q)->rear)extern void InitQueue(Queue *q);extern void InQueue(Queue *q, uint8_t item);extern uint8_t DeQueue(Queue *q, uint8_t *item);#endif
    /**  ******************************************************************************  * @file    queue.c  * @author  zhy  * @version 1.0  * @date    2021-01-25  * @brief   对队列相关的函数  ******************************************************************************  */#include "queue.h"/**  * @brief 初始化队列  * @note 无 * @param {Queue} *q 被初始化队列的指针 * @retval 无 */void InitQueue(Queue *q){       q->front = q->mem;    q->rear = q->mem;}  
    /** * @brief 进队列 * @note 无 * @param {Queue} *q 进入的队列 * @param {uint8_t} item 进入的元素 * @retval 无 */void InQueue(Queue *q, uint8_t item){ *(q->front) = item; //将值写入队头 q->front = q->mem + ((q->front + 1 - q->mem) & 0xff); //队头自增 if (q->front == q->rear) //若队头和队尾相等——队列满,队尾被覆盖 { q->rear = q->mem + ((q->rear + 1 - q->mem) & 0xff); //队尾自增 }}
    /** * @brief 出队列 * @note 无 * @param {Queue} *q 从该队列出 * @param {uint8_t} *item 获得的数据 * @retval 队伍状态 */uint8_t DeQueue(Queue *q, uint8_t *item){ if (q->front != q->rear) //队伍不空 { *item = *(q->rear); //获得队尾值 q->rear = q->mem + ((q->rear + 1 - q->mem) & 0xff); //队尾自增 return 1; //正确得到数据 } return 0; //队伍空}

    串口UART实现

    串口实现采用中断驱动模式,支持高效的数据传输。

    /**  ******************************************************************************  * @file    usart.h  * @author  zhy  * @version 1.0  * @date    2021-01-22  * @brief   与串口相关配置,USART1  ******************************************************************************  */#ifndef __USART_H__#define __USART_H__  
    /* 外部结构体类型声明 */typedef struct Queue Queue;/* 对外接口 */extern void UartInit(void);extern Queue queueRx; //串口接收队列#endif
    /**  ******************************************************************************  * @file    usart.h  * @author  zhy  * @version 1.0  * @date    2021-01-22  * @brief   这是与串口相关的C文件。  *             主要实现了重构printf函数。  *             串口1的配置  ******************************************************************************  */#include "usart.h"#include "stm32f4xx.h"#include "queue.h"/* 全局变量区 */Queue queueRx; //串口接收队列/**  * @brief 重构函数  * @note 在printf中自动调用 * @param {int} ch  发送的数据 * @param {FILE} *f 此处用不到 * @retval 当前发送值 */int fputc(int ch, FILE *f){       while ((USART1->SR & USART_SR_TXE) == 0)        ;                     //判断是否发送完成    USART1->DR = (uint8_t)ch; //发送数据    return ch;}  
    /** * @brief 串口初始化 * @note * @param 无 * @retval 无 */void UartInit(void){ /* 1.启动时钟 */ __HAL_RCC_USART1_CLK_ENABLE(); //开启串口1时钟 /* 2.配置外设相关参数 */ UART_HandleTypeDef huart; //串口句柄 huart.Instance = USART1; //串口1 huart.Init.BaudRate = 115200; //波特率 huart.Init.WordLength = UART_WORDLENGTH_8B; //字长 huart.Init.StopBits = UART_STOPBITS_1; //1位停止位 huart.Init.Parity = UART_PARITY_NONE; //非校验 huart.Init.HwFlowCtl = UART_HWCONTROL_NONE; //无硬件控制 huart.Init.Mode = UART_MODE_TX_RX; //收发模式 huart.Init.OverSampling = UART_OVERSAMPLING_8; //8倍过采样 HAL_UART_Init(&huart); //串口1初始化 /* 3.接收中断相关 */ __HAL_UART_ENABLE_IT(&huart, UART_IT_RXNE); //使能接收中断}
    /** * @brief 串口底层初始化 * @note 此函数会在HAL_UART_Init中被调用 * @param {UART_HandleTypeDef} *huart 串口句柄 * @retval 无 */void HAL_UART_MspInit(UART_HandleTypeDef *huart){ if (USART1 == huart->Instance) //若是串口1 { /* 1.启动底层时钟 */ __HAL_RCC_GPIOA_CLK_ENABLE(); /* 2.配置gpio底层 */ GPIO_InitTypeDef gpioInit; gpioInit.Pin = GPIO_PIN_9 | GPIO_PIN_10; gpioInit.Mode = GPIO_MODE_AF_PP; gpioInit.Pull = GPIO_PULLUP; gpioInit.Speed = GPIO_SPEED_FAST; gpioInit.Alternate = GPIO_AF7_USART1; HAL_GPIO_Init(GPIOA, &gpioInit); /* 3.配置接收中断 */ InitQueue(&queueRx); //使能接收队列 HAL_NVIC_EnableIRQ(USART1_IRQn); //使能中断 HAL_NVIC_SetPriority(USART1_IRQn, 3, 3); //设置抢断优先级与子优先级 }}
    /** * @brief 串口1中断服务函数 * @note 在此处,只开放接收中断。那么当中断触发时,必然是接收中断。 * 因为中断函数时间越短越好,采用直接操作寄存器的方式。 * @param {*}无 * @retval 无 */void USART1_IRQHandler(void){ if ((USART1->SR & USART_SR_RXNE) && (USART1->CR1 & USART_CR1_RXNEIE)) //当接收中断置位且有中断标记 { InQueue(&queueRx, USART1->DR); //将接收的内容放入队列,读取寄存器,自动清除中断标记位 }}

    总结

    本文通过对HAL库中的串口通信流程进行优化,实现了一个高效的串口通信方案。同时,对系统文件夹中的程序进行了简化,提高了代码的可移植性和运行效率。通过重新定义位带操作和优化延迟函数,确保了系统的实时性和稳定性。未来将继续优化相关库文件,提升系统性能。

    上一篇:STM32F429第十六篇之外部中断
    下一篇:STM32CubeMX第三篇之串口实验

    发表评论

    最新留言

    哈哈,博客排版真的漂亮呢~
    [***.90.31.176]2025年05月02日 05时32分59秒