打印
[AVR单片机]

atmega128长期工作后会出现程序跑飞,求大神分析原因。

[复制链接]
1279|9
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
21zzj|  楼主 | 2016-7-12 08:53 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
最近做的一个项目,大概是通过定时器定时20ms读取部分外部IO状态,当状态发生改变时,把数据发送到串口,同时串口按协议接收上位命令对其它IO做出输出或关闭输出。在长期工作后,某些机器会不停地往串口发同一串数据,有时候数据长度不对,有时候数据有乱码。用的是WINavr+avr studio.出问题时还能正常进入定时器中断,在主程序大循坏加看门狗没作用,加了很多冗余标志位也没用。

代码如下,希望大神帮忙下,很着急。弄了两个月没找到原因。
#include<avr/io.h>
#include<avr/iom128.h>
#include<avr/interrupt.h>
#include <avr/wdt.h>
#define CLI() do { __asm__ __volatile__ ("cli"); } while (0)
#define SEI() do { __asm__ __volatile__ ("sei"); } while (0)
#define NOP() do { __asm__ __volatile__ ("nop"); } while (0)
#define uchar unsigned char
#define uint  unsigned int

volatile uchar wdt1=1;
volatile uchar RE_rcorrect[128]; //正确接收到的新码;
volatile uchar wdt2=1;
//------------------------
volatile uchar RD_count;
volatile uchar Rdata;
volatile uchar usart0_Rbuff[128];  //接收缓冲器
volatile uchar usart0_REflag=0;
volatile uchar usart0_RSflag=0;
volatile uchar wdt3=1;
volatile uchar send_flag=0;
volatile uchar send_Tflag=0;
volatile uchar Send_buff[128];  //发送缓冲器
volatile uchar REtime=0;
volatile uchar RecDataBCC=0;
volatile uchar IN_BCC=0;
volatile uchar OUT_BCC=0;
volatile uchar Send_buff[128];  //发送缓冲器
volatile uchar wdt4=1;
volatile   uchar  temA=0;
volatile   uchar  temB=0;
volatile   uchar  temA1=0;
volatile   uchar  temB1=0;
volatile uchar Send_buff[128];  //发送缓冲器
volatile   uchar  PasttemA=0;
volatile   uchar  PasttemB=0;
volatile   uchar  passmark=0;
volatile   uchar  passcount=0;
volatile uchar wdt5=1;
volatile uchar Send_tem[128];  //T发送缓冲器
void usart0_send(uchar sdata);
void String_send(uchar *p,uchar Lenth);

volatile uchar wdt6=1;

volatile uchar  t1=0;
volatile uchar  t2=0;
volatile uchar  t3=0;
volatile uchar  t4=0;
volatile uchar usart0_ST1buff[128];  //T发送缓冲器

volatile uchar send_INmark=0;
volatile uchar send_OUTmark=0;





void timer1_init(void)
{
        TCCR1A=0x00;      //普通模式,不与OCnA相连。
        TCCR1B=0x03;          //Clk/64       
        TCCR1C=0x00;          //不设置强制输出通道
       
        TCNT1H=0xEC;           //定时20ms=次数/每秒次数=(FFFF-初值)/(F_CU/分频)
        TCNT1L=0x77;
       
//        TCNT1H=0xF6;           //定时10ms=次数/每秒次数=(FFFF-初值)/(F_CU/分频)
//        TCNT1L=0x3B;
        TIMSK|=_BV(TOIE1);//使能定时器1;
}


       
ISR(SIG_OVERFLOW1)//定时器1中断
{
                uchar  i=0;
                NOP();NOP();NOP();NOP();NOP();NOP();NOP();NOP();NOP();NOP();
                wdt1=0;
                TCNT1H=0xEC;           //定时20ms=次数/每秒次数=(FFFF-初值)/(F_CU/分频)
                TCNT1L=0x77;


       //--------------------------------------
                if(REtime<=0)    //串口接收超时丢弃。
                {
                        usart0_RSflag=0;       
                }
                else
                {
                        REtime--;
                }               
               
               
                 
                temA=PINA;
                temB=PINB&0x0f;
                NOP();NOP();NOP();NOP();NOP();NOP();NOP();NOP();NOP();NOP();
                temA1=PINA;
                temB1=PINB&0x0f;
                if((temA==temA1)&&(temB==temB1))
                {
                        IN_BCC=0;
                        Send_tem[0]=0x02;       
                        IN_BCC=0x42;
                    Send_tem[1]=0x42;
                        for(i=0;i<8;i++)
                        {
                                if(((temA>>i)&0x01)==0x01)
                                {
                                        Send_tem[2+i]=0x30;               
                                }
                                else
                                {
                                        Send_tem[2+i]=0x31;       
                                }
                                IN_BCC=IN_BCC^Send_tem[2+i];       
                        }
                        for(i=0;i<4;i++)
                        {
                                if(((temB>>i)&0x01)==0x01)
                                {
                                        Send_tem[10+i]=0x30;               
                                }
                                else
                                {
                                        Send_tem[10+i]=0x31;       
                                }
                                IN_BCC=IN_BCC^Send_tem[10+i];       
                        }
                        Send_tem[14]=IN_BCC;
                        Send_tem[15]=0x03;       
                       
                        if((PasttemA!=temA)||(PasttemB!=temB))
                        {
                       
                                 PasttemA=temA;
                                 PasttemB=temB;       
                 send_Tflag=1; //注销这一行即可取消主动反馈
                                 send_INmark=1;//注销这一行即可取消主动反馈

                        }
                                               
               
                }       
        wdt1=1;                 
}



void usart0_init()
{
        UCSR0B=0x00; //close USART
        UCSR0A=0x00;
        UCSR0C=0x06;
        UBRR0H=0x00;
        UBRR0L=0x67;
        UCSR0B=0xd8;          //open USARTd8       
}

ISR(USART0_TX_vect)
{
        NOP();
        UCSR0A |= (1<<6);                      //发送结束清零
}




void usart0_send(uchar sdata)
{
        NOP();NOP();NOP();NOP();

        if((sdata!=0x02)&&(sdata!=0x30)&&(sdata!=0x31)&&(sdata!=0x42)&&(sdata!=0x43)&&(sdata!=0x44)&&(sdata!=0x45)&&(sdata!=0x03))
        {
                wdt6=0;
    }
    else
        {
                //wdt6=1;
                while(!(UCSR0A&(1<<UDRE0)));
                 UDR0=sdata;
    }
        NOP();NOP();NOP();NOP();
        // while(!(UCSR0A&(1<<TXC0)));
    // UCSR0A |= _BV(TXC0);         
}



ISR(USART0_RX_vect)  //按协议存储除了命令头02和结束03之外的所有数据
{   
       
        NOP();NOP();NOP();NOP();
        wdt2=0;
        if(RXC0)   //接收到数据
        {
          REtime=50;//1没接收完命令将丢弃,定时器中1s后usart0_RSflag=0;
          Rdata=UDR0;         //提取数据         
          if(Rdata==0x02)        //命令头
           {
                 usart0_RSflag=1;//符合协议开始接收
                 RD_count=0;     //接收个数计数
                 RecDataBCC=0;         //初始化BCC校验值       
           }
          else if(usart0_RSflag)       
           {     
                     if((Rdata==0x03)&&(RecDataBCC==0)) //如果收到03并且BCC校验正确
                          {
                                    usart0_RSflag=0;                        //结束接收
                                        if(usart0_Rbuff[0]==0x43)   //输出命令
                                        {
                                            for(uchar i=0;i<14;i++)       
                                                         RE_rcorrect[i]=usart0_Rbuff[i];
                                                usart0_REflag=1;
                                                send_OUTmark=1;                       
                                        }
                                        else if(usart0_Rbuff[0]==0x41) //输入命令
                                        {
                                                send_Tflag=1;
                                                send_INmark=1;               
                                        }
                          }
                          else if((Rdata==0x03)&&(RecDataBCC!=0))//校验不正确
                          {
                                           usart0_RSflag=0;
                                          RD_count=0;     
                                          RecDataBCC=0;                                                   
                          }       
                          else
                          {
                                     usart0_Rbuff[RD_count++]=Rdata;  //存储数据
                                         RecDataBCC=RecDataBCC^Rdata;         //        BCC校验                   
                          }
           }

          if(RD_count>14)  //定长,收到命令头后只存14个数据,超出长度也不符合协议
                  {
                  usart0_RSflag=0;        //重新开始接收
                }
        }
        wdt2=1;
}




void usart0_handle(void)
{
  uchar i;
  wdt3=0;
  switch(RE_rcorrect[0])
   {   
//-----------------------设置码 12路状态设置-------------------------------
        case 0x43:
          switch(RE_rcorrect[1])
           {
            case 0x30: PORTB&=~(1<<PB4);
                          break;
                case 0x31: PORTB|=(1<<PB4);
                          break;
                default:  break;
           }
          switch(RE_rcorrect[2])
           {
            case 0x30: PORTB&=~(1<<PB5);
                          break;                
                case 0x31: PORTB|=(1<<PB5);
                          break;
            default:  break;                                 
           }
          switch(RE_rcorrect[3])
           {
            case 0x30: PORTB&=~(1<<PB6);
                          break;        
                case 0x31: PORTB|=(1<<PB6);
                          break;        
                default:  break;
           }  
          switch(RE_rcorrect[4])
           {
            case 0x30: PORTB&=~(1<<PB7);
                          break;        
                case 0x31: PORTB|=(1<<PB7);
                          break;
                default:  break;
           }
          switch(RE_rcorrect[5])
           {
            case 0x30: PORTC&=~(1<<PC2);
                  break;        
                case 0x31: PORTC|=(1<<PC2);
                  break;        
                default:  break;
           }
          switch(RE_rcorrect[6])
           {
            case 0x30: PORTC&=~(1<<PC3);
                  break;
                case 0x31: PORTC|=(1<<PC3);
                  break;
                default:  break;
           }   
          switch(RE_rcorrect[7])
           {
            case 0x30: PORTC&=~(1<<PC4);
                          break;
                case 0x31: PORTC|=(1<<PC4);
                          break;
                default:  break;
           }
          switch(RE_rcorrect[8])
           {
            case 0x30: PORTC&=~(1<<PC5);
                          break;                
                case 0x31: PORTC|=(1<<PC5);
                          break;
            default:  break;                                 
           }
          switch(RE_rcorrect[9])
           {
            case 0x30: PORTC&=~(1<<PC6);
                          break;        
                case 0x31: PORTC|=(1<<PC6);
                          break;        
                default:  break;
           }  
          switch(RE_rcorrect[10])
           {
            case 0x30: PORTC&=~(1<<PC7);
                          break;        
                case 0x31: PORTC|=(1<<PC7);
                          break;
                default:  break;
           }
          switch(RE_rcorrect[11])
           {
            case 0x30: PORTC&=~(1<<PC0);
                  break;        
                case 0x31: PORTC|=(1<<PC0);
                  break;        
                default:  break;
           }
          switch(RE_rcorrect[12])
           {
            case 0x30: PORTC&=~(1<<PC1);
                  break;
                case 0x31: PORTC|=(1<<PC1);
                  break;
                default:  break;
           }     
                         
         //--------------------------------
       OUT_BCC=0;
           Send_buff[0]=0x02;   
           Send_buff[1]=0x44;
           OUT_BCC=0x44;       
           for(i=0;i<12;i++)
           {
                    Send_buff[i+2]=RE_rcorrect[i+1];
                  OUT_BCC=OUT_BCC^RE_rcorrect[i+1];
       }
           Send_buff[14]=OUT_BCC;
           Send_buff[15]=0x03;                     
          // send_flag=1;
         //-------------------------------                  
  break;

  default:  break;


    }
        wdt3=1;          
}







//------------------------------
//IO
//------------------------------
void IO_iniaitil(void)
{
  DDRE=0xfe; //E口设成输出 11111110
  PORTE=0xff;

  DDRA=0x00;  //A口设成输入
  PORTA=0xff; //
  DDRB=0xF0;  //B口低四位设成输入,高四位为输出
  PORTB=0x0f; //
  
  DDRC=0xff;  //C口设成输出
  PORTC=0x00; //00000000

  wdt1=1;
  wdt2=1;
  wdt3=1;
  wdt4=1;
  wdt5=1;
  wdt6=1;

  
}
           

int main(void)
{  
   CLI();  
   IO_iniaitil();
   timer1_init();
   usart0_init();                 
   SEI();        //开总中断
   wdt_enable(WDTO_250MS); //启动开门狗250ms
   wdt_reset();//开始喂看门狗
   while(1)
    {
                 
                 if((send_Tflag==1)&&(send_INmark==1))//输入
                  {               
                                  wdt4=0;
                                  send_Tflag=0;
                                TIMSK&=~_BV(TOIE1);//关闭定时器中断
                                for(t1=0;t1<16;t1++)
                                {
                                        usart0_ST1buff[t1]=Send_tem[t1];
                }
                                TIMSK|=_BV(TOIE1);//使能定时器1中断;                
                                for(t2=0;t2<16;t2++)
                                {
                                           usart0_send(usart0_ST1buff[t2]);
                }
                                send_INmark=0;
                wdt4=1;               
                  }       


             if((usart0_REflag==1)&&(send_OUTmark==1))//输出       
              {
                             wdt5=0;
                           usart0_REflag=0;
                           usart0_handle();
                           for(t3=0;t3<16;t3++)
                           {
                                           usart0_send(Send_buff[t3]);
               }
                           send_OUTmark=0;
                           wdt5=1;
                  }

                 if((wdt1==1)&&(wdt2==1)&&(wdt3==1)&&(wdt4==1)&&(wdt5==1)&&(wdt6==1))
                 {
                            wdt_reset();//喂看门狗
                 }
   }
}
故障现象为串口不停发送Send_buff[0]到Send_buff[15]的数据,有时候是不是0-15的数据,只有其中一段或者夹着乱码,此时定时器还能进中断更新Send_buff[]的数据。搞了很多天没找到原因,压力很大,求解救。

相关帖子

沙发
21zzj|  楼主 | 2016-7-12 08:58 | 只看该作者
用的晶振是16M的,熔丝位设置Extend:0xFE,Hight:0x09,Low:0x3F。

使用特权

评论回复
板凳
winterchen20| | 2016-7-12 16:40 | 只看该作者
找找硬件方面的原因  EMC等等

使用特权

评论回复
地板
joing1999| | 2016-7-12 18:23 | 只看该作者
加个看门狗

使用特权

评论回复
5
21zzj|  楼主 | 2016-7-12 18:30 | 只看该作者
winterchen20 发表于 2016-7-12 16:40
找找硬件方面的原因  EMC等等

你是说是硬件干扰造成程序跑飞的吗?

使用特权

评论回复
6
21zzj|  楼主 | 2016-7-12 18:31 | 只看该作者

已经加了啊,没用的,看门狗不是所有跑飞都能恢复的

使用特权

评论回复
7
JerryWu75| | 2016-7-12 21:13 | 只看该作者
感觉像是中断与main循环对这两个变量send_Tflag和send_INmark操作的冲突.
如果main循环中进行操作的变量会被中断程序进行操作的话,需要进行互锁。或者最简单的办法就是main循环中操作时关闭中断,操作结束后再开中断。

使用特权

评论回复
8
21zzj|  楼主 | 2016-7-13 08:43 | 只看该作者
JerryWu75 发表于 2016-7-12 21:13
感觉像是中断与main循环对这两个变量send_Tflag和send_INmark操作的冲突.
如果main循环中进行操作的变量会 ...

但是我在定时中断里面改变这两个变量send_Tflag和send_INmark是发生在PORTA或PORTB状态发生变化之后的啊,在出故障时这两组IO都没发生改变就一直发数据了,而且有时候串口不停发数据是从usart0_ST1buff[1]到usart0_ST1buff[15]的,而不是从0开始。

使用特权

评论回复
9
21zzj|  楼主 | 2016-7-13 08:52 | 只看该作者
JerryWu75 发表于 2016-7-12 21:13
感觉像是中断与main循环对这两个变量send_Tflag和send_INmark操作的冲突.
如果main循环中进行操作的变量会 ...

而且你说的这种方法有尝试过的,还是会出现该种问题的。对了,我现在模拟该种情况是要把熔丝位CKOPT关掉,然后用手接触晶振让程序跑飞来模拟的,我不太肯定工业现场是不是因为干扰造成程序跑飞而造成的,问题并不在程序本身上,但是我现场这个设备是放在一个铁盒子里面,照理来说应该抗干扰挺强的啊,在实验室怎么都测试不出该种故障,后面是要把熔丝位CKOPT关掉,然后用手接触晶振让程序跑飞来模拟的。

使用特权

评论回复
10
JerryWu75| | 2016-7-13 09:48 | 只看该作者
铁盒子有没有接地,屏蔽如果不接地是达不到屏蔽的目的的.
另外我觉得你可以考虑将发送程序放到中断中去处理,避免背景循环和中断之间的中途.之前我用TI的DSP时,就发生过背景循坏和中断操作同一个变量导致的异常,后来把背景循环中的所有程序全部移到定时器中断中进行处理,背景循环变成空循坏才解决问题.

使用特权

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

本版积分规则

2

主题

10

帖子

0

粉丝