Attiny85 сон в PWR_DOWN и подъем по INT0

K2K11

✩✩✩✩✩✩✩
27 Май 2021
3
0
Добрый день. Прошу помощи. Не могу разбудить Attiny85 по INT0.
Суть задачи: Attiny85 асинхронно мигает несколькими диодами заданное количество раз. После каждого цикла уменьшается счётчик, и при достижении нуля Attiny засыпает. Проснуться она должна по высокому уровню на PB2 (INT0) от ПИР датчика AM312. Засыпать то засыпает, а вот обратно не просыпается. Подскажите где ошибка?
код PWR_DWN для Attiny85:
#include <avr/sleep.h>

int ledPin = 1;            // LED
int interruptPin = 2;      // пин INT0
volatile byte  cikl;       // счётчик цикла

void setup(){
  pinMode(ledPin, OUTPUT);         // пин с LED
  pinMode(interruptPin, INPUT);    //  пин прерывания, подтянут к земле на AM312 и меняется на высокий при наличии движения
  digitalWrite(ledPin, LOW);
  cikl = 3;                              // стартовое число циклов
}
void sleepNow(){   
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);   // режим PWR_DOWN
    sleep_enable();                        // разрешаем сон
    attachInterrupt(digitalPinToInterrupt(interruptPin), interruptFunc, RISING); // при изменении сигнала с LOW на HIGH         
    sleep_mode();                          // засыпаем
}

void interruptFunc (){ 
   //РОТА ПОДЪЕМ!!!
    cikl = random (7,20);                  // псевдо random назначение кол-ва доп циклов
  }

void loop(){
  // по кругу исполняется длинный цикл  пока счётчик "cikl" > 0
  if (cikl>0)  {cikl--;                        // имитация цикла
                digitalWrite(ledPin, HIGH);
                delay(300);
                digitalWrite(ledPin, LOW);
                delay(300);
                }
  if (cikl==0){ sleepNow(); }                    // засыпаем до прерывания
}
 

Bruzzer

★★★✩✩✩✩
23 Май 2020
501
149
@K2K11,
По даташиту просыпание из глубокого сна для INT0 возможно только по уровню (LOW). Но с другой стороны, для 328 тоже написано, что только по LOW а реально работает и по фронту. Попробуйте сначала по LOW, потом на работающем примере попробуйте поменять на смену сигнала.
Вообще при использовании сна (особенно PWR_DOWN ) желательно ознакомиться с даташитом на МК, т.к. есть особенности. Например надо учитывать, что функция прерывания может не вызваться при короткой длительности сигнала. А длительность пробуждения зависит от настроек тактовой частоты и т.д.

Пример из сообщения Геннадий П на первый взгляд не совсем корректный, т.к. не учитывает возможность пропуска вызова функции прерывания при достаточно коротком LOW. И при прерывании по уровню, надо запрещать прерывание в функции прерывания.
 
  • Аррр! -2
Реакции: ТехнарьКто

K2K11

✩✩✩✩✩✩✩
27 Май 2021
3
0
Спасибо за наводку. Капельки слились в мааааленький ручеёк. Курение datasheet-а с примерами из форумов даёт результат. Теперь могу оставить свою подсказку для последователей.

Attiny85 сон и подъем по внешнему прерыванию исправленный:
#include <avr/sleep.h>

#define ledPin  1            // LED
#define ledPinWake  0        // LED указывающий на срабатывание прерывания
#define interruptPin  3      // пин прерывания
volatile byte  cikl;         // счётчик цикла*
// *-переменные указываемые в обработчкие прерывания должны быть "volatile"

void setup(){
  pinMode(ledPin, OUTPUT);         // пин с LED
  pinMode(ledPinWake, OUTPUT);     // пин с LED прерывания
  pinMode(interruptPin, INPUT);    //  пин прерывания, подтянут к земле на AM312 и меняется на высокий при наличии движения
  digitalWrite(ledPin, LOW);
  digitalWrite(ledPinWake, LOW);
  cikl = 2;                               // стартовое число циклов
}
void sleepNow(){ 
  GIMSK |= _BV(PCIE);                     // включить прерывание смены pin
  PCMSK |= _BV(PCINT3);                   // прерывание включено для PCINT3 (PB3),но  можно назначить любую ножку PCINT0....PCINT5

  MCUCR |=_BV(1<<ISC01);                  // срабатывание при 0->1
  MCUCR |=_BV(1<<ISC00);                  // комбинация ISC01 ISC00 назначается из таблицы 11-1 стр 51 datasheet

  ADCSRA &= ~_BV(ADEN);                   // ADC off
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);    // выбор режима сна

  sleep_enable();                         // разрешаем сон
  sei();                                  // или interrupts() повторно включает глобальные прерывания

  sleep_cpu();                            // sleep

  cli();                                  // или noInterrupts() отключает бит флага глобальных прерываний.
  sleep_disable();                        // запретить прерывания
  ADCSRA |= _BV(ADEN);                    // ADC on

  sei();                                  // включает глобальные прерывания

}

void loop(){
  // по кругу исполняется длинный цикл  пока счётчик "cikl" > 0
  if (cikl>0)  {cikl--;                        // имитация цикла
                digitalWrite(ledPin, HIGH);
                delay(500);
                digitalWrite(ledPin, LOW);
                delay(500);
                }
  if (cikl==0){ sleepNow(); }                    // засыпаем до прерывания
}
ISR(PCINT0_vect) { // обработка прерывания. Да, мы указывали PB3, но это была переадресация ножки. Отработает тут
  cikl = random (4,10);            // назначаем новое число циклов
  digitalWrite(ledPinWake, HIGH);  // LED указываем на срабатывание прерывания
  //delay вроде как в прерываниях не работает, потому мигнем моментально (всё равно видно)
  digitalWrite(ledPinWake, LOW);}
И ещё момент:
  • PCINT0 и INT0 разные вещи!. INT0 срабатывает только по низкому уровню.
  • PCINT может работать по любому условию назначаемому в бите MCUCR (MCUCR назначается из таблицы 11-1 стр 51 datasheet)
 
Изменено:

Bruzzer

★★★✩✩✩✩
23 Май 2020
501
149
@K2K11,
Несколько замечаний. (для вашей программы не существенных).
в строке 28 есть sei(); но нет предыдущего cli();
Настройки делаются каждый раз при вызове void sleepNow(), т.к. они одни и те же, то можно (но не обязательно) вынести их в Setup.
Комментарий в строке 50 про "переадресация ножки" не сразу понятен. (для Attiny85 все прерывания PCINT отрабатываются в PCINT0)
 
  • Аррр! -2
Реакции: ТехнарьКто