串口发送数据
我们要说一下数据发送的流程,数据发送的关键是发送寄存器和移位寄存器的相互配合,我们用一组图片来讲述数据发送的流程。
有6个字节要从串口Tx发送出去
字节1进入发送数据寄存器
发送数据寄存器快速把数据推到移位寄存器
在移位寄存器向外推送数据时发送寄存器重新加载字节2
移位寄存器把数据推送完毕
发送寄存器把数据推送到移位寄存器
重复上述的流程直至所有数据发送完毕
标志位
TxE标志位
TXE(Transmit data register empty)标志位是串口通信中的一个重要状态标志,用于指示发送数据寄存器(Transmit Data Register, TDR)是否为空。
TXE标志位的状态
- TXE = 1:表示TDR中的数据已经全部移到了移位寄存器,且没有新的数据进入TDR。此时,TDR为空,可以发送新的数据。
- TXE = 0:表示TDR里还有数据未发送,此时不能向TDR写入新的数据,需要等待TXE变为1。
TXE标志位的使用
在串口通信中,通常会在发送数据之前检查TXE标志位,以确保TDR为空,避免数据覆盖或丢失。例如,在发送单个字符或字符串时,可以通过循环等待TXE标志位为1来确保数据可以安全地写入TDR。
TC标志位
在嵌入式系统和微控制器编程中,特别是在使用STM32等微控制器进行串口通信时,TC
(Transmission Complete)标志位是一个重要的状态标志,用于指示一帧数据的发送是否已经完成。
TC标志位的状态
- TC = 1:表示一帧数据已经从数据寄存器(TDR)完全发送到移位寄存器,并且移位寄存器中的数据也已经通过TX线发送出去,此时可以认为数据发送完成。(TDR和移位寄存器都为空的时候TC才能标志为1)
- TC = 0:表示数据发送尚未完成,或者移位寄存器中还有数据待发送。
TC标志位的使用
在串口通信中,TC
标志位通常用于确保数据帧的完整发送。在发送完一帧数据后,可以检查TC
标志位来确认发送是否成功完成。如果TC
标志位为1,则可以继续发送下一帧数据或执行其他操作;如果TC
标志位为0,则可能需要等待或采取其他措施来确保数据发送完成。
编程接口
USART_Cmd函数
USART_Cmd
函数是一个在STM32微控制器编程中用于使能(启用)或失能(禁用)USART(通用同步/异步收发传输器)外设的函数。USART是一种常用的串行通信接口,用于微控制器与其他设备之间的数据交换。
void USART_Cmd(USARTTypeDef *USARTx,FunctionalState NewState);
USARTx
:这是一个指向USART外设的指针,STM32微控制器通常有多个USART外设(如USART1、USART2等),通过该参数选择要操作的USART外设。NewState
:这是一个枚举值,用于指定USART外设的新状态。它可以是ENABLE
(启用USART)或DISABLE
(禁用USART)。- 在调用
USART_Cmd
函数之前,通常需要先配置USART外设的参数(如波特率、数据位、停止位、校验位等),这通常通过另一个函数(如USART_Init
)来完成
USART_GetFlagStatus函数
USART_GetFlagStatus
函数用于检查指定的USART外设中特定标志位的状态。它通过读取USART的状态寄存器(SR)来确定指定的标志位是否被设置。如果标志位被设置,函数返回SET
;如果标志位未被设置,函数返回RESET
。
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);
USARTx
:这是一个指向USART外设的指针,STM32微控制器通常有多个USART外设(如USART1、USART2等),通过这个参数来指定要检查哪一个USART外设的标志位状态。USART_FLAG
:这是一个uint16_t
类型的值,用于指定要检查的USART标志位。STM32的USART外设具有多个状态标志位和错误标志位,这个参数指定了要查询的具体哪个标志位。FlagStatus
:这是一个枚举类型,通常有两个可能的值:SET
(表示标志位被设置)和RESET
(表示标志位未被设置)。
USART_SendData函数
USART_SendData
函数将指定的数据(Data
)写入到USART外设的发送数据寄存器(DR)中。然后,它会等待发送移位寄存器为空,之后数据会逐位被发送出去。在发送过程中,可以通过查询USART的状态寄存器(SR)中的相关标志位来了解发送状态,例如使用USART_FLAG_TXE
(发送数据寄存器空标志位)来判断数据是否已经完全发送到移位寄存器中,以及使用USART_FLAG_TC
(传输完成标志位)来判断数据是否已经完全发送到通信总线上。
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);
参数说明
USARTx
:这是一个指向USART外设的指针,STM32微控制器通常有多个USART外设(如USART1、USART2等),通过这个参数来指定要向哪一个USART外设发送数据。Data
:这是一个uint16_t
类型的值,表示要发送的数据。由于这个函数一次只能发送单个字符或较小的数据包,因此这个参数通常是一个较小的数值。
硬件连接
代码1
void My_USART_SendBytes(USART_TypeDef *USARTx,uint8_t *pData, uint16_t Size);int main(){GPIO_InitTypeDef GPIO_InitStruct;RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//开启AFIO模块时钟GPIO_PinRemapConfig(GPIO_Remap_USART1,ENABLE);//设置重载映射RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;GPIO_Init(GPIOB,&GPIO_InitStruct);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;GPIO_Init(GPIOB,&GPIO_InitStruct);RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//打开时钟USART_InitTypeDef USART_InitStruct;//建立串口结构体USART_InitStruct.USART_BaudRate = 115200;//波特率115200USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;//双向传输模式USART_InitStruct.USART_WordLength = USART_WordLength_8b;//8位数据位USART_InitStruct.USART_Parity = USART_Parity_No;//无校验USART_InitStruct.USART_StopBits = USART_StopBits_1;//1位停止位USART_Init(USART1,&USART_InitStruct);USART_Cmd(USART1,ENABLE);uint8_t bytesToSend[] = {1,2,3,4,5};My_USART_SendBytes(USART1,bytesToSend,5);}//@作用:使用串口一次性发送多个字节
//@参数:pData—要发送的数据 Size—字节的数量
void My_USART_SendBytes(USART_TypeDef *USARTx,uint8_t *pData, uint16_t Size){for(uint32_t i = 0;i < Size; i++){//等待发送数据寄存器空while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET){}//发送的数据写到发送数据寄存器USART_SendData(USARTx,pData[i]);}//等待数据发送完成while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET){}}
安装串口测试软件
设置串口参数和程序内一致
USART_InitStruct.USART_BaudRate = 115200;//波特率115200USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;//双向传输模式USART_InitStruct.USART_WordLength = USART_WordLength_8b;//8位数据位USART_InitStruct.USART_Parity = USART_Parity_No;//无校验USART_InitStruct.USART_StopBits = USART_StopBits_1;//1位停止位
STLINK、USB TO TTL连接计算机USB,编译程序无误后,烧录入STM32内存,按重置键。串口测试程序接受到串口传输的数据 。
格式化打印数据
像printf一样输出数据,就是格式化打印数据,为了满足相关要求我们需要重写fputc函数
int fputc(int ch,FILE *f){//等待TDR为空while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET){}//发送数据 USART_SendData(USART1,(uint8_t)ch);return ch;
}
我们为了更好的管理代码将代码1进行重写,并测试格式化打印数据
void My_USART_SendBytes(USART_TypeDef *USARTx,uint8_t *pData, uint16_t Size);
void my_USART_Init();int main(){my_USART_Init();printf("Hello world. \r\n");//My_USART_SendBytes(USART1,bytesToSend,5);}//@作用:使用串口一次性发送多个字节
//@参数:pData—要发送的数据 Size—字节的数量
void My_USART_SendBytes(USART_TypeDef *USARTx,uint8_t *pData, uint16_t Size){for(uint32_t i = 0;i < Size; i++){//等待发送数据寄存器空while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET){}//发送的数据写到发送数据寄存器USART_SendData(USARTx,pData[i]);}//等待数据发送完成while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET){}}
//@作用:初始化串口,初始化GPIO对应引脚
//@参数:无参数
void my_USART_Init(){GPIO_InitTypeDef GPIO_InitStruct;RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//开启AFIO模块时钟GPIO_PinRemapConfig(GPIO_Remap_USART1,ENABLE);//设置重载映射RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;GPIO_Init(GPIOB,&GPIO_InitStruct);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;GPIO_Init(GPIOB,&GPIO_InitStruct);RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//打开时钟USART_InitTypeDef USART_InitStruct;//建立串口结构体USART_InitStruct.USART_BaudRate = 115200;//波特率115200USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;//双向传输模式USART_InitStruct.USART_WordLength = USART_WordLength_8b;//8位数据位USART_InitStruct.USART_Parity = USART_Parity_No;//无校验USART_InitStruct.USART_StopBits = USART_StopBits_1;//1位停止位USART_Init(USART1,&USART_InitStruct);USART_Cmd(USART1,ENABLE);
}int fputc(int ch,FILE *f){//等待TDR为空while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET){}//发送数据 USART_SendData(USART1,(uint8_t)ch);return ch;
}
测试结果如下