【STM32】USART串口通讯
1.USART简介
STM32芯片具有多个USART外设用于串口通讯,它是 Universal Synchronous Asynchronous Receiver and Transmitter的缩写, 即通用同步异步收发器可以灵活地与外部设备进行全双工数据交换。有别于USART, 它还有具有UART外设(Universal Asynchronous Receiver and Transmitter),它是在USART基础上裁剪掉了同步通信功能, 只有异步通信。简单区分同步和异步就是看通信时需不需要对外提供时钟输出,我们平时用的串口通信基本都是UART。
USART在STM32应用最多莫过于“打印”程序信息,一般在硬件设计时都会预留一个USART通信接口连接电脑, 用于在调试程序是可以把一些调试信息“打印”在电脑端的串口调试助手工具上,从而了解程序运行是否正确、指出运行出错位置等等。
USART只需两根信号线(TX和RX)即可完成双向通信,对硬件要求低,使得很多模块都预留USART接口来实现与其他模块或者控制器进行数据传输, 比如GSM模块,WIFI模块、蓝牙模块等等。在硬件设计时,注意还需要一根“共地线”。
2.USART标准库简介
typedef struct {uint32_t USART_BaudRate; // 波特率uint16_t USART_WordLength; // 字长uint16_t USART_StopBits; // 停止位uint16_t USART_Parity; // 校验位uint16_t USART_Mode; // USART模式uint16_t USART_HardwareFlowControl; // 硬件流控制
} USART_InitTypeDef;
-
USART_BaudRate:波特率设置。一般设置为2400、9600、19200、115200。标准库函数会根据设定值计算得到USARTDIV值, 并设置USART_BRR寄存器值。
-
USART_WordLength:数据帧字长,可选8位或9位。它设定USART_CR1寄存器的M位的值。如果没有使能奇偶校验控制, 一般使用8数据位;如果使能了奇偶校验则一般设置为9数据位。
-
USART_StopBits:停止位设置,可选0.5个、1个、1.5个和2个停止位, 它设定USART_CR2寄存器的STOP[1:0]位的值,一般我们选择1个停止位。
-
USART_Parity:奇偶校验控制选择,可选USART_Parity_No(无校验)、 USART_Parity_Even(偶校验)以及USART_Parity_Odd(奇校验),它设定USART_CR1寄存器的PCE位和PS位的值。
-
USART_Mode:USART模式选择,有USART_Mode_Rx和USART_Mode_Tx, 允许使用逻辑或运算选择两个,它设定USART_CR1寄存器的RE位和TE位。
-
USART_HardwareFlowControl:硬件流控制选择,只有在硬件流控制模式才有效, 可选有使能RTS、使能CTS、同时使能RTS和CTS、不使能硬件流。
当使用同步模式时需要配置SCLK引脚输出脉冲的属性,标准库使用一个时钟初始化结构体USART_ClockInitTypeDef来设置, 因此该结构体内容也只有在同步模式才需要设置。
typedef struct {uint16_t USART_Clock; // 时钟使能控制uint16_t USART_CPOL; // 时钟极性uint16_t USART_CPHA; // 时钟相位uint16_t USART_LastBit; // 最尾位时钟脉冲
} USART_ClockInitTypeDef;
-
USART_Clock:同步模式下SCLK引脚上时钟输出使能控制,可选禁止时钟输出(USART_Clock_Disable)或开启时钟输出(USART_Clock_Enable); 如果使用同步模式发送,一般都需要开启时钟。它设定USART_CR2寄存器的CLKEN位的值。
-
USART_CPOL:同步模式下SCLK引脚上输出时钟极性设置,可设置在空闲时SCLK引脚为低电平(USART_CPOL_Low)或高电平(USART_CPOL_High)。 它设定USART_CR2寄存器的CPOL位的值。
-
USART_CPHA:同步模式下SCLK引脚上输出时钟相位设置,可设置在时钟第一个变化沿捕获数据(USART_CPHA_1Edge)或在时钟第二个变化沿捕获数据。 它设定USART_CR2寄存器的CPHA位的值。USART_CPHA与USART_CPOL配合使用可以获得多种模式时钟关系。
-
USART_LastBit:选择在发送最后一个数据位的时候时钟脉冲是否在SCLK引脚输出,可以是不输出脉冲(USART_LastBit_Disable)、 输出脉冲(USART_LastBit_Enable)。它设定USART_CR2寄存器的LBCL位的值。
3.USART示例
3.1硬件设计
为利用USART实现开发板与电脑通信,需要用到一个USB转USART的IC,我们选择CH340芯片来实现这个功能,这个芯片需要安装驱动。

3.2编程要点
- 使能RX和TX引脚GPIO时钟和USART时钟;
- 初始化GPIO,并将GPIO复用到USART上;
- 配置USART参数;
- 配置中断控制器并使能USART接收中断;
- 使能USART;
- 在USART接收中断服务函数实现数据接收和发送。
void Init_USART(void)
{RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);//使能GPIOA时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能USART1时钟//USART1对应引脚复用映射GPIO_PinAFConfig(GPIOA, GPIO_PinSource9,GPIO_AF_USART1);//PA9复用为USART1GPIO_PinAFConfig(GPIOA, GPIO_PinSource10,GPIO_AF_USART1);//PA10复用为USART1//USART1端口配置GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.GPIO_Pin=GPIO_Pin_9|GPIO_Pin_10;GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF;//复用功能GPIO_InitStruct.GPIO_OType=GPIO_OType_PP;//推挽复用输出GPIO_InitStruct.GPIO_PuPd=GPIO_PuPd_UP;//上拉GPIO_InitStruct.GPIO_Speed=GPIO_Fast_Speed;//速度50MHzGPIO_Init(GPIOA,&GPIO_InitStruct);//初始化PA9,PA10//配置USART参数USART_InitTypeDef USART_Init_Struct;USART_Init_Struct.USART_BaudRate=115200;USART_Init_Struct.USART_HardwareFlowControl=USART_HardwareFlowControl_None;USART_Init_Struct.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;USART_Init_Struct.USART_Parity=USART_Parity_No;USART_Init_Struct.USART_StopBits=USART_StopBits_1;USART_Init_Struct.USART_WordLength=USART_WordLength_8b;USART_Init(USART1,&USART_Init_Struct);//配置中断控制器并使能USART接收中断NVIC_InitTypeDef NVIC_Init_Struct;NVIC_Init_Struct.NVIC_IRQChannel=USART1_IRQn;NVIC_Init_Struct.NVIC_IRQChannelCmd=ENABLE;NVIC_Init_Struct.NVIC_IRQChannelPreemptionPriority=1;NVIC_Init_Struct.NVIC_IRQChannelSubPriority=0;NVIC_Init(&NVIC_Init_Struct);USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//使能USARTUSART_Cmd(USART1,ENABLE);
}
void USART1_IRQHandler(void)
{uint8_t ucTemp;if(SET==USART_GetITStatus(USART1,USART_IT_RXNE)){ucTemp = USART_ReceiveData(USART1);USART_SendData(USART1,ucTemp);}
}
int main(void)
{//重新设置系统时钟HSE_SetSysClock(8, 336, 2, 7);//设置中断分组NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);Init_USART();/* Infinite loop */while (1){}
}
但是,出问题了!!!

出现乱码了!!!到底是哪里出问题了呢???
最后,我们在USART_Init函数中发现蹊跷了。关于波特率的设置,它通过配置值115200,然后通过系统时钟去计算应该配置的寄存器的值。但是,系统时钟的获取函数好像有点问题,因为之前那个宏定义的值不对,所以我们在main函数中又自己实现了系统时钟配置函数。详细可以看【STM32】时钟树系统-CSDN博客。现在看来,我们还是的修改标准库文件了,因为它的函数接口都在使用那个错误的宏定义。
所以,修改了标准库文件中的两个宏定义后,我们就可以在main函数中省略自己配置系统时钟函数了。
int main(void)
{//设置中断分组NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);Init_USART();/* Infinite loop */while (1){}
}
同时也发现无乱码了。

4.使用USART打印日志
#ifndef __BSP_USART_H
#define __BSP_USART_H#ifdef __cplusplus
extern "C"{#endif#include "stm32f4xx.h"
#include "stdio.h"#define LOGGER_USART USART1
#define LOGGER_USART_BAUDRATE 115200
#define LOGGER_USART_CLK RCC_APB2Periph_USART1
#define LOGGER_USART_IRQHandler USART1_IRQHandler
#define LOGGER_USART_IRQ USART1_IRQn#define USART1_TX_PIN GPIO_Pin_9
#define USART1_TX_GPIO_Port GPIOA
#define USART1_TX_GPIO_CLK RCC_AHB1Periph_GPIOA
#define USART1_TX_AF GPIO_AF_USART1
#define USART1_TX_SOURCE GPIO_PinSource9#define USART1_RX_PIN GPIO_Pin_10
#define USART1_RX_GPIO_Port GPIOA
#define USART1_RX_GPIO_CLK RCC_AHB1Periph_GPIOA
#define USART1_RX_AF GPIO_AF_USART1
#define USART1_RX_SOURCE GPIO_PinSource10void Init_USART(void);#ifdef __cplusplus
}
#endif#endif
#include "bsp_usart.h"void Init_USART(void)
{RCC_AHB1PeriphClockCmd(USART1_TX_GPIO_CLK|USART1_RX_GPIO_CLK,ENABLE);//使能GPIOA时钟RCC_APB2PeriphClockCmd(LOGGER_USART_CLK,ENABLE);//使能USART1时钟//USART1对应引脚复用映射GPIO_PinAFConfig(USART1_TX_GPIO_Port, USART1_TX_SOURCE,USART1_TX_AF);//PA9复用为USART1GPIO_PinAFConfig(USART1_RX_GPIO_Port, USART1_RX_SOURCE,USART1_RX_AF);//PA10复用为USART1//USART1端口配置GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.GPIO_Pin=USART1_TX_PIN;GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF;//复用功能GPIO_InitStruct.GPIO_OType=GPIO_OType_PP;//推挽复用输出GPIO_InitStruct.GPIO_PuPd=GPIO_PuPd_UP;//上拉GPIO_InitStruct.GPIO_Speed=GPIO_Fast_Speed;//速度50MHzGPIO_Init(USART1_TX_GPIO_Port,&GPIO_InitStruct);//初始化PA9GPIO_InitStruct.GPIO_Pin=USART1_RX_PIN;GPIO_Init(USART1_RX_GPIO_Port,&GPIO_InitStruct);//初始化PA10//配置USART参数USART_InitTypeDef USART_Init_Struct;USART_Init_Struct.USART_BaudRate=LOGGER_USART_BAUDRATE;USART_Init_Struct.USART_HardwareFlowControl=USART_HardwareFlowControl_None;USART_Init_Struct.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;USART_Init_Struct.USART_Parity=USART_Parity_No;USART_Init_Struct.USART_StopBits=USART_StopBits_1;USART_Init_Struct.USART_WordLength=USART_WordLength_8b;USART_Init(LOGGER_USART,&USART_Init_Struct);//配置中断控制器并使能USART接收中断NVIC_InitTypeDef NVIC_Init_Struct;NVIC_Init_Struct.NVIC_IRQChannel=LOGGER_USART_IRQ;NVIC_Init_Struct.NVIC_IRQChannelCmd=ENABLE;NVIC_Init_Struct.NVIC_IRQChannelPreemptionPriority=1;NVIC_Init_Struct.NVIC_IRQChannelSubPriority=0;NVIC_Init(&NVIC_Init_Struct);USART_ITConfig(LOGGER_USART,USART_IT_RXNE,ENABLE);//使能USARTUSART_Cmd(LOGGER_USART,ENABLE);
}void LOGGER_USART_IRQHandler(void)
{uint8_t ucTemp;if(SET==USART_GetITStatus(LOGGER_USART,USART_IT_RXNE)){ucTemp = USART_ReceiveData(LOGGER_USART);USART_SendData(LOGGER_USART,ucTemp);}
}//重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{/* 发送一个字节数据到串口 */USART_SendData(LOGGER_USART, (uint8_t) ch);/* 等待发送完毕 */while (USART_GetFlagStatus(LOGGER_USART, USART_FLAG_TXE) == RESET);return (ch);
}
#include "bsp_usart.h"void delay(uint32_t cnt)
{while(cnt--);
}int main(void)
{//设置中断分组NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);Init_USART();/* Infinite loop */while (1){printf("hello,i am logger!\r\n");delay(0xfffffff);}
}
这样,我们就可以使用printf函数直接输出到串口上了。
在C语言标准库中,fputc函数是printf函数内部的一个函数,功能是将字符ch写入到文件指针f所指向文件的当前写指针位置, 简单理解就是把字符写入到特定文件中。我们使用USART函数重新修改fputc函数内容,达到类似“写入”的功能。
fgetc函数与fputc函数非常相似,实现字符读取功能。在使用scanf函数时需要注意字符输入格式。
还有一点需要注意的,使用fput和fgetc函数达到重定向C语言标准库输入输出函数必须在MDK的工程选项把“Use MicroLIB”勾选上, MicoroLIB是缺省C库的备选库,它对标准C库进行了高度优化使代码更少,占用更少资源。
为使用printf、scanf函数需要在文件中包含stdio.h头文件。
相关文章:
【STM32】USART串口通讯
1.USART简介 STM32芯片具有多个USART外设用于串口通讯,它是 Universal Synchronous Asynchronous Receiver and Transmitter的缩写, 即通用同步异步收发器可以灵活地与外部设备进行全双工数据交换。有别于USART, 它还有具有UART外设(Univers…...
Qt6中如何将QList转为QSet?
QSet是一个具有唯一值的哈希集合。比较少用。比较有用的是QSet里面的intersect查找两个集合中不同元素,并合并。 转换过程比较简单,第一种是直接用迭代器。 QSet<int> set(list.begin(), list.end()); 第二种就是逐一遍历赋值: QLi…...
aspectj:AOP编程备忘录-切面定义的注意事项
AOP编程时定义切面时需要注意的事 Around 以Around注解拦截构造方法(Constructor)时切面定义只能用call方式而不能是execution,否则 ProceedingJoinPoint.proceed()返回的是null,得不到构造的实例。 execution execution切入点要修改对象内部&#x…...
大数据面试题之Hive(1)
目录 说下为什么要使用Hive?Hive的优缺点?Hive的作用是什么? 说下Hive是什么?跟数据仓库区别? Hive架构 Hive内部表和外部表的区别? 为什么内部表的删除,就会将数据全部删除,而外部表只删除表结构?为什么用外部表更好? Hive建表语句?创建表…...
【Git】分布式版本控制工具
一、简介 二、目标 Git分布式版本控制工具 一、简介 Git是一种分布式版本控制系统,用于跟踪和管理源代码的变化。它由林纳斯托瓦兹(Linus Torvalds)于2005年开发,并迅速成为最流行的版本控制工具之一。以下是关于Git的一些关键…...
排序之插入排序----直接插入排序和希尔排序(1)
个人主页:C忠实粉丝 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 C忠实粉丝 原创 排序之插入排序----直接插入排序和希尔排序(1) 收录于专栏【数据结构初阶】 本专栏旨在分享学习数据结构学习的一点学习笔记,欢迎大家在评论区交流讨…...
快速创建条形热力图
Excel中的条件格式可以有效的凸显数据特征,如下图中B列所示。 现在需要使用图表展现热力条形图,如下图所示。由于颜色有多个过渡色,因此手工逐个设置数据条的颜色,基本上是不可能完成的任务,使用VBA代码可以快速创建这…...
go switch 与 interface
go switch 与 interface 前言 前言 github.com/google/cel-go/common/types/ref type Val interface {// ConvertToNative converts the Value to a native Go struct according to the// reflected type description, or error if the conversion is not feasible.ConvertTo…...
BaseMapper 接口介绍
基于 mybatis-mapper/provider 核心部分实现的基础的增删改查操作,提供了一个核心的 io.mybatis.mapper.BaseMapper 接口和一个 预定义 的 io.mybatis.mapper.Mapper 接口,BaseMapper 接口定义如下: /*** 基础 Mapper 方法,可以在…...
HAL-Cubemax定时器使用记录
title: HAL-Cubemax定时器使用记录 tags: STM32HalCubemax 文章目录 HAL-Cubemax定时器使用记录分享一种思路1.创建一个ms(毫秒)级延时中断2.创建计数的变量3.在需要延时的函数中对变量阈值进行判断4.验证实例--完整使用记录代码 问题往期内容基础库HAL cubemax VSCODE GCC …...
同时使用磁吸充电器和Lightning时,iPhone充电速度会变快吗?
在智能手机的世界里,续航能力一直是用户关注的焦点。苹果公司以其创新的MagSafe技术和传统的Lightning接口,为iPhone用户提供了多样化的充电解决方案。 然而,当这两种技术同时使用时,它们能否带来更快的充电速度?本文…...
零成本搭建个人图床服务器
前言 图床服务器是一种用于存储和管理图片的服务器,可以给我们提供将图片上传后能外部访问浏览的服务。这样我们在写文章时插入的说明图片,就可以集中放到图床里,既方便多平台文章发布,又能统一管理和备份。 当然下面通过在 Git…...
SpringBoot 搭建sftp服务 实现远程上传和下载文件
maven依赖: <dependency><groupId>com.jcraft</groupId><artifactId>jsch</artifactId><version>0.1.55</version> </dependency>application.yml sftp:protocol: sftphost: port: 22username: rootpassword: sp…...
IDEA中使用leetcode 刷题
目录 1.IDEA下载leetcode插件 2.侧边点开插件 3.打开网页版登录找到cookie复制 4.回到IDEA登录 5.刷题 6.共勉 1.IDEA下载leetcode插件 2.侧边点开插件 3.打开网页版登录找到cookie复制 4.回到IDEA登录 5.刷题 6.共勉 算法题来了不畏惧, 挑战前行是成长的舞台…...
华为海思CPU解读
安全可靠CPU测评结果(华为海思篇) 中国信息安全测评中心于2024年5月20日发布安全可靠测评结果公告(2024年第1号),公布依据《安全可靠测评工作指南(试行)》的测评结果,自发布起有效期…...
中介子方程三十三
XXFXXuXXWXXuXXdXXrXXαXXuXpXXdXXpXuXXαXXrXXdXXuXWXπXXWXeXyXeXbXπXpXXNXXqXeXXrXXαXXuXpXXdXXpXuXXαXXrXXeXqXXNXXpXπXbXeXyXeXWXXπXWXuXXdXXrXXαXXuXpXXdXXpXuXXαXXrXXdXXuXXWXXuXXFXXEXXyXXEXXrXXαXXuXpXXdXXpXuXXαXXrXXEXXyXXαXiXXαXiXrXkXtXyXXpXVXXdXuXWX…...
今年哪两个行业可能有贝塔?
银行和综合板块存在比较明显的行业贝塔,背后原因是:银行板块中,最小的几家银行市值也不小;综合板块中,最大的几家市值也不大。 一、今年哪两个行业可能有贝塔? 我们一直强调今年市场呈现出【行业弱beta、风…...
嵌入式软件开发工具使用介绍
软件开发工具 辅助开发工具 硬件工具与仪器设备 逻辑分析仪使用 串口数据解码分析 示波器使用 1.示波器简介 TBS 1052B(Tektronix)系列数字存储示波器在紧凑的设计中提供了经济的性能。 由于多种标配功能, 包括 USB 连接、34 种自动测量、…...
【TB作品】MSP430G2553,单片机,口袋板, 交通灯控制系统
题8 交通灯控制系统 十字路口交通灯由红、绿两色LED显示器(两位8段LED显示器)组成,LED显示器显示切换倒计时,以秒为单位,每秒更新一次;为确保安全,绿LED计数到0转红,经5秒延时&#…...
windows 安装 Kubernetes(k8s)
windows 安装 docker 详情见: https://blog.csdn.net/sinat_32502451/article/details/133026301 minikube Minikube 是一种轻量级的Kubernetes 实现,可在本地计算机上创建VM 并部署仅包含一个节点的简单集群。 下载地址:https://github.…...
【kafka】Golang实现分布式Masscan任务调度系统
要求: 输出两个程序,一个命令行程序(命令行参数用flag)和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽,然后将消息推送到kafka里面。 服务端程序: 从kafka消费者接收…...
深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法
深入浅出:JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中,随机数的生成看似简单,却隐藏着许多玄机。无论是生成密码、加密密钥,还是创建安全令牌,随机数的质量直接关系到系统的安全性。Jav…...
Frozen-Flask :将 Flask 应用“冻结”为静态文件
Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是:将一个 Flask Web 应用生成成纯静态 HTML 文件,从而可以部署到静态网站托管服务上,如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...
相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...
Linux 中如何提取压缩文件 ?
Linux 是一种流行的开源操作系统,它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间,使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的,要在 …...
基于IDIG-GAN的小样本电机轴承故障诊断
目录 🔍 核心问题 一、IDIG-GAN模型原理 1. 整体架构 2. 核心创新点 (1) 梯度归一化(Gradient Normalization) (2) 判别器梯度间隙正则化(Discriminator Gradient Gap Regularization) (3) 自注意力机制(Self-Attention) 3. 完整损失函数 二…...
HubSpot推出与ChatGPT的深度集成引发兴奋与担忧
上周三,HubSpot宣布已构建与ChatGPT的深度集成,这一消息在HubSpot用户和营销技术观察者中引发了极大的兴奋,但同时也存在一些关于数据安全的担忧。 许多网络声音声称,这对SaaS应用程序和人工智能而言是一场范式转变。 但向任何技…...
算法打卡第18天
从中序与后序遍历序列构造二叉树 (力扣106题) 给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。 示例 1: 输入:inorder [9,3,15,20,7…...
在golang中如何将已安装的依赖降级处理,比如:将 go-ansible/v2@v2.2.0 更换为 go-ansible/@v1.1.7
在 Go 项目中降级 go-ansible 从 v2.2.0 到 v1.1.7 具体步骤: 第一步: 修改 go.mod 文件 // 原 v2 版本声明 require github.com/apenella/go-ansible/v2 v2.2.0 替换为: // 改为 v…...
