Управление переходом в спящий режим

Westwood
Offline
Зарегистрирован: 17.03.2020

Здравствуйте! Хотел бы обратиться за конструктивной помощью. Схема следующая: На ардуино висят кнопка и светодиод. Светодиод по стандарту горит постоянно. Счетчик отсчитывает 60 секунд, после прохождения которых ардуино уходит в сон в режим Power down. На кнопке лежит 2 задачи: в активный период работы ардуино при нажатии на неё должен сбрасываться счётчик, тем самым откладывая на неопределённое время уход в сон; во-вторых, кнопка служит для пробуждения из сна и возобновления "активного" режима работы. Вопрос в том, какими путями можно реализовать обработку таких двух действий одной кнопкой, учитывая, что приоритетным у неё является именно обработка прерывания, через которое реализовано пробуждение? Надеюсь, не слишком сумбурно. Заранее спасибо!

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

Реализовать такую логику можно.  От нас чо нада?

rkit
Offline
Зарегистрирован: 23.11.2016

путем использования переменной, наверно

asam
asam аватар
Offline
Зарегистрирован: 12.12.2018

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

 

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

Westwood пишет:

На ардуино висят кнопка и светодиод. Светодиод по стандарту горит постоянно. 

Зачем постоянно горящему светодиоду "висеть" на ардуино? Чем ему батарейка не угодила (или какое там питание)? Почему б прямо на неё не повеситься?

Westwood пишет:

уходит в сон в режим Power down.

Смысл уходы в сон, когда засыпает устройство, потребляющее около 1-2%  от общего потребления схемы?

Westwood
Offline
Зарегистрирован: 17.03.2020

Евгений П, светодиод тут висит постоянно просто для демонстрации модели. В реальном проекте логика следующая. Есть таймер, который измеряет время между двумя срабатываниями датчиков и выводит это на LED дисплей. Та самая кнопка используется для сброса показаний таймера до нуля. Так вот необходимо на эту же кнопку повесить те самые задачи, что я уже описал. То есть вместо светодиода подразумевается работа таймера. 

По поводу потребления. Необходимо отказаться от кнопки вкл/выкл устройства и не ставить ее в разрыв с батарейкой, а реализовать вот такой вот режим сна во время неиспользования устройства. Потребление там меньше 1мка. 

 

Westwood
Offline
Зарегистрирован: 17.03.2020

DetSimen, может как-то блок-схемно увидеть пути реализации, или как-то на словах. Может схемы похожие есть. Для меня пока что остаётся непонятным в связи с небольшим опытом работы как одной ноге атмеги, а конкретно 2-м цифровом входе, одновременно обрабатывать пробуждения, когда устройство уже спит, и одновременно сбрасывать счётчик, когда устройство активно.  

Westwood
Offline
Зарегистрирован: 17.03.2020

asam пишет:

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

 

Вы имеете в виду в обработчик прерывания все запихнуть? 

 

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

при запуске таймера некой переменной bool isStart присвоить true

в обработчике соответственно проверять if isStart {сбросить счетчик и присвоить isStart=false}

-NMi-
Offline
Зарегистрирован: 20.08.2018

Тебе нужна нога(и) INTx - только они имеют вектор во сне.

Кнопку можно "поделить" делителями и использовать одновременно (попеременно) в качестве входа и выхода.

asam
asam аватар
Offline
Зарегистрирован: 12.12.2018

Westwood пишет:

asam пишет:

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

Вы имеете в виду в обработчик прерывания все запихнуть?

 

Что все? Похоже вы не понимете как спящий режим работает. Вот вы подали команду, например, на Power-down Mode. Грубо говоря при этом почти все выключается, а исполнение команд останавливается ровно на этой команде. Затем происходит событие будящее процессор. Например прерывание по нажатию кнопки. Процессор просыпается и видит "обана, да у меня тут прерывание INT0!" и передает управление туда. То есть обработчик прерывания всегда срабатывает когда процессор уже не спит. По окончанию прерывания управление передается на команды следующие за командой на усыпление.

asam
asam аватар
Offline
Зарегистрирован: 12.12.2018

-NMi- пишет:

Тебе нужна нога(и) INTx - только они имеют вектор во сне.

Кнопку можно "поделить" делителями и использовать одновременно (попеременно) в качестве входа и выхода.

Опять гонишь. Во сне вектор имеют всегда Pin Change (то есть почти все входы) TWI и WDT.  Плюс могут быть и другие, например Timer2, ADC итд в зависимости от режима сна.

-NMi-
Offline
Зарегистрирован: 20.08.2018

asam пишет:

Опять гонишь.

Опять? гдеетты меня ещё в гоневе заметил? Предъяви!!!

И по поводу INTофф то-же предъяви из даташита!!!

asam
asam аватар
Offline
Зарегистрирован: 12.12.2018

-NMi- пишет:

asam пишет:

Опять гонишь.

Опять? гдеетты меня ещё в гоневе заметил? Предъяви!!!

И по поводу INTофф то-же предъяви из даташита!!!

 

Да регулярно. Копаться лень, посмотри сам свои посты.

по поводу интов - http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf

Раздел 9.1 Sleep Modes, Table 9-1.

vlad072
Offline
Зарегистрирован: 01.08.2017

Есть портативное устройство на Atmega368 (условно счётчик импульсов). Иногда (редко) не хочет просыпаться из power_down, приходится дёргать питание. Куда копать?

#include <GyverButton.h>
#include <GyverOLED.h>
#include <GyverPower.h>
#include <powerConstants.h>


const uint8_t logo[] PROGMEM = {
	0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xC0, 0xE0, 0xE0, 0x70, 0xF0, 0xB8, 0xF8, 0xD8, 0xFC, 0xFC, 0xEC, 0xFC, 0xFE, 0xF6, 0x76, 0x7E, 0xFE, 0xFF, 0xFF, 0xBB, 0xBB, 0xBB, 0xBB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xBB, 0xBB, 0xBB, 0xBF, 0xFF, 0xFE, 0x7E, 0x76, 0xF6, 0xFE, 0xFE, 0xEC, 0xEC, 0xFC, 0xDC, 0xD8, 0xB8, 0xB0, 0x70, 0x70, 0xE0, 0xC0, 0xC0, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 
	0xC0, 0xF8, 0xFC, 0x7E, 0x1F, 0xC7, 0xF3, 0xF9, 0xFC, 0xFE, 0xFF, 0x7F, 0x1F, 0xCF, 0xC7, 0xC3, 0xC3, 0xC1, 0xC3, 0xC6, 0xC7, 0xCF, 0xCF, 0xC7, 0xC7, 0xC7, 0xC7, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC7, 0x47, 0x07, 0x0F, 0x0F, 0x1F, 0x3F, 0x7E, 0xFF, 0xFD, 0xFB, 0xF3, 0xE3, 0x81, 0x01, 0x80, 0x80, 0x80, 0x81, 0x03, 0x07, 0x0F, 0x1F, 0xFE, 0xF8, 0xF0, 
	0x07, 0x3F, 0x7F, 0xFC, 0xF0, 0xE7, 0x9F, 0xBF, 0x7F, 0xFF, 0xFF, 0xFC, 0xF0, 0xE0, 0xC0, 0xC0, 0x80, 0x00, 0x80, 0xC0, 0xC0, 0xE0, 0xE0, 0xE0, 0xC0, 0xC0, 0xC0, 0xC0, 0x84, 0x86, 0x86, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0xC6, 0xC6, 0xC6, 0xE6, 0xE6, 0xE6, 0xF6, 0xFE, 0xFE, 0x7F, 0x7F, 0xBF, 0x1F, 0x0F, 0x07, 0x06, 0x0F, 0x0F, 0x0F, 0x0F, 0x8F, 0xC7, 0xE6, 0xFA, 0xFF, 0x3F, 0x1F, 
	0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x07, 0x0F, 0x0E, 0x1D, 0x1D, 0x1B, 0x3B, 0x3F, 0x37, 0x7F, 0x6F, 0x6F, 0x7E, 0xFE, 0xDF, 0xDD, 0xDD, 0xFF, 0xFF, 0xFF, 0xFB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xDD, 0xDF, 0xFF, 0xFE, 0x7F, 0x6F, 0x7F, 0x7F, 0x37, 0x3F, 0x3A, 0x1C, 0x1C, 0x0E, 0x0E, 0x07, 0x07, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 
};

GButton button(3);
GyverOLED<SSD1306_128x32, OLED_BUFFER> oled;

volatile uint32_t count;
uint8_t pitch;
volatile uint32_t tactive;

void(*reboot)(void) = 0;

void setup() {
  DDRD = 0xF0; PORTD = 0xEF;            // PD2=ir_sensor PD3=button PD4=button_gnd PD5,6,7=periph power 
  EICRA = 0x0A;
  EIMSK = 0x03;
  analogReference(INTERNAL);
  delay(100);

  button.setStepTimeout(800);
  button.setClickTimeout(200);

  power.setSleepMode(POWERDOWN_SLEEP);

  oled.init();  
  oled.clear();
  oled.drawBitmap(0, 0, logo, 64, 32);
  oled.setScale(1);
  oled.setCursorXY(76, 0); oled.print("SMD");
  oled.setCursorXY(76, 12); oled.print("counter");
  oled.setCursorXY(76, 24); oled.print("rev 1.2");
  oled.update();
  while(battLvl() == 0);
  delay(2000);

  count = 0; pitch = 1;
  tactive = millis();
}

void loop() {
 // oled.rect(0,0,127,31);oled.update();return;

  button.tick();
  if (button.isStep()) {
    pitch++;
    if (pitch > 6) pitch = 1;
  } else if (button.isClick()) {
    count = 0;
  } else if (button.isTriple()) {
    poweroff();
  }
  
  oled.clear();
  drawBattery( constrain(battLvl()-486, 0, 110) );
  printCount(count/pitch);
  printPitch(pitch*4);
  oled.update();

  if ((millis() - tactive) > 600000) poweroff();
  if (battLvl() < 470) {
    oled.setScale(2); oled.clear();
    oled.setCursorXY(0, 10); oled.print("low batery");
    oled.update();
    delay(1000);
    poweroff();
  }
}

ISR(INT0_vect) {
  count++;
  tactive = millis();
}

ISR(INT1_vect) {
  tactive = millis();
}

void poweroff() {
  oled.setScale(2);  oled.clear();
  oled.setCursorXY(0, 10); oled.print("shutdown...");
  oled.update();
  delay(1000);
  oled.clear(); oled.update();
//  DDRD &= ~0xE0; PORTD &= ~0xE0;
  DDRC &= ~0x30;
  DDRD = 0x10; PORTD = 0x08;
  EIMSK = 0x02;
  power.sleep(SLEEP_FOREVER);
  reboot();
}

void printCount(uint32_t number) {
  if (number > 99999) number = 0;
  oled.setScale(4);
  if (number > 9999)
    oled.setScale(3), oled.setCursorXY(36, 5);
  else if (number > 999)
    oled.setCursorXY(36, 0);
  else if (number > 99)
    oled.setCursorXY(60, 0);
  else if (number > 9)
    oled.setCursorXY(84, 0);
  else
    oled.setCursorXY(108, 0);
  oled.print(number);
}

void drawBattery(uint8_t percent) {
  oled.setCursorXY(0, 0);
  oled.drawByte(0x3C); oled.drawByte(0x3C); //head
  oled.drawByte(0xFF);  // edge
  for (uint8_t i = 0; i < 100 / 8; i++) oled.drawByte(i < (100-percent)/8 ? 0x81 : 0xFF);
  oled.drawByte(0xFF);  // tail
}

void printPitch(uint8_t number) {
  oled.setCursorXY(0, 15);
  oled.setScale(2);
  oled.print(number);
}

int16_t battLvl() {
  static uint8_t steps = 0;
  static int32_t sum = 0;
  static int16_t ret = 0;
  if (steps >= 128) {
    ret = sum/steps;
    sum = 0; steps = 0;
  } else {
    steps++;
    sum += analogRead(A6);
  }
  return(ret);
}