打印

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

[复制链接]
29398|51
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
llycom|  楼主 | 2010-5-2 15:07 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 llycom 于 2010-5-4 10:43 编辑

用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;   
    }
沙发
llycom|  楼主 | 2010-5-2 15:20 | 只看该作者
刚才试验了一个延时2MS也可以不死机,但1MS还是死机。因为STM32需要一直工作在中断下面,如果增加这个延时,会大大减低工作效率,不知道如何解决。请问有没有人遇到过类似的问题?谢谢!

使用特权

评论回复
板凳
lostgb| | 2010-5-2 21:43 | 只看该作者
本帖最后由 lostgb 于 2010-5-2 21:52 编辑

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

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


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

你可以翻翻那篇**看一下,挺详细的。

使用特权

评论回复
地板
llycom|  楼主 | 2010-5-2 22:46 | 只看该作者
谢谢楼上提供的信息,其实那贴子的方法早就试过了,没有用,所以没有写出来,原来有一段代码如下,加不加都不行:
        //溢出-如果发生溢出需要先读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);,试了一下,也不行。只有加一个延时才有用,不知道为什么?

使用特权

评论回复
5
lostgb| | 2010-5-3 13:15 | 只看该作者
在你的接收中断里面
只要进入中断就执行
Rs485Rx[Rs485Rxcont]=USART_ReceiveData(USART2);
Rs485Rxcont++;

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

或者试一下这样如何?
if(接收标志未置位)
{
       接收;
       入队;
       if(满足条件)
       {
              接收标志置位;
       }
}
else
{
       接收;
       抛弃;
}

使用特权

评论回复
6
llycom|  楼主 | 2010-5-3 21:23 | 只看该作者
非常感谢楼上的兄弟!按您的方法试了还是不行,只有加了延时才行。
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);        
       
           }

使用特权

评论回复
7
shizhewen| | 2010-5-4 07:53 | 只看该作者
假如你要每次需要接收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);

使用特权

评论回复
8
llycom|  楼主 | 2010-5-4 09:28 | 只看该作者
谢谢楼上的意见,试了一下也没有成功。为了防止溢出,还试验了增加一条读数据的指令,不起作用。代码如下:
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);        
       
           }

使用特权

评论回复
9
shizhewen| | 2010-5-4 09:37 | 只看该作者
肯定可以,你每次发送数据只能是7个,不要加
if(USART_GetITStatus(USART2,USART_IT_RXNE)!=RESET)
这是轮询模式下,
是whlie(USART_GetITStatus(USART2,USART_IT_RXNE)!=RESET);
只要进入中断,说明你的标志位肯定置位了。
不需要在判断一次了
还有Rs485Rxcont必须为全局变量并且初始化为0
奥,对了,你在后面加上个delay吧
可能就行了。

使用特权

评论回复
10
llycom|  楼主 | 2010-5-4 10:08 | 只看该作者
本帖最后由 llycom 于 2010-5-4 10:12 编辑

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

使用特权

评论回复
11
香水城| | 2010-5-4 10:35 | 只看该作者
在中断开始处你有一个判断语句如下,请问如果这个判断不成功,即产生的中断不是USART_IT_RXNE的时候,程序做何处理?

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

另外,你的波特率是多少?

使用特权

评论回复
12
llycom|  楼主 | 2010-5-4 10:39 | 只看该作者
回香主:1、中断里还有一个判断模块是:
//溢出-如果发生溢出需要先读SR,再读DR寄存器则可清除不断入中断的问题
        if(USART_GetFlagStatus(USART2,USART_FLAG_ORE)==SET)
        {
                  USART_ClearFlag(USART2,USART_FLAG_ORE); //读SR其实就是清除标志
                  USART_ReceiveData(USART2);    //读DR
         }
其它就没有处理了。
2、波特率是19200

使用特权

评论回复
13
xlsbz| | 2010-5-4 10:50 | 只看该作者
本帖最后由 xlsbz 于 2010-5-4 10:54 编辑

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

//单缓冲器模式 该实例采用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),否则将会无法跳出中断服务程序,出现一次中断请求无数次响应的后果。*/



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


}

使用特权

评论回复
14
llycom|  楼主 | 2010-5-4 10:53 | 只看该作者
这个程序是让STM32模拟同时多个MODBUS的下位机的数据接收,采用中断的方式,中断的时间间隔约是100MS左右一次。STM32的系统时钟是72MHZ的,对100MS的事件应该是比较快了。
主程序里是对接收标志位进行查询的方式进行,现在看来应该是查询的时间大于了中断间隔时间100MS,因为加了延时就可以运行,唯一不同的是中断里的时间加长了。
但如果是主程序查询的速度不行,中断里加了溢出处理后,为什么也不行呢?
如果把主程序里的中断处理子程序加在中断响应里做可能会解决问题吗?但这样好像不符合一般的中断处理规范了。

使用特权

评论回复
15
skyfight| | 2010-5-4 12:18 | 只看该作者
STM32的串行中断是每收到一个字节中断一次,建议你接收一次清中断,退出中断,在接受下一次,我也用的RS485,115200的速度很稳定。

使用特权

评论回复
16
llycom|  楼主 | 2010-5-4 13:02 | 只看该作者
回楼上,USART的中断就是接收一个字节后清中断,然后再接收下一次的啊。
您是说一次接收完多个字节再清中断?

使用特权

评论回复
17
yybj| | 2010-5-4 20:37 | 只看该作者
如果是来一次中断一次,那肯定不能接受多个再清了,不然第二次就进不了中断了

使用特权

评论回复
18
txcy| | 2010-5-4 20:56 | 只看该作者
中断如果不请,下次肯定就进不去了

使用特权

评论回复
19
香水城| | 2010-5-4 21:07 | 只看该作者
回香主:1、中断里还有一个判断模块是:
//溢出-如果发生溢出需要先读SR,再读DR寄存器则可清除不断入中断的问题
        if(USART_GetFlagStatus(USART2,USART_FLAG_ORE)==SET)
        {
                  USART_ClearFlag(USART2,USART_FLAG_ORE); //读SR其实就是清除标志
                  USART_ReceiveData(USART2);    //读DR
         }
其它就没有处理了。
llycom 发表于 2010-5-4 10:39


楼主位的程序中没有这段代码,而且USART_ClearFlag()不能清除USART_FLAG_ORE。

你最好能够在中断返回之前看看是否已经清除了需要清除的标志。

其实,你并没有描述清楚最根本的问题,“程序可以运行,但一般过20分钟左右就死机了”,这个死机的现象是什么?出现这种现象时各个寄存器的状态是什么样的?

使用特权

评论回复
20
司徒老鹰| | 2010-5-4 21:33 | 只看该作者
搞这么复杂啊

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

6

主题

43

帖子

0

粉丝