ATMEGA 8 спящий режим и TIMER 2

gzp13
Offline
Зарегистрирован: 06.04.2015

Здравствуйте, не могу разобраться с TIMER2. Загоняю атмегу в сон(режим POWER SAVE), от внешнего прерывания она просыпается, а вот от счетчика таймера TIMER2 ни в какую. Посмотрите пожалуйста код, что не так.


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

#define F_CPU 1000000UL

byte flag = 0;
byte intFlag = 0;
#define ButtonOne           2
#define ButtonTwo           3

volatile int state = LOW;

//**********************************************************************
void flash() {
  static boolean output = HIGH;

  digitalWrite(12, output);
  output = !output;
}

void setup(){
  //Save Power by writing all Digital IO LOW - note that pins just need to be tied one way or another, do not damage devices!
  for (int i = 0; i < 20; i++) {
    if (i != ButtonOne || i != ButtonTwo  ) {
      pinMode(i, OUTPUT);
      digitalWrite(i, LOW);
    }
  }
  pinMode(12, OUTPUT);
  MsTimer2::set(500, flash); // 500ms period
  MsTimer2::start();

  pinMode(13, OUTPUT);
  digitalWrite(13, LOW);

  pinMode(ButtonOne, INPUT_PULLUP);
}


  //************************************************************************

  void loop(){
    
      if (intFlag==1)
      {     
      state = !state;    
      intFlag=0;  

      }
      // тут код который должен выполняться при пробуждении ( можно обработать с какой именно кнопки нажата и сделать обработку для каждой кнопки свое действие )
      digitalWrite(13, state);
   
      ADCSRA = 0;      
      set_sleep_mode (SLEEP_MODE_PWR_SAVE);

      sleep_enable();
      attachInterrupt(digitalPinToInterrupt(ButtonOne), sleepISR1, LOW);  // LOW    
      interrupts();
      sleep_cpu();
      }  

    void sleepISR1() {  
    // Запретить спящий режим, чтобы мы больше не заходили в него, кроме как намеренно, по коду
    sleep_disable();  //проснулись
    // код или действие при пробуждении по этому прерыванию
    if (digitalRead(ButtonOne)==LOW && flag==0 )
    _delay_ms(200);
    {    
    intFlag=1;
    flag=1;
    }
    if (digitalRead(ButtonOne)==HIGH && flag==1 )
    {
    _delay_ms(50);
    flag=0;                     //снова заснули
    }
        detachInterrupt(digitalPinToInterrupt(ButtonOne));

    // Теперь мы продолжаем запуск основной функции Loop () сразу после того, как мы пошли спать
  
}

 

Green
Offline
Зарегистрирован: 01.10.2015

А разве должно? Таймер 2 только в асинхронном режиме может будить.
 

Kakmyc
Offline
Зарегистрирован: 15.01.2018

Не тот режим .
Нужен режим Idle, а включаешь Power save

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Kakmyc пишет:
Нужен режим Idle, а включаешь Power save
Почему? Ему нужен именно Power Save

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

gzp13,

Посмотрите даташит. Там (стр. 54) явно сказано, что таймер 2 будит из Power Save только в асинхронном режимет, т.е. когда в ASSR установлен бит AS2. А Вы этого не делаете.

Собственно, Вам это уже сказали в #1

Green
Offline
Зарегистрирован: 01.10.2015

- Вы будите утром свою жену?
- Конечно буду!
Какие то нездоровые ассоциации.))

gzp13
Offline
Зарегистрирован: 06.04.2015

Спасибо за подсказки, но я так понимаю ассинронный режим это работа только от внешнего кварца? И этот режим включается ASSR =(1<< AS2) ?

Green
Offline
Зарегистрирован: 01.10.2015

Обычно часовой кварц 32 кгц, даже конденсаторы не нужны. Только перед этим нужно переключиться на intrc.

gzp13
Offline
Зарегистрирован: 06.04.2015

Не очень понял куда переключиться. Тогда мне надо выставлять фьюзы на работу от внешнего кварца?

gzp13
Offline
Зарегистрирован: 06.04.2015

Хорошо,не получится как я понял мне задействовать режим POWER SAVE,  а можно тогда применить режим POWER DOWN с WDT, чтобы WDT срабатывал через 2 часа и выполнялись какие то действия,потом снова засыпал. Пробую включить WDT что то не получается.

wdt_enable(WDTO_1S); //включаем сторожевой таймер со сбросом через 1 секунды

WDTCR |= (1<<WDCE) | (1<<WDE);  //разрешение прерываний от сторожевого таймера

ISR(WDT_vect) //подпрограмма обработки прерывания от сторожевого таймера
{
  state = !state;
digitalWrite(13, state);
wdt_reset(); //сброс сторожевого таймера
}
Green
Offline
Зарегистрирован: 01.10.2015

В Мега8 нет вектора WDT - только через RESET. Ну и 2 сек максимум.
intrc - это внутренний генератор.

gzp13
Offline
Зарегистрирован: 06.04.2015

т.е я не смогу реализовать на атмеге8 пробуждение из сна через каждые 2 часа?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

gzp13 пишет:

т.е я не смогу реализовать на атмеге8 пробуждение из сна через каждые 2 часа?

На 328 максимальный вочдог 8 сек.  Когда мне надо спасть 10 минут, я сплю в цикле 600/8 раз. Каждый раз проснувшись, смотрю от чего проснулся. Если от вочдога, ложусь спать дальше. Если же от внешнего прерывания - начинаю обрабатывать его.

Также и Вы можете спать по две секунды  в цикле 3600 раз. Не совсем тоже самое, что спасть два часа, конечно, но лучше, чем ничего.

gzp13
Offline
Зарегистрирован: 06.04.2015

это я понимаю, не могу организовать это в скетче.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

А чего там организовывать? Ну, попробуйте, если не получится, выкладывайте, посмотрим.

gzp13
Offline
Зарегистрирован: 06.04.2015

вот код,в спящий режим входит, но выходит из него по WDT а не по кнопке. Светодиод не загорается через 10сек по прерыванию от WDT. Совсем запутался.

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

#define F_CPU 1000000UL

byte flag = 0;
byte intFlag = 0;
#define ButtonOne           2
#define ButtonTwo           3

volatile int state = LOW;
int count = 0;
//**********************************************************************
ISR (WDT_vect) {

  wdt_reset();
  count++;
}

int main(void) {
  for (int i = 0; i < 20; i++) {
    if (i != ButtonOne || i != ButtonTwo  ) {
      pinMode(i, OUTPUT);
      digitalWrite(i, LOW);
    }
  }
  pinMode(12, OUTPUT);
  digitalWrite(12, LOW);
  wdt_enable(WDTO_2S);
  WDTCR |= (1 << WDCE) | (1 << WDE); //разрешаем прерывания по WDT
  pinMode(13, OUTPUT);
  digitalWrite(13, LOW);
  pinMode(ButtonOne, INPUT_PULLUP);
  //************************************************************************
  while (1) {

    if (intFlag == 1) //сработало внешнее прерывание
    {
      state = !state;
      intFlag = 0;
    }
    // тут код который должен выполняться при пробуждении ( можно обработать с какой именно кнопки нажата и сделать обработку для каждой кнопки свое действие )
    digitalWrite(13, state);
    if (count > 2) // через 10сек должен загореться светодиод,но не загорается.
    {
      digitalWrite(12, HIGH);
      count=0;
    }
    WDTCR |= (1 << WDCE) | (1 << WDE); //разрешаем прерывания по WDT
    ADCSRA = 0;
     wdt_reset();
     set_sleep_mode (SLEEP_MODE_PWR_DOWN);
    sleep_enable();
    attachInterrupt(digitalPinToInterrupt(ButtonOne), sleepISR1, LOW);  // LOW
    interrupts();
    sleep_cpu();
    }
    }
void sleepISR1() {
  // Запретить спящий режим, чтобы мы больше не заходили в него, кроме как намеренно, по коду
  sleep_disable();
  // код или действие при пробуждении по этому прерыванию
  if (digitalRead(ButtonOne) == LOW && flag == 0 )
    {
    _delay_ms(200);    
    intFlag = 1;
    flag = 1;
  }
  if (digitalRead(ButtonOne) == HIGH && flag == 1 )
  {
    _delay_ms(50);
    flag = 0;
  }
   detachInterrupt(digitalPinToInterrupt(ButtonOne));

  // Теперь мы продолжаем запуск основной функции Loop () сразу после того, как мы пошли спать

}


 

Upper
Offline
Зарегистрирован: 23.06.2020

Какой МК?

В любом случае надо не ИЛИ а И

24   if (i != ButtonOne || i != ButtonTwo  ) {

Дальше не проверял.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Не, ну так не делается. Здесь у Вас насажена куча деревьев, за которыми абсолютно не виден лес. Попробуйте всю низкоуровневую байду сложить в отдельные маленькие функции. Тогда, выписывая логику в этих функциях, Вы её (логику) хоть понимать будете.

gzp13
Offline
Зарегистрирован: 06.04.2015

Лес вроде просвечивается) Логику я понимаю, контроллер спит глубоким сном, WDT работает каждые 2сек и прибавляет счетчик count. Когда выполнится условие count>2 должен загореться светодиод. Но одновременно с WDT работает еще и прерывание INT0, при нажатии на кнопку контроллер тоже должен выйти из режима сна.

gzp13
Offline
Зарегистрирован: 06.04.2015
Вот так же доджен загораться диод от WDT?

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

#define F_CPU 1000000UL
int count = 0;
//**********************************************************************
ISR (WDT_vect) {
  wdt_reset();
  count++;
  wdt_disable();
}

int main(void) {

  pinMode(12, OUTPUT);
  digitalWrite(12, LOW);
 
  //************************************************************************
  while (1) {

   
    if (count > 2) // через 4сек должен загореться светодиод,но не загорается.
    {
      digitalWrite(12, HIGH);
      count=0;
    }
    wdt_enable(WDTO_2S);
       ADCSRA = 0;
    wdt_reset();
     set_sleep_mode (SLEEP_MODE_PWR_DOWN);
    sleep_enable();    
    interrupts();
      sleep_cpu();
    }
    }
   

 

gzp13
Offline
Зарегистрирован: 06.04.2015

Вот так же доджен загораться диод от WDT?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Нет, не должен.

alexbmd
Offline
Зарегистрирован: 15.01.2016

ЕвгенийП пишет:

 Каждый раз проснувшись, смотрю от чего проснулся. Если от вочдога, ложусь спать дальше. Если же от внешнего прерывания - начинаю обрабатывать его.

А не могли бы вы показать это кусочек кода ?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Да, показать-то нетрудно. ТОлько есть беда.

Вы пишете, что у Вас ATmega8, а потом в посте #19 используете вектор WDT_vect, которого у ATmega8 попросту нет. 

Так, с каким контроллером Вы работаете? Для какого Вам нужен кусочек кода? C watchdog? Или без watchdog? Что Вам нужно-то?

gzp13
Offline
Зарегистрирован: 06.04.2015

про кусочек кода я не писал)Точнее не я писал. Работаю с ATMEGA8. Вот я бы тоже посмотрел на кусочек кода. Никак не могу добиться правильной работы WDT и режима сна. Т.е я не правильно указал имя прерывания от WDT?

gzp13
Offline
Зарегистрирован: 06.04.2015

Блин,получается у атмеги8 нет вектора прерывания???

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Этого - нет.  Если нужно не больше 6 ног, возьми тиньку, у нее есть

gzp13
Offline
Зарегистрирован: 06.04.2015

т.е я не смогу сделать пробуждение атмеги от wdt???

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

gzp13 пишет:

т.е я не смогу сделать пробуждение атмеги от wdt???

От WTD - нет, только перезагрузка. Можно взять тиньку или 328 - там нет проблем.

Ну, или будить от чего-нибудь другого (например, от 555 таймера).

А что у Вас там ещё в проекте есть? А то RTC тоже будить умеют (это их основная работа :-)

gzp13
Offline
Зарегистрирован: 06.04.2015

Ну я делаю автополив, будет дисплей, датчик почвы,температуры воздуха,мотор который я хочу подключить к LIPO аккумулятору 12В и контроллером измерять напряжение раз в сутки, чтобы не разрядить в ноль. Плюс хочу измерять питание самого контроллера,т.е нужно два аналоговых входа. Я вот думаю может прикрутить часы реального времени DS3231? https://tixer.ru/catalog/modules/rtc-modules/chasy_r_v_rtc_na_ds3231_dlya_raspberry_pi/

Эти подойдут я думаю?

Вообщем вывод по атмеге я сделал такой,для экономичных режимов она подходит только если работать от внешних прерываний, правильно?

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

gzp13 пишет:

Эти подойдут я думаю?

У этого модуля вывода INT нету, но его можно прям с ноги микросхемы взять, там ОК. 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Для измерения питания самого контроллера не нужен аналоговый вход.

Модуль часов лучше возьмите нормальный, типа такого https://aliexpress.ru/item/4000004876793.html Он Вам и время посчитает, и разбудит, когда скажете.

alexbmd
Offline
Зарегистрирован: 15.01.2016

ЕвгенийП пишет:

На 328 ..... Каждый раз проснувшись, смотрю от чего проснулся. Если от вочдога, ложусь спать дальше. Если же от внешнего прерывания - начинаю обрабатывать его...

если не трудно , могли бы показать эту строчку?

gzp13
Offline
Зарегистрирован: 06.04.2015

а вообще на будущее,при задействовании большого количества пинов,лучше всего использовать для экономичных режимов атмегу 328?

alexbmd
Offline
Зарегистрирован: 15.01.2016

для реально экономичных ARM или что-то похожее

gzp13
Offline
Зарегистрирован: 06.04.2015

А все таки если использовать режим SAVE,то можно использовать timer2 ,тогда нужен внешний кварци кварц на 32768кГц, как его нужно подключить?Точнее как подключить понятно, что нужно делать с фьюзами? В ардуино IDE нет прошивки с кварцем такой частоты.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

gzp13 пишет:

а вообще на будущее,при задействовании большого количества пинов,лучше всего использовать для экономичных режимов атмегу 328?

Из AVR лучше те, которые picoPower - в документации написано. Например, ATtiny85

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

alexbmd пишет:

если не трудно , могли бы показать эту строчку?

Не жалко, только давайте я просто выдеру из готового проекта. Делать работающий пример, правда в лом. Вот смотрите. Это для 328. Функция sleeping спит total раз на время period. Обращаются к ней, например

sleeping(WDTO_8S, 10); // спать 10 раз по 8 секунд.

сама функция и обработчик прерывания

static volatile int8_t wdtWakeUpCounter = 0;

ISR(WDT_vect) {
	wdt_disable();
	wdtWakeUpCounter++;
}

static inline void sleeping(const uint8_t period, const uint8_t total) {
	const uint8_t maskWDTCSR = bit(WDIE) | period;
	set_sleep_mode(SLEEP_MODE_PWR_DOWN);
	sleep_enable();
	clock_prescale_set(clock_div_256);	// понижаем тактовую частоту
	for (wdtWakeUpCounter = 0; wdtWakeUpCounter < total;) {
		cli();
		wdt_reset();
		MCUSR &= ~bit(WDRF); // чистим WDRF
		WDTCSR = bit(WDCE) | bit(WDE); 
		WDTCSR = maskWDTCSR;
		sei();
		// если проснулись не от WD - спим дальше
		const uint8_t oldCounter = wdtWakeUpCounter;
		do {
			sleep_bod_disable();
			sleep_cpu();
		} while (oldCounter == wdtWakeUpCounter);
	};
	sleep_disable();
	clock_prescale_set(F_CPU == 1000000L ? clock_div_8 : clock_div_1);	// повышаем тактовую частоту
}

собственно цикл сна в строке №13.

Только прежде, чем её вызывать, нужно убедиться. что ВСЕ пины находятся в понятном состоянии и не собираются меняться, а то она будет просыпаться не от WD, а от чего попало. Я это делаю, но на всякий случай таки обрабатываю ситуацию, если проснулась не от WD - это проверка и цикл в строках №№ 20-25. Т.е. проснулись от левого источника - тут же ложимся спать дальше. WD когда надо - разбудит.

Ну, конечно, перед сном (перед вызовом этой функции) надо выключать всё типа USART, SPI, TWI, таймеры, нехрен им батарейку жрать. Я это делаю вызовом power_all_disable(); ну а потом надо не забыть что надо включить и проинициализировать.

Upper
Offline
Зарегистрирован: 23.06.2020

Скажите пожалуйста, зачем вы понижаете тактовую частоту перед сном?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Upper пишет:

Скажите пожалуйста, зачем вы понижаете тактовую частоту перед сном?

Низачем. Это не единственная лишняя вещь там. Например, выключение цифровых буферов на аналоговых пинах в режиме power down ничего не даёт, т.к. они там и так выключены. Это была заготовка в которую просто были собраны все "выключающие техники". И так и использовалось не особо вникая что нужно, а что не нужно в данном конкретном случае.

alexbmd
Offline
Зарегистрирован: 15.01.2016

Евгений,

спасибо. пару уточнений для себя

1) в строчке 24 по замыслу автора мы просыпаемся от переполнения таймера ватчдога 8с. и только если мы проснулись от ватчдога согласно строчке 25 мы пойдём на следующий for. Но, правильно я понимаю, в случае если у нас отключенны все прерывания таймеры и тд кроме ватчдога то проснуться от чегото левого мы не можем в принципе ?

2) в строчке 16 мы очищаем , записывая ноль, соответсвующий бит в регистре. тут всё понятно. что с ногой на выход, что с флаговым регистром, что с другими (если не всеми) регистрами - 0 это очищаем, 1 это выставляем. Но вот просматривая даташит 328 касательно INTF1, не могу понять две вещи. Флаговый регистр при срабатывании выставляется в 1 а при очищении в 0. вроде всё понятно. но
а) там пишут что он автоматически очищается при срабатывании рутины прерывания. НО, также его можно альтернативно очистить... другими словами я понимаю в ручную имеется ввиду. но для каких случаев нам может понадобится очищать его в ручную если он сам автоматически очищается ?  я наверно знаю только об одном случае - когда мы пишем низкоуровневый код с "нейкед" процедурами. есть ли другие случаи ?
б) как выше мы уже видели и читали этот флаг INTF1 при очищении выставляется в 0. НО, как тогда понять строчку в даташите что он может быть очищен записью в него единицы ? ведь запись единицы это поднятие флага

 

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

alexbmd пишет:
в строчке 24 по замыслу автора мы просыпаемся от переполнения таймера ватчдога 8с. и только если мы проснулись от ватчдога согласно строчке 25 мы пойдём на следующий for. Но, правильно я понимаю, в случае если у нас отключенны все прерывания таймеры и тд кроме ватчдога то проснуться от чегото левого мы не можем в принципе ?

В принципе так, но тут есть нюанс. Я как-то наткнулся на то, за был закрыть SofwareSerial (самогонный аналог), а он, зараза, PCINT настраивает - вот и повод проснуться. В общем, я решил, что "на Аллаха надейся, а верблюда привязывай", мало кто ещё чего неочевидным образом настроит - этот цикл ничего не стоит - пусть типа будет.

alexbmd пишет:
для каких случаев нам может понадобится очищать его в ручную если он сам автоматически очищается ?

В строке №14 мы запрещаем обработку прерываний. Значит, если прерывание прилетит между строками 14 и 16, оно не будет обработано, а флаг будет взведён, чтобы оно обработалось как только sei это разрешит. Вот мы его и "сбрасываем" от греха подальше. Это довольно стандартная практика.

alexbmd пишет:
как тогда понять строчку в даташите что он может быть очищен записью в него единицы ?
Не знаю, почему так сделано, но так у многих флаговых регистров. Но к нашей строке 16 это не имеет отношения. Флаг WDRF чистится записью нуля, что мы там и делаем.

Sergey545
Offline
Зарегистрирован: 15.04.2021

Дружище, какой вариант есть еще для измерения напряжения питания контроллера? 

Sergey545
Offline
Зарегистрирован: 15.04.2021

ЕвгенийП пишет:

Для измерения питания самого контроллера не нужен аналоговый вход.

Модуль часов лучше возьмите нормальный, типа такого https://aliexpress.ru/item/4000004876793.html Он Вам и время посчитает, и разбудит, когда скажете.

 

Дружище, какой вариант есть еще для измерения напряжения питания контроллера? 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Sergey545 пишет:

Дружище, какой вариант есть еще для измерения напряжения питания контроллера? 

http://gammon.com.au/power - страница большая, поищите на ней через Ctrl+F раздел "Detecting low voltage"