3 STM32单片机-串口驱动

系列文章目录



前言

使用STM32通过ESP8266连接OneNET云平台,需要使用串口,而且打印调试信息也需要串口,因此这里添加一下串口驱动文件。分别是串口1和串口2。这里准备一个通用串口初始化驱动文件,将来有了更新,不再关注串口驱动文件,只需要根据不同的板子修改板子配置文件就可以了,达到一劳永逸的效果。


1 串口介绍和资料获取

1.1 串口介绍

串口一般用来打印调试信息,也有一部分与其他模块通信,比如ESP8266。

串口通信分为同步模式和异步模式,大部分场景下都是使用异步模式,仅特殊情况下需要使用同步模式。本文主要介绍串口异步模式。

串口号TXDRXD
USART1PA9PA10
USART2PA2PA3
USART3PB10PB11
UART4PC10PC11
UART5PC12PD2

1.2 实验现象

上电后打印“这是串口实验”,串口输入回显

1.3 资料获取

通过网盘分享的文件:STM32F103_USART.rar
链接: https://pan.baidu.com/s/1Ee5Uspy-hP5KyZL4O-aFLw 提取码: 0000
–来自百度网盘超级会员v7的分享

1.4 相关链接

咸鱼:【胜磊电子】

淘宝:【胜磊电子】

拼多多:【胜磊电子】

哔哩哔哩:【胜磊电子】

2 USART 宏

2.1 USART中断类型(USART_IT_xxx)

中断类型宏用于中断处理函数中判断具体触发的中断(通过USART_GetITStatus函数),或配置需要使能的中断(通过USART_ITConfig函数)。

宏定义含义说明触发场景
USART_IT_PE奇偶校验错误中断接收数据的奇偶校验结果与预设不符时触发
USART_IT_TXE发送数据寄存器空中断发送数据寄存器(TDR)中的数据已被移位寄存器取走,可写入新数据时触发
USART_IT_TC发送完成中断一帧数据(包括停止位)完全发送完成时触发
USART_IT_RXNE接收数据寄存器非空中断接收数据寄存器(RDR)中存入新数据,可读取时触发
USART_IT_ORE_RX接收溢出中断(由RXNEIE位使能时)接收数据未及时读取,新数据覆盖旧数据导致溢出时触发
USART_IT_IDLE空闲线检测中断接收线从数据传输状态转为空闲状态(持续 1 个停止位以上高电平)时触发
USART_IT_LBD LIN总线断开检测中断LIN 模式下检测到总线断开信号时触发
USART_IT_CTS CTS(清除发送)信号变化中断硬件流控制中,CTS 引脚电平变化时触发(需使能硬件流控制)
USART_IT_ERR错误中断(总标志,包含 ORE、NE、FE)任何错误(溢出、噪声、帧错误)发生时触发
USART_IT_ORE_ER溢出错误中断(由EIE位使能时)与USART_IT_ORE_RX类似,区别在于使能方式不同
USART_IT_NE噪声错误中断接收数据中检测到噪声干扰时触发
USART_IT_FE帧错误中断接收数据未检测到有效的停止位时触发(如波特率不匹配导致帧同步失败)

2.2 USART 状态标志(USART_FLAG_xxx)

状态标志宏用于查询方式判断 USART 的工作状态(通过USART_GetFlagStatus函数),或清除状态标志(通过USART_ClearFlag函数)。

宏定义含义说明置位条件
USART_FLAG_CTSCTS 信号标志CTS 引脚电平为低时置位(表示对方可接收数据)
USART_FLAG_LBDLIN 总线断开检测标志LIN 模式下检测到总线断开时置位
USART_FLAG_TXE发送数据寄存器空标志发送数据寄存器(TDR)为空,可写入新数据时置位
USART_FLAG_TC发送完成标志一帧数据完全发送完成(包括停止位)时置位
USART_FLAG_RXNE接收数据寄存器非空标志接收数据寄存器(RDR)中有新数据,可读取时置位
USART_FLAG_IDLE空闲线标志接收线进入空闲状态时置位
USART_FLAG_ORE溢出错误标志接收数据溢出(新数据覆盖旧数据)时置位
USART_FLAG_NE噪声错误标志接收数据中检测到噪声时置位
USART_FLAG_FE帧错误标志接收数据未检测到有效停止位时置位
USART_FLAG_PE奇偶校验错误标志接收数据奇偶校验失败时置位

3 USART 标准库函数

3.1 初始化与复位函数

  1. void USART_DeInit(USART_TypeDef* USARTx)
    功能:将指定 USART 外设(如 USART1、USART2)恢复为复位默认状态,清除所有配置。
    应用场景:重新配置 USART 前,彻底清除旧配置。

  2. void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef*
    USART_InitStruct)
    功能:根据USART_InitStruct结构体的参数配置 USART(波特率、数据位、停止位、校验位等)。
    核心配置函数,必须在使用 USART 前调用。

  3. void USART_StructInit(USART_InitTypeDef* USART_InitStruct)
    功能:为USART_InitTypeDef结构体设置默认值(如波特率 9600、8 位数据位、无校验等)。
    通常在初始化结构体前调用,确保未设置的参数有合理默认值。

  4. void USART_ClockInit(USART_TypeDef* USARTx, USART_ClockInitTypeDef*
    USART_ClockInitStruct)
    功能:配置 USART 的时钟(仅同步模式需要,异步模式无需配置)
    用于设置时钟极性、相位、分频等同步通信参数。

  5. void USART_ClockStructInit(USART_ClockInitTypeDef*
    USART_ClockInitStruct)
    功能:为 USART 时钟配置结构体设置默认值。(仅同步模式需要,异步模式无需配置)

3.2使能与模式控制函数

  1. void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState)
    功能:使能或禁用 USART 外设(ENABLE开启,DISABLE关闭)。
    初始化完成后必须调用USART_Cmd(USARTx, ENABLE)才能启用 USART。

  2. void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT,
    FunctionalState NewState)
    功能:使能或禁用指定的 USART 中断(如接收中断USART_IT_RXNE、发送完成中断USART_IT_TC)。
    中断处理的核心配置函数,需与 NVIC 配置配合使用。

  3. void USART_DMACmd(USART_TypeDef* USARTx, uint16_t USART_DMAReq,
    FunctionalState NewState)
    功能:使能或禁用 USART 的 DMA 请求(如接收 DMAUSART_DMAReq_Rx、发送 DMAUSART_DMAReq_Tx)。
    用于 USART 与 DMA 配合实现高速数据传输(无需 CPU 干预)。

  4. void USART_HalfDuplexCmd(USART_TypeDef* USARTx, FunctionalState
    NewState)
    功能:使能或禁用半双工模式(TX 和 RX 共用一根引脚)。
    适用于仅需单向通信的场景(如 RS485 半双工)。(不常用)

  5. void USART_OverSampling8Cmd(USART_TypeDef* USARTx, FunctionalState
    NewState)
    功能:切换过采样模式(默认 16 倍采样,开启后为 8 倍采样)。
    8 倍采样可提高波特率上限,但抗干扰能力略有下降。(不常用)

3.3数据收发函数

  1. void USART_SendData(USART_TypeDef* USARTx, uint16_t Data)
    功能:发送一个数据(8 位或 9 位,由初始化时的USART_WordLength决定)。
    需先检测USART_FLAG_TXE(发送寄存器空)再调用,避免数据覆盖。

  2. uint16_t USART_ReceiveData(USART_TypeDef* USARTx)
    功能:读取接收的数据(返回 8 位或 9 位数据)。
    调用后会自动清除接收中断标志USART_IT_RXNE,无需手动清除。

  3. void USART_SendBreak(USART_TypeDef* USARTx)
    功能:发送 break 信号(持续发送低电平,至少 13 位数据时长)。
    用于 LIN 总线等协议中的同步或错误处理。

3.4 特殊模式与协议支持函数

  1. void USART_LINCmd(USART_TypeDef* USARTx, FunctionalState NewState)
    功能:使能或禁用 LIN(本地互联网络)模式。
    配合USART_LINBreakDetectLengthConfig可实现 LIN 总线通信。

  2. void USART_IrDACmd(USART_TypeDef* USARTx, FunctionalState NewState)
    功能:使能或禁用 IrDA(红外数据协会)模式。
    用于红外通信,需先通过USART_IrDAConfig配置红外模式。

  3. void USART_SmartCardCmd(USART_TypeDef* USARTx, FunctionalState
    NewState)
    功能:使能或禁用智能卡模式。
    支持 ISO7816 标准的智能卡通信,需配合奇偶校验和特定时序。

3.5 标志位与中断状态函数

  1. FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t
    USART_FLAG)
    功能:获取 USART 的状态标志(如USART_FLAG_TXE发送空、USART_FLAG_TC发送完成、USART_FLAG_RXNE接收非空)。
    用于查询方式收发数据(非中断模式)。

  2. void USART_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG)
    功能:清除指定的状态标志(如USART_FLAG_TC需手动清除)。

  3. ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT)
    功能:判断指定的中断是否触发(如USART_IT_RXNE接收中断、USART_IT_TC发送完成中断)。
    中断处理函数中必须调用,用于区分不同中断类型。

  4. void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t
    USART_IT)
    功能:清除中断挂起标志(如USART_IT_TC、错误中断等)。
    中断处理完成后必须调用,避免重复触发中断。

3.6 其他辅助函数

  1. void USART_SetAddress(USART_TypeDef* USARTx, uint8_t USART_Address)
    功能:设置 USART 的从机地址(多机通信时用于地址匹配)。

  2. void USART_WakeUpConfig(USART_TypeDef* USARTx, uint16_t
    USART_WakeUp)
    功能:配置 USART 的唤醒方式(地址匹配唤醒或空闲线唤醒)。

  3. void USART_SetGuardTime(USART_TypeDef* USARTx, uint8_t
    USART_GuardTime)
    功能:设置智能卡模式下的保护时间(用于卡与设备间的时序匹配)。

这些函数覆盖了 USART 的所有基础功能和扩展协议(LIN、IrDA、智能卡等),通过组合使用可实现各种串口通信场景,从简单的异步通信到复杂的工业总线协议。

4 硬件设计

USART1_TX->PA9
USART1_RX->PA10

5 软件设计

5.1 工程目录

在这里插入图片描述

5.2 main.c

#include "board_config.h"

/************************************************
 串口驱动
 实验现象:上电后打印“这是串口实验”,串口输入回显
 
 淘宝店铺:https://shop475501589.taobao.com/?spm=pc_detail.29232929/evo365560b447259.shop_block.dshopinfo.5dd97dd6JvMuG3
 咸鱼店铺:https://www.goofish.com/personal?spm=a21ybx.item.itemHeader.1.c17a3da6hy8k28&userId=3890583014
 哔哩哔哩:https://space.bilibili.com/482024430?spm_id_from=333.788.upinfo.detail.click
 作者:胜磊电子
************************************************/

/************************************* 全局变量 *******************************************************/


/************************************* 本地函数 *******************************************************/
// 处理接收到的数据
void Usart_ProcessData(USART_HandleTypeDef* huart) {
    if (huart->rx_complete) {
        // 回显接收到的数据
		UsartPrintf(USART1, "收到数据: %s\r\n", huart->rx_buffer);

        // 重置接收状态
        huart->rx_len = 0;
        huart->rx_complete = 0;
        memset(huart->rx_buffer, 0, huart->rx_buffer_size);
    }
}

/*
************************************************************
*	函数名称:	main
*
*	函数功能:	
*
*	入口参数:	无
*
*	返回参数:	0
*
*	说明:
************************************************************
*/
int main(void)
{
	// 初始化所有外设
    BOARD_InitAll();

	UsartPrintf(USART_DEBUG,"这是串口实验\r\n");
	
    while (1) {
		// 处理两个串口的数据
        Usart_ProcessData(&huart1);
    }
}


5.3 board_config

5.3.1 board_config.c

/**
 * @file board_config.c
 * @brief 板级配置实现文件,初始化板上所有外设
 * @author 胜磊电子
 * @date 未定义
 * @version 1.0
 * @copyright 淘宝店铺:https://shop475501589.taobao.com/?spm=pc_detail.29232929/evo365560b447259.shop_block.dshopinfo.5dd97dd6JvMuG3<br>
 * 咸鱼店铺:https://www.goofish.com/personal?spm=a21ybx.item.itemHeader.1.c17a3da6hy8k28&userId=3890583014<br>
 * 哔哩哔哩:https://space.bilibili.com/482024430?spm_id_from=333.788.upinfo.detail.click
 */
#include "board_config.h"

/************************************* 外设对象定义 *******************************************************/

/** 
 * @brief 板载LED1对象
 * @details 对应引脚为PB0
 */
LED_TypeDef BOARD_LED1; /**< 板载LED1(PB0) */

/** 
 * @brief 板载LED2对象
 * @details 对应引脚为PB1
 */
LED_TypeDef BOARD_LED2; /**< 板载LED2(PB1) */

/** 
 * @brief 板载蜂鸣器对象
 * @details 对应引脚为PB8
 */
Beep_TypeDef BOARD_BEEP; /**< 板载蜂鸣器(PB8) */


/** 
 * @brief USART1句柄对象
 * @details 初始化接收/发送缓冲区、缓冲区大小及状态标志
 */
USART_HandleTypeDef huart1 = {
    .rx_buffer = (uint8_t[USART1_RX_BUFFER_SIZE]){0}, ///< 接收缓冲区(大小由USART1_RX_BUFFER_SIZE定义)
    .rx_buffer_size = USART1_RX_BUFFER_SIZE,          ///< 接收缓冲区大小
    .rx_len = 0,                                      ///< 初始接收长度为0
    .rx_complete = 0,                                 ///< 初始接收完成标志为0
	.tx_buffer = (uint8_t[USART1_TX_BUFFER_SIZE]){0}, ///< 发送缓冲区(大小由USART1_TX_BUFFER_SIZE定义)
    .tx_complete = 1                                  ///< 初始发送完成标志为1(空闲状态)
};

/** 
 * @brief USART1初始化参数
 * @details 配置USART1的硬件信息、中断使能及优先级
 */
USART_InitParams usart1_params = {
	.usart = USART1,                                  ///< 对应USART1外设
	.rcc_apb_periph_usart = RCC_APB2Periph_USART1,    ///< USART1时钟使能宏
	.rcc_apb_periph_gpio = RCC_APB2Periph_GPIOA,      ///< 对应GPIOA时钟使能宏
	.gpio_port = GPIOA,                               ///< TX/RX引脚所在端口为GPIOA
	.gpio_pin_tx = GPIO_Pin_9,                        ///< TX引脚为PA9
	.gpio_pin_rx = GPIO_Pin_10,                       ///< RX引脚为PA10
	.enable_idle_interrupt = 1,                       ///< 使能空闲中断
	.enable_tc_interrupt = 0,                         ///< 禁用发送完成中断
	.nvic_irq_channel = USART1_IRQn,                  ///< 中断通道为USART1_IRQn
	.nvic_preemption_priority = 1,                    ///< 抢占优先级为1
	.nvic_sub_priority = 1                             ///< 子优先级为1
};

/************************************* 外设初始化函数 *******************************************************/

/**
 * @brief 初始化板载LED
 * @details 初始化BOARD_LED1(PB0)和BOARD_LED2(PB1)对应的GPIO引脚
 * @return 无
 */
static void BOARD_InitLEDs(void) {
    // 初始化LED1 (PB0)
    LED_Init(&BOARD_LED1, GPIOB, GPIO_Pin_0);
    
    // 初始化LED2 (PB1)
    LED_Init(&BOARD_LED2, GPIOB, GPIO_Pin_1);
}

/**
 * @brief 初始化板载蜂鸣器
 * @details 初始化BOARD_BEEP(PB8)对应的GPIO引脚
 * @return 无
 */
static void BOARD_InitBeep(void) {
    // 初始化蜂鸣器 (PB8)
    Beep_Init(&BOARD_BEEP, GPIOB, GPIO_Pin_8);
}

/**
 * @brief 初始化板载USART
 * @details 初始化USART1,设置波特率为115200
 * @return 无
 */
static void BOARD_InitUSART(void) {
    // 初始化USART1,波特率为115200
    Usart_Init(&huart1, &usart1_params, 115200);
}

/************************************* 全局初始化函数 *******************************************************/

/**
 * @brief 初始化板上所有外设
 * @details 配置NVIC优先级分组,依次初始化LED、蜂鸣器和USART外设
 * @return 无
 * @note NVIC优先级分组配置为2(2位抢占优先级,2位子优先级)
 */
void BOARD_InitAll(void) {
    // 配置中断优先级分组为2(2位抢占优先级,2位子优先级)
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);	
	
    // 初始化LED
    BOARD_InitLEDs();				
	
    // 初始化蜂鸣器
    BOARD_InitBeep();						
	
    // 初始化USART
    BOARD_InitUSART();				
}


5.3.2 board_config.h

/**
 * @file board_config.h
 * @brief 板级配置头文件,定义外设对象及初始化函数
 * @author 胜磊电子
 * @date 未定义
 * @version 1.0
 * @copyright 淘宝店铺:https://shop475501589.taobao.com/?spm=pc_detail.29232929/evo365560b447259.shop_block.dshopinfo.5dd97dd6JvMuG3<br>
 * 咸鱼店铺:https://www.goofish.com/personal?spm=a21ybx.item.itemHeader.1.c17a3da6hy8k28&userId=3890583014<br>
 * 哔哩哔哩:https://space.bilibili.com/482024430?spm_id_from=333.788.upinfo.detail.click
 */
#ifndef BOARD_CONFIG_H
#define BOARD_CONFIG_H

#include "led.h"
#include "beep.h"
#include "usart.h"

/**
 * @brief 调试打印使用的USART外设
 */
#define USART_DEBUG				USART1		// 调试打印所使用的串口组

/**
 * @brief USART1接收缓冲区大小定义
 */
#define USART1_RX_BUFFER_SIZE 	512			// 接收缓冲区配置

/**
 * @brief USART1发送缓冲区大小定义
 */
#define USART1_TX_BUFFER_SIZE 	512			// 发送缓冲区配置


// 导出LED对象供外部使用
extern LED_TypeDef BOARD_LED1; /**< 板载LED1对象 */
extern LED_TypeDef BOARD_LED2; /**< 板载LED2对象 */

// 导出蜂鸣器对象供外部使用
extern Beep_TypeDef BOARD_BEEP; /**< 板载蜂鸣器对象 */

// 导出串口对象供外部使用
extern USART_HandleTypeDef huart1; /**< USART1句柄对象 */
extern USART_InitParams usart1_params; /**< USART1初始化参数对象 */


/**
 * @brief 板级所有外设初始化函数
 * @details 初始化板上所有外设(LED、蜂鸣器、USART等)
 * @return 无
 */
void BOARD_InitAll(void);


#endif /* BOARD_CONFIG_H */

5.4 usart

5.4.1 usart.c

/**
 * @file usart.c
 * @brief USART外设初始化及数据收发功能的实现文件
 * @author 胜磊电子
 * @date 未定义
 * @version 1.0
 * @copyright 淘宝店铺:https://shop475501589.taobao.com/?spm=pc_detail.29232929/evo365560b447259.shop_block.dshopinfo.5dd97dd6JvMuG3<br>
 * 咸鱼店铺:https://www.goofish.com/personal?spm=a21ybx.item.itemHeader.1.c17a3da6hy8k28&userId=3890583014<br>
 * 哔哩哔哩:https://space.bilibili.com/482024430?spm_id_from=333.788.upinfo.detail.click
 */
#include "usart.h"

/**
 * @brief 初始化USART外设
 * @details 配置USART对应的GPIO引脚、波特率、数据格式、中断优先级等参数,并使能外设
 * @param[in,out] huart USART句柄指针,用于存储运行状态
 * @param[in] params USART初始化参数结构体指针
 * @param[in] baudrate 波特率设置(如115200)
 * @return 无
 * @note 根据USART外设挂载的总线(APB1或APB2)使能对应时钟,默认使能RXNE中断
 */
void Usart_Init(USART_HandleTypeDef *huart, USART_InitParams *params, uint32_t baudrate) {
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    // 存储硬件实例
    huart->usart = params->usart;

    // 使能时钟(APB2外设包含USART1,APB1包含USART2/3等)
    if (params->rcc_apb_periph_usart <= RCC_APB2Periph_USART1) {
        RCC_APB2PeriphClockCmd(params->rcc_apb_periph_usart | params->rcc_apb_periph_gpio, ENABLE);
    } else {
        RCC_APB1PeriphClockCmd(params->rcc_apb_periph_usart, ENABLE);
        RCC_APB2PeriphClockCmd(params->rcc_apb_periph_gpio, ENABLE);
    }

    // 配置TX引脚(复用推挽输出)
    GPIO_InitStructure.GPIO_Pin = params->gpio_pin_tx;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(params->gpio_port, &GPIO_InitStructure);

    // 配置RX引脚(浮空输入)
    GPIO_InitStructure.GPIO_Pin = params->gpio_pin_rx;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(params->gpio_port, &GPIO_InitStructure);

    // 配置USART参数
    USART_InitStructure.USART_BaudRate = baudrate;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_Init(params->usart, &USART_InitStructure);

    // 配置中断优先级
    NVIC_InitStructure.NVIC_IRQChannel = params->nvic_irq_channel;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = params->nvic_preemption_priority;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = params->nvic_sub_priority;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    // 使能接收非空中断(基础接收功能)
    USART_ITConfig(params->usart, USART_IT_RXNE, ENABLE);

    // 使能空闲中断(如果配置)
    if (params->enable_idle_interrupt) {
        USART_ITConfig(params->usart, USART_IT_IDLE, ENABLE);
    }

    // 使能发送完成中断(如果配置)
    if (params->enable_tc_interrupt) {
        USART_ITConfig(params->usart, USART_IT_TC, ENABLE);
    }

    // 使能USART外设
    USART_Cmd(params->usart, ENABLE);
}

/**
 * @brief 发送字符串数据
 * @details 采用轮询方式逐字节发送数据,等待每个字节发送完成后再继续
 * @param[in] USARTx USART外设基地址(如USART1)
 * @param[in] str 待发送数据的指针
 * @param[in] len 待发送数据的长度
 * @return 无
 */
void Usart_SendString(USART_TypeDef *USARTx, unsigned char *str, unsigned short len)
{
    unsigned short count = 0;
    
    for(; count < len; count++)
    {
        USART_SendData(USARTx, *str++);									// 发送数据
        while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET);		// 等待发送完成
    }
}

/**
 * @brief 格式化打印函数
 * @details 支持可变参数格式化输出,内部使用vsnprintf函数处理格式化,然后通过USART发送
 * @param[in] USARTx USART外设基地址(如USART1)
 * @param[in] fmt 格式化字符串
 * @param[in] ... 可变参数列表
 * @return 无
 * @note 内部缓冲区大小为296字节,发送前会等待TXE标志确保发送寄存器空闲
 */
void UsartPrintf(USART_TypeDef *USARTx, char *fmt,...)
{
    unsigned char UsartPrintfBuf[296];
    va_list ap;
    unsigned char *pStr = UsartPrintfBuf;
    
    va_start(ap, fmt);
    vsnprintf((char *)UsartPrintfBuf, sizeof(UsartPrintfBuf), fmt, ap);							// 格式化输出
    va_end(ap);
    
    // 发送第一个字节前等待TXE标志置位,确保硬件准备就绪
    while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);
    
    while(*pStr != 0)
    {
        USART_SendData(USARTx, *pStr++);
        while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET);
    }
}

/**
 * @brief 接收数据函数
 * @details 采用轮询方式接收数据,最多接收max_len字节,返回实际接收长度
 * @param[in] USARTx USART外设基地址(如USART1)
 * @param[out] buffer 接收数据的存储缓冲区
 * @param[in] max_len 缓冲区最大长度
 * @return 实际接收的数据长度
 * @note 当RXNE标志置位时读取数据,直到无数据或达到最大长度
 */
unsigned short Usart_ReceiveData(USART_TypeDef *USARTx, unsigned char *buffer, unsigned short max_len)
{
    unsigned short count = 0;
    
    // 检查是否有数据可接收
    while (USART_GetFlagStatus(USARTx, USART_FLAG_RXNE) == SET && count < max_len)
    {
        // 读取数据并存储到缓冲区
        buffer[count++] = (unsigned char)USART_ReceiveData(USARTx);
    }
    
    return count;
}

/**
 * @brief USART中断处理函数
 * @details 处理RXNE(接收非空)、IDLE(空闲)、TC(发送完成)中断,更新句柄状态
 * @param[in,out] huart USART句柄指针,用于更新运行状态
 * @param[in] params USART初始化参数结构体指针,用于判断中断使能状态
 * @return 无
 * @note RXNE中断:接收数据到缓冲区;IDLE中断:标记接收完成;TC中断:处理连续发送
 */
void Usart_IRQHandler(USART_HandleTypeDef *huart, USART_InitParams *params) {
    uint16_t temp;

    // 1. 接收非空中断(基础接收处理)
    if (USART_GetITStatus(huart->usart, USART_IT_RXNE) != RESET) {
        if (huart->rx_len < huart->rx_buffer_size - 1) {
            huart->rx_buffer[huart->rx_len++] = USART_ReceiveData(huart->usart);
        } else {
            USART_ReceiveData(huart->usart); // 清除标志防止溢出
            huart->rx_len = 0;
            memset(huart->rx_buffer, 0, huart->rx_buffer_size);
        }
    }

    // 2. 空闲中断(如果使能)
    if (params->enable_idle_interrupt && USART_GetITStatus(huart->usart, USART_IT_IDLE) != RESET) {
        temp = huart->usart->SR; // 读取状态寄存器清除标志
        temp = huart->usart->DR;
        (void)temp; // 避免未使用变量警告

        if (huart->rx_len > 0) {
            huart->rx_complete = 1; // 标记接收完成
        }
    }

    // 3. 发送完成中断(如果使能)
    if (params->enable_tc_interrupt && USART_GetITStatus(huart->usart, USART_IT_TC) != RESET) {
        // 清除发送完成标志
        USART_ClearITPendingBit(huart->usart, USART_IT_TC);

        // 发送下一个字节
        if (huart->tx_sent < huart->tx_len) {
            USART_SendData(huart->usart, huart->tx_buffer[huart->tx_sent++]);
        } else {
            // 发送完成
            huart->tx_complete = 1;
        }
    }
}

5.4.2 usart.h

/**
 * @file usart.h
 * @brief USART外设初始化及数据收发功能的头文件定义
 * @author 胜磊电子
 * @date 未定义
 * @version 1.0
 * @copyright 淘宝店铺:https://shop475501589.taobao.com/?spm=pc_detail.29232929/evo365560b447259.shop_block.dshopinfo.5dd97dd6JvMuG3<br>
 * 咸鱼店铺:https://www.goofish.com/personal?spm=a21ybx.item.itemHeader.1.c17a3da6hy8k28&userId=3890583014<br>
 * 哔哩哔哩:https://space.bilibili.com/482024430?spm_id_from=333.788.upinfo.detail.click
 */
#ifndef _USART_H_
#define _USART_H_

#include "system_config.h"

/**
 * @brief USART初始化参数结构体
 * @details 用于配置USART外设的硬件信息、中断使能状态及优先级等参数
 */
typedef struct {
    USART_TypeDef *usart;               ///< USART外设基地址(如USART1、USART2等)
    uint32_t rcc_apb_periph_usart;      ///< USART外设对应的时钟使能宏(如RCC_APB2Periph_USART1)
    uint32_t rcc_apb_periph_gpio;       ///< 对应GPIO端口的时钟使能宏(如RCC_APB2Periph_GPIOA)
    GPIO_TypeDef *gpio_port;            ///< TX/RX引脚所在的GPIO端口(如GPIOA、GPIOB等)
    uint16_t gpio_pin_tx;               ///< TX引脚对应的引脚号(如GPIO_Pin_9)
    uint16_t gpio_pin_rx;               ///< RX引脚对应的引脚号(如GPIO_Pin_10)
    uint8_t enable_idle_interrupt;      ///< 空闲中断使能标志(1=使能,0=禁用)
    uint8_t enable_tc_interrupt;        ///< 发送完成中断使能标志(1=使能,0=禁用)
    uint8_t nvic_irq_channel;           ///< 对应的NVIC中断通道(如USART1_IRQn)
    uint8_t nvic_preemption_priority;   ///< 抢占优先级
    uint8_t nvic_sub_priority;          ///< 子优先级
} USART_InitParams;

/**
 * @brief USART句柄结构体
 * @details 用于存储USART外设的运行状态、收发缓冲区及相关计数信息
 */
typedef struct {
    USART_TypeDef *usart;               ///< USART外设基地址(与初始化参数中的usart一致)
    uint8_t *rx_buffer;                 ///< 接收缓冲区指针
    uint16_t rx_buffer_size;            ///< 接收缓冲区大小
    uint16_t rx_len;                    ///< 当前接收数据长度
    uint8_t rx_complete;                ///< 接收完成标志(由中断设置,1=完成)
    
    uint8_t *tx_buffer;                 ///< 发送缓冲区指针(可选使用)
    uint16_t tx_len;                    ///< 待发送数据总长度
    uint16_t tx_sent;                   ///< 已发送数据长度
    uint8_t tx_complete;                ///< 发送完成标志(由TC中断设置,1=完成)
} USART_HandleTypeDef;

/**
 * @brief 初始化USART外设
 * @param[in,out] huart USART句柄指针,用于存储运行状态
 * @param[in] params USART初始化参数结构体指针
 * @param[in] baudrate 波特率设置(如115200)
 * @return 无
 * @note TX引脚配置为复用推挽输出,RX引脚配置为浮空输入
 */
void Usart_Init(USART_HandleTypeDef *huart, USART_InitParams *params, uint32_t baudrate);

/**
 * @brief 发送字符串数据
 * @param[in] USARTx USART外设基地址(如USART1)
 * @param[in] str 待发送数据的指针
 * @param[in] len 待发送数据的长度
 * @return 无
 * @note 采用轮询方式发送,等待每个字节发送完成后再发送下一个
 */
void Usart_SendString(USART_TypeDef *USARTx, unsigned char *str, unsigned short len);

/**
 * @brief 格式化打印函数
 * @param[in] USARTx USART外设基地址(如USART1)
 * @param[in] fmt 格式化字符串
 * @param[in] ... 可变参数列表
 * @return 无
 * @note 内部使用vsnprintf进行格式化,最大支持295字节数据(含结束符)
 */
void UsartPrintf(USART_TypeDef *USARTx, char *fmt,...);

/**
 * @brief 接收数据函数
 * @param[in] USARTx USART外设基地址(如USART1)
 * @param[out] buffer 接收数据的存储缓冲区
 * @param[in] max_len 缓冲区最大长度
 * @return 实际接收的数据长度
 * @note 采用轮询方式接收,最多接收max_len字节数据
 */
unsigned short Usart_ReceiveData(USART_TypeDef *USARTx, unsigned char *buffer, unsigned short max_len);

/**
 * @brief USART中断处理函数
 * @param[in,out] huart USART句柄指针,用于更新运行状态
 * @param[in] params USART初始化参数结构体指针,用于判断中断使能状态
 * @return 无
 * @note 需要在对应的中断服务程序中调用,处理RXNE、IDLE、TC中断
 */
void Usart_IRQHandler(USART_HandleTypeDef *huart, USART_InitParams *params);

#endif /* _USART_H_ */


5.5 utils

5.5.1 system_config.h

/**
 * @file system_config.h
 * @brief 工具头文件定义
 * @author 胜磊电子
 * @date 未定义
 * @version 1.0
 * @copyright 淘宝店铺:https://shop475501589.taobao.com/?spm=pc_detail.29232929/evo365560b447259.shop_block.dshopinfo.5dd97dd6JvMuG3<br>
 * 咸鱼店铺:https://www.goofish.com/personal?spm=a21ybx.item.itemHeader.1.c17a3da6hy8k28&userId=3890583014<br>
 * 哔哩哔哩:https://space.bilibili.com/482024430?spm_id_from=333.788.upinfo.detail.click
 */
 
#ifndef SYSTEM_CONFIG_H
#define SYSTEM_CONFIG_H

// 包含所有必要的STM32标准库头文件
#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"

// 其他常用头文件
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <stdarg.h>
#include <string.h>
#include <stdio.h>

// GPIO 配置结构体
typedef struct {
    GPIO_TypeDef* GPIOx;           // GPIO 端口
    uint16_t GPIO_Pin;             // GPIO 引脚
    GPIOMode_TypeDef GPIO_Mode;    // GPIO 模式
    GPIOSpeed_TypeDef GPIO_Speed;  // GPIO 速度
} GPIO_Config_TypeDef;

// 如果没有定义NULL,手动定义
#ifndef NULL
#define NULL ((void*)0)
#endif

#endif /* SYSTEM_CONFIG_H */    


总结

以上,就是STM32通用串口驱动了,将来可以直接使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值