Сон и просыпаться по прерыванию

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

Сделал игрушку ребенку: волшебная палочка, кто то на форуме писал и мне захотелось. Все просто микроконтроллер, 3 RGB светодиода и датчик вибрации из датчика холла и магнита на пружинке

преобразователь с контроллером заряда на FM6316 неплохо работает, имеет низкое собственное потребление, отключается при снижении напряжения на аккмуляторе до 2.5В

и самое слабое место микроконтроллер постоянно работает на 8мгц. где то находил инфу что можно будить по прерыванию, но видно плохо гуглил, так как у самого не получилось

код достаточно простой. радиомодуль тоже есть, но пока не использую, так как не решил чем им управлять

#define red    9
#define green 10
#define blue  11

boolean state=0;
unsigned long time=0;

byte rgbColour[3];

void setup() {
  attachInterrupt(1, Start, CHANGE);     //Прерывание по нарастающему фронту на D2
  setColourRgb(0,0,0);
}

void loop() {
  Serial.println(127);
  if(state==0) setColourRgb(0,0,0);
  while(state==1){
    rgbColour[0] = 255;
    rgbColour[1] = 0;
    rgbColour[2] = 0;  

    for(byte decColour = 0; decColour < 3; decColour += 1) {
      byte incColour = decColour == 2 ? 0 : decColour + 1;
      for(byte i = 0; i < 255; i += 1) {
        Serial.println(i);
        rgbColour[decColour] -= 1;
        rgbColour[incColour] += 1;
        setColourRgb(rgbColour[0], rgbColour[1], rgbColour[2]);
        delay(5);
        if(millis()-time>30000) state=0;
      }
    }
  }
}

void setColourRgb(byte red1, byte green1, byte blue1) {
  analogWrite(red, red1);
  analogWrite(green, green1);
  analogWrite(blue, blue1);
}

void Start(){
  state=1;
  time=millis();
}

в итоге пока все убрал про сон

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

хотя надо наверно побольше поэкспериментировать, вроде подробно описано, просто пока в голове не уложилось

http://arduino.ru/forum/obshchii/spyashchii-rezhim

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

jeka_tm пишет:

Сделал игрушку ребенку: волшебная палочка, кто то на форуме писал и мне захотелось. 

радиомодуль тоже есть, но пока не использую, так как не решил чем им управлять

Это я писал. А радиомодулем я гирлянды на ёлке включал. Девочка загадала желание, палочкой махнула, она помигала маленько и ёлочка зажглась - мило получилось. Дети весь вечер ёлку включали и выключали.

А просыпается она у меня как раз по прерыванию. Сейчас код поищу.

////////////////////////////////////////////////////////////////////////
//
//	Ребёнку к новому году: Волшебная палочка.
//	(модуль палочки)
//
//	По взмаху палочки (замыкание контака PIN_WAND на землю) происходит:
//		1. включается 3-секундное LED-show на пинах LED_PIN_FIRST-LED_PIN_LAST включительно (завершается тем, что все горят);
//		2. в эфир на 433МГц (через пин PIN_FS1000A) передаётся текст MAGIC_WORD;
//		3. выдерживается пауза в PAUSE_TIME миллисекунд и гасятся все светодиоды.
//	Остальным занимается модуль исполнителя (включает иллюминацию ёлки).
//
/////////
//
#include <VirtualWire.h>
#include <avr/sleep.h>
#include <avr/power.h>

enum CONTROL_PINS	{ PIN_WAND = 3, PIN_FS1000A = 2 };
enum MODES	{ MODE_INITIALIZING = 0, MODE_WAITING, MODE_SHOW, MODE_SEND, MODE_FINAL };
enum LED_PINS { LED_PIN_FIRST = 4, LED_PIN_LAST = 12 };

#define TOTAL_MIRACLES	3
#define	PAUSE_TIME	5000
#define	INTERRUPT_NUMBER	(PIN_WAND-2)

static uint8_t currentMode = MODE_INITIALIZING;
static const uint8_t TotalLeds = LED_PIN_LAST - LED_PIN_FIRST + 1;
static const int8_t MagicWordLength = 2;

const int8_t ledPin(const short logPin) { 
	return logPin % TotalLeds + LED_PIN_FIRST; 
}

uint8_t * MagicWord(void) { 
	static uint8_t magicCounter = 0;
	static uint8_t wBuffer[3] = { '0', '+', '\0' };
	wBuffer[0] = '0' + magicCounter++;
	magicCounter %= TOTAL_MIRACLES;
	return wBuffer;
}

void forAllLeds (const uint8_t hiLow, const bool setupPins = false) {
	for (int8_t pin=LED_PIN_FIRST; pin <= LED_PIN_LAST; pin++) {
		if (setupPins) pinMode(pin, OUTPUT);
		digitalWrite(pin, hiLow); 
	}
}

void firstLedsOn (int8_t amount) {
	for (int8_t pin=0; pin < amount; pin++) digitalWrite(ledPin(pin), HIGH); 
}

////////////////////////////////////////
//
//	Инициализация передатчика
//
void InitFS1000A(void) {
	vw_set_ptt_inverted(true);
	vw_setup(2000);
	vw_set_tx_pin(PIN_FS1000A);
}

////////////////////////////////////////
//
//	Обработчик прерывания FALLING на пине PIN_WAND
//
void wandInterrupt(void) {
	if (currentMode == MODE_WAITING) currentMode++;
}


////////////////////////////////////////
//
//	Общая инициализация
//
void setup() {
	pinMode(PIN_FS1000A, OUTPUT);
	pinMode(PIN_WAND, INPUT);	// pulled UP externally
	InitFS1000A();
	forAllLeds(LOW, true);
	attachInterrupt(INTERRUPT_NUMBER, wandInterrupt, FALLING);
}


void getWaitingMode(void) {
	currentMode = MODE_WAITING;
	ADCSRA = 0; 
	power_adc_disable();
	set_sleep_mode(SLEEP_MODE_PWR_DOWN);
	cli();
	sleep_enable();
	sleep_bod_disable();
	sei();
	sleep_cpu();
	sleep_disable();
}

////////////////////////////////////////
//
//	Передача сигнала с ожиданием завершения
//
void SendIt(void) {
   vw_send(MagicWord(), MagicWordLength);
	vw_wait_tx(); 
}

////////////////////////////////////////
//
//	Светодиодное шоу
//
void allBlinks(const short msDelay, const short limit = 80) {
	short led1 = 0;
	short led2 = 5;
	for (short i=0; i<limit; i++) {
		digitalWrite(ledPin(led1), HIGH); 
		digitalWrite(ledPin(led2), HIGH); 
		delay(msDelay);
		digitalWrite(ledPin(led1++), LOW); 
		digitalWrite(ledPin(led2++), LOW); 
		delay(1);
	}
}

void ledShow(void) {
	short delayTime = 1000;
	for (int8_t amount = 1; amount <= TotalLeds; amount++, delayTime-=80) {
		 firstLedsOn(amount);
		 delay(delayTime);
		 if (amount < TotalLeds) {
			 forAllLeds(LOW);
			 delay(delayTime);
		 }
	}

	allBlinks(90, 50);
	allBlinks(30, 100);
	allBlinks(10, 300);
	
	forAllLeds(HIGH);
}


////////////////////////////////////////
//
//	Конечный автомат работы палочки
//
void loop() {
	switch (currentMode) {
		case MODE_INITIALIZING:
			getWaitingMode();
		break;
		case MODE_SHOW:
			ledShow();
			currentMode++;
		break;
		case MODE_SEND:
			SendIt();
			currentMode++;
		break;
		case MODE_FINAL:
			delay(PAUSE_TIME);
			forAllLeds (LOW);
			getWaitingMode();
		break;
	}
}

За засыпание отвечает функция getWaitingMode. Внутри палочки алюминевый стакан (гильза стрелянная) внутри которого пружинка с гайкой на конце. Гильза подключена к земле, пружинка притянута к питанию и подсоединена к пину PIN_WAND. На пине PIN_WAND сидит прерывание по FALLING. Когда палочкой махнут, гайка касается гильзы - происходит прерывание, оно и просыпается. Смотрите сами.

Gres
Gres аватар
Offline
Зарегистрирован: 26.03.2013

У меня так реализовано(куски кода из скетча, лишнее выкинул), но вместе со всей этой кухней использую сторожевой таймер:

void setup()
{
/*********************************************************************************  
     *     SLEEP_MODE_IDLE         -the least power savings
     *     SLEEP_MODE_ADC
     *     SLEEP_MODE_PWR_SAVE
     *     SLEEP_MODE_STANDBY
     *     SLEEP_MODE_PWR_DOWN     -the most power savings
/*********************************************************************************/
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);           // Настраиваем режим энергосбережения

}
/*********************************************************************************/ 

void Sleep_on()
{

  attachInterrupt(0, wakeon, LOW);               //Прерывание вызывается только при смене значения на порту с HIGH на LOW, идем просыпаться выполняя фунцию wakeon
/*
 * 
    LOW вызывает прерывание, когда на порту LOW
    CHANGE прерывание вызывается при смене значения на порту, с LOW на HIGH и наоборот
    RISING прерывание вызывается только при смене значения на порту с LOW на HIGH
    FALLING прерывание вызывается только при смене значения на порту с HIGH на LOW

 */

  sleep_enable();                                 //Разрешаем спящий режим
  sleep_mode();                                   //Засыпаем
  sleep_disable();                                 //Запрещаем спящий режим
}

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

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

void wakeon()                                       //Фунция просыпания
{
 
  sleep_disable();                                   //Запрещаем спящий режим
  detachInterrupt(0);                          //Отключаем прерывания
} 

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

 

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

спасибо. если правильно понял должно получиться вроде этого, по крайней мере компилируется

#include <avr/sleep.h>

#define red    9
#define green 10
#define blue  11

#define time_on  30000

unsigned long time=0;
byte rgbColour[3];

void setup() {
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);           // Настраиваем режим энергосбережения
  setColourRgb(0,0,0);
}

void loop() {
  ((time-millis())>time_on)? Sleep_on() : Rainbow();
}


//=============================================== Радуга переливающаяся
void Rainbow(){
  rgbColour[0] = 255;
  rgbColour[1] = 0;
  rgbColour[2] = 0;  

  for(byte decColour = 0; decColour < 3; decColour += 1) {
    byte incColour = decColour == 2 ? 0 : decColour + 1;
    for(byte i = 0; i < 255; i += 1) {
      Serial.println(i);
      rgbColour[decColour] -= 1;
      rgbColour[incColour] += 1;
      setColourRgb(rgbColour[0], rgbColour[1], rgbColour[2]);
      delay(5);
    }
  }
}

//=============================================== Управление светодиодами
void setColourRgb(byte red1, byte green1, byte blue1) {
  analogWrite(red, red1);
  analogWrite(green, green1);
  analogWrite(blue, blue1);
}


//=============================================== Функция засыпания
void Sleep_on(){
  attachInterrupt(1, wakeon, CHANGE);             //Прерывание вызывается только при смене значения на порту с HIGH на LOW, идем просыпаться выполняя фунцию wakeon
  setColourRgb(0,0,0);                            //выключаем светодиоды на всякий случай
  sleep_enable();                                 //Разрешаем спящий режим
  sleep_mode();                                   //Засыпаем
  sleep_disable();                                //Запрещаем спящий режим
}

//=============================================== Функция просыпания))
void wakeon(){                                    
  sleep_disable();                                //Запрещаем спящий режим
  detachInterrupt(0);                             //Отключаем прерывания
  time=millis();
} 

 

Gres
Gres аватар
Offline
Зарегистрирован: 26.03.2013

Да, вроде все верно, теперь только в работе нужно проверить это. У меня нормально работает, в данном режиме максимальное энергосбережение, но отключена вся периферия, в том числе и АЦП, если  в момент сна нужно чтобы работала какая то периферия, необходимо выбрать другой режим, но потребление увеличится. У меня будит дуню внешнее прерывание с GSM модуля, при входящем звонке на него(он тоже спит до получения входящего звонка).

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

да никакая перифирия не нужна. контроль за батарей на микросхеме питания

вечером проверю

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

что то не получилось. ни в какую не хочешь отрабатывать прерывание. не просыпается. мультиметром проверял датчик холла работает

#include <avr/sleep.h>

#define red    9
#define green 10
#define blue  11

#define time_on  3000

unsigned long time=0;
byte rgbColour[3];

void setup() {
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);           // Настраиваем режим энергосбережения
  setColourRgb(0,0,0);
}

void loop() {
  ((millis()-time)>time_on)? Sleep_on() : Rainbow();
}


//=============================================== Радуга переливающаяся
void Rainbow(){
  rgbColour[0] = 255;
  rgbColour[1] = 0;
  rgbColour[2] = 0;  

  for(byte decColour = 0; decColour < 3; decColour += 1) {
    byte incColour = decColour == 2 ? 0 : decColour + 1;
    for(byte i = 0; i < 255; i += 1) {
      Serial.println(i);
      rgbColour[decColour] -= 1;
      rgbColour[incColour] += 1;
      setColourRgb(rgbColour[0], rgbColour[1], rgbColour[2]);
      delay(5);
    }
  }
}

//=============================================== Управление светодиодами
void setColourRgb(byte red1, byte green1, byte blue1) {
  analogWrite(red, red1);
  analogWrite(green, green1);
  analogWrite(blue, blue1);
}


//=============================================== Функция засыпания
void Sleep_on(){
  attachInterrupt(1, wakeon, RISING);             //Прерывание вызывается только при смене значения на порту с HIGH на LOW, идем просыпаться выполняя фунцию wakeon
  setColourRgb(0,0,0);                            //выключаем светодиоды на всякий случай
  sleep_enable();                                 //Разрешаем спящий режим
  sleep_mode();                                   //Засыпаем
  sleep_disable();                                //Запрещаем спящий режим
}

//=============================================== Функция просыпания))
void wakeon(){                                    
  sleep_disable();                                //Запрещаем спящий режим
  detachInterrupt(1);                             //Отключаем прерывания
  time=millis();
} 

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

Jeka_M
Jeka_M аватар
Offline
Зарегистрирован: 06.07.2014

jeka_tm пишет:

attachInterrupt(1, wakeon, RISING); //Прерывание вызывается только при смене значения на порту с HIGH на LOW

RISING - это возрастающий фронт с LOW на HIGH. Ниспадающий с HIGH на LOW будет FALLING.

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

я знаю. мне посути можно любой ставить. конкрентный фронт не интересует. пробовал разные

есть варианты что не так?

Gres
Gres аватар
Offline
Зарегистрирован: 26.03.2013

jeka_tm Для начала просто светодиод зажеч, ногу аппаратного прерывания например подтянуть к + и в ручную ее замкнуть на -. Я перед железной версией симулировал в Протеусе, только там приходилось вачдог отключать или выставлять самые большие тайминги.

Вот тут проект положил целиком, подержу пару дней.

Gres
Gres аватар
Offline
Зарегистрирован: 26.03.2013

Да, этот проект до конца так и не доделал, вернее испытал только на макетке, нужно плату рисовать, да травить, ждет своего часа, нет времени пока. А тот код, что приводил ранее из рабочего проекта, давно и без сбоев работающий в железе, но схему я не рисовал на него, там все просто, все в голове. Могу бросить и проект, но без схемы он будет бесполезен.

Gres
Gres аватар
Offline
Зарегистрирован: 26.03.2013

jeka_tm пишет:

что то не получилось. ни в какую не хочешь отрабатывать прерывание. не просыпается. мультиметром проверял датчик холла работает

#include <avr/sleep.h>

#define red    9
#define green 10
#define blue  11

#define time_on  3000

unsigned long time=0;
byte rgbColour[3];

void setup() {
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);           // Настраиваем режим энергосбережения
  setColourRgb(0,0,0);
}

void loop() {
  ((millis()-time)>time_on)? Sleep_on() : Rainbow();
}


//=============================================== Радуга переливающаяся
void Rainbow(){
  rgbColour[0] = 255;
  rgbColour[1] = 0;
  rgbColour[2] = 0;  

  for(byte decColour = 0; decColour < 3; decColour += 1) {
    byte incColour = decColour == 2 ? 0 : decColour + 1;
    for(byte i = 0; i < 255; i += 1) {
      Serial.println(i);
      rgbColour[decColour] -= 1;
      rgbColour[incColour] += 1;
      setColourRgb(rgbColour[0], rgbColour[1], rgbColour[2]);
      delay(5);
    }
  }
}

//=============================================== Управление светодиодами
void setColourRgb(byte red1, byte green1, byte blue1) {
  analogWrite(red, red1);
  analogWrite(green, green1);
  analogWrite(blue, blue1);
}


//=============================================== Функция засыпания
void Sleep_on(){
  attachInterrupt(1, wakeon, RISING);             //Прерывание вызывается только при смене значения на порту с HIGH на LOW, идем просыпаться выполняя фунцию wakeon
  setColourRgb(0,0,0);                            //выключаем светодиоды на всякий случай
  sleep_enable();                                 //Разрешаем спящий режим
  sleep_mode();                                   //Засыпаем
  sleep_disable();                                //Запрещаем спящий режим
}

//=============================================== Функция просыпания))
void wakeon(){                                    
  sleep_disable();                                //Запрещаем спящий режим
  detachInterrupt(1);                             //Отключаем прерывания
  time=millis();
} 

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

У 8 меги вероятно другие ноги внешнего прерывания, я ее не мучал, нужно даташиты глянуть.

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

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

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

Gres
Gres аватар
Offline
Зарегистрирован: 26.03.2013

Из глубокого сна возможно разбудить только внешним прерыванием, для Atmega8 INT0 (PD2 - 4 нога МК в DIP корпусе) и INT1 (PD3- 5 нога МК в DIP корпусе).

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

у меня на 2 ноге smd, PD3, это INT1

Gres
Gres аватар
Offline
Зарегистрирован: 26.03.2013

В этом корпусе TQFP -  INT1(PD3 - 1 нога), а INT0(PD2 - 32 нога).

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

да 1 нога. да работало все без сна. включалось переливание по прерыванию. посмотри первый код

может сама мега8 просыпаться должна как то иначе

вечером попробую убрать все. только тест сна

Gres
Gres аватар
Offline
Зарегистрирован: 26.03.2013

Посмотрел, все верно вроде, в библиотеке Sleep.h заглянул и там все хорошо вроде, поддержка Atmega8 есть, нужные биты в регистр MCUCR должны устанавливаться. Нужно отлаживать, какой то проверочный код ввести, может многократно успевает срабатывать и только уснув, сразу просыпается. Я у себя с фронтами игрался, нормально запустил в своем случае с attachInterrupt(0, wakeon, LOW);

Там есть тонкости по обработке фронтов и количеству срабатываний.

На Atmega8 не пробовал, но с 48 и 328 в TQFP корпусах все работает замечательно.