cumtnj 发表于 2009-12-29 22:34:49

Mega48的训狗心得

1.测试M48看门狗初始化时的状态
-----------------------------------------
M48使用外部晶振 7.3728M
-----------------------------------------
#include <avr/io.h>
#include <avr/delay.h>
#include <avr/wdt.h>

/*
wdt.h里面
看门狗溢出时间常量定义
        #define WDTO_15MS   0
        #define WDTO_30MS   1
        #define WDTO_60MS   2
        #define WDTO_120MS3
        #define WDTO_250MS4
        #define WDTO_500MS5
        #define WDTO_1S   6
        #define WDTO_2S   7
        下面的4S/8S定义只能用于 ATtiny2313, ATmega48, ATmega88 and the ATmega168.
        #define WDTO_4S   8
        #define WDTO_8S   9
看门狗操作函数
        wdt_disable()
        关闭看门狗
        wdt_enable(timeout)
        使能看门狗及溢出时间设定
        wdt_reset()
        复位看门狗(喂狗)
*/
//管脚定义

#defineWDT_EN                7        //PD7 看门狗的喂狗控制引脚
#defineLED_WD                3        //PD3 看门狗复位指示
#defineLED_BO                2        //PD2 BOD复位指示
#defineLED_EXT      1        //PD1 RESET引脚复位指示
#defineLED_PO                0        //PD0 上电复位指示
//以上信号皆为低电平有效

int main(void)
{
    unsigned char CPU_STATUS;
    unsigned inti;
       
   //上电默认DDRx=0x00,PORTx=0x00 输入,无上拉电阻
       
    PORTB = 0xFF;        //不用的管脚使能内部上拉电阻。
    PORTC = 0x00;      
    PORTD = 0xFF;
    DDRB= 0x00;
    DDRC= 0x00;
    DDRD=(1<<LED_WD)|(1<<LED_BO)|(1<<LED_EXT)|(1<<LED_PO);        //输出驱动LED

    while(1)
    {

    }
}

后记:这时程序的执行现象是4个LED灯一直在闪!!!!!!!!!
思考:在主程序中没有打开看门狗的操作啊,但是根据程序判断,M48在不停的复位。这时只有去求助参考手册!!!!!!

P43-->看门狗定时器
      “如果没有及时复位定时器,一旦时间超过复位周期, ATmega48/88/168 就复位,并执行复位向量指向的程序”-----> 这时你会想,我在烧写的时候配置熔丝位WDTON = 1(未编程),即工作在安

      全级别1,WDT初始化状态是禁止的(可以参考时间序列禁止WDT),为什么看门狗还会有动作呢?
      仔细看Datesheet!!!!!!!!!
P43-->WDTCSR 寄存器中的WDE(使能看门狗)在初始时的状态是个“x”,也就是不确定!!!!!因此必须清零,才能保证看门狗不会乱咬人。
   //在安全级别1时,按照正确的时序关闭看门狗
   void WDT_off(void)
   {
      //MCUSR 中的WDRF清零

         MCUSR = 0X00;

      //置位WDCE 和 WDE

      WDTCSR = (1<<WDCE) |(1<<WDE);

      //关闭WDT

      WDTCSR = 0X00;

      }
      熔丝位配置为WDTON = 1,这时是软件狗,可以打开和关闭的(如果WDTON = 0的话就成了硬件狗,这时工作在安全级别2,只能修改狗狗的溢出时间,没法关闭小狗);在while(1)之前加上      

WDT_off(); 后小狗就被驯服了,呵呵…………

2.看门狗经典必读帖子

    (1)"AVR看门狗使用范例 avr wdt看门狗详解.mht"
    (2)

3.细说AVR看门狗

AVR看门狗使用教程 :http://www.avrvi.com/avr_examples/avr_wdt.html


一个硬件单元,当程序由于某种原因跑“飞”了,它就Reset程序。就像小狗看门一样。

//Watchdog initialize
// prescale: 2048K
void watchdog_init(void)
{
WDR(); //this prevents a timout on enabling
WDTCR = 0x0F; //WATCHDOG ENABLED - dont forget to issue WDRs
}
上面是用ICC的App Builder生成的看门狗初始化程序。最后一行代码提醒狗主人,别忘了及时清零看门狗定时器(喂狗),否则,小狗就咬人了。

—————————————————————————————————————————————

一个相对独立的计数自动重启单片机的硬件部件,如果启用它后,不在一定的时间内清除它的计数值,就会达到计数的最高值而溢出,然后它就指挥单片机重启。
所以要在你的程序里适当的加入清看门狗的指令,一旦你的单片机程序出了问题,当然就不能按照你的程序原先设定那样自动清看门狗了,也就是常说的程序跑飞了,这个时候看门狗就会重启单片机试图

解决问题。一般只对瞬间干扰造成的问题有效,要是长时间的干扰或是软硬件问题,看门狗的意义不是很大。

—————————————————————————————————————————————

我的理解 就象是监视程序执行的保安一样,
程序正常执行时会在他的益处时间之内给他一
个复位信号,当程序跑飞的时候他在溢出时间之内是收不到复
位信号的,这时看门狗就会在设定的时间内产生系统复位的信号!

—————————————————————————————————————————————

AVR的看门狗是软狗,也是硬狗!

如果熔丝位不设定,就是软狗,因为程序可以关闭,也可以打开
如果熔丝位设定了,就是硬狗,因为程序只可以清除,而无法打开或关闭!

—————————————————————————————————————————————

是不是在程序中加入
WDR();
就算“喂狗”了?喂狗好象要计算好时间吧?我每执行一个函数就喂狗一次如何?
如果你的循环体内每循环一次的时间不超过看门狗的复位时间,主要喂狗一次就可以了。

cumtnj 发表于 2009-12-30 11:28:30

续上
------------------------------------------------------------------------------------------------------------------------
4.只要你保证真正的仔细的把上面所有的东西都仔细看过,看门狗也就真正掌握了!!!!!!!!!!!

5.熔丝位配置为WDTON = 1,即启动软件狗

//配置IO口时,以上IO口都设置为:输出高电平,因此以上信号皆为低电平有效
void WDT_on(void)
{
        //置位WDCE 和 WDE
        WDTCSR = (1<<WDCE) |(1<<WDE);
        //设置定时溢出值
        WDTCSR = 0x29; //WATCHDOG ENABLED - dont forget to issue WDRs
}
void WDT_off(void)
{
        //MCUSR 中的WDRF清零       
        MCUSR = 0X00;       
        //置位WDCE 和 WDE       
        WDTCSR = (1<<WDCE) |(1<<WDE);
        //关闭WDT
        WDTCSR = 0X00;
}
int main(void)
{
         unsigned char CPU_STATUS;
         unsigned inti;
       
         //上电默认DDRx=0x00,PORTx=0x00 输入,无上拉电阻
       
         PORTB = 0xFF;        //不用的管脚使能内部上拉电阻。
         PORTC = 0x00;      
         PORTD = 0xFF;
         DDRB= 0x00;
         DDRC= 0x00;
         DDRD= (1<<LED_WD)|(1<<LED_BO)|(1<<LED_EXT)|(1<<LED_PO);        //输出驱动LED
         WDT_on();          //!!!!!!!!!!!!!!!!!!!!!!!!!!!

         CPU_STATUS = MCUSR;                        //读取MCU控制和状态寄存器
   
         CPU_STATUS &= 0x0F;
         switch (CPU_STATUS)
         {
          case (1<<WDRF):                        //看门狗引起的复位?
             PORTD&=~(1<<LED_WD);
             break;
          case (1<<BORF):                        //BOD引起的复位?
             PORTD&=~(1<<LED_BO);
             break;
          case (1<<EXTRF):                  //RESET引脚引起的复位?
                  PORTD&=~(1<<LED_EXT);
             break;
          case (1<<PORF):                        //上电引起的复位?
             PORTD&=~(1<<LED_PO);
             break;
          default:                                //多种复位同时发生?
             PORTD=~CPU_STATUS;       
             break;
          }
          MCUSR=0x00;                        //清除标志位!!!!!!!!!!!!!!!!!!!   放置位置影响程序的结果
   

          while(1)
          {

         }
}
还要继续实验,为什么有时RESET复位一直存在或看门狗复位一直存在?????????    后记:“MCUSR=0x00; //清除标志位”,与这句话放的位置有关,应该放在“打开看门狗之前”
int main(void)
{
    unsigned char CPU_STATUS;
    unsigned inti;
       
//上电默认DDRx=0x00,PORTx=0x00 输入,无上拉电阻
       
    PORTB = 0xFF;        //不用的管脚使能内部上拉电阻。
    PORTC = 0x00;      
    PORTD = 0xFF;
    DDRB= 0x00;
    DDRC= 0x00;
    DDRD= (1<<LED_WD)|(1<<LED_BO)|(1<<LED_EXT)|(1<<LED_PO);        //输出驱动LED
   
    MCUSR=0x00;                                        //清除标志位!!!!!!!!!!!!!!!!!!!   放置位置影响程序的结果
    WDT_on();                                         //!!!!!!!!!!!!!!!!!!!!!!!!!!!

    CPU_STATUS = MCUSR;                                //读取MCU控制和状态寄存器
   
    CPU_STATUS &= 0x0F;
    switch (CPU_STATUS)
    {
       case (1<<WDRF):                        //看门狗引起的复位?
        PORTD&=~(1<<LED_WD);
        break;
       case (1<<BORF):                        //BOD引起的复位?
        PORTD&=~(1<<LED_BO);
        break;
       case (1<<EXTRF):                //RESET引脚引起的复位?
        PORTD&=~(1<<LED_EXT);
        break;
       case (1<<PORF):                        //上电引起的复位?
         PORTD&=~(1<<LED_PO);
        break;
       default:                        //多种复位同时发生?
        PORTD=~CPU_STATUS;       
        break;
    }

    while(1)
    {

    }
}

cumtnj 发表于 2009-12-30 11:57:51


--------------------------------------------------------------------------------------------------
6. 在定时器0中断(2ms)喂狗熔丝位:WDTON = 0(硬件看门狗) , 16CK/14CK+65ms

程序:
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/signal.h>
#include <avr/wdt.h>
//时钟为外部晶振7.3728M
/*
wdt.h里面
看门狗溢出时间常量定义
        #define WDTO_15MS   0
        #define WDTO_30MS   1
        #define WDTO_60MS   2
        #define WDTO_120MS3
        #define WDTO_250MS4
        #define WDTO_500MS5
        #define WDTO_1S   6
        #define WDTO_2S   7
        下面的4S/8S定义只能用于 ATtiny2313, ATmega48, ATmega88 and the ATmega168.
        #define WDTO_4S   8
        #define WDTO_8S   9
看门狗操作函数
        wdt_disable()
        关闭看门狗
        wdt_enable(timeout)
        使能看门狗及溢出时间设定
        wdt_reset()
        复位看门狗(喂狗)
*/
//管脚定义

#defineWDT_EN                7        //PD7 看门狗的喂狗控制引脚
                                                // 高电平,不喂狗
                                                // 低电平,喂狗
#defineLED_WD                3        //PD3 看门狗复位指示
#defineLED_BO                2        //PD2 BOD复位指示
#defineLED_EXT    1        //PD1 RESET引脚复位指示
#defineLED_PO                0        //PD0 上电复位指示

#defineWDR()           asm("wdr")   //喂狗,看门狗计数清零,wdr --->是看门狗复位指令
//配置IO口时,以上IO口都设置为:输出高电平,因此以上信号皆为低电平有效
void WDT_on(void)
{
        //置位WDCE 和 WDE
        WDTCSR = (1<<WDCE) |(1<<WDE);
        //设置定时溢出值
        WDTCSR = 0x0f; //WATCHDOG ENABLED - dont forget to issue WDRs使能看门狗不能用 |=,必须要直接赋值=
}
void WDT_off(void)
{
        //MCUSR 中的WDRF清零       
        MCUSR = 0X00;       
        //置位WDCE 和 WDE       
        WDTCSR = (1<<WDCE) |(1<<WDE);
        //关闭WDT
        WDTCSR = 0X00;
}

// actual value:2.000mSec (0.0%)
void Timer0_INIT(void)
{
        TCCR0B = 0x00; //stop
        TCNT0= 0x06; //set count
        TCCR0A = 0x00;
        TCCR0B = 0x03; //start timer ,64分频
        TIMSK0 = 0x01;
}
SIGNAL(SIG_OVERFLOW0)
{
        TCNT0= 0x06;      //0x06定时2ms
        TCCR0B = 0x03;
       
        wdt_reset();                //复位看门狗(喂狗)
}
int main(void)
{
            unsigned char CPU_STATUS;
        cli();
//上电默认DDRx=0x00,PORTx=0x00 输入,无上拉电阻
            PORTB = 0xFF;                                                                        //不用的管脚使能内部上拉电阻。
            PORTC = 0x00;      
            PORTD = 0xFF;
        DDRB= 0x00;
            DDRC= 0x00;
           DDRD= (1<<LED_WD)|(1<<LED_BO)|(1<<LED_EXT)|(1<<LED_PO);        //输出驱动LED
            Timer0_INIT();
            MCUSR = 0x00;                                 //清除标志位
            wdt_enable(WDTO_2S);                //使能看门狗,溢出时间为2秒
            //WDT_on();      
        //WDT_off();
        sei();
       
        //下面这段程序主要是来测试MCUSR里面状态位
        CPU_STATUS = MCUSR;                                //读取MCU控制和状态寄存器
            CPU_STATUS &= 0x0F;
            switch (CPU_STATUS)
            {
                case (1<<WDRF):                        //看门狗引起的复位?
                        PORTD&=~(1<<LED_WD);
                        break;
                case (1<<BORF):                        //BOD引起的复位?
                        PORTD&=~(1<<LED_BO);
                        break;
                case (1<<EXTRF):                  //RESET引脚引起的复位?
                        PORTD&=~(1<<LED_EXT);
                        break;
                case (1<<PORF):                        //上电引起的复位?
                        PORTD&=~(1<<LED_PO);
                        break;
                default:                                  //多种复位同时发生?
                        PORTD=~CPU_STATUS;       
                        break;
            }
       
            while(1)
        {
                    //wdt_reset();                //复位看门狗(喂狗)
                //        WDR();            //喂狗,清零看门狗计数器
            }
}

cumtnj 发表于 2009-12-30 11:58:35

欢迎大家拍砖,有不足的地方请指教,谢谢!

erxun 发表于 2009-12-30 12:05:27

校友啊

breakcelee 发表于 2009-12-30 12:11:26

mark

Roader 发表于 2009-12-30 13:26:04

好铁! 未及细看,先顶!

kokoji1982 发表于 2009-12-30 13:52:42

不错的资料,对我帮助很大,谢谢!!!

xyq4513 发表于 2009-12-30 13:58:21

Mark

cumtnj 发表于 2009-12-30 19:18:30

以前学习的时候有点匆忙,所以好多地方没有深入学习,只有在去做项目的时候才深入研究,唉,真希望时间可以从来,呵呵感慨一下!希望大家都交流一下,帮助我把看门狗这一块完善,真正的深入进去研究很有意思的!

ankjin 发表于 2009-12-30 21:20:01

mark

astudent 发表于 2009-12-31 09:17:25

关注

cumtnj 发表于 2010-1-1 14:13:47

come on! 来点小补充,呵呵
---------------------------------------------------------------------------------------------
7.补充:仔细看Datasheet,一定要读英文的!!!!!!!!!

下面是一个网友的帖子:

【9楼】 sharpufo Dota Star

积分:364
派别:
等级:------
来自:
上电复位后的WDRF是零。而看门狗溢出后WDRF置位,引起热复位后,溢出标志WDRF是不会自动清零的,初始化时不清零WDRF的话将会引起不断复位。把看门狗的初始化语句放在闪灯之前,同时先清WDRF,WDE。
见datasheet:
“the device will be reset and the Watchdog Timer will stay enabled.
If the code is not set up to handle the Watchdog, this might lead to an eternal loop of
time-out resets. To avoid this situation, the application software should always clear the
Watchdog System Reset Flag (WDRF) and the WDE control bit in the initialisation routine”
出问题时最好能先好好读一下datasheet。

结论:遇到问题先从自己找原因,通过自己不断的读Datasheet来解决问题,而且一定要读官方英文的Datasheet!

jomin 发表于 2010-7-20 20:21:55

救命稻草啊非常感谢!

jiege0119 发表于 2010-7-20 20:46:53

记下

amwygah 发表于 2010-10-4 01:48:41

MARK

guxingganyue 发表于 2010-11-1 21:02:45

mark,写的很好,收藏了

骚黄 发表于 2014-3-31 23:56:06

M8换M168也遇到奇怪问题了,看门狗导致的复位重启后,还没执行到初始化看门狗的地方就狂重启的问题已经搞定了。目前遇到了配置溢出时间不对的问题,有可能就是因为使用了“|=”的问题,明天回单位看看。。。{:lol:}

xjtyOnly51 发表于 2014-4-2 09:32:58

thanks a lot
页: [1]
查看完整版本: Mega48的训狗心得