求助STM32的USART2中断接收死机问题。

standstone   2010-5-2 15:07 楼主


用STM32中断接收USART2的数据,置位接收标志,在主程序中进行处理。程序可以运行,但一般过20分钟左右就死机了,原来以为是收发对象有问题,用计算机进行验证后发现是STM32的问题。而STM32的中断设置都是与网上的例程没有区别,不知道什么原因。我的中断大概是100MS左右一次,每次接收7个字节的数据(MODBUS协议)
最近发现在接收中断里加一个延时程序,就可以不死机,一般要延时5MS左右才行。请大家帮忙分析一下,谢谢了!
下面是程序代码:
中断处理:
void USART2_IRQHandler(void){

    //处理接收到的数据
    if(USART_GetITStatus(USART2,USART_IT_RXNE)!=RESET)
    {
      
   
  Rs485Rx[Rs485Rxcont]=USART_ReceiveData(USART2);

        Rs485Rxcont++;
        if(Rs485Rxcont>=7)
  {
            Usart2_Get_Flag=TRUE;   
         Delay(3);
        }
  

   // Clear the USART2 Receive interrupt
  USART_ClearITPendingBit(USART2,USART_IT_RXNE);  

    }
}

时钟:
/* Enable USART2 and GPIOA clock */
   RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
中断优先级:
/* Configure the NVIC Preemption Priority Bits */  
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
  
  /* Enable the USART2 Interrupt */
  NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQChannel;
  //NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);

GPIO:
/* Configure USART2 Tx (PA.02) as alternate function push-pull */
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
   GPIO_Init(GPIOA, &GPIO_InitStructure);
     
   /* Configure USART2 Rx (PA.3) as input floating */
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
   GPIO_Init(GPIOA, &GPIO_InitStructure);

USART2:
*/
  USART_InitStructure.USART_BaudRate = 19200;
  USART_InitStructure.USART_WordLength = USART_WordLength_8b;
  USART_InitStructure.USART_StopBits = USART_StopBits_1;
  USART_InitStructure.USART_Parity = USART_Parity_No;
  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
  USART_InitStructure.USART_Clock = USART_Clock_Disable;
  USART_InitStructure.USART_CPOL = USART_CPOL_Low;
  USART_InitStructure.USART_CPHA = USART_CPHA_2Edge;
  USART_InitStructure.USART_LastBit = USART_LastBit_Disable;   
  USART_Init(USART2, &USART_InitStructure);
  USART_ITConfig(USART2,USART_IT_RXNE,ENABLE);    //开串口2接收中断
      
  /* Enable USART2 */
  USART_Cmd(USART2, ENABLE);

中断处理:
if(Usart2_Get_Flag)
{
    GPIO_WriteBit(GPIOC, GPIO_Pin_4, (BitAction)(1));
    printf("%1c%1c%1c%1c%1c%1c%1c",0x03,0x03,0x02,0x02,0x04,0xC1,0x27);
    GPIO_WriteBit(GPIOC, GPIO_Pin_4, (BitAction)(0));
       Rs485Rxcont=0;
      Usart2_Get_Flag=FALSE;   
    }

回复评论 (38)

                                 刚才试验了一个延时2MS也可以不死机,但1MS还是死机。因为STM32需要一直工作在中断下面,如果增加这个延时,会大大减低工作效率,不知道如何解决。请问有没有人遇到过类似的问题?谢谢!
点赞  2010-5-2 15:20


我也遇到过,会停在串口中断中不出来了。

我是把   USART_ClearITPendingBit(USART2,USART_IT_RXNE);
改成 USART_ReceiveData(USART2); 空读一下数据解决的,也就是接收
到的字节该读还读,只不过不入接收队列了。


论坛里有过一篇文章讲这个来着,文章中的解决办法是在串口中断中添加
if(USART_GetFlagStatus(USART2, USART_FLAG_ORE) != RESET)
{
       USART_ReceiveData(USART2);
}
清 ORE 标志

你可以翻翻那篇文章看一下,挺详细的。
点赞  2010-5-2 21:43
谢谢楼上提供的信息,其实那贴子的方法早就试过了,没有用,所以没有写出来,原来有一段代码如下,加不加都不行:
        //溢出-如果发生溢出需要先读SR,再读DR寄存器则可清除不断入中断的问题
        if(USART_GetFlagStatus(USART2,USART_FLAG_ORE)==SET)
        {
                  USART_ClearFlag(USART2,USART_FLAG_ORE); //读SR其实就是清除标志
                  USART_ReceiveData(USART2);    //读DR
         }
您提到的方法:把   USART_ClearITPendingBit(USART2,USART_IT_RXNE);
改成 USART_ReceiveData(USART2);,试了一下,也不行。只有加一个延时才有用,不知道为什么?
点赞  2010-5-2 22:46

5楼 mbb 

在你的接收中断里面
只要进入中断就执行
Rs485Rx[Rs485Rxcont]=USART_ReceiveData(USART2);
Rs485Rxcont++;

会不会是主函数轮询的频率没有中断接收的频率快,导致时间一长 Rs485Rx[] 溢出了?

或者试一下这样如何?
if(接收标志未置位)
{
       接收;
       入队;
       if(满足条件)
       {
              接收标志置位;
       }
}
else
{
       接收;
       抛弃;
}
点赞  2010-5-3 13:15
非常感谢楼上的兄弟!按您的方法试了还是不行,只有加了延时才行。
if(USART_GetITStatus(USART2,USART_IT_RXNE)!=RESET)
    {
             
                  if(Usart2_Get_Flag==FALSE)
                {
                Rs485Rx[Rs485Rxcont]=USART_ReceiveData(USART2);
                Rs485Rxcont++;
                if(Rs485Rxcont>=7)
                        {
                    Usart2_Get_Flag=TRUE;                       
                                Rs485Rxcont=0;
                                   //Delay(2);
                }
                  }
                  else
                  USART_ReceiveData(USART2);

                        // Clear the USART2 Receive interrupt
                USART_ClearITPendingBit(USART2,USART_IT_RXNE);        
       
           }
点赞  2010-5-3 21:23
假如你要每次需要接收7个数据,那么就把下面的程序加到中断服务子程序中即可:  
     if(Rs485Rxcont==7)
         Rs485Rxcont=0;
                if(Rs485Rxcont<7)
  {
      Rs485Rx[Rs485Rxcont++]=USART_ReceiveData(USART2);                           
   }
   // Clear the USART2 Receive interrupt
  USART_ClearITPendingBit(USART2,USART_IT_RXNE);
点赞  2010-5-4 07:53
谢谢楼上的意见,试了一下也没有成功。为了防止溢出,还试验了增加一条读数据的指令,不起作用。代码如下:
if(USART_GetITStatus(USART2,USART_IT_RXNE)!=RESET)
    {         
                if(Rs485Rxcont>=7){
         Rs485Rxcont=0;
                Usart2_Get_Flag=TRUE;
                USART_ReceiveData(USART2);       
                 }

                      if(Rs485Rxcont<7){
                        Rs485Rx[Rs485Rxcont++]=USART_ReceiveData(USART2);
              }
                                  
                // Clear the USART2 Receive interrupt
                USART_ClearITPendingBit(USART2,USART_IT_RXNE);        
       
           }
点赞  2010-5-4 09:28
肯定可以,你每次发送数据只能是7个,不要加
if(USART_GetITStatus(USART2,USART_IT_RXNE)!=RESET)
这是轮询模式下,
是whlie(USART_GetITStatus(USART2,USART_IT_RXNE)!=RESET);
只要进入中断,说明你的标志位肯定置位了。
不需要在判断一次了
还有Rs485Rxcont必须为全局变量并且初始化为0
奥,对了,你在后面加上个delay吧
可能就行了。
点赞  2010-5-4 09:37


回楼上,1、Rs485Rxcont必须为全局变量并且初始化为0。
原程序里就是这样做的。
2、如果加上Delay(2)是可以的,但每次都在中断里花费2MS的时间,系统的效率不高啊,因为还有其它的事情要做。
当主程序里简化成只有一个中断服务子程序,而且处理起来也比较快。不加DELAY就不能运行,不明白为什么加上Delay就可以成功呢?难道是中断得太频繁吗?
反而当主程序里事情比较多的时候,不加delay则可以运行,但过半小时左右会死机。
点赞  2010-5-4 10:08
在中断开始处你有一个判断语句如下,请问如果这个判断不成功,即产生的中断不是USART_IT_RXNE的时候,程序做何处理?

if(USART_GetITStatus(USART2,USART_IT_RXNE)!=RESET)

另外,你的波特率是多少?
点赞  2010-5-4 10:35
回版主:1、中断里还有一个判断模块是:
//溢出-如果发生溢出需要先读SR,再读DR寄存器则可清除不断入中断的问题
        if(USART_GetFlagStatus(USART2,USART_FLAG_ORE)==SET)
        {
                  USART_ClearFlag(USART2,USART_FLAG_ORE); //读SR其实就是清除标志
                  USART_ReceiveData(USART2);    //读DR
         }
其它就没有处理了。
2、波特率是19200
点赞  2010-5-4 10:39


//不知道我是否是答非所问??????????????!!!!!!!!!!!

//单缓冲器模式 该实例采用LIB_V3.0.0

/*接收数据*/

void USART1_IRQHandler(void)
{  
static __IO uint16_t clockRXD[1];

if  (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {      /* 若接收数据寄存器满*/
   clockRXD[0] = USART_ReceiveData(USART1); /*此语句作用:将USART_DR寄存器的内容传到clockRXD[0]里。另外,在单缓冲器模式下,软件读USART_DR寄存器则完成对RXNE位清除。[注意]在多缓冲器模式下,必须通过软件清零"传输完成标志"DMA1_FLAG_TCx(即:令DMA_IFCR的位CTCIFx=1),否则将会无法跳出中断服务程序,出现一次中断请求无数次响应的后果。*/



/*在中断服务程序中,由于主机响应中断时并不知道是哪个中断源发出中断请求,因此必须在中断服务程序中对中断源进行判别,然后分别进行处理。当然,如果只涉及到一个中断请求,是不用做上述判别的。但是无论什么情况,做上述判别是个好习惯*/


}
点赞  2010-5-4 10:50
这个程序是让STM32模拟同时多个MODBUS的下位机的数据接收,采用中断的方式,中断的时间间隔约是100MS左右一次。STM32的系统时钟是72MHZ的,对100MS的事件应该是比较快了。
主程序里是对接收标志位进行查询的方式进行,现在看来应该是查询的时间大于了中断间隔时间100MS,因为加了延时就可以运行,唯一不同的是中断里的时间加长了。
但如果是主程序查询的速度不行,中断里加了溢出处理后,为什么也不行呢?
如果把主程序里的中断处理子程序加在中断响应里做可能会解决问题吗?但这样好像不符合一般的中断处理规范了。
点赞  2010-5-4 10:53
                                 STM32的串行中断是每收到一个字节中断一次,建议你接收一次清中断,退出中断,在接受下一次,我也用的RS485,115200的速度很稳定。
点赞  2010-5-4 12:18
回楼上,USART的中断就是接收一个字节后清中断,然后再接收下一次的啊。
您是说一次接收完多个字节再清中断?
点赞  2010-5-4 13:02
                                 如果是来一次中断一次,那肯定不能接受多个再清了,不然第二次就进不了中断了
点赞  2010-5-4 20:37
                                 中断如果不请,下次肯定就进不去了
点赞  2010-5-4 20:56
                                 搞这么复杂啊
点赞  2010-5-4 21:33
回版主:
1、楼主位的程序中是没有这段代码,因为加不加都是一样的结果。现在都加了,现象还是如前。
2、死机时就是所有的程序都停了,但晶振还是工作的。因为时间比较长,没有调试运行,各个寄存器的状态无法知道。
3、“而且USART_ClearFlag()不能清除USART_FLAG_ORE。”,请问正确的清除方法是什么?谢谢!
点赞  2010-5-4 21:34
12下一页
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复