STM32串口通信是一种在STM32微控制器中常用的数据传输方式,它允许设备之间通过串行接口进行数据交换。以下是对STM32串口通信知识点的详细讲解:
一、串口通信的基本概念
串口通信(Serial Communication)是一种设备间常用的串行通讯方式,其通信协议可分层为协议层和物理层。物理层规定通信协议中具有机械、电子功能的特性,从而确保原始数据在物理媒体的传播;协议层主要规定通讯逻辑,统一双方的数据打包、解包标准。
二、STM32串口通信的接口
STM32提供了两种串口通信接口:
-
UART(Universal Asynchronous Receiver/Transmitter,通用异步收发器):UART是一种异步通信协议,它使用起始位、数据位、校验位和停止位来定义一个字符的传输格式。UART不需要时钟信号,通信双方需要约定好波特率(即每秒传输的码元个数),以便对信号进行解码。
-
USART(Universal Synchronous/Asynchronous Receiver/Transmitter,通用同步异步收发器):USART是一种同步/异步通信协议,它支持全双工通信,并具备更高的数据传输速率和更好的抗干扰能力。USART在UART的基础上增加了同步通信功能,即可以使用时钟信号来同步数据传输。
三、STM32串口通信的分类
根据数据传输的方向,串口通信可以分为单工、半双工和全双工:
- 单工:数据传输只支持数据在一个方向上传输。
- 半双工:允许数据在两个方向上传输,但在某一时刻,只允许数据在一个方向上传输。
- 全双工:允许数据同时在两个方向上传输,要求发送设备和接收设备都有独立的接收和发送能力。
四、STM32串口通信的配置
在进行STM32串口通信之前,需要对串口通信参数进行配置,这些参数包括波特率、数据位、停止位、校验位等。在STM32中,这些参数可以通过配置相应的寄存器来实现,例如通过配置USART_BRR寄存器可以设置波特率,通过配置USART_CR1和USART_CR2寄存器可以设置数据位、停止位和校验位等参数。
五、STM32串口通信的编程实现
在STM32串口通信编程时,通常会使用HAL库或标准外设库等提供的API函数来配置串口通信参数和发送/接收数据。编程步骤大致如下:
- 初始化串口通信参数:使用HAL库或标准外设库提供的函数配置串口通信参数,如波特率、数据位、停止位和校验位等。
- 发送数据:使用HAL库或标准外设库提供的发送函数将数据写入串口发送缓冲区,然后等待数据发送完成。
- 接收数据:使用HAL库或标准外设库提供的接收函数从串口接收缓冲区读取数据。在接收数据时,需要判断接收缓冲区是否有数据可读,并读取相应的数据长度和格式。
- 错误处理:在串口通信过程中,可能会出现各种错误情况,如发送超时、接收溢出等。在编程时,需要添加相应的错误处理代码,以处理这些异常情况。
六、STM32串口通信的应用
STM32串口通信广泛应用于各种嵌入式系统中,如机器人控制系统、工业自动化控制系统、智能家居控制系统等。通过串口通信,STM32微控制器可以与其他设备或上位机进行数据交换,实现远程监控、数据传输等功能。
七、注意事项
- 在进行STM32串口通信时,需要确保通信双方的参数设置一致,包括波特率、数据位、停止位和校验位等。
- 在设计硬件连接时,需要确保TX和RX引脚正确交叉连接,即发送方的TX连接到接收方的RX,反之亦然。
- 如果STM32微控制器需要与PC机或其他使用不同电平标准的设备进行通信,可能需要使用电平转换芯片进行电平转换。
串行通信和并行通信
串行通信:速度教并行慢,但是占用的硬件资源少,通常只需要时钟线,一两根数据线还 有片选线即可。
并行通信:速度快,但是需要很多根数据线、地址线等。
同步通信和异步通信(串行通信)
同步通信:
1.同步是指在约定的通信速率下,发送端和接收端的时钟信号和相位始终保持一 致,保证通信双方在发送和接收数据时具有完全一致的定时关系。
2.同步通信把许多字符组成一个信息帧,每帧的开始用同步字符来表示。
3.在绝大多数场合下,发送端和接收端,采用的都是同一个时钟,所以在传送数据的同时还要发送时钟信号,以便接收端可以使用时钟信号来确定每一个信息位。
4.同步通信一次通信只能传送一帧信息。
异步通信:
1.数据是以字符为单位组成字符帧传输的。
2.字符帧由发送端一帧一帧的发送,每帧数据均是低位在前,高位在后,通过传输线被接收端一帧一帧的接收。
3.发送端和接收端可以有各自独立的时钟来控制数据的发送和接收,这两个时钟各自独立,互不同步。
4.接收端依靠字符帧格式来判断发送端是何时开始和结束发送的。
5.字符帧也叫做数据帧,由起始位,数据位,奇偶校验位,停止位等部分组成, 是异步通信的一个重要指标。
6.同步通信的另一个重要指标是波特率。
常见的通信协议:
电平特性:
TTL与232
接口标准:
串口传输协议:
参数概念:
波特率:一般选波特率都会有9600,19200,115200等选项,其实意思就是每秒传 输这么多个比特数(bit)。 起始位:先发出一个逻辑 ‘0’的信号,表示传输数据的开始。
数据位:可以是5~8位逻辑 “0” 或“1”,先传输Bit0,再传输bit1,以此类推。 校验位:数据位加上这一位之后,使得“1”的位数应为偶数(偶校验)或者奇数(奇校 验),以此来校验数据传送的正确性,校验位是可选的,可以不传输。 停止位:它是一个字符数据的结束标志,数据线变回逻辑“1”。
STM32F103的USART资源
1. STM32103有三个通用同步异步收发器USART,两个通用异步收发器UART,USART
也可以当作UART使用。
2. 通常使用的是UART功能,USART在某些场合会使用到,选择通信方式使用场合决 定。
3. 可以通过电平转化芯片变为RS232/RS485电平。
STM32F103的UART框图
端口引脚
1. TX:数据发送端口
2. RX:数据接收端口
3. SW_RX:在单线和智能卡模式下接收数据,属于内部端口,没有实际的外部引脚
4. RTS:在硬件流控制下用于指示设备准备好可以接收数据了,低电平表示可以接收数 据。
5. CTS:在硬件流控制下用于指示设备以及发送完数据了,如果是高电平那么在本次数据 发送完成后会阻塞下一次的传输,只有在低电平的时候才允许下一次传输。
6. CK:同步时钟端口,在同步通信模式下使用,用于输出同步时钟信号。
实现串口通信实战
相关函数讲解:
//异步收发器
typedef struct{
uint32_t USART_BaudRate; //波特率 配置波特率 115200/9600
uint16_t USART_WordLength; //字长 配置控制数据位的字长:
//八位/九位
uint16_t USART_WordLenth; //停止位 配置停止位 0.5,1,1,5,2
uint16_t USART_Parity; //校验位 配置校验位 无,奇,偶
uint16_t USART_Mode; //USART模式 配置工作模式 收/发
uint16_t USART_HardwareFlowControl; //硬件流控制
}USART_InitTypeDef;
//同步收发器
typedef struct{
uint16_t USART_Clock; //同步时钟 配置是否打开
uint16_t USART_CPOL; //极性 配置串口空闲为低电平,高电平
uint16_t USART_CPHA; //相位 配置选择边沿
uint16_t USART_LastBit; //最后一位时钟 配置使能还是关闭
}USART_ClockInitTypeDef;
初始化函数:
void USART_Init //初始化函数
(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);
void USART_Cmd //串口使能函数
(USART_TypeDef* USARTx, FunctionalState NewState);
void USART_ITConfig //中断配置函数
(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState);
void USART_SendData //串口发送函数
(USART_TypeDef* USARTx, uint16_t Data);
uint16_t USART_ReceiveData //串口接收读取函数
(USART_TypeDef* USARTx);
FlagStatus USART_GetFlagStatus //获取相应的串口标志位
(USART_TypeDef* USARTx, uint16_t USART_FLAG);
ITStatus USART_GetITStatus //中断状态位获取
(USART_TypeDef* USARTx, uint16_t USART_IT);
如何配置串口的发送:
1.配置时钟: GPIO口的时钟,串口的时钟, 引脚复用的时钟
2.配置GPIO的结构体
3.配置串口的结构体
4.串口的发送
USART相关标志位
串口接收字符讲解:
主函数:
配置的串口函数:
成功接收信息:
串口发送字符:
我可以先定义一个发送函数(在串口函数里面)
在主函数里面使用
烧录,查看,达到当我们按下一次复位键就输出一次 OK
串口发送字符串:
主函数调用
成功发送字符串
printf重定向
重定向(Redirection)在计算机科学中,特别是在操作系统和程序设计中,是一个将输出流(如标准输出stdout、标准错误等)或输入流(如标准输入stdin)从一个默认位置或设备改道到另一个位置或设备的过程。这个过程允许程序将其输出发送到除原始默认目标之外的其他地方,或者从非标准源读取输入。比如原先使用print是直接把数据输出到屏幕上,可以通过重定向把它输出到串口上。
将标准C库中的 fputc
函数重定向为通过STM32的USART1发送单个字符。这个函数的作用是将字符 ch
(在C语言中,字符通常通过其ASCII码值以整数形式传递)发送到USART1,并等待直到该字符被完全发送出去(TXE标志被设置)。
在这个特定的重定向实现中,FILE *f
参数没有被使用,因为已经决定将所有输出都发送到USART1。然而,为了保持与标准 fputc
函数的兼容性,应该保留这个参数,即使您不打算使用它。在更复杂的重定向场景中,您可能会根据 FILE *f
的值来决定将数据发送到哪个输出流。
可以使用标准输出printf
我们也需要被调用的头文件中定义标准输出头文件#include <stdio.h>
成功输出:
成功输入
串口中断接收:
配置串口中断函数的代码:
#include "usart.h"
#include "stm32f10x.h"
// USART初始化函数
void usart_init(void)
{
GPIO_InitTypeDef gpioinitStructure; // GPIO初始化结构体
USART_InitTypeDef usartinitStructure; // USART初始化结构体
NVIC_InitTypeDef nvicinitStructure; // NVIC初始化结构体
// 配置NVIC的优先级分组为2
// NVIC_PriorityGroup_2定义了抢占优先级和子优先级的位数分配
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
// 使能GPIOA、AFIO(复用功能输入输出,但注意在STM32F10x中可能不需要显式启用AFIO)和USART1的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); // 在STM32F10x中,如果不需要配置重映射,这行代码可能不是必需的
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
// 配置PA9为USART1的TX(发送)引脚,复用推挽输出
gpioinitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
gpioinitStructure.GPIO_Pin = GPIO_Pin_9;
gpioinitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &gpioinitStructure);
// 配置PA10为USART1的RX(接收)引脚,浮空输入
gpioinitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
gpioinitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_Init(GPIOA, &gpioinitStructure);
// 配置USART1的参数
usartinitStructure.USART_BaudRate = 115200; // 波特率
usartinitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 禁用硬件流控制
usartinitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; // 设置为接收和发送模式
usartinitStructure.USART_Parity = USART_Parity_No; // 无奇偶校验
usartinitStructure.USART_StopBits = USART_StopBits_1; // 停止位为1
usartinitStructure.USART_WordLength = USART_WordLength_8b; // 数据位为8位
USART_Init(USART1, &usartinitStructure); // 应用USART初始化到USART1
// 使能USART1的接收中断
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
// 使能USART1
USART_Cmd(USART1, ENABLE);
// 配置NVIC以响应USART1中断
nvicinitStructure.NVIC_IRQChannel = USART1_IRQn; // 设置中断通道为USART1中断
nvicinitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能该中断通道
nvicinitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 设置抢占优先级为1
nvicinitStructure.NVIC_IRQChannelSubPriority = 1; // 设置子优先级为1
NVIC_Init(&nvicinitStructure); // 应用NVIC初始化
}
// 发送一个字节
void USARTSendByte(USART_TypeDef* USARTx, uint16_t Data)
{
USART_SendData(USARTx, Data); // 发送数据
// 等待发送完成
while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);
}
// 发送字符串
void USARTSendstr(USART_TypeDef* USARTx, char *str)
{
uint16_t i = 0;
do
{
USARTSendByte(USARTx, *(str+i)); // 发送每个字符
i++;
} while(*(str+i) != '\0'); // 直到字符串结束
// 等待所有字符发送完成
while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET);
}
主函数代码:
成功发送,并控制LED灯