Сон на AVR

У меня на pro mini (все лишнее выпаяно) с внутренним генератором и делителем на 2 при питании от двух АА станция живет немногим больше года (13-14 месяцев). Просыпается раз в минуту по watchdog (на самом деле там за минуту несколько раз просыпается и засыпает, интервалы вычисляются, привязка к стандартным значениям времени сна watchdog), измеряет и кидает данные в nrf24 (без подтверждения приема). Вся периферия глушится на время сна, пины на вход с подтяжкой. Питание датчиков коммутируется p-канальным мосфетом. Во сне 7-8 мкА (если правильно помню, но точно меньше 10 мкА).

Управление полевиком землёй или через биполярник? Интересно где потери меньше: при удержании высокого уровня во сне или физической подтяжки на транзисторе? Математически даже 100кОм проигрывают вроде?

Землей включаю. Полевик IRLML6401. Подтянут к питанию резистором порядка 10-51кОм (не смог схему найти, видимо на старом винте, но лень его подключать).

При нуле не разбудит из Power down.

Вот тут я был не прав. Сброс по WDT не ведёт к ресету МК(речь про Power down) - он продолжает работать с той точки, где заснул. Это очень удобно, т.к. можно спать сколько угодно времени(в разумных пределах конечно)).
А если нужен непрерывный сон - цикл while(1) в помощь

Спойлер
#include <util/delay.h>

#define Power PB0
#define sleep_dis()  MCUCR &= ~(1 << SE)
#define sleep_en()   MCUCR |= (1 << SE) 
#define wdt_res()    asm volatile("wdr\n\r")
#define sleep_CPU()  asm volatile("sleep\n\r")


void Res()
{
  PORTB |= (1 << Power);
  _delay_ms(250);
  PORTB &= ~(1 << Power);
  _delay_ms(250);
}

void Vkl()
{
  PORTB |= (1 << Power);
  _delay_ms(50);
  PORTB &= ~(1 << Power);
  _delay_ms(50);
}

ISR (WDT_OVERFLOW_vect) // watchdog interrupt
{
  Serial.print("q");
} 

 
void setup() {
  Serial.begin(9600);
  Serial.print("z");
  DDRB |= (1 << Power);
  Res();
  
  //===настройка WDT ==============
  WDTCSR |= (1<<WDCE) | (1<<WDE);//включаем сброс по wdt
  WDTCSR = (1 << WDP0) | (1 << WDP3) | (1 << WDIE);//уст. делитель на 8сек и вкл. прерывание от wdt
  
 //==== настройка сна ==============
  MCUCR |= (1 << SM0);//SM0=1;  режим Power down, 
}

void sleep8sec()
{
 cli();
  sleep_en();
  sei();
  sleep_CPU();
  sleep_dis(); 
}

void loop() {

  Vkl();
  wdt_res();
  for(uint8_t i = 0; i < 3; i++)//спим 3 раза по 8сек
  sleep8sec();
}

P.S. Для Тини 2313

Ха! А я то думал куда WDT_OVERFLOW затолкать, ведь в таблице векторов в даташите одиночного WDT тупо нет. Заменил в своём исправленном - работает как дОлжно.
Спасибо за скетч и уделённое время. Бум ковырять дальше.

#include <avr/sleep.h>
#include <avr/io.h>
#include <util/delay.h>
#include <avr/wdt.h>

#define Power PB0

void Res()
{
  PORTB |= (1 << Power);
  _delay_ms(250);
  PORTB &= ~(1 << Power);
  _delay_ms(250);
}

void Vkl()
{
  PORTB |= (1 << Power);
  _delay_ms(50);
  PORTB &= ~(1 << Power);
  _delay_ms(50);
}

ISR (WDT_OVERFLOW_vect) // watchdog interrupt
{
  wdt_disable();  // disable watchdog
}  // end of WDT_vect

void setup() {
  DDRB |= (1 << Power);
  Res();
}

void loop() {

  Vkl();

  MCUSR = 0;   // clear various "reset" flags
  WDTCR = (0 << WDIF) | (0 << WDIE) | (1 << WDP3) | (1 << WDCE) | (0 << WDE) | (0 << WDP2) | (0 << WDP1) | (1 << WDP0);
  WDTCR = (1 << WDIF) | (1 << WDIE) | (1 << WDP3) | (0 << WDCE) | (0 << WDE) | (0 << WDP2) | (0 << WDP1) | (1 << WDP0);
  wdt_reset();  // pat the dog

  set_sleep_mode (SLEEP_MODE_PWR_DOWN);
  noInterrupts ();   // timed sequence follows
  sleep_enable();
  interrupts ();             // guarantees next instruction executed
  sleep_cpu ();
  sleep_disable();   // cancel sleep as a precaution

}

Заманался искать чтение пина с ентими кракозябрами. Хоть миллис в этом аддоне работает, ничего изобретать не пришлось. Типа всё получилось, но интересует вопрос: Не проще ли(надёжней, правильней) было бы оставить делеи, а порт читать через прерывание? И гарантированно достучится и есть выбор из нескольких типов срабатывания!?

//#include <util/delay.h>

#define Power PB0
#define Done  PB1
#define sleep_dis()  MCUCR &= ~(1 << SE)
#define sleep_en()   MCUCR |= (1 << SE)
#define wdt_res()    asm volatile("wdr\n\r")
#define sleep_CPU()  asm volatile("sleep\n\r")
const uint32_t interval = 2000;

ISR (WDT_OVERFLOW_vect) // watchdog interrupt
{
}

void setup() {

  DDRB |= (1 << Power);

  //===настройка WDT ==============
  WDTCSR |= (1 << WDCE) | (1 << WDE); //включаем сброс по wdt
  WDTCSR = (1 << WDP0) | (1 << WDP3) | (1 << WDIE);//уст. делитель на 8сек и вкл. прерывание от wdt

  //==== настройка сна ==============
  MCUCR |= (1 << SM0);//SM0=1;  режим Power down,
}

void sleep8sec()
{
  cli();
  sleep_en();
  sei();
  sleep_CPU();
  sleep_dis();
}

void loop() {

  PORTB |= (1 << Power);

  static  uint32_t previousMillis = 0;
  uint32_t currentMillis = millis();

  if ((currentMillis - previousMillis >= interval) || ((PINB & 1 << Done))) {
    previousMillis = currentMillis;
    PORTB &= ~(1 << Power);

    wdt_res();
    for (uint8_t i = 0; i < 1; i++) //спим 3 раза по 8сек
      sleep8sec();
  }
}

Не знаю, у меня аддон ATTinyCore, всё норм. работает.

Спойлер

Конечно через прерывание, только INT0 надо , вроде другие в Повер дауне на 2313 -ой не пашут

Енто аддон от чувака “ЧУВААААК”(с) Засада в том, что обычные выражения ардуины компилируются без ошибок, но нифига не работают. :slightly_smiling_face:
Там ещё прикол есть с выбором частоты: типа есть настройка на внутренний 1МГц(чего нет в даташите), а реально зашивается 8МГц с делителем на восемь и, соответственно, жрёть больше чем при на честных четырёх.

Вот, не зря написал вроде. Т.к. был не уверен. Подкинул в Протеусе - PCINT тоже работают, (по крайней мере в симуляторе). Но(!) , они приводят к ресету МК. Так что - смотри сам, что тебе нужно, хозяин-барин))
INT0 тоже, может к ресету приведут(не проверял)