浅谈一下STM32串口中断之TXE,TC,RXNE标志位
之前做一个项目,用到了串口中断,但是对TXE、TC和RXNE标志位的作用和使用方法不是很清楚,导致在调试过程中遇到了一些问题。通过查阅相关资料和实际操作,我对这三个标志位有了更深入的了解,因此决定写这篇博客,分享我的经验和心得。如果对您有所帮助,点点关注,不迷路哦
墨小羽ovo个人主页
前言
在STM32中,串口通信是一种常用的通信方式,而串口中断是一种高效的通信方式,它可以在数据发送或接收时自动触发中断,从而实现数据的实时处理。在串口中断中,TXE、TC和RXNE是三个非常重要的标志位,它们分别表示发送数据寄存器为空、发送完成和接收数据寄存器非空。本文将简要介绍这三个标志位的作用和使用方法。
写这篇博客的原因
1.串口发送数据的时候出现丢包,调试代码调试不出来
2.我区分不出来串口中断,发送一串字符串时候,是不是每次都触发中断,还是只触发一次中断,是发送完第一个字符就触发串口发送中断,还是发送完所有字符才触发中断。
1. 串口通信代码编写流程
在STM32中,串口通信的代码编写流程如下:
1. 配置串口引脚和时钟。
2. 配置串口参数,如波特率、数据位、停止位等。
3. 配置串口中断,如发送中断、接收中断等。初始化NVIC
4. 启动串口。
5. 在中断服务程序中处理数据发送和接收。
1.1 FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG) 和void USART_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG)
1.第一个函数用于获取串口标志位的状态,第二个函数 用于清除标志位
2.这两个函数是轮询的方式,即不断检查标志位的状态,直到标志位为1或者超时。这种方式虽然简单,但是效率较低,因此在实际应用中,通常会使用中断的方式来处理数据发送和接收。
1.2 USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState)和void USART_ITClearPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT)
1.函数用于使能或禁用串口中断,
2.第二个函数是清除中断标志位的
1.3 USART_SendData(USART_TypeDef* USARTx, uint16_t Data)
函数用于向串口发送数据,其中USARTx表示串口类型,Data表示要发送的数据。函数返回值为void类型,没有返回值。
1.4 USART_ReceiveData(USART_TypeDef* USARTx)
函数用于从串口接收数据,其中USARTx表示串口类型。函数返回值为uint16_t类型,表示接收到的数据。
2.发送接收示意图
2.1 发送示意图
个人理解:
1.当要发送一个字符时,数据A放入TDR后,发送到移位寄存器后,触发TXE标志位,然后发送完成,触发TC标志位。
2. TXE标志位
TXE标志位表示发送数据寄存器为空,即可以发送数据。当发送数据寄存器为空时,TXE标志位会被置1,此时可以发送数据。在发送数据时,需要先判断TXE标志位是否为1,如果为1,则可以将数据写入发送数据寄存器,否则需要等待TXE标志位为1。
2.1 轮询方式发送-TXE标志位的使用
在发送数据时,需要先判断TXE标志位是否为1,如果为1,则可以将数据写入发送数据寄存器,否则需要等待TXE标志位为1。例如,以下代码演示了如何使用TXE标志位发送数据:
(1)发送一个字符
USART_SendData(USART1, 'A');
(2)发送两个字符(错误示范)
USART_SendData(USART1, 'A');
USART_SendData(USART1, 'B');
原因:1.USART_SendData()函数只是将数据放在TDR寄存器中,
2.还没来得及发送到移位寄存器,就又往TDR寄存器中放数据,导致数据丢失
(3)发送多个字符(正确示范)
USART_SendData (USART1,"H');
while (USART_GetFlagstatus(USART1,USART_FLAG_TXE) == RESET);USART_SendData (USART1,'K');
while (USART_GetFlagStatus ( USART1,USART_FLAG_TXE) == RESET);USART_SendData (USART1,”G');
while (USART_GetFlagstatus( USART1,USART_FLAG_TXE) == RESET);
输出运行结果:
HKG
(4)发送字符串
1.发送字符串时,最后加一个TC标志位的判断
2.不然,当发送最后一个字符时候,TXE标志位变为1,而此时最后一个字符只是发送到了移位寄存器,还没来得及
发送出去,所以需要等待TC标志位为1,才能关闭串口发送
,不然会导致数据丢失
2.2 中断方式发送-TXE标志位的使用
补充:1.TXE:发送寄存器空中断,使能TXE后
,当TDR发送数据寄存器为空时,会触发中断,在中断服务程序中发送数据。
2.TXE在复位后,默认为1,即默认开启发送中断。所以如果没有真正发送数据,TXE中断都会
触发,影响程序运行,所以初始化时候需要关闭TXE中断。
3. 使用TXE中断,发送字符,都是在中断服务程序中发送的。
示例程序:
void USART_SendByte(USART_TypeDef* USARTx, uint8_t *Data)
{
pDataByte = Data;
USART_ITConfig(USARTx, USART_IT_TXE, ENABLE); //使能发送中断
}
void USART1_IRQHandler(void)
{
if (USART_GetITStatus(USART1, USART_IT_TXE) != RESET)
{
USART_SendData(USART1, *pDataByte);
pDataByte++;
if (*pDataByte == ‘\0’)
{
USART_ITConfig(USART1, USART_IT_TXE, DISABLE); //发送完毕,关闭发送中断
}
}
}
3. TC标志位
TC标志位表示发送完成,即数据已经发送完毕。当数据发送完毕时,TC标志位会被置1,此时可以关闭串口发送中断,或者进行其他操作。在发送数据时,需要先判断TC标志位是否为1,如果为1,则可以关闭串口发送中断,否则需要等待TC标志位为1。
理解:
1.使用TC标志位,需要先读SR寄存器,再写入DR寄存器,或者是通过写入’0’来清除。
3.1 轮询方式发送-TC标志位的使用
while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
USART_SendData (USART1,"H');
while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
USART_SendData (USART1,"k');
while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
USART_SendData (USART1,"G');
while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
运行结果:
HKG
3.2 中断方式发送-TC标志位的使用
1. 使用TC标志位,是发送一个字节后进入中断的,后续的发送都是在中断服务函数中进行的。
2.可以在初始化时候,USART_ITConfig(USART1, USART_IT_TC, ENABLE);使能TC中断
USART_ITConfig(USART1, USART_IT_TC, ENABLE);//Tramsimssion Complete后,才产生中断. 开TC中断必须放在这里,否则还是会丢失第一字节
USART_Cmd(USART1, ENABLE); //使能USART1
示例程序:
1.使用TC标志位,需要先读SR寄存器,再写入DR寄存器,或者是通过写入’0’来清除。
void USART_SendByte(USART_TypeDef* USARTx, uint8_t *Data)
{
pDataByte = Data;
USART_ClearFlag(USARTX,USART_FLAG_TC); //清除TC标志位
USART_SendData(USART1, *(pDataByte++) ); //必须要++,不然会把第一个字符t发送两次
}
void USART1_IRQHandler(void)
{
if (USART_GetITStatus(USART1, USART_IT_TC) != RESET)
{
USART_SendData(USART1, *pDataByte++);
if (*pDataByte == '\0')
{
USART_ClearFlag(USART1, USART_FLAG_TC);//不然TC一直是set, TCIE也是打开的,导致会不停进入中断.
}
}
}
4. RXNE标志位
理解:
1.当接收到一个字符时,数据A放入RDR寄存器,然后触发RXNE标志位,然后读取RDR寄存器中的数据,最后清除RXNE标志位。
补充: 当接收移位寄存器从RX引脚接收到
一个字节的数据后,数据会自动存入RDR寄存器,并触发RXNE标志位。这时候RXNE等于1,说明数据可以读出来。
2.当接收缓冲区为空时,RXNE标志位为0,此时可以等待接收数据。
3.当接收缓冲区非空时,RXNE标志位为1,此时可以读取接收缓冲区中的数据。
4.1 轮询方式接收-RXNE标志位的使用
- 有了TXE的经验
2.不断轮询获取
RXNE标志位,当RXNE为1时,读取RDR寄存器中的数据,然后清除RXNE标志位。
示例程序:
uint8_t USART ReceiveByte(USART_TypeDef USARTx)
{
while(USART_GetF1agStatus(USARTx,USART_FLAG_RXNE)==RESET);
return (uint8_tfUSART_ReceiveData(USART1) ;
)
4.2 中断方式接收-
RXNE标志位的使用
注意:
1.初始化完串口接收中断的代码后,当接收数据时候,硬件会自动将数据放入RDR寄存器中,然后触发RXNE标志位
2. 硬件会检测到接收数据,自动跳转到中断处理函数
接收1个字节触发一次接收中断
3.RXNE(Receive Data register not empty interrupt)- 每接受到一个字节产生中断,接受寄存器不为空时,产生中断 —读寄存器DR清零,也可软件手动清零
使用方法:
1. 开启中断,初始化NVIC
NVIC结构体
:
NVIC_InitTypeDef NVIC_InitStructure;
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
2. 在中断服务函数中,读取RDR寄存器中的数据,然后清除RXNE标志位。
3. 在中断处理函数中,读取RXNE标志位,当RXNE==1时,说明接收到数据,可以读出数据了
void USART1_IRQHandler(void)
{
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
uint16_t Data = USART_ReceiveData(USART1);
// 处理接收到的数据
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
//可以把接收的数据再打印出来
USART_SendData(USART1, Data);
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}
}
}
参考资料
海创电子-STM32F103入门篇,阶段-阶段二
STM32串口通信
keysking串口原理与中断模式收发
总结
TXE、TC和RXNE等标志位在串口通信中扮演着至关重要的角色,它们的状态直接决定了数据的发送和接收过程。通过深入了解轮询和中断两种模式的工作原理和应用场景,我们可以更加灵活地选择适合自己项目的通信方式,从而实现更高效、更可靠的串口通信。希望本教程能够对大家在物联网开发过程中有所帮助。最后呢,在编写博客的过程中,我尽量保持内容的准确性和完整性,但也难免会有疏漏或错误之处。欢迎各位读者指出其中的问题,帮助我不断进步。谢谢大家的阅读,