Обсуждаем контроллеры компании Atmel.
Ответить

ATTiny13 - помогите разобраться со sleep-mode!

Сб дек 01, 2012 07:48:39

Доброго времени суток всем!
Нужна Ваша помощь с простой задачкой - МК, на одном пине (PB3) которого - кнопка, на другом (PB4) - светодиод.
Замыкаем кнопку -светодиод загорается на 4 секунды. Потом гаснет, независимо от состояния кнопки. А главное - пока кнопка не нажата - МК должен пребывать в глубоком сне и не жрать почти ни капли тока.


Собственно код:

Код:
#include <avr/io.h>      // инициализация портов ввода-вывода МК
#include <avr/wdt.h>      // здесь организована работа с ватчдогом
#include <avr/sleep.h>      // здесь описаны режимы сна
#include <avr/interrupt.h>                 // работа с прерываниями
#include <avr/delay.h>      // описание программных задержек

// Обработчик прерываний
ISR (WDT_vect)
{
   WDTCR |=_BV(WDE);   // разрешаем прерывания по ватчдогу, иначе будет резет!
}


// Основная программа
int main()
{

   // Инициализация порта кнопки (PB3)
   DDRB &=~_BV(PB3);      // ставит в DDRB в бит PB3 - "0" (инициирует его работу как "вход")
   PORTB |= _BV(PB3);      // ставит в PORTB в бит PB3 - "1" (при замыкании пина на землю она становится нулем, при размыкании - еденицей)
   
   // Условие включения светодиода по нажатию кнопки
   if((PINB & (1 << PB3)) == 0)
      {        
      // Иницализация порта светодиода (PB4)
      DDRB |= _BV(PB4);    // Указатель пина
      PORTB |= _BV(PB4);   // Выставить на PB4 - "1"
      _delay_ms (4000);   // Задержка
      PORTB &= ~_BV(PB4);   // Выставить на PB4 - "0"
               
            // Инициализация цикла контроля ложного включения светодиода
            while ((PINB & (1 << PB3)) == 0)
            {   // пока кнопка нажата (после завершения цикла включения светодиода)...
               PORTB &= ~_BV(PB4);   // ...светодиод не горит (до отключения, и последующего включения кнопки)
            }


};
      
      // Инициализация ватчдога   
      wdt_reset();         // сброс
      wdt_enable(WDTO_120MS);                 // разрешение ватчдога раз в 120мс
      WDTCR |= _BV(WDE);      // разрешение прерываний по ватчдогу (иначе будет резет)!
      sei();            // разрешение прерывания

      // Инициализация режима сна
      set_sleep_mode (SLEEP_MODE_PWR_DOWN);
      while(1)
      {
         sleep_enable();   // разрешение режима сна
         sleep_cpu();   // активация режима сна
      }
}



Я понимаю что код - не ахти (с МК вожусь считанные месяцы, через раз, по выходным), но в протеусе - работает как надо. А вот в реале - светодиод моргает непрерывно, пока нажата кнопка. Помогите пожалуста разобраться - где косяк?

Re: ATTiny13 - помогите разобраться со sleep-mode!

Сб дек 01, 2012 11:32:49

1. Кнопка на вход прерывания по низкому уровню. Только прерывание низким уровнем разбудит МК из глубокого сна.
2. В векторе прерывания по низкому уровню отключаем IRQ по уровню чтоб постоянно не молотил, переключим на "по фронту" или вообще отключим если будем потом мониторить статус кнопок софтварно. Запускаем антидребезг кнопки(например таймер).
3. Когда отработал антидребезг реагируем на события(включаем светомузыку, лампочки, биперы и делаем что надо).
4. Ждем/ничего не делаем пока нажата кнопка.
5. Когда отпустили кнопку ждем антидребезг и снова разрешаем прерывание по низкому уровню, усыпляем проц.

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

Re: ATTiny13 - помогите разобраться со sleep-mode!

Сб дек 01, 2012 11:44:23

а кнопку будут держать дольше 4 секунд или не обязательно?

Re: ATTiny13 - помогите разобраться со sleep-mode!

Сб дек 01, 2012 12:05:44

ibiza11 писал(а):а кнопку будут держать дольше 4 секунд или не обязательно?

В том то и дело что дольше. Поэтому у меня такой кривоватый код в итоге получился.

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

А насчет IRQ - честно сказать не просвещен пока. В свое время удачно получилось режим глубокого сна организовать именно на Тини13 - power down. В этом режиме там почти все обесточено в МК, только независимо тактируемы вочдог фурычит. Вот от него то я и плясал. Поэтому и прерывания все - по вачдогу. Благо в Тини он несколько более широкими возможностями наделен, нежели в Мегах.

Re: ATTiny13 - помогите разобраться со sleep-mode!

Сб дек 01, 2012 12:12:30

тогда вообще зачем здесь микроконтроллер?)))
одновибратор на 4 секунды, а кнопка включает питание одновибратора)))
если обязательно с микроконтроллером, то можно аналогично кнопкой рулить питанием тиньки13, а в программе написать тупо:
Код:
LedOn();
delay_ms(4000);
LedOff();
while(1);
:write:

Re: ATTiny13 - помогите разобраться со sleep-mode!

Сб дек 01, 2012 12:57:35

ibiza11 писал(а):тогда вообще зачем здесь микроконтроллер?)))


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

Код:
LedOn();
delay_ms(4000);
LedOff();
while(1);


вообще у меня практически это и сделано. Но что-то не работает оно как надо.

Re: ATTiny13 - помогите разобраться со sleep-mode!

Сб дек 01, 2012 13:06:00

то можно аналогично кнопкой рулить питанием тиньки13

Re: ATTiny13 - помогите разобраться со sleep-mode!

Сб дек 01, 2012 13:46:36

Будить МК по внешнему прерыванию у меня пока получается только из режима Idle. И этот вариант я оставил на крайний случай, если так не выйдет ничего.
Из Power-Down так будить вообще не получится (если я правильно все понял), так как все лишнее обесточено. Остается только ватчдог.
Да уже и бог бы с ним - просто интересно, в чем у меня-то проблема? В целях повышения образованности, так сказать, хотелось бы разобраться. Тем более что в симуляции все как надо работает...

Re: ATTiny13 - помогите разобраться со sleep-mode!

Сб дек 01, 2012 17:52:36

ATTINY13 можно будить из powerdown либо watchdog-ом как Вы делаете, либо низким уровнем, но только на входе INT0, т.к. только при этом низкий уровень отслеживается асинхронно. Первый подход приводит к гораздо большему токопотреблению, по ДШ около 6 мкА при питании от 5в.

Re: ATTiny13 - помогите разобраться со sleep-mode!

Вс дек 02, 2012 09:11:10

Попробовал переделать на прерывание по INT0.
Вот что получилось:
Код:
#include <avr/io.h>         // инициализация портов ввода-вывода МК
#include <avr/sleep.h>      // здесь описаны режимы сна
#include <avr/interrupt.h>   // работа с прерываниями
#include <avr/delay.h>      // описание программных задержек

// Обработчик прерываний
SIGNAL(SIG_INTERRUPT0)  // Прерывание по низкому уровню на PB1
{           

   PORTB |= _BV(PB4);   // Выставить на PB4 - "1"
   _delay_ms (4000);   // Задержка
   PORTB &= ~_BV(PB4); // Выставить на PB4 - "0"
   while ((PINB & (1 << PB1)) == 0)
   {   
   }
}       


// Основная программа
int main()
{

//   PORTB &= ~_BV(PB4);   // Выставить на PB4 - "0"
//   DDRB |= _BV(PB4);    // Указатель пина

   PORTB = 0b11101111;  // Выставить на PB4 - "0"
   DDRB  = 0b00010000;  // Указатель пина
      
   // Инициализация прерываний по INT0   
   GIMSK = 0b01000000; // Разрешение прерываний INT0 на входе PB1
   MCUCR = 0b00000000; // при перепаде низком уровне на PB1
   sei(); // Общее разрешение прерываний
      
   // Инициализация режима сна
   set_sleep_mode (SLEEP_MODE_PWR_DOWN);
   while(1)
   {
      sleep_enable();   // разрешение режима сна
      sleep_cpu();   // активация режима сна
   }
}

В симуляции работает (хотя это видимо не многого стоит) - подскажите, нет ли опять каких явных ошибок?

Также возник вопрос - почему перестала работать конструкция:
Код:
//   PORTB &= ~_BV(PB4);   // Выставить на PB4 - "0"
//   DDRB |= _BV(PB4);    // Указатель пина

в теле основной программы?

И еще - в обработчике прерывания пришлось опять вставить цикл, ждущий когда будет отпущена кнопка на PB1 (то же самое что мне не нравилось в предидущей версии программы). И все это время МК будет в нормальном режиме работать, а не энергосберегающем, можно ли как-то избавиться от этого? Все мои попытки привели пока только к тому что светодиод не гаснет, пока нажата кнопка...

Re: ATTiny13 - помогите разобраться со sleep-mode!

Вс дек 02, 2012 10:01:37

Если в программном коде более понятно, то примерно такая структура выходит:
Код:
volatile uint8_t have_int0 = FALSE;
volatile uint8_t goto_sleep = FALSE;

// Обработчик прерываний INT0
SIGNAL(SIG_INTERRUPT0)  // Прерывание по низкому уровню на PB1
{           

   PORTB |= _BV(PB4);   // Выставить на PB4 - "1"
   
   disable_int0(); //отключить INT0 IRQ, обязательно иначе тутже сюда залетим снова
   enable_timer1(); //включить таймер на 4 сек
   have_int0 = TRUE;//флаг было прерывание, можно пойти в IDLE
}

// Обработчик прерываний TIM1
interrupt TIMER1_OVF_IRQ  // 4 сек прошло - залетели сюда
{
   PORTB &= ~_BV(PB4); // Выставить на PB4 - "0"

   disable_timer1();// отключить TIM1
   enable_int0(); // включить прерывание по низкому уровню
   goto_sleep = TRUE; //теперь можно полностью отключиться
}

// Основная программа
int main()
{
   device_init();//настройка портов и периферии
   while(1)
   {
   if( have_int0 == TRUE )
      {
      have_int0 = FALSE;
      sleep_idle();//усыпить в IDLE чтоб меньше жрало, но периферия работает
      };
   if( goto_sleep == TRUE )
      {
      goto_sleep = FALSE;
      sleep_power_down();//теперь все совсем отключено
      };
   }
}

Re: ATTiny13 - помогите разобраться со sleep-mode!

Вс дек 02, 2012 13:21:29

uk8amk, спасибо!
А насчет PORTB &= ~_BV(PB4); не подскажете? Почему оно вдруг перестало нормально работать? Разве это не то же самое что и PORTB = 0b11101111;??? Я проверил - дело именно в PORTB, а не DDRB.

Re: ATTiny13 - помогите разобраться со sleep-mode!

Вс дек 02, 2012 18:46:21

не одно и тоже первое - установить в "0" один пин порта, второе установить весь порт
тут почитайте: http://we.easyelectronics.ru/AVR/avr-po ... -spat.html

Re: ATTiny13 - помогите разобраться со sleep-mode!

Пн дек 03, 2012 16:19:10

Спасибо! Вроде разобрался. Вот еще задумался - я в самом начале фьюзы оставил все по дефолту. Только вотчдог разрешил.
Старший байт - все 1. (Внутренний RC-генератор 9.6МГц; Задержка запуска: 14 тактов + 64мс)
Младший байт:
SPIOEN - 0;
EESAVE -1;
WDTON - 0;
CKDIV8 - 0;
SUT1 - 1;
SUT0 - 0;
CKSEL1 - 1;
CKSEL0 - 0;

Может я чего-то не учел?

Re: ATTiny13 - помогите разобраться со sleep-mode!

Сб янв 06, 2018 20:45:27

Написал прошивку под этот контроллер, чтобы отправлял частоту в радиомодуль по I2C. Большую часть времени он будет ждать, когда нажмут на кнопку. Хочу загнать его в sleep mode, чтобы не молотил впустую. Никак не могу с ним разобраться. Контроллер не отправляет данные в TEA5767.
Спойлер
Код:
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/sleep.h>
#include <avr/eeprom.h>
#include "I2C.h"

void tea5767SetFreq(unsigned int);

#define MAX_STATIONS 32
#define FM_FREQ_MIN  870
#define FM_FREQ_MAX 1075

unsigned char vars = 0b0000;//save, next, -, +
EEMEM unsigned int stations[MAX_STATIONS] = { 1007, 1045, 1016, 924, 979, 1000, 1037, 877, 1064, 952, 1068, 997, 976, 894, 1025, 964, 945, 995, FM_FREQ_MIN, FM_FREQ_MIN, FM_FREQ_MIN, FM_FREQ_MIN, FM_FREQ_MIN, FM_FREQ_MIN, FM_FREQ_MIN, FM_FREQ_MIN, FM_FREQ_MIN, FM_FREQ_MIN, FM_FREQ_MIN, FM_FREQ_MIN, FM_FREQ_MIN, FM_FREQ_MIN };
unsigned char divider = 0;
unsigned char currentStation = 0;
unsigned int freq = 0;

ISR (TIM0_COMPA_vect) //~20Hz
{
   if (++divider >= 5) //~4Hz
   {
      if (!(PINB & (1 << 5))) vars |= 1; //+
      if (!(PINB & (1 << 3))) vars |= 1 << 1; //-
      if (!(PINB & (1 << 4))) vars |= 1 << 2; else vars &= ~(1 << 2); //next
      if (!(PINB & (1 << 1))) vars |= 1 << 3; else vars &= ~(1 << 3); //save
      divider = 0;
   }
}

int main(void)
{
   if (freq == 0)
   freq = eeprom_read_word(&stations[currentStation]);

   CLKPR = 0x00;

   // Input/Output Ports initialization
   // Port B initialization
   // Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
   // State5=P State4=P State3=P State2=T State1=P State0=T
   DDRB = 0b000000;
   PORTB = 0b111010;

   // Timer/Counter 0 initialization
   // Clock source: System Clock
   // Clock value: 4,688 kHz
   // Mode: CTC top=OCR0A
   // OC0A output: Disconnected
   // OC0B output: Disconnected
   TCCR0A = 0x02;
   TCCR0B = 0x05;
   TCNT0 = 0x00;
   OCR0A = 0xea;
   OCR0B = 0x00;

   // Timer/Counter 0 Interrupt(s) initialization
   TIMSK0 = 0x04;

   set_sleep_mode(SLEEP_MODE_IDLE);
   
   asm volatile("sei");
   
   /* Replace with your application code */
   while (1)
   {
      if (vars & 1) //+
      {
         if (++freq >= FM_FREQ_MAX) freq = FM_FREQ_MIN;
         tea5767SetFreq(freq);
         vars &= ~1;
      }
      if (vars & (1 << 1)) //-
      {
         if (--freq <= FM_FREQ_MIN) freq = FM_FREQ_MAX;
         tea5767SetFreq(freq);
         vars &= ~(1 << 1);
      }
      if (vars & (1 << 2)) //next
      {
         currentStation = (currentStation < MAX_STATIONS) ? (currentStation - 1 > 0 && eeprom_read_word(&stations[currentStation]) == eeprom_read_word(&stations[currentStation - 1])) ? 0 : currentStation + 1 : 0;
         freq = eeprom_read_word(&stations[currentStation]);
         tea5767SetFreq(freq);
         while (vars & (1 << 2)) _delay_us(30);
      }
      if (vars & (1 << 3)) //save
      {
         eeprom_update_word(&stations[currentStation], freq);
         while (vars & (1 << 3)) _delay_us(30);
      }
      sleep_enable();
      sleep_cpu();
   }
}

void tea5767SetFreq(unsigned int freq)
{
   unsigned int div = ((unsigned long)freq * 12500 + 28125) >> 10; // /1024
   unsigned char var1 = (div >> 8) & 0x3F;
   asm volatile ("cli");
   i2c_start(0xC0);
   i2c_write(var1);
   i2c_write(div & 0xff);
   i2c_write(0b00001010);
   i2c_write(0b00011110);
   i2c_write(0);
   i2c_stop();
   asm volatile ("sei");
}

Схема во вложении. Хочу сделать всё сразу, т.к. выпаивать микру очень неудобно, а надо прошивать "Тритоном", т.к. Reset используется как порт.
Всё работает только когда отправляю в Idle режим.
Как правильно настроить PCINT, чтобы правильно срабатывали кнопки? Чтобы можно было отправлять в PWR_DOWN.
Вложения
1.PNG
(2.89 KiB) Скачиваний: 611
Ответить