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

Обсуждаем контроллеры компании Atmel.
Ответить
Родился
Сообщения: 17
Зарегистрирован: Вс авг 26, 2012 17:46:05

Сообщение Avernul »

Доброго времени суток всем!
Нужна Ваша помощь с простой задачкой - МК, на одном пине (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();	// активация режима сна
		}
}

Я понимаю что код - не ахти (с МК вожусь считанные месяцы, через раз, по выходным), но в протеусе - работает как надо. А вот в реале - светодиод моргает непрерывно, пока нажата кнопка. Помогите пожалуста разобраться - где косяк?
Реклама
Поставщик валерьянки для Кота
Сообщения: 2222
Зарегистрирован: Вт ноя 27, 2007 11:32:06
Откуда: Tashkent

Сообщение uk8amk »

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

Для прерывания по низкому уровню вход дожен быть подтянут резистором ввех, кнопка замыкает на землю. Можно параллельно кнопке малый кондер добавить чтоб от наводок всяких избавиться.
Реклама
Поставщик валерьянки для Кота
Аватара пользователя
Сообщения: 1900
Зарегистрирован: Сб фев 21, 2009 13:11:40
Откуда: Москва

Сообщение ibiza11 »

а кнопку будут держать дольше 4 секунд или не обязательно?
Ставим плюсы: )
Родился
Сообщения: 17
Зарегистрирован: Вс авг 26, 2012 17:46:05

Сообщение Avernul »

ibiza11 писал(а):а кнопку будут держать дольше 4 секунд или не обязательно?
В том то и дело что дольше. Поэтому у меня такой кривоватый код в итоге получился.

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

А насчет IRQ - честно сказать не просвещен пока. В свое время удачно получилось режим глубокого сна организовать именно на Тини13 - power down. В этом режиме там почти все обесточено в МК, только независимо тактируемы вочдог фурычит. Вот от него то я и плясал. Поэтому и прерывания все - по вачдогу. Благо в Тини он несколько более широкими возможностями наделен, нежели в Мегах.
Реклама
Эиком - электронные компоненты и радиодетали
Поставщик валерьянки для Кота
Аватара пользователя
Сообщения: 1900
Зарегистрирован: Сб фев 21, 2009 13:11:40
Откуда: Москва

Сообщение ibiza11 »

тогда вообще зачем здесь микроконтроллер?)))
одновибратор на 4 секунды, а кнопка включает питание одновибратора)))
если обязательно с микроконтроллером, то можно аналогично кнопкой рулить питанием тиньки13, а в программе написать тупо:

Код: Выделить всё

LedOn();
delay_ms(4000);
LedOff();
while(1);
:write:
Ставим плюсы: )
Реклама
Родился
Сообщения: 17
Зарегистрирован: Вс авг 26, 2012 17:46:05

Сообщение Avernul »

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

Код: Выделить всё

LedOn();
delay_ms(4000);
LedOff();
while(1);
вообще у меня практически это и сделано. Но что-то не работает оно как надо.
Реклама
Поставщик валерьянки для Кота
Аватара пользователя
Сообщения: 1900
Зарегистрирован: Сб фев 21, 2009 13:11:40
Откуда: Москва

Сообщение ibiza11 »

то можно аналогично кнопкой рулить питанием тиньки13
Ставим плюсы: )
Родился
Сообщения: 17
Зарегистрирован: Вс авг 26, 2012 17:46:05

Сообщение Avernul »

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

Сообщение Ser60 »

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

Сообщение Avernul »

Попробовал переделать на прерывание по 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 (то же самое что мне не нравилось в предидущей версии программы). И все это время МК будет в нормальном режиме работать, а не энергосберегающем, можно ли как-то избавиться от этого? Все мои попытки привели пока только к тому что светодиод не гаснет, пока нажата кнопка...
Поставщик валерьянки для Кота
Сообщения: 2222
Зарегистрирован: Вт ноя 27, 2007 11:32:06
Откуда: Tashkent

Сообщение uk8amk »

Если в программном коде более понятно, то примерно такая структура выходит:

Код: Выделить всё

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();//теперь все совсем отключено
		};
	}
}
Родился
Сообщения: 17
Зарегистрирован: Вс авг 26, 2012 17:46:05

Сообщение Avernul »

uk8amk, спасибо!
А насчет PORTB &= ~_BV(PB4); не подскажете? Почему оно вдруг перестало нормально работать? Разве это не то же самое что и PORTB = 0b11101111;??? Я проверил - дело именно в PORTB, а не DDRB.
Друг Кота
Аватара пользователя
Сообщения: 3832
Зарегистрирован: Сб сен 10, 2011 17:46:25

Сообщение oleg110592 »

не одно и тоже первое - установить в "0" один пин порта, второе установить весь порт
тут почитайте: http://we.easyelectronics.ru/AVR/avr-po ... -spat.html
Родился
Сообщения: 17
Зарегистрирован: Вс авг 26, 2012 17:46:05

Сообщение Avernul »

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

Может я чего-то не учел?
Сверлит текстолит когтями
Аватара пользователя
Сообщения: 1296
Зарегистрирован: Ср мар 10, 2010 22:28:34
Откуда: Запад Беларуси

Сообщение Xatrix »

Написал прошивку под этот контроллер, чтобы отправлял частоту в радиомодуль по 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 КБ) 658 скачиваний
Изображение
Изображение
Ответить

Вернуться в «AVR»