chenxp99 发表于 2014-4-10 10:54:49

请问深入浅出中的实例7 如何改WDT唤醒呢

中断唤醒的键盘扫描

用WDT的中断模式来唤醒powerdown,但WDT这部分的设置始终搞不好,AVR132的文章看的迷糊,能否给出一个AVR STUDIO 的中断设置以及返回休眠的例子呢

chenxp99 发表于 2014-4-10 15:03:13

用WDT中断来处理定时唤醒只是一个应用思路,直接作为键盘扫描并不合适。WDT没有在书中介绍,太遗憾了

Gorgon_Meducer 发表于 2014-4-11 10:28:38

chenxp99 发表于 2014-4-10 15:03
用WDT中断来处理定时唤醒只是一个应用思路,直接作为键盘扫描并不合适。WDT没有在书中介绍,太遗憾了 ...

将WDT配置为中断模式(只产生中断,不产生复位动作),然后写一个中断处理程序,这个程序里面什么都不用做,
简单说就是用这个WDT中断唤醒下系统而已。

chenxp99 发表于 2014-4-11 11:47:07

本帖最后由 chenxp99 于 2014-4-11 11:59 编辑

Gorgon_Meducer 发表于 2014-4-11 10:28
将WDT配置为中断模式(只产生中断,不产生复位动作),然后写一个中断处理程序,这个程序里面什么都不用 ...

下面的设置代码,运行后完全没有反应,哪里出错了呢

ISR(WDT_vect) {
        if (lstate)
        {
                PORTC |= (1<<PC5);
                lstate = 0;
        }
        else
        {
                PORTC &= ~(1<<PC5);
                lstate = 1;
        }
}

main
      ......
        lstate=1;

        cli();
        wdt_reset();
        WDTCSR |= (1<<WDCE)|(1<<WDE);
        WDTCSR |= (1<<WDIE)|(0<<WDE)|(1<<WDP3)|(0<<WDP2)|(0<<WDP1)|(1<<WDP0);        //8s interrupt mode
        sei();

Gorgon_Meducer 发表于 2014-4-11 11:52:48

本帖最后由 Gorgon_Meducer 于 2014-4-11 12:00 编辑

chenxp99 发表于 2014-4-11 11:47
下面的设置代码,运行后完全没有反应,哪里出错了呢

芯片型号?你的WDT寄存器操作时序有问题,我记得WDT操作是有严格时序的。你这样用与或操作多半是
没法满足要求的。

这里是我以前写的一个WDT初始化代码的例子——没有初始化成中断模式,但是你可以修改下:
你要特别注意代码的注释——我从PDF上抄下来的。特别说明下,这里SAFE_ATOM_CODE是处理关中断开中断的,
以保证括号里面的代码执行的时候不会被中断处理程序打断。


void Enable_Watchdog(void)
{
    SAFE_ATOM_CODE(
      //! In the same operation, write a logic one to WDCE and WDE.
      WDTCR = BIT(WDCE) | BIT(WDE);
      /*! Within the next four clock cycles, in the same operation,
         *write the WDE and WDP bits asdesired, but with the WDCE bit cleared.
         */
      WDTCR = BIT(WDE) | (0x06 << WDP0);//! 0.24s(3.0v) 0.22s(5.0v)
    );
}


你要注意到,代码里面对看门狗寄存器的操作都是直接的写操作,并且有严格的顺序
1、同时对WDCE和WDE位置位
2、在步骤1之后的4个时钟周期内,同时置位WDE以及对应的WDP设置。

你的代码用了与或操作,也没有做中断保护,根本不可能保证上面的时序要求。另外特别说明下,对于某些编译器
比如GCC,即便你代码写成我这样,不开优化,生成的代码会非常笨,仍然会超过4个时钟周期的要求。

chenxp99 发表于 2014-4-11 11:58:04

无效
        cli();
        wdt_reset();
        WDTCSR = (1<<WDCE)|(1<<WDE);
        WDTCSR = (1<<WDIE)|(1<<WDCE)|(0<<WDE)|(1<<WDP3)|(0<<WDP2)|(0<<WDP1)|(1<<WDP0);
        sei();
闪烁频率不对
        cli();
        wdt_reset();
        WDTCSR = (1<<WDCE)|(1<<WDE);
        WDTCSR = (1<<WDIE)|(1<<WDCE)|(0<<WDE)|(1<<WDP3)|(0<<WDP2)|(0<<WDP1)|(1<<WDP0);
        sei();

Gorgon_Meducer 发表于 2014-4-11 12:00:19

本帖最后由 Gorgon_Meducer 于 2014-4-11 12:02 编辑

chenxp99 发表于 2014-4-11 11:58
无效

闪烁频率不对

看我前面的回复,你的操作时序是错的,你没有认真看数据手册。
频率不对是你没有正确的配置WDT的溢出时间参数,另外AVR的WDT的时间精度是+/-30%到+/-50%。手册上给出的时间只是参考,完全不当真

chenxp99 发表于 2014-4-11 12:00:56

Gorgon_Meducer 发表于 2014-4-11 11:52
芯片型号?你的WDT寄存器操作时序有问题,我记得WDT操作是有严格时序的。你这样用与或操作多半是
没法满 ...

芯片是LGT8F880A

Gorgon_Meducer 发表于 2014-4-11 12:03:46

chenxp99 发表于 2014-4-11 12:00
芯片是LGT8F880A

那你发错论坛了,亲,看门狗频率的问题,你要去问LGT,我这里看到的手册是参考AVR的,当然,他们号称是兼容的。

chenxp99 发表于 2014-4-11 12:51:37

本帖最后由 chenxp99 于 2014-4-11 13:12 编辑

Gorgon_Meducer 发表于 2014-4-11 12:03
那你发错论坛了,亲,看门狗频率的问题,你要去问LGT,我这里看到的手册是参考AVR的,当然,他们号称是兼 ...

AVR的WDT的时间精度是+/-30%到+/-50%,这个精度也太夸张了。。。

那边超冷清,没人解答这么低级的问题,网上看到以前也有人有同样的问题,但都没结果。老外倒是有结果,误差蛮大的。谢谢傻孩子老师哈

WDT设置的溢出时间是8s,实际闪烁频率基本是1Hz,相当于实际溢出时间是0.5s,WDT的时钟误差不会这么大吧,会否还有别的问题存在呢

Gorgon_Meducer 发表于 2014-4-11 13:05:44

chenxp99 发表于 2014-4-11 12:51
AVR的WDT的时间精度是+/-30%到+/-50%,这个精度也太夸张了。。。

那边超冷清,没人解答这么低级的问题 ...

一般WDT都有自己专用的RC振荡器,我们叫做Internal RC Oscillator,这个振荡器目的就是给WDT提供一个
时钟,不需要精度高,所以一般也没有人特别去校准它,就算校准了,它的温票也很厉害,需要根据当前实际
温度来实时的校准……的确遇到过有项目需要用内部WDT的OSC来做的,我做过的……痛苦死了……

总之,对你有帮助就好。

chenxp99 发表于 2014-9-3 21:28:50

Gorgon_Meducer 发表于 2014-4-11 10:28
将WDT配置为中断模式(只产生中断,不产生复位动作),然后写一个中断处理程序,这个程序里面什么都不用 ...

我在使用看门狗的中断模式来做长时段延时唤醒时,遇到一个现象,第一次可以正常唤醒(端口灯有闪),但继续休眠后再无法唤醒。WDT中断服务只有一句" i++;"i 已加volatile
        while (i<delay1)        //delay1
        {
                cli();
                set_sleep_mode(SLEEP_MODE_PWR_DOWN);
                sleep_enable();
                sei();
                sleep_cpu();
          }
当处于唤醒状态时,WDT定时中断服务能够正常工作。后来将WDT中断服务里的 i++ 移至 主程序循环中才解决了重复休眠唤醒问题,但是为什么呢

        while (i<delay1)        //delay1
        {
                cli();
                set_sleep_mode(SLEEP_MODE_PWR_DOWN);
                sleep_enable();
                sei();
                sleep_cpu();
                i++;
    }

也曾尝试在sleep_cpu()后面加入 延时200ms ,让全局变量 i 的(中断)修改和(主程序)读取操作不要挨的太近, 但没有效果(低主频)。猜测中断服务对变量的修改并没有被主程序读到,该如何解决呢

Gorgon_Meducer 发表于 2014-9-4 23:33:49

chenxp99 发表于 2014-9-3 21:28
我在使用看门狗的中断模式来做长时段延时唤醒时,遇到一个现象,第一次可以正常唤醒(端口灯有闪),但继 ...

没弄懂你的问题,你的问题究竟是休眠后无法唤醒还是重复唤醒?
请给出一个例子代码,描述期望现象,实际现象。

chenxp99 发表于 2014-9-5 12:37:25

Gorgon_Meducer 发表于 2014-9-4 23:33
没弄懂你的问题,你的问题究竟是休眠后无法唤醒还是重复唤醒?
请给出一个例子代码,描述期望现象,实际 ...

希望重复唤醒

      volatile i;
      const delay1=1000;
      isr(wdt_vect)
      { i++;}

      main()
      {
            while (i<delay1)      //delay1
            {
                cli();
                set_sleep_mode(SLEEP_MODE_PWR_DOWN);
                sleep_enable();
                sei();
                sleep_cpu();
             }
      }

实际运行只唤醒了第一次

Gorgon_Meducer 发表于 2014-9-5 12:56:01

chenxp99 发表于 2014-9-5 12:37
希望重复唤醒

      volatile i;


在那个while里面toggle IO而不是用变量I,借助示波器来检查是否休眠了多次。

chenxp99 发表于 2014-9-5 13:30:40

Gorgon_Meducer 发表于 2014-9-5 12:56
在那个while里面toggle IO而不是用变量I,借助示波器来检查是否休眠了多次。 ...

手头没示波器,我用PORT点灯看过,只亮了一次

Gorgon_Meducer 发表于 2014-9-5 20:19:31

chenxp99 发表于 2014-9-5 13:30
手头没示波器,我用PORT点灯看过,只亮了一次

toggle,不是单纯的置位或者清零,你可以进入前让灯保持亮的状态,然后休眠醒来后的toggle会
熄灭灯,如果灯一直是熄灭的,则可以判定是只进入了一次。
页: [1]
查看完整版本: 请问深入浅出中的实例7 如何改WDT唤醒呢