ATtiny13 и режим IDLE

Обсуждаем контроллеры компании Atmel.
Ответить
Родился
Сообщения: 4
Зарегистрирован: Чт дек 14, 2017 11:07:27

Сообщение Pavel15121981 »

Доброго времени суток. Прошу проанализировать людей знающих. Вот написал такой код:

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

#define F_CPU 16000UL
#define fwd PORTB |=(1<<PB2); PORTB &=~(1<<PB0);		// fwd - вперед
#define bwd PORTB |=(1<<PB0); PORTB &=~(1<<PB2);		// bwd - назад
#define stp PORTB &=~(1<<PB0); PORTB &=~(1<<PB2);		// stp - стоп
#define pause1	4										// 6ч - 1350
#define pause2	3										// 3ч - 675
#define pause3	2										// 90мин - 330
#define pause4	1										// 45 мин - 165
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <avr/sleep.h>

// - Varriable -//
unsigned int timer=0;									// счетчик таймера (увеличивается в прерывании каждые 16 сек)

void movie ()
{
	fwd													// включаем двигатель вперед
	_delay_ms(600);										// пауза 0,5 секунды
	stp
	_delay_ms(100);
	bwd
	_delay_ms(300);
	stp
	timer = 0;
}

unsigned int pause ()									// время между сработками (pause*16.25)секунд
{
	ADMUX = (0<<REFS0)|									// Bit 6 – Источник опорного напряжения (0 - VCC, как источник опорного напряжения, 1 - Встроенный источник опорного напряжения)
			(1<<ADLAR)|
			(1<<MUX1)|									// Bit 1 – Analog Channel and Gain Selection Bits (Выбор канала ADC (см. datasheet)) в данном случае выбираем ADC2
			(0<<MUX0);									// Bit 0 – Analog Channel and Gain Selection Bits (Выбор канала ADC (см. datasheet))
	ADCSRA |= (1 << ADSC);								// Начинаем преобразование
	while ((ADCSRA & (1 << ADIF)) == 0);				// Ждем флага окончания преобразования
	ADCSRA|=(1<<ADIF);
	if (ADCH <= 53)										// в зависимости от АЦП изменяем величину переменной pause
		{
			return (pause4);
		}
	if ((ADCH <= 89) && (ADCH >= 54))
		{
			return (pause3);
		}
	if ((ADCH <= 125) && (ADCH >= 90))
		{
			return (pause2);
		}
	return (pause1);
}

bool light ()
{
	ADMUX = (0<<REFS0)|									// Bit 6 – Источник опорного напряжения (0 - VCC, как источник опорного напряжения, 1 - Встроенный источник опорного напряжения)
			(1<<ADLAR)|
			(1<<MUX1)|									// Bit 1 – Analog Channel and Gain Selection Bits (Выбор канала ADC (см. datasheet)) в данном случае выбираем ADC2
			(1<<MUX0);									// Bit 0 – Analog Channel and Gain Selection Bits (Выбор канала ADC (см. datasheet))
	ADCSRA |= (1 << ADSC);								// Начинаем преобразование
	while ((ADCSRA&(1 << ADIF))== 0);					// Ждем флага окончания преобразования
	ADCSRA|=(1<<ADIF);
	if ((ADCH <= 176))									// в зависимости от АЦП изменяем light
		{
			return false;			
		}
	else
		{
			return true;	
		}		
}

ISR (TIM0_OVF_vect)
{
	timer++;												//Увеличение timer на 1 каждые 16 секунд
	if (timer >= pause())									// если timer больше полученного значения pause
	{
		if (light())										// и "светло"
			{
				movie();									// срабатываем
				timer = 0;									// обнуляем timer
			}
		else												// а если timer больше полученного значения pause и "темно"
			{													
				asm volatile ("nop");
			}
	}
}

ISR (INT0_vect)
{
	movie();
	timer = 0;
}

int main(void)
{
    DDRB	= 0b00101;									//0 - Назад (выход), 1 - Кнопка (вход), 2 - вперед (выход), 3 - датчик света (вход), 4 - датчик режима(вход)
    PORTB	= 0b00010;
	TCCR0A	|=(0<<COM0A0)								//Инициализация таймера
			|(0<<COM0A1)
			|(0<<COM0B0)
			|(0<<COM0B1)
			|(0<<WGM01)
			|(0<<WGM00);
	TCCR0B	|=(0<<FOC0A)
			|(0<<FOC0B)
			|(0<<WGM02)
			|(1<<CS02)
			|(0<<CS01)
			|(1<<CS00);
	TCNT0	=0;
	ADMUX = (0<<REFS0)|									// Bit 6 – Источник опорного напряжения (0 - VCC, как источник опорного напряжения, 1 - Встроенный источник опорного напряжения)
			(1<<ADLAR); 								// Bit 5 – ADC Left Adjust Result (1 - левое выравнивание результата; 0 - правое выравнивание результата измерения)
	ADCSRA = (1<<ADEN)|									// Bit 7 – ADC включатель (1- включен, 0 - выключен)
			(0<<ADATE)|									// Bit 5 – ADC постоянное преобразование (1 - включено, 0 - выключено)
			(1<<ADPS2)|									// Bit 2 – ADC Prescaler Select Bits Выбор частоты преобразования (см. datasheet)
			(1<<ADPS1)|									// Bit 1 – ADC Prescaler Select Bits Выбор частоты преобразования (см. datasheet)
			(1<<ADPS0);									// Bit 0 – ADC Prescaler Select Bits Выбор частоты преобразования (см. datasheet)
	TIMSK0	|=(1<<TOIE0);								// Разрешение прерывания по переполнению таймера 0
	GIMSK	|=(1<<INT0);								// включение внешних прерываний
	sei();												// Общее разрешение прерываний
	  while (1) 
    {
        set_sleep_mode(SLEEP_MODE_IDLE); //Закомментировав 
        sleep_enable();                                 // эти строчки
        sleep_cpu();                                      // все работает так как надо!
	if (timer >= 5000)
		{
			cli();												// общий запрет прерываний
			TIMSK0 &=~(1<<TOIE0);								// отключение прерываний по таймеру (1 раз в 16 секунд)
			sei();												// общее разрешение прерываний
			set_sleep_mode(SLEEP_MODE_PWR_DOWN);				// установка "летаргического сна"
			sleep_enable();										// разрешение спящего режима
			sleep_cpu();										// сон
			sleep_disable();									// запрет сна
			cli();												// общий запрет прерываний
			TIMSK0	|=(1<<TOIE0);								// включение прерываний по переполнению таймера (1 раз в 16 секунд)
			sei();												// общее разрешение прерываний
			TCNT0	=0;											// сброс счетчика
			timer = 0;
		}
    }
}
В нем реализована сработка некоего двигателя с частотой, которая задается в переменной pause в зависимости от значения ADC3. Величина pause сравнивается с переменной timer, и когда timer становится >= pause, происходит сработка. Причем , если пришло время сработки, а переменная light (зависит от ADC2) = false, то сработки не будет. Весь этот алгоритм находится внутри прерывания по переполнению таймера 0, т.е. по моим настройкам примерно 1 раз в 16 секунд.
Все остальное время целесообразно спать.
Проблема: из режима IDLE контроллер по прерыванию то ли не выходит, толи выходит не полностью. ADC3 и ADC2 не реагируют на изменение напряжения. Как только закомментирую строчки перевода в спячку - все работает идеально. Кстати, внешнее прерывание в любом случае отрабатывает прекрасно! Ничего не понимаю. Помогите, кто чем может. Пожалуйста
Реклама
Родился
Сообщения: 4
Зарегистрирован: Чт дек 14, 2017 11:07:27

Сообщение Pavel15121981 »

Спасибо всем, кто не остался в стороне, Ответом на мои вопросы является фраза: "Не фиг в прерывании вызывать функции". Нужно перерабатывать алгоритм. Тему можно закрывать.
Реклама
Друг Кота
Аватара пользователя
Сообщения: 7077
Зарегистрирован: Пт ноя 11, 2016 05:48:09
Откуда: Сердце Пармы

Сообщение Ivanoff-iv »

с чего вдруг такое ограничение?

Добавлено after 2 minutes 36 seconds:
я наоборот стараюсь всё в прерывания засунуть и, тем самым, например, избавиться от конструкций типа _delay_ms (xxx).

Добавлено after 3 minutes 24 seconds:
так, по прерыванию "системного" таймера удобно разом и семисегментник крутить и кнопки (со встроенным антидребезгом и определением длительности и кратности нажатия) и прочую периферию разквартировать, чтобы у них дружная "коммуналка" получилась...
и функции применяю - полёт нормальный.
Для тех, кто не учил магию мир полон физики :)
Безграмотно вопрошающим про силовую или высоковольтную электронику я не отвечаю, а то ещё посадят за участие в (само)убиении оболтуса...
Друг Кота
Аватара пользователя
Сообщения: 6323
Зарегистрирован: Вт апр 24, 2007 07:45:40
Откуда: Minsk

Сообщение Jack_A »

Ivanoff-iv писал(а): и функции применяю - полёт нормальный.
Это при условии, что время выполнения функции гарантировано меньше периода таймера. Иначе результат непредсказуем.
СпойлерПомню, в оочень старом проекте на оооочень медленном старом ( выпускаемом ло сих пор ) МК довольно большое вычисление распиливал на части, и пока АЦП наполнял 64 точки первого буфера, МК без особой спешки просчитывал второй.
А delay - действительно зло. Таймерные прерывания нужно наполнять полезной работой - с учетом вышесказанного.
Это мое мнение, и я так думаю :)
Блин, юбилейный 4000-й пост! Пойду хлебну валерьянки
Изображение
Реклама
Эиком - электронные компоненты и радиодетали
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18678
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

Jack_A писал(а):А delay - действительно зло
и еще goto :)))
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Реклама
Друг Кота
Аватара пользователя
Сообщения: 6323
Зарегистрирован: Вт апр 24, 2007 07:45:40
Откуда: Minsk

Сообщение Jack_A »

Ну это смотря какой goto . В Фортране-IV это, пожалуй, единственный способ организации ветвлений и циклов :)
Изображение
Реклама
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18678
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

Jack_A писал(а):Ну это смотря какой goto
то есть delay() зло безапелляционно, а goto - еще бабушка надвое сказала? ;)
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Друг Кота
Аватара пользователя
Сообщения: 6323
Зарегистрирован: Вт апр 24, 2007 07:45:40
Откуда: Minsk

Сообщение Jack_A »

Yes, Sir !
Но вообще я безапелляционно никогда не заявляю. Даже в этом ответе - сомневаюсь :)
Изображение
Родился
Сообщения: 4
Зарегистрирован: Чт дек 14, 2017 11:07:27

Сообщение Pavel15121981 »

И все-таки проблема осталась. Код был упрощен до вот этого:

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

#define F_CPU 16000UL
#define fwd PORTB |=(1<<PB2); PORTB &=~(1<<PB0);		// fwd - вперед
#define bwd PORTB |=(1<<PB0); PORTB &=~(1<<PB2);		// bwd - назад
#define stp PORTB &=~(1<<PB0); PORTB &=~(1<<PB2);		// stp - стоп
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <avr/sleep.h>

unsigned int timer=0, pause;									// счетчик таймера (увеличивается в прерывании каждые 16 сек)
unsigned char light, i;

void movie ()
{
	fwd													// включаем двигатель вперед
	_delay_ms(500);										// пауза 0,5 секунды
	stp
	_delay_ms(100);
	bwd
	_delay_ms(200);
	stp
	timer = 0;
}

ISR (TIM0_OVF_vect)
{
	timer++;												//Увеличение timer на 1 каждые 16 секунд
	
}

ISR (INT0_vect)
{
	movie();
}

int main(void)
{
    DDRB	= 0b00101;									//0 - Назад (выход), 1 - Кнопка (вход), 2 - вперед (выход), 3 - датчик света (вход), 4 - датчик режима(вход)
    PORTB	= 0b00010;
	TCCR0A	|=(0<<COM0A0)								//Инициализация таймера
			|(0<<COM0A1)
			|(0<<COM0B0)
			|(0<<COM0B1)
			|(0<<WGM01)
			|(0<<WGM00);
	TCCR0B	|=(0<<FOC0A)
			|(0<<FOC0B)
			|(0<<WGM02)
			|(1<<CS02)
			|(0<<CS01)
			|(1<<CS00);
	TCNT0	=0;
	ADMUX = (0<<REFS0)|									
			(1<<ADLAR)| 								
			(1<<MUX1)|									
			(1<<MUX0);
	ADCSRA = (1<<ADEN)|									
			(0<<ADATE)|									
			(1<<ADPS2)|									
			(1<<ADPS1)|								
			(1<<ADPS0);								
	TIMSK0	|=(1<<TOIE0);								
	GIMSK	|=(1<<INT0);								
	sei();												
while (1) 
    {
		ADMUX	&=~(1<<MUX0);								
		ADCSRA	|= (1 << ADSC);								
		while ((ADCSRA&(1 << ADIF))== 0);					
		i= ADCH;
		if (ADCH >=171)										
		{
			pause = 1;
		}
		else
		{
			if ((ADCH < 171) && (ADCH > 122))
			{
				pause = 2;
			}
			else
			{
				if ((ADCH < 122) && (ADCH > 50))
				{
					pause = 3;
				}
				else
				{
					pause = 4;
				}
			}
			
		}
		
		ADMUX	|=(1<<MUX0);								
		ADCSRA	|= (1 << ADSC);								
		while ((ADCSRA&(1 << ADIF))== 0);					
		light = ADCH;
		
		if (timer >= pause)									
			{
				if (light<=100)										
					{
						movie();										
					}
				else												
					{													
						_delay_us(1);
					}
		
			}
		set_sleep_mode(SLEEP_MODE_IDLE);				//при комментировании 
		sleep_enable();								// этих строчек
		sleep_cpu();								// все работает
		sleep_disable();								// нормально
		if (timer >= 5000)
			{
				cli();												// общий запрет прерываний
				TIMSK0 &=~(1<<TOIE0);								// отключение прерываний по таймеру (1 раз в 16 секунд)
				sei();											// общее разрешение прерываний
				set_sleep_mode(SLEEP_MODE_PWR_DOWN);				// установка "летаргического сна"
				sleep_enable();										// разрешение спящего режима
				sleep_cpu();										// сон
				sleep_disable();									// запрет сна
				cli();												// общий запрет прерываний
				TIMSK0	|=(1<<TOIE0);								// включение прерываний по таймеру (1 раз в 16 секунд)
				sei();												// общее разрешение прерываний
				TCNT0	=0;											// сброс счетчика
				timer = 0;
			}
    }
}
ADC3 вообще не считывается! ADC2 считывается с ошибкой, т.е. не то, что должно быть. В чем подвох? В чем ошибка? Где собака зарыта? В протеусе работает идеально. Если IDLE убрать - тоже прекрасно работает.
Друг Кота
Аватара пользователя
Сообщения: 7077
Зарегистрирован: Пт ноя 11, 2016 05:48:09
Откуда: Сердце Пармы

Сообщение Ivanoff-iv »

почитай еррату на чип, может там чего есть?
Для тех, кто не учил магию мир полон физики :)
Безграмотно вопрошающим про силовую или высоковольтную электронику я не отвечаю, а то ещё посадят за участие в (само)убиении оболтуса...
Родился
Сообщения: 4
Зарегистрирован: Чт дек 14, 2017 11:07:27

Сообщение Pavel15121981 »

Случилось чудо!!
Наконец-то получилось!
Ответ нашелся в datasheet`e Attiny13 на стр.82, пункт 14.3, 3 абзац (перевод с помощью онлайн переводчика):
АЦП включен по настройка АЦП включить бит aden в ADCSRA. Опорное напряжение и
входных каналов не вступят в силу пока Аден установлен. АЦП не потребляет
мощность, когда Аден очищается, поэтому рекомендуется отключить перед входом АЦП мощности
сохранение режима сна.
как-то так...
Итог: в первом варианте перед переходом в IDLE устанавливаю ADEN в 0. И перед запуском конвертации устанавливаю ADEN в 1. Все работает прекрасно.
Может кому-нибудь мой опыт пригодится ;-)
Друг Кота
Аватара пользователя
Сообщения: 7077
Зарегистрирован: Пт ноя 11, 2016 05:48:09
Откуда: Сердце Пармы

Сообщение Ivanoff-iv »

как ни странно, но мне, примерно через месяц эта инфа скорее всего и пригодится :) тиньки 13 придут, полюбому чего нибудь из них наделаю.
Для тех, кто не учил магию мир полон физики :)
Безграмотно вопрошающим про силовую или высоковольтную электронику я не отвечаю, а то ещё посадят за участие в (само)убиении оболтуса...
Ответить

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