Attiny85 Watchdog срабатывает только один раз

Я делаю что-то вроде фонарика с разными модами. Судя по аппаратному сайту, он очень похож на Nanjg105c. Существует Attiny85, который генерирует ШИМ-сигнал для AMC7135.

У меня есть одна кнопка, если я нажму на нее, Аттини заснет, если я удержу кнопку, Аттини изменит свой режим. Для проверки напряжения батареи я использую сторожевой таймер, который выполняет преобразование АЦП каждые 8 секунд.

    #include "OneButton.h"
#include <avr/sleep.h>               // Спящие режимы
#include <avr/power.h>              // Управление энергопотреблением
#include <avr/wdt.h>              // Сторожевой таймер
#include <util/delay.h>

#define mid 4000                // Средний уровень заряда батареи
#define low 3200                // Низкий уровень заряда батареи, мигание и затемнение светодиода
#define crit 2600               // Критический уровень заряда батареи, выключение


const byte LEDr = 1;              // контакт 1
const byte LEDw = 4;              // контакт 4
const byte ModeNr = 3;
volatile bool WDR_flag = false;
bool PWR_flag = false;

OneButton button1(2, true);
int count = 1;
int flag = 0;
long Vcc;
int PWMr = 255;
int PWMw_l = 50;
int PWMw_h = 255;

ISR(PCINT0_vect)
{
    // делаем здесь что-нибудь интересное
}

ISR(WDT_vect){
  WDR_flag=true;
 }

void setup()
{
    pinMode(3, OUTPUT);
    pinMode(LEDr, OUTPUT);            // включаем светодиод (HIGH - уровень напряжения)
    pinMode(LEDw, OUTPUT);
    digitalWrite(LEDr, HIGH);           // включаем светодиод (HIGH - уровень напряжения)
    digitalWrite(LEDw, HIGH);
    delay(1000);
    button1.attachClick(sleep);
    button1.attachLongPressStart(changeMode);
    WDT_on();
}

void loop()
{
  uint8_t i = 0;
    button1.tick();               // продолжаем следить за кнопками:
  if(WDR_flag == true){           // делаем каждые 8 секунд
    WDR_flag = false;           //сбросить флаг WDT
    digitalWrite(3, HIGH);      // отладка с третьим светодиодом
    delay(250);
    digitalWrite(3, LOW);
    WDT_off();
    //ADC_on();
    /*Vcc = readVcc();            //чтение напряжения батареи
    if(Vcc < low && count == 3){
      if(PWMw_h <= PWMw_l){       // изменить режим на низкий, когда выход ШИМ меньше, чем низкий ШИМ
        count = 2;
      }else
    {
    i = 0;
        while (i++<3 && PWR_flag == false) {         //мигаем 3 раза красным светодиодом перед затемнением белого светодиода
          analogWrite(LEDr, PWMr);
          _delay_ms(500);
          digitalWrite(LEDr, LOW);
          _delay_ms(250);
      PWR_flag = true;
        }     
      PWMw_h = PWMw_h / 2;        // уменьшите мощность ШИМ белого светодиода вдвое
      }
      if(Vcc <= crit){          // переходим в спящий режим, когда батарея разряжена
        sleep();
      }
    }
    if(Vcc < low && count == 1){
      i = 0;
      while (i++<3) {
        digitalWrite(LEDr, LOW);
        _delay_ms(250);
        analogWrite(LEDr, PWMr);
        _delay_ms(500);
        }
      PWMr = PWMr / 2;
      if(Vcc <= crit){
        sleep();  
      }
    }*/
    //ADC_off(); //экономим энергию
    WDT_on;                 // снова запускаем сторожевой таймер

  }
  switch (count) {
    case 1:                 //режим красного светодиода
    analogWrite(LEDr, PWMr);
    digitalWrite(LEDw, LOW);
    break;
  case 2:                   //низкий режим белого светодиода
    digitalWrite(LEDr, LOW);
    analogWrite(LEDw, PWMw_l);
    break;
  case 3:                   // белый светодиод высокого режима
    digitalWrite(LEDr, LOW);
    analogWrite(LEDw, PWMw_h);
    break;
  }
}



void sleep()                  // Эта функция будет вызвана, когда кнопка 1 была нажата 1 раз
{
    digitalWrite(LEDr, LOW);
    digitalWrite(LEDw, LOW);
    //готовим сон
    WDT_off();
    GIFR |= bit(PCIF);              // очищаем все необработанные прерывания
    GIMSK |= _BV(PCIE);             // Включить прерывания смены контакта
    PCMSK |= _BV(PCINT2);             // Использовать PB2 в качестве вывода прерывания
    ADC_off();
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);    // заменяет указанное выше выражение

    sleep_enable();               // Устанавливает бит разрешения спящего режима в регистре MCUCR (SE BIT)
    sei();                    // Разрешить прерывания
    sleep_cpu();                // спать

    cli();                    // Отключить прерывания
    PCMSK &= ~_BV(PCINT2);            // Отключаем PB2 как вывод прерывания
    sleep_disable();              // Очистить бит SE
    WDT_on();

    flag = 1;                 //устанавливаем флаг, uc до этого был в спящем режиме
} 

void changeMode()               // Эта функция будет вызываться один раз при длительном нажатии.
{
    if (flag == 1) {
        flag = 0;
    }
    else {
        if (count < ModeNr) {
            count++;
        }
        else {
            count = 1;
        }
    }
}

void WDT_on() {
  // Настраиваем сторожевой таймер только на прерывание, а не на сброс
  cli();                    // Отключить прерывания
  wdt_reset();                // Сброс WDT
  WDTCR |= (1 << WDCE) | (1 << WDE);       // Запустить временную последовательность
  WDTCR = (1 << WDIE) | (1 << WDP3) | (1<<WDP0);      // Цикл сторожевого таймера = 8 с
  //WDTCR = (1 <<WDIE); // Включение сторожевого прерывания
  sei();                    // Разрешить прерывания
}

void WDT_off(){
  cli();                    // Отключить прерывания
  wdt_reset();                // Сброс WDT
  MCUSR &= ~(1<<WDRF);            // Очистить флаг сброса сторожевого таймера
  WDTCR |= (1<<WDCE) | (1<<WDE);        // Запустить временную последовательность
  WDTCR = 0x00;               // Отключить WDT
  sei();                    // Разрешить прерывания
}

void ADC_on() {
  ADCSRA = (1 << ADEN );            // питание АЦП включено
}

void ADC_off() {
  ADCSRA &= ~(1<<7);              // АЦП выключен
}

long readVcc() {
  // Чтение ссылки 1,1 В относительно AVcc
  // установить ссылку на Vcc и измерение на внутреннюю ссылку 1,1 В

  ADMUX = _BV(MUX3) | _BV(MUX2);
  delay(2);                  // Подождите, пока Vref установится
  ADCSRA |= _BV(ADSC);              // Начать преобразование
  while (bit_is_set(ADCSRA,ADSC));        // измерение

  uint8_t low_adc  = ADCL;              // сначала нужно прочитать ADCL - затем он блокирует ADCH
  uint8_t high_adc = ADCH;              // разблокирует оба

  long result = (high_adc<<8) | low_adc;

  result = 1126400L / result;           // Рассчитать Vcc (в мВ); 1126400 = 1,1*1024*1000
  int x = 0;
  /*while (x++<2) {         //отладка с третьим светодиодом
          digitalWrite(3, HIGH);
          _delay_ms(500);
          digitalWrite(3, LOW);
          _delay_ms(250);
        } */  
  return result;                // Vcc в милливольтах
}

Моя проблема в том, что сторожевой таймер срабатывает только один раз. после усыпления attiny сторожевой таймер также срабатывает только один раз.

Я думаю, проблема в функции WDT_off()...

, 👍1


1 ответ


2

В приведенной ниже строке отсутствуют скобки (), что делает ее вызовом функции; на самом деле он не вызывает WDT_on.

WDT_on;                 // снова запускаем сторожевой таймер

должно быть:

WDT_on();                 // снова запускаем сторожевой таймер
,