USART_ClearITPendingBith和 USART_ClearFlag的区别

起初

stm32 v3.5 库函数里面,对于串口USART有这样两个函数:
                USART_ClearFlag()和USART_ClearITPendingBit()


查库函数定义,说一个是清除标志,一个是清除中断预处理位。然后我看了stm32f10x_usart.c文件,发现两个函数都操作的是USART->SR寄存器,但是这个寄存器只有一组标志位,没有什么中断预处理位。。

实际上两个函数实现的功能是一样的,都是清除相对应的标志位,只是标志位和中断位含义不一样,是标志位但
是不一定会产生中断。例如:
#define USART_IT_TXE                         ((uint16_t)0x0727)
#define USART_IT_TC                          ((uint16_t)0x0626)
#define USART_IT_RXNE                        ((uint16_t)0x0525)
这是中断位,可以产生中断

#define USART_FLAG_TXE                       ((uint16_t)0x0080)
#define USART_FLAG_TC                        ((uint16_t)0x0040)
#define USART_FLAG_RXNE                      ((uint16_t)0x0020)
这是标志位,有的标志位不能产生中断

标志位在程序中可以作为判定条件,支持程序的运行,中断则是跳转到中断函数执行。两个函数实现的功能是一
样的,在中断程序中可以用两个中的任一个。我想区分两个函数是为了更清晰吧。
还有
#define USART_IT_TC                          ((uint16_t)0x0626)
#define USART_FLAG_TC                        ((uint16_t)0x0040)

这两个数值不同是因为标志位只是为了清除标志位而设的,而中断位设置成这个值是因为在其他函数中这一位还
有其他用途。而且还要注意:
void USART_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG)
{
  /* Check the parameters */
  assert_param(IS_USART_ALL_PERIPH(USARTx));
  assert_param(IS_USART_CLEAR_FLAG(USART_FLAG));
  assert_param(IS_USART_PERIPH_FLAG(USARTx, USART_FLAG)); /* The CTS flag is not available for UART4 and UART5 */   
   
  USARTx->SR = (uint16_t)~USART_FLAG;
}

 这一步 USARTx->SR = (uint16_t)~USART_FLAG; 似乎应该是  USARTx->SR &= (uint16_t)~USART_FLAG;
其实状态位只能有硬件置位,软件只能读和清零,所以这样写也是正确的。

没有很明白,所以转在这里,等我哪天灵光乍现了,再来加上我的理解
上面的内容是别人的,我略作修改,下面的内容是我原创。不知道本文到底是属于原创还是转载,但是......我的更简洁、精辟

 -----------------------------------------------------------------------------------------------------------------------

灵光乍现了

我是在使用TC的时候遇见这个问题的,所以这里就只分析TC这个位

先研究简单点的USART_ClearFlag函数

//调用形式
USART_ClearFlag(USART3,USART_FLAG_TC);

//USART_FLAG_TC的定义
#define USART_FLAG_TC               ((uint16_t)0x0040)

//USART_ClearFlag函数的原型
void USART_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG)
{
  /* Check the parameters */
  assert_param(IS_USART_ALL_PERIPH(USARTx));
  assert_param(IS_USART_CLEAR_FLAG(USART_FLAG));
  assert_param(IS_USART_PERIPH_FLAG(USARTx, USART_FLAG)); /* The CTS flag is not available for UART4 and UART5 */   
   
  USARTx->SR = (uint16_t)~USART_FLAG;
}

一目了然,最后USARTx->SR = ~(0100'0000);

再来研究复杂点的USART_ClearITPendingBit函数(注意,这两个函数的第二个参数,是不一样的

//调用形式
USART_ClearITPendingBit(USART3,USART_IT_TC);

//USART_IT_TC的定义
#define USART_IT_TC           ((uint16_t)0x0626)//=0000'0110'0010'0110

//USART_ClearITPendingBit的函数原型
void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT)
{
  uint16_t bitpos = 0x00, itmask = 0x00;
  /* Check the parameters */
  assert_param(IS_USART_ALL_PERIPH(USARTx));
  assert_param(IS_USART_CLEAR_IT(USART_IT));
  assert_param(IS_USART_PERIPH_IT(USARTx, USART_IT)); /* The CTS interrupt is not available for UART4 and UART5 */
  
  bitpos = USART_IT >> 0x08;                     //=0000'0110
  itmask = (uint16_t)((uint16_t)0x01 << bitpos); //=0100'0000
  USARTx->SR = (uint16_t)~itmask;                //=~(0100'0000)
}

可以看到最后还是USARTx->SR=~(0100'0000);

对比一下USART_ClearFlag和USART_ClearITPendingBit的参数

USART_ClearFlag的参数 USART_ClearITPendingBit的参数
USART_FLAG_CTS    = 0x0200 USART_IT_CTS   = 0x096A
USART_FLAG_LBD    = 0x0100 USART_IT_LBD    = 0x0846
USART_FLAG_TC      = 0x0040 USART_IT_TC      = 0x0626
USART_FLAG_RXNE = 0x0020 USART_IT_RXNE = 0x0525

这里可以发现一个规律USART_ClearFlag参数只有一个位是“1”,其位置正好等于USART_ClearITPendingBit的参数左移八位后的结果,所以这里可以非常非常肯定的讲:函数USART_ClearFlag和函数USART_ClearITPendingBit的功能totally一样

----------------------------------------------------------------------------------------------------------------------------

涉及内容扩展:

STM32的USART发送数据时如何使用TXE和TC标志

     在USART的发送端有2个寄存器,一个是程序可以看到的寄存器——发送数据寄存器(通过USART_DR查看),另一个是程序看不到的寄存器——发送移位寄存器,对应的两个USART数据发送标志,一个是TXE=发送数据寄存器空,另一个是TC=发送移位寄存器空

USART_ClearITPendingBith和 USART_ClearFlag的区别_第1张图片

(这粗箭头和这细箭头,简直不要太形象哦,粗箭头是八位八位的传,细箭头是一位一位的传)

    当USART_DR中的数据传送到移位寄存器后,TXE被设置,此时移位寄存器开始向TX信号线按位传输数据,但因为TDR已经变空,所以程序可以把下一个要发送的字节(操作USART_DR)写入TDR中,而不必等到移位寄存器中所有位发送结束,所有位发送结束时(送出停止位后)硬件会设置TC标志

  另一方面,在刚刚初始化好USART还没有发送任何数据时,也会有TXE标志,因为这时发送数据寄存器是空的。TXEIE和TCIE的意义很简单,TXEIE允许在TXE标志为'1'时产生中断,而TCIE允许在TC标志为'1'时产生中断。

  至于什么时候使用哪个标志,需要根据你的需要自己决定。但我认为TXE允许程序有更充裕的时间填写TDR寄存器,保证发送的数据流不间断。TC可以让程序知道发送结束的确切时间,有利于程序控制外部数据流的时序。

USART_ClearITPendingBith和 USART_ClearFlag的区别_第2张图片

 

位7

TXE:发送数据寄存器空 (Transmit data register empty)

当TDR寄存器中的数据被硬件转移到移位寄存器的时候,该位被硬件置位。如果USART_CR1寄存器中的TXEIE为1,则产生中断。对USART_DR的写操作,将该位清零

0:数据还没有被转移到移位寄存器;
1:数据已经被转移到移位寄存器。
注意:单缓冲器传输中使用该位。
位6

TC:发送完成 (Transmission complete)

当包含有数据的一帧发送完成后,并且TXE=1时,由硬件将该位置’1’。如果USART_CR1中的TCIE为’1’,则产生中断。由软件序列清除该位(先读USART_SR,然后写入USART_DR)。 TC位也可以通过写入’0’来清除,只有在多缓存通讯中才推荐这种清除程序。

0:发送还未完成;
1:发送完成
位5 RXNE:读数据寄存器非空 (Read data register not empty)
当RDR移位寄存器中的数据被转移到USART_DR寄存器中,该位被硬件置位。如果USART_CR1寄存器中的RXNEIE为1,则产生中断。对USART_DR的读操作可以将该位清零。 RXNE位也可以通过写入0来清除,只有在多缓存通讯中才推荐这种清除程序。
0:数据没有收到;
1:收到数据,可以读出。

三种发送方法

1、不开发送完成中断:

/*******************************************************************************
* 函数名  : UART1_SendString
* 描述    : USART1发送字符串
* 输入    : *s字符串指针
* 注释    :0==RESET,表示发送还未完成
USART_FLAG_TC!=RESET,就是=SET,表示发送完成,此时执行USART_GetFlagStatus会把USART_FLAG_TC清零(未证实)
*******************************************************************************/
void UART1_SendString(u8* s)
{
  while(*s)//检测字符串结束符
  {
   //USART_FLAG_TC==RESET时,表示发送还未完成。
    while(USART_GetFlagStatus(USART1, USART_FLAG_TC)==RESET); 
    USART_SendData(USART1 ,*s++);//发送当前字符
  }
}

2、开中断,额外设置一个标志:

u8 FLAG_TC=0;//定义全局变量
/*******************************************************************************
* 函数名  : UART1_SendString
* 描述    : USART1发送字符串
* 输入    : *s字符串指针
*******************************************************************************/
void UART1_SendString(char* s)
{
  FLAG_TC=0;//提前准备一下
  while(*s)//检测字符串结束符
  {
    USART_SendData(USART1 ,*s++);//发送当前字符
    while( FLAG_TC==0);	//0:发送还未完成;1:发送完成
    FLAG_TC=0;
  }
}


void USART1_IRQHandler(void)
{
  if (USART_GetITStatus(USART1, USART_IT_TC) != RESET)//发送完成中断,= SET
  {
    USART_ClearITPendingBit(USART1,USART_IT_TC);
    FLAG_TC=1;
  }
}

 

开中断时,就不能通过简单的判断标志位USART_FLAG_TC的状态去决定能否发送下一个字符。比方说,使用 

while(*s)
{

   USART_SendData(USART1 ,*s++);//发送字符

   while(USART_GetFlagStatus(USART1, USART_FLAG_TC)==RESET); 

   ...
}

执行完发送字符的语句后,因为不可能这么快完成发送,所以程序接下来执行的是while语句。在等待期间,字符发送完毕,这时就会进入到中断,如果在中断里清除标志位,退出中断后,标志位USART_FLAG_TC仍然是RESET;如果不在中断里清除标志位,就不能退出中断。所以程序就会死在while里。

所以这里就需要额外设置一个标志。

3、开中断,数据发送由函数启动,在中断里完成:

//定义全局变量。也可以为了简化,把这四个参数结合起来包含在一个结构体里
u8 TxLength;   //发送数据长度
u8 TxIndicator;//发送指示器,表示目前发送完成哪一位了,下面要发送的是第(TxIndicator+1)位
u8 TxBuff[256];//Data 
u8 TxFnd;      //发送完成标志
     
/*************************************************************************
*程序名称    :   SendFirstByte
*功能        :   启动发送第一个字节
*@Notes     :   鸡贼啊,剩下的都放在USART_FLAG_TC中断里面,因为这个中断是
                 发送完成中断,是利用“发送第一个字节”来“启动发送”
*@Notes     :   需要注意,如果发送结果是乱码的话,一种供参考的解决方案是
                把数据直接赋值给TxBuff,而不要通过函数的形参传递
*输入参数    :   u8 txbuf[]: 需要发送的数据,u8 len : 数据中的字节数
*返回值      :   无
*************************************************************************/
void SendFirstByte( u8 txbuf[], u8 len )
{
	TxBuff      = txbuf;//需要发送的数据
	TxLength    = len  ;//发送数据长度
	TxIndicator = 1    ;//0已经发送,也是用来启动发送的

	USART_SendData(USART1, txbuf[0]);  /**@Notes:只发送了txbuf的第一个字节*/
}

/*************************************************************************
*程序名称    :   USART1_IRQHandler
*功能        :   完成发送数据   
*************************************************************************/
void USART1_IRQHandler(void)
{
	if (USART_GetITStatus(USART1, USART_IT_TC) != RESET)
	{
		USART_ClearITPendingBit(USART1,USART_IT_TC);
		if( TxIndicator < TxLength  )//数组的索引max永远小于数组元素的个数
		{
			USART_SendData(USART1, TxBuff[TxIndicator++]);
		}
		else//最后一字节数据发送完成
		{
			TxFnd = 0;
			TxIndicator = 0;
		}
	}
}

USART收发过程中常遇问题总结:

Q:为什么使用USART发送一串字符,最后自接收到了最后一个?

A:是因为发送的间隔太短了,可以在发送每个字符之前先判断一下上一个字符是否发送完成,可以参考上面的“三种发送方法”

Q:为什么使用USART发送一串字符,有时候接收不到第一个字符,有时候又可以接收得到?

A:解决方案是把判断能否发送的语句放在发送数据之前

功德圆满

这下算是功德圆满了吧

 

你可能感兴趣的:(STM32)