历史上的今天
返回首页

历史上的今天

今天是:2024年09月24日(星期二)

正在发生

2019年09月24日 | USART_ClearITPendingBith和 USART_ClearFlag的区别

2019-09-24 来源:eefocus

起初

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的参数

image.png?imageView2/2/w/550

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


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


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

image.png?imageView2/2/w/550


三种发送方法

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:解决方案是把判断能否发送的语句放在发送数据之前

推荐阅读

史海拾趣

冠西电子(COSMO)公司的发展小趣事

近年来,冠西电子积极布局全球市场,设立了多个分公司和办事处,以便更好地服务全球客户。同时,冠西电子还不断关注新兴技术和市场需求的变化,积极寻求新的发展机遇。展望未来,冠西电子将继续秉承“专业、创新、品质、服务”的理念,不断推动公司业务的发展和进步。

以上五个故事概要涵盖了冠西电子(COSMO)公司从创立到发展的主要阶段和事实。每个阶段都体现了冠西电子在技术、市场、品质、品牌等方面的努力和成就。

爱特姆(ATOM)公司的发展小趣事

随着电子行业的快速发展,爱特姆不断加大对技术研发的投入,致力于推出更先进、更稳定的连接器产品。公司成功研发出一系列具有自主知识产权的新产品,不仅提升了产品的性能和质量,也进一步巩固了其在行业内的领先地位。

东晶(ECEC)公司的发展小趣事

为了满足市场需求和降低成本,东晶电子不断进行产能扩张和自动化升级。公司引进了国际先进的生产设备和技术,提高了生产效率和产品质量。同时,公司还加大了对自动化设备的投入,减少了人力成本,提高了生产效率。这些措施不仅提升了公司的竞争力,也为公司的长远发展奠定了基础。

BLACK&DECKER公司的发展小趣事

BLACK&DECKER公司的历史可以追溯到1910年,由Alonzo G. Decker和Duncan S. Black在美国马里兰州巴尔的摩共同创立。两位创始人的初始投资来自于Black先生卖掉他的二手车所得的600美元,再加上1200美元的贷款。公司起初主要生产牛奶瓶装盖机、棉花采集机、糖果浸包机等工业用设备。然而,真正让BLACK&DECKER崭露头角的,是1916年他们发明的现代手枪钻原型。这一创新产品解决了当时德国电动工具笨重、难以操控的问题,为电动工具行业带来了革命性的变革。

Display Elektronik GmbH公司的发展小趣事

随着环保意识的日益增强,Display Elektronik GmbH将绿色环保理念融入到了公司的发展中。公司采用环保材料和生产工艺,减少了对环境的污染。同时,Display Elektronik GmbH还积极推广绿色显示技术,为客户提供更加环保、节能的显示解决方案。这种对环保的关注和投入,让Display Elektronik GmbH在行业中树立了良好的形象。

CHENMKO公司的发展小趣事

随着环保意识的日益增强,Display Elektronik GmbH将绿色环保理念融入到了公司的发展中。公司采用环保材料和生产工艺,减少了对环境的污染。同时,Display Elektronik GmbH还积极推广绿色显示技术,为客户提供更加环保、节能的显示解决方案。这种对环保的关注和投入,让Display Elektronik GmbH在行业中树立了良好的形象。

问答坊 | AI 解惑

怎么学好应用电子技术

我刚刚接触应用电子技术,还不知道它是干什么的,我想向各位请教怎么才可以学好,还就业前景好不好…

查看全部问答>

51单片机

本帖最后由 paulhyde 于 2014-9-15 09:30 编辑 分享...  …

查看全部问答>

如何提高模拟设计水平?

模拟技术是目前所有电子产品设计中不可或缺的部分,我们对模拟技术可以说一点都不陌生,从接触电子产品开始,我们就接触模拟产品了。模拟技术更多需要经验积累,对新入门的工程师来说,如何提升自己的模拟设计水平?我们欢迎广大资深工程师都来谈谈 ...…

查看全部问答>

降阻剂的危害——你必须知道的接地内幕

为了更好地降低接地电阻,日本人不知道怎么就想出了一昏招,在接地体上包裹导电水泥等导电的混凝土(中国人美其名曰为降阻剂),从而增加接地体散流面,进一步降低接地电阻。使用降阻剂是效率极为低下的降阻措施,而且一般都会腐蚀接地体,大大缩短 ...…

查看全部问答>

repllog.exe不会自动运行!

加了activesync组件。但是repllog.exe在插上usb时不会自动运行,结果无法同步,需要我自己手动运行repllog.exe。 请问是缺少了什么组件导致repllog.exe不自动运行的。…

查看全部问答>

时钟抖动测量和分析

 时钟抖动测量和分析…

查看全部问答>

什么才能让你火

最近想做ucos的研究,发现这个和比的版块比起来,这块地好像不热闹啊。大家谈谈对ucos的认识吧,也让我学习一下啊,谢谢了…

查看全部问答>

请教版主:关于Serialwire调试

看STM32有两种调试方式,JTAG接口和串行接口 ,串行接口是debug口吗? 我看资料写的是 在TMS/TCK 上输出一指定的JTAG 序? (分别映射到SWDIO 和SWCLK),禁止JTAG-DP,并激活SW-DP。 指定的序?是: 1. 输出超过50个TCK周期的TMS(SWDIO) ...…

查看全部问答>

请教430复位电路的参数问题

参考了很多应用及430的部分手册,都推荐上电复位的阻容组合为47K,0.1u,当然这个参数应该是没有问题的,430能可靠复位。这个参数组合能保证复位脚在上电时有足够的低电平时间。请教各位高手,我的理解不知对不对,当选择阻容组合为1k,4.7u,复位 ...…

查看全部问答>

ADXRS450和ADXL345的应用心得

这段时间查看了不少关于ADXRS450和ADXL345的资料,对ADI的这两款高性能MEMS芯片有了更深刻的认识。ADXRS450确实是一款强大的陀螺仪,它优异的抗振动性,出色的稳定性,以及数字输出等都和EWS86,EWTS8NSN11,MEV50等芯片不在同一档次上。ADI不愧是 ...…

查看全部问答>