系列文章目录
前言
使用STM32通过ESP8266连接OneNET云平台,需要使用串口,而且打印调试信息也需要串口,因此这里添加一下串口驱动文件。分别是串口1和串口2。这里准备一个通用串口初始化驱动文件,将来有了更新,不再关注串口驱动文件,只需要根据不同的板子修改板子配置文件就可以了,达到一劳永逸的效果。
1 串口介绍和资料获取
1.1 串口介绍
串口一般用来打印调试信息,也有一部分与其他模块通信,比如ESP8266。
串口通信分为同步模式和异步模式,大部分场景下都是使用异步模式,仅特殊情况下需要使用同步模式。本文主要介绍串口异步模式。
| 串口号 | TXD | RXD |
|---|---|---|
| USART1 | PA9 | PA10 |
| USART2 | PA2 | PA3 |
| USART3 | PB10 | PB11 |
| UART4 | PC10 | PC11 |
| UART5 | PC12 | PD2 |
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_CTS | CTS 信号标志 | CTS 引脚电平为低时置位(表示对方可接收数据) |
| USART_FLAG_LBD | LIN 总线断开检测标志 | 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 初始化与复位函数
-
void USART_DeInit(USART_TypeDef* USARTx)
功能:将指定 USART 外设(如 USART1、USART2)恢复为复位默认状态,清除所有配置。
应用场景:重新配置 USART 前,彻底清除旧配置。 -
void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef*
USART_InitStruct)
功能:根据USART_InitStruct结构体的参数配置 USART(波特率、数据位、停止位、校验位等)。
核心配置函数,必须在使用 USART 前调用。 -
void USART_StructInit(USART_InitTypeDef* USART_InitStruct)
功能:为USART_InitTypeDef结构体设置默认值(如波特率 9600、8 位数据位、无校验等)。
通常在初始化结构体前调用,确保未设置的参数有合理默认值。 -
void USART_ClockInit(USART_TypeDef* USARTx, USART_ClockInitTypeDef*
USART_ClockInitStruct)
功能:配置 USART 的时钟(仅同步模式需要,异步模式无需配置)。
用于设置时钟极性、相位、分频等同步通信参数。 -
void USART_ClockStructInit(USART_ClockInitTypeDef*
USART_ClockInitStruct)
功能:为 USART 时钟配置结构体设置默认值。(仅同步模式需要,异步模式无需配置)。
3.2使能与模式控制函数
-
void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState)
功能:使能或禁用 USART 外设(ENABLE开启,DISABLE关闭)。
初始化完成后必须调用USART_Cmd(USARTx, ENABLE)才能启用 USART。 -
void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT,
FunctionalState NewState)
功能:使能或禁用指定的 USART 中断(如接收中断USART_IT_RXNE、发送完成中断USART_IT_TC)。
中断处理的核心配置函数,需与 NVIC 配置配合使用。 -
void USART_DMACmd(USART_TypeDef* USARTx, uint16_t USART_DMAReq,
FunctionalState NewState)
功能:使能或禁用 USART 的 DMA 请求(如接收 DMAUSART_DMAReq_Rx、发送 DMAUSART_DMAReq_Tx)。
用于 USART 与 DMA 配合实现高速数据传输(无需 CPU 干预)。 -
void USART_HalfDuplexCmd(USART_TypeDef* USARTx, FunctionalState
NewState)
功能:使能或禁用半双工模式(TX 和 RX 共用一根引脚)。
适用于仅需单向通信的场景(如 RS485 半双工)。(不常用) -
void USART_OverSampling8Cmd(USART_TypeDef* USARTx, FunctionalState
NewState)
功能:切换过采样模式(默认 16 倍采样,开启后为 8 倍采样)。
8 倍采样可提高波特率上限,但抗干扰能力略有下降。(不常用)
3.3数据收发函数
-
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data)
功能:发送一个数据(8 位或 9 位,由初始化时的USART_WordLength决定)。
需先检测USART_FLAG_TXE(发送寄存器空)再调用,避免数据覆盖。 -
uint16_t USART_ReceiveData(USART_TypeDef* USARTx)
功能:读取接收的数据(返回 8 位或 9 位数据)。
调用后会自动清除接收中断标志USART_IT_RXNE,无需手动清除。 -
void USART_SendBreak(USART_TypeDef* USARTx)
功能:发送 break 信号(持续发送低电平,至少 13 位数据时长)。
用于 LIN 总线等协议中的同步或错误处理。
3.4 特殊模式与协议支持函数
-
void USART_LINCmd(USART_TypeDef* USARTx, FunctionalState NewState)
功能:使能或禁用 LIN(本地互联网络)模式。
配合USART_LINBreakDetectLengthConfig可实现 LIN 总线通信。 -
void USART_IrDACmd(USART_TypeDef* USARTx, FunctionalState NewState)
功能:使能或禁用 IrDA(红外数据协会)模式。
用于红外通信,需先通过USART_IrDAConfig配置红外模式。 -
void USART_SmartCardCmd(USART_TypeDef* USARTx, FunctionalState
NewState)
功能:使能或禁用智能卡模式。
支持 ISO7816 标准的智能卡通信,需配合奇偶校验和特定时序。
3.5 标志位与中断状态函数
-
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t
USART_FLAG)
功能:获取 USART 的状态标志(如USART_FLAG_TXE发送空、USART_FLAG_TC发送完成、USART_FLAG_RXNE接收非空)。
用于查询方式收发数据(非中断模式)。 -
void USART_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG)
功能:清除指定的状态标志(如USART_FLAG_TC需手动清除)。 -
ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT)
功能:判断指定的中断是否触发(如USART_IT_RXNE接收中断、USART_IT_TC发送完成中断)。
中断处理函数中必须调用,用于区分不同中断类型。 -
void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t
USART_IT)
功能:清除中断挂起标志(如USART_IT_TC、错误中断等)。
中断处理完成后必须调用,避免重复触发中断。
3.6 其他辅助函数
-
void USART_SetAddress(USART_TypeDef* USARTx, uint8_t USART_Address)
功能:设置 USART 的从机地址(多机通信时用于地址匹配)。 -
void USART_WakeUpConfig(USART_TypeDef* USARTx, uint16_t
USART_WakeUp)
功能:配置 USART 的唤醒方式(地址匹配唤醒或空闲线唤醒)。 -
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通用串口驱动了,将来可以直接使用。
1万+

被折叠的 条评论
为什么被折叠?



