历史上的今天
返回首页

历史上的今天

今天是:2024年12月21日(星期六)

2018年12月21日 | STM32 串口中断里面使用printf 出现错误

2018-12-21 来源:eefocus

其实学单片机使用的时候,往往大家都觉得简单,很快就过了,但其实有一些东西是值得深思的,我以前在写程序的时候往往都是发送数据,那么调用重写的printf()函数就可以了,但这次的项目中用到了NRF双全工通讯,这就需要串口的收发,这回就发生了写问题,具体的流水账就不说了。简单说就是串口收可比发讲究多了。


void USART1_IRQHandler(void)                //串口1中断服务程序

{

u8 i;

if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)//如果寄存器中有数据

  {

   USART_RX_BUF[RxCounter++] = USART_ReceiveData(USART1);

//在这个位置,没有加这条数据帧判断语句,出现了类似于移位的错误,我需要连续发送40次才能得到正确的序列,具体原因不明

//当时我考虑的是报上位机的发送,移到下位机,没想到一下子就可以了

if( (USART_RX_BUF[0]=='$') &&  (USART_RX_BUF[1]=='M') && (USART_RX_BUF[2] == '<' )&& (USART_RX_BUF[27] == '<' )&& (USART_RX_BUF[28] == '<' ) )

 {

if(RxCounter==29) 

{

   RxCounter =0;

}

 }

 

上述代码段是我地面站的串口中断程序,虽然不难,但是有讲究:


1.串口每次中段读出的是多少数据,那么打开固件库看,是一个uint16_t类型,所以说每次串口是读了一个字进来,那我们用uint16_t的一个变量接受自然没问题,那么用u8接收可以么,因为我马上就要将串口数据无线发出去,无线通讯中一般都是char型为基本单元,答案是可以,所以应该是发生了隐式类型转换,如果编译器不允许,那么考虑拆分字节传输。


2.我没加这条协议判断语句的时候USART_RX_BUF里面的内容一致不是我想要的,数据头和尾一直内容不对,后来我发现,我连续上位机传输40次的时候就出现了正确序列,这说明上位机确实将正确数据发出去了,我在上位机的debug中也验证这一点,那么地面站程序接受的时候不知道发生了什么。


怀疑:串口地面站接受程序有问题,上位机因为是linux的qt写的,考虑上位机输出有问题。


解决:上面是一种解决方法,另一种如下:

void USART1_IRQHandler(void)                //串口1中断服务程序

{

u8 i;

if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)//如果寄存器中有数据

  {

  USART_ClearITPendingBit(USART1,USART_IT_RXNE); //清除中断标志

  USART_RX_BUF[RxCounter++] = USART_ReceiveData(USART1);

//在这个位置,没有加这条数据帧判断语句,出现了类似于移位的错误,我需要连续发送40次才能得到正确的序列,具体原因不明

//当时我考虑的是报上位机的发送,移到下位机,没想到一下子就可以了

if(RxCounter==29) 

{

   RxCounter =0;

 }


上面代码段的这种方案很好理解,信道优良,短距离,没什么干扰,比较理想的情况。


第一种的话我还不能完全表述出来,就是在不清除标志位的情况下,这种就不分析了,比较复杂,还是用第二种方法比较好,中断一次,都一个字,依次读完。

 

最后贴出一篇文章,几种串口手法程序的优缺点,供以后查看和大家参考:


实例一:

void USART1_IRQHandler(u8 GetData)

{

u8 BackData;

if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //中断产生

{  

USART_ClearITPendingBit(USART1,USART_IT_RXNE); //清除中断标志.

GetData = UART1_GetByte(BackData);   //也行GetData=USART1->DR;   

USART1_SendByte(GetData);      //发送数据

GPIO_SetBits(GPIOE, GPIO_Pin_8 ); //LED闪烁,接收成功发送完成

delay(1000);

GPIO_ResetBits(GPIOE, GPIO_Pin_8 );

}

}  

 

这是最基本的,将数据接收完成后又发送出去,接收和发送在中断函数里执行,main函数里无其他要处理的。


优点:简单,适合很少量数据传输。


缺点:无缓存区,并且对数据的正确性没有判断,数据量稍大可能导致数据丢失 。

 

 

实例二:

void USART2_IRQHandler()  

{

if(USART_GetITStatus(USART2,USART_IT_RXNE) != RESET) //中断产生

{  

USART_ClearITPendingBit(USART2,USART_IT_RXNE); //清除中断标志

Uart2_Buffer[Uart2_Rx_Num] = USART_ReceiveData(USART2);

Uart2_Rx_Num++;

}

 

if((Uart2_Buffer[0] == 0x5A)&&(Uart2_Buffer[Uart2_Rx_Num-1] == 0xA5))  //判断最后接收的数据是否为设定值,确定数据正确性

Uart2_Sta=1;

if(USART_GetFlagStatus(USART2,USART_FLAG_ORE) == SET) //溢出

{

USART_ClearFlag(USART2,USART_FLAG_ORE);  //读SR

USART_ReceiveData(USART2); //读DR  

}     

}

 

if( Uart2_Sta )

{

for(Uart2_Tx_Num=0;Uart2_Tx_Num < Uart2_Rx_Num;Uart2_Tx_Num++)

USART2_SendByte(Uart2_Buffer[Uart2_Tx_Num]); //发送数据

 

Uart2_Rx_Num = 0; //初始化

Uart2_Tx_Num = 0;

Uart2_Sta = 0;

}

 

 

这是加了数据头和数据尾的接收方式,数据头和尾的个数可增加,此处只用于调试之用。中断函数用于接收数据以及判断数据的头尾,第二个函数在main函数里按照查询方式执行。


优点:较简单,采用缓存区接收,对提高数据的正确行有一定的改善 。


缺点:要是第一次数据接收错误,回不到初始化状态,必须复位操作 。

 

实例三:

vvoid USART2_IRQHandler() 

     if(USART_GetITStatus(USART2,USART_IT_RXNE) != RESET) //中断产生 

     { 

        USART_ClearITPendingBit(USART2,USART_IT_RXNE); //清除中断标志. 

        Uart2_Buffer[Uart2_Rx] = USART_ReceiveData(USART2); 

        Uart2_Rx++; 

        Uart2_Rx &= 0x3F; //判断是否计数到最大

      } 

      if(USART_GetFlagStatus(USART2,USART_FLAG_ORE) == SET) //溢出 

      { 

          USART_ClearFlag(USART2,USART_FLAG_ORE); //读SR 

          USART_ReceiveData(USART2); //读DR 

       } 

}

 

if( Uart2_Tx != Uart2_Rx ) 

    USART2_SendByte(Uart2_Buffer[Uart2_Tx]); //发送数据 

    Uart2_Tx++; 

    Uart2_Tx &= 0x3F; //判断是否计数到最大

}  

 

 

采用FIFO方式接收数据,由0x3F可知此处最大接收量为64个,可变,中断函数只负责收,另一函数在main函数里执行,FIFO方式发送。


优点:发送和接收都很自由,中断占用时间少,有利于MCU处理其它。


缺点:对数据的正确性没有判断,一概全部接收。

 

实例四: 

void USART2_IRQHandler() 

     if(USART_GetITStatus(USART2,USART_IT_RXNE) != RESET) //中断产生 

     { 

        USART_ClearITPendingBit(USART2,USART_IT_RXNE); //清除中断标志

        Uart2_Buffer[Uart2_Rx] = USART_ReceiveData(USART2); 

        Uart2_Rx++; 

        Uart2_Rx &= 0xFF; 

     } 

     if(Uart2_Buffer[Uart2_Rx-1] == 0x5A) //头 

        Uart2_Tx = Uart2_Rx-1; 

     if((Uart2_Buffer[Uart2_Tx] == 0x5A)&&(Uart2_Buffer[Uart2_Rx-1] == 0xA5)) //检测到头的情况下检测到尾 

     { 

            Uart2_Len = Uart2_Rx-1- Uart2_Tx; //长度 

            Uart2_Sta=1; //标志位 

     } 

     if(USART_GetFlagStatus(USART2,USART_FLAG_ORE) == SET) //溢出 

     { 

            USART_ClearFlag(USART2,USART_FLAG_ORE); //读SR 

            USART_ReceiveData(USART2); //读DR 

     } 

}

 

if( Uart2_Sta ) 

        for(tx2=0;tx2 <= Uart2_Len;tx2++,Uart2_Tx++) 

        USART2_SendByte(Uart2_Buffer[Uart2_Tx]); //发送数据 

        Uart2_Rx = 0; //初始化 

        Uart2_Tx = 0; 

        Uart2_Sta = 0; 

}

 

 

数据采用数据包的形式接收,接收后存放于缓存区,通过判断数据头和数据尾(可变)来判断数据的“包”及有效性,中断函数用于接收数据和判断头尾以及数据包长度,另一函数在main函数里执行,负责发送该段数据。


优点:适合打包传输,稳定性和可靠性很有保证,可随意发送,自动挑选有效数据。


缺点:缓存区数据长度要根据“包裹”长度设定, 要是多次接收后无头无尾,到有头有尾的那一段数据恰好跨越缓存区最前和最后位置时,可能导致本次数据丢失,不过这种情况几乎没有可能

推荐阅读

史海拾趣

Good Sky Electric Co Ltd公司的发展小趣事
Z32A、Z32K、Z3025J型摇臂钻床的主电路通常包括断路器、主电路熔断器、接触器正反转主触点、热继电器以及电机等关键元件。这些元件共同协作,确保钻床的安全稳定运行。
Dymec公司的发展小趣事

随着业务的不断拓展,Dymec公司逐渐意识到品质对于品牌的重要性。公司决定加大对产品质量的投入,从原材料采购到生产流程,再到质量检测,每一个环节都严格把控。这种对品质的执着追求使得Dymec公司的产品在市场上获得了良好的口碑,公司也逐渐成为了电子连接器行业的知名品牌。

宇阳科技(EYANG)公司的发展小趣事

宇阳科技自2001年成立以来,一直致力于片式多层陶瓷电容器(MLCC)的研发和生产。2002年,公司成功研发出0402 BME微型片式多层陶瓷电容器,并通过了新产品鉴定,填补了国内市场的空白。这一创新不仅彰显了宇阳科技在MLCC领域的技术实力,也为公司赢得了市场的认可,为后续的发展奠定了坚实的基础。

Gem Asia Enterprise Co Ltd公司的发展小趣事

面对电子行业供应链复杂多变的挑战,Gem Asia积极实施供应链优化和数字化转型战略。公司引入先进的ERP系统和物联网技术,实现了从原材料采购、生产制造到物流配送的全链条数字化管理。这一举措不仅提高了生产效率和产品质量,还降低了运营成本,增强了企业的市场竞争力。同时,Gem Asia还加强了与上下游企业的合作,共同构建了一个高效协同的供应链生态系统。

Electro Technik Industries公司的发展小趣事

随着电子技术的快速发展,ETI意识到只有不断创新才能在竞争中立于不败之地。公司加大了对研发的投入,积极引进国内外先进技术,并鼓励员工提出创新性的想法。在一次偶然的实验中,ETI的研发团队成功研发出了一种具有高效能、低功耗特点的电子元器件,这一突破性的技术成果迅速获得了市场的认可,为ETI带来了可观的收益。

驰兴电感(Coilank)公司的发展小趣事

驰兴电感始终坚持“品质第一”的原则,对产品的每一个细节都进行严格把控。公司引进了先进的生产设备和检测仪器,确保产品从原材料到成品的每一个环节都符合高标准的质量要求。这种对品质的执着追求,使得驰兴电感的产品在市场上赢得了良好的口碑,树立了高品质的品牌形象。

问答坊 | AI 解惑

GSM基站的优化建设[推荐]

引用《移动在线》 GSM数字移动通信发展非常迅速,从早期规划的大区制,到后来的小区制,直到现在的微蜂窝、微微蜂窝,相对应的天线从早期架设在屋面铁塔上,到后来天线降到屋面上,直到现在要把天线设置在屋面下的外墙侧面上。所有的这些变化都说 ...…

查看全部问答>

LM3S 系列单片机休眠与深度休眠应用笔记(二)

1.4 深度睡眠模式设置步骤 处理通过调用WFI 指令即可进入睡眠模式,但要进入深度睡眠实现最低的功耗需要正确配置,其步骤如下: 1. 使能ACG 自动时钟门控。这样睡眠模式和深度睡眠模式的外设时钟可以单独控制。 2. 配置寄存器DCGC0、 DCGC1 和DC ...…

查看全部问答>

烧写完WINCE5.0后,显示屏不停的开关,启动不了

    新买的博创公司的270A开发板,用的是pxa270芯片,我按照它上面的介绍烧写WINCE5.0后,发现显示屏不停的开关,并没有出现像他们说的烧写完后自动运行.下面是超级终端出现的代码,各位帮忙解答一下,我对这是个绝对的菜 FlashWrite: dwLengt ...…

查看全部问答>

高分求一源码

哪位哥们有sirf atlasIII的BSP呀?,发一份给我好吗?我的QQ是648433648,邮箱:sunboyljp@163.com,不弟不胜感谢!…

查看全部问答>

51单片机的全局变量在主程序中输入值改变后,在中断中却没有改变

  我的主程序中,在AD转换后赋值一全局变量,在输入值改变后,它是随着改变的,在串口中断中发送给上位机,中断发出的值为什么不随输入的改变而改变呢?请有经验的人给以指点.非常感谢!!…

查看全部问答>

wince怎么编译这么慢

我用WINCE60的platform builder去编译一个IMAGE,每次都很慢,。。。。…

查看全部问答>

求字模提取软件

大家有好用的字模提取软件吗?我手里有几个不过功能不太全想求一个:功能全的,能设置字体的等等…

查看全部问答>

求教数据存储问题

MSP430F449中我想把一个整形的数A存入某地方 掉电也不丢失! 作为以后程序运行的参数。 应该是放在FLASH里面吧?或者建议是在哪儿? 怎么写和读? 应该读写各一句话就能搞定的吧?不是例子里面那么复杂!! 求给个例子或者指个方向 谢谢 ...…

查看全部问答>

请问广州哪里有零售漆包线、绝缘纸和绝缘漆的地方??

本帖最后由 paulhyde 于 2014-9-15 09:00 编辑 请问广州哪里有零售漆包线、绝缘纸和绝缘漆的地方?? 昨天下午走了一下午,传说中有这些东西的解放中,都没看见有卖忘各位朋友指出哪里有得买  …

查看全部问答>

在同一空间如果存在多个ZigBee网络,终端设备如何辨别并加入属于自己的网络?

请斑竹指教,谢谢!   [ 本帖最后由 lvhoujun 于 2011-9-19 22:52 编辑 ]…

查看全部问答>