Прервать бесконечный цикл по таймеру - как?

Обсуждаем контроллеры компании Atmel.
Ответить
Родился
Сообщения: 5
Зарегистрирован: Пн дек 02, 2013 12:20:58

Сообщение aleksend »

Всем здравствуйте. Осваиваю свой первый в жизни микроконтроллер - Attiny2313. Хочу сделать подсветку на RGB-светодиоде. Алгоритм работы должен быть такой: при включении девайса RGB-светодиод выдает цвет из памяти (EEPROM). При нажатии кнопки запускается выбор цвета (светодиод переливается цветами по всему спектру). При отпускании кнопки светодиод горит выбранным цветом (которым он светился при отпускании кнопки). Вроде бы все просто. Но дело в том, что когда работает бесконечный цикл функции selcolor, на прерывание по таймеру МК не реагирует, и никак невозможно из этого бесконечного цикла его вырвать. Что я делаю не так. Или может кто-то подскажет более правильный алгоритм???
Код (реализацию ШИМ почерпнул в интернете). Работа с EEPROM пока не реализована. Кнопка висит на PB0, а светодиод на PD0,PD1,PD2:

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

#include <avr/io.h>//подключаем библиотеку ввода/вывода
#include <avr/eeprom.h>
#include <avr/interrupt.h>
    
#define nop() {asm("nop");}//функция необходимая для реализации задержки
     
volatile unsigned int ir,ig,ib,p,pp,c,buf,ms,x;//перечисление переменных    

// функция задержки
void delay_ms(int ms)
{
    for (x=ms;x>0;x--) nop();
}
     
// функция включения красного светодиода
void ledon0(int ms)
{
    PORTD &= ~_BV(PD0); delay_ms(ms);
}
Спойлер

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

// функция включения зеленого светодиода
void ledon1(int ms)
{
    PORTD &= ~_BV(PD1); delay_ms(ms);
}
     
// функция включения синего светодиода
void ledon2(int ms)
{
    PORTD &= ~_BV(PD2); delay_ms(ms);
}
     
// функция выключения красного светодиода
void ledoff0(int ms)
{
    PORTD |= _BV(PD0); delay_ms(ms);
}
     
// функция выключения зеленого светодиода
void ledoff1(int ms)
{
    PORTD |= _BV(PD1); delay_ms(ms);
}
     
// функция выключения синего светодиода
void ledoff2(int ms)
{
    PORTD |= _BV(PD2); delay_ms(ms);
}
  
void normwrk(void)
{
	while(1)
	{
		//Когда кнопка выбора цвета не нажата, остается выбранный цвет
		ledon1(ig); ledoff1(pp-ig);
		ledon0(ir); ledoff0(pp-ir);
		ledon2(ib); ledoff2(pp-ib);
	}
}   
void selcolor(void)
{
	while(1)
	{
			//Зажигаем красный светодиод
			PORTD &= ~_BV(PD0);
     
			//плавное зажигание зеленого светодиода
			for (ig = 0; ig < pp; ig++)
			{
				p=pp-ig; ledon1(ig); ledoff1(p);
			}
			//Зеленый светодиод остается гореть
			PORTD &= ~_BV(PD1);
     
			//плавное гашение красного светодиода
			for (ir = 0; ir < pp; ir++)
			{
				p=pp-ir; ledon0(p); ledoff0(ir);
			}
     
			//Зеленый светодиод остается гореть
			PORTD &= ~_BV(PD1);
     
			//плавное зажигание синего светодиода
			for (ib = 0; ib < pp; ib++)
			{
				p=pp-ib; ledon2(ib); ledoff2(p);
			}
     
			//Синий светодиод продолжает гореть
			PORTD &= ~_BV(PD2);
     
			//Плавное гашение зеленого светодиода
			for (ig = 0; ig < pp; ig++)
			{
				p=pp-ig; ledon1(p); ledoff1(ig);
			}
     
			//Синий светодиод продолжает гореть
			PORTD &= ~_BV(PD2);
     
			//постепенное зажигание красного светодиода
			for (ir = 0; ir < pp; ir++)
			{
				p=pp-ir; ledon0(ir); ledoff0(p);
			}
     
			//Красный светодиод продолжает гореть
			PORTD &= ~_BV(PD0);
     
			//Плавно тушим синий светодиод
			for (ib = 0; ib < pp; ib++)
			{
				p=pp-ib; ledon2(p); ledoff2(ib);
			}
	}
}
// interrupt service routine (ISR) for timer 0 A compare match
SIGNAL (SIG_TIMER0_COMPA)
{
	static char toggle = 0;
    // toggle the LED on each interrupt    
    if (toggle==0 && bit_is_set(PINB,PB0)) 
	{
		delay_ms(50);
		if (toggle==0 && bit_is_set(PINB,PB0))
		{
			toggle = 1;
			selcolor();
		}
    }
    else if (toggle==1 && !bit_is_set(PINB,PB0))
    {
           toggle = 0;
   	   normwrk();
    }
}
int main( void ) //главная программа
{

    //конфигурирование на вывод порта D
    DDRD |= _BV(PD0);//конфигурирование битов
    DDRD |= _BV(PD1);//PD0, PD1 и PD2
    DDRD |= _BV(PD2);//на выход
    PORTD = 0xFF; //включаем подтягивающие резисторы
    PORTB = 0b11111111; //Конфигурирование битов порта B ("в воздухе" с порта B будет читаться логическая 1)	
    DDRB = 0b00000000; //Конфигурирование порта B как входа 
     
    pp=255; //задаем скорость зажигания /гашения
    
	ir=ig=ib=128;
	TCCR0A = 0b00000000; //обычный режим таймера
	TCCR0B = 0b00000011; //режим предделителя CLK/1024
	TIMSK = 0b00000001; //разрешаем прерывания по переполнению
	sei();              // global interrupt enable

	normwrk();
	while(1){}
}
Последний раз редактировалось aleksend Пн дек 02, 2013 19:00:13, всего редактировалось 1 раз.
Реклама
Друг Кота
Аватара пользователя
Сообщения: 3872
Зарегистрирован: Пт янв 29, 2010 10:27:40
Откуда: Москва

Сообщение Engineer_Keen »

aleksend писал(а):когда работает бесконечный цикл функции selcolor, на прерывание по таймеру МК не реагирует, и никак невозможно из этого бесконечного цикла его вырвать. Что я делаю не так.
Вызов этого вашего selcolor уже идет из обработчика прерывания таймера (по совпадению с OCRA). Так как внутри обработчика прерывания эти самые прерывания выключены аппаратно, ничто его прервать не может, а так как в OCRA ничего не занесено, то там по умолчанию 0 и этот обработчик вместе с selcolor будет вызываться постоянно.

Меняйте структуру программы. Зачем вам selcolor в обработчике, проверяйте кнопку в основном цикле и оттуда его вызывайте.
Реклама
Родился
Сообщения: 5
Зарегистрирован: Пн дек 02, 2013 12:20:58

Сообщение aleksend »

проверяйте кнопку в основном цикле и оттуда его вызывайте.
То есть, вот так?

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

void normwrk(void)
{
   while(1)
   {
      //ШИМ, горит выбранный цвет
   }
}   
void selcolor(void)
{
   while(1)
   {
      //ШИМ, перебираются цвета по спектру
   }
}
SIGNAL (SIG_TIMER0_COMPA)
{
   //разве проверка кнопки делается не по таймеру?
}
int main( void ) //главная программа
{
    //конфигурирование портов и др.

   sei();              // global interrupt enable

   normwrk();
   while(1)
   {
     //тут на каждой итерации проверять кнопку? не шаг ли это назад по сравнению с прерываниями?
     if (bit_is_set(PINB,PB0))
     {
      delay_ms(50); 
      if (bit_is_set(PINB,PB0))
      {
         selcolor();
      }
    }
    else if (!bit_is_set(PINB,PB0))
    {
       normwrk();
    }
   }
}
Друг Кота
Аватара пользователя
Сообщения: 3872
Зарегистрирован: Пт янв 29, 2010 10:27:40
Откуда: Москва

Сообщение Engineer_Keen »

aleksend писал(а):То есть, вот так?
Да хоть так.
aleksend писал(а):разве проверка кнопки делается не по таймеру?
Хоть по таймеру, хоть по прерыванию от UART... Вы определяете как и когда проверять кнопку исходя из конкретной задачи.
aleksend писал(а):тут на каждой итерации проверять кнопку? не шаг ли это назад по сравнению с прерываниями?
А что, контроллер чем-то сильно занят? Между прочим для кнопок есть свои прерывания INT0-1 и PCINT0-7, так что если очень хочется через прерывания - логично использовать их.
Реклама
Эиком - электронные компоненты и радиодетали
Родился
Сообщения: 5
Зарегистрирован: Пн дек 02, 2013 12:20:58

Сообщение aleksend »

Ну ОК, переписал код по-вашим рекомендациям. Проверяю кнопку в основном цикле и оттуда вызываю рабочие функции. В результате же получил, что постоянно работает функция selcolor(), и МК не реагирует на кнопку :cry: И вообще, с какой стати при старте сразу же запускается selcolor()? Получается, сразу же при старте выставляется в лог.1 бит bit_is_set(PINB,PB0)? С какой стати-то? Мне кажется, пока не нажмем кнопку, вообще светодиоды не должны гореть. Что у меня не так?

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

#include <avr/io.h>//подключаем библиотеку ввода/вывода
    
#define nop() {asm("nop");}//функция необходимая для реализации задержки
     
volatile unsigned int ir,ig,ib,p,pp,c,buf,ms,x;//перечисление переменных    
    
// функция задержки
void delay_ms(int ms)
{
    for (x=ms;x>0;x--) nop();
}
  
void normwrk(void)
{
	while(1)
	{
           //шим, горит выбранный цвет
	}
}   
void selcolor(void)
{
	while(1)
	{
	   //шим, пробегаем цвета по спектру
	}
}

int main( void ) //главная программа
{
	static char toggle = 0;
	while(1)
	{
		if (toggle==0 && bit_is_set(PINB,PB0)) 
		{
                        //устранение дребезга
			delay_ms(50); 
			if (toggle==0 && bit_is_set(PINB,PB0))
			{
				toggle = 1;
				selcolor();
			}
		}
		else if (toggle==1 && !bit_is_set(PINB,PB0))
		{
			toggle = 0;
			normwrk();
		}
	}
}
Реклама
Друг Кота
Аватара пользователя
Сообщения: 3872
Зарегистрирован: Пт янв 29, 2010 10:27:40
Откуда: Москва

Сообщение Engineer_Keen »

Вполне возможно просто ловит помехи.
Вывод кнопки подтянуть к +питания (резистором или записав 1 в соответствующий бит PORTB), кнопку нажимать замыканием пина на землю. Нажатие проверять на "0" а не на "1".
Реклама
Родился
Сообщения: 5
Зарегистрирован: Пн дек 02, 2013 12:20:58

Сообщение aleksend »

На данный момент работоспособность я проверял в Proteus (реальный МК буду тестировать как приду домой), поэтому помехи исключены. Может у вас найдется рабочий алгоритм для подобного устройства? Ведь наверняка я изобретаю велосипед..
Друг Кота
Аватара пользователя
Сообщения: 3872
Зарегистрирован: Пт янв 29, 2010 10:27:40
Откуда: Москва

Сообщение Engineer_Keen »

aleksend писал(а):В результате же получил, что постоянно работает функция selcolor(), и МК не реагирует на кнопку :cry:
Ну так

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

void selcolor(void)
{
	while(1)
	{
	   //шим, пробегаем цвета по спектру
	}
}
Условие while(1) и будет вечно выполняться, стоит один раз сюда зайти и все, если внутри цикла никакого выхода не будет, то будут срабатывать только прерывания, а между ними этот цикл.
aleksend писал(а):И вообще, с какой стати при старте сразу же запускается selcolor()?
Вот это второй вопрос...
Я вообще си не люблю, так что условия в голове не могу точно смоделировать, но для начала, я бы убрал из рассмотрения переменную toggle и отладил выполнение кода без нее.
Собутыльник Кота
Аватара пользователя
Сообщения: 2512
Зарегистрирован: Пн апр 06, 2009 19:33:29
Откуда: Молдова, Кишинев

Сообщение BCluster »

Схему покажите. В протеусе тоже надо кнопку подтягивать. Вообще все проще решается.

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

void selcolor()
{
     //меняем цвет один раз, без while(1)
}

void normwork()
{
     while(1)
     {
         // выдаем сигнал на светодиод тут
         // тут же проверяем кнопку, пока она нажата, будем менять цвет.
              while (bit_is_set(PINB,PB0))
              {
                       selcolor();
              } // как только отпускаем кнопку возвращаемся в бесконечный цикл нормальной работы
      }
}

void main()
{
       normwork();
}
Контактная информация:
Собутыльник Кота
Аватара пользователя
Сообщения: 2512
Зарегистрирован: Пн апр 06, 2009 19:33:29
Откуда: Молдова, Кишинев

Сообщение BCluster »

Посмотрел ваш код выбора цвета. Он не годится ни для того, что предложил я, ни для того, как вы хотите сделать.
Контактная информация:
Родился
Сообщения: 5
Зарегистрирован: Пн дек 02, 2013 12:20:58

Сообщение aleksend »

Ну вот такая простая схемка.
proteus.jpg
(45.5 КБ) 683 скачивания
Собутыльник Кота
Аватара пользователя
Сообщения: 2512
Зарегистрирован: Пн апр 06, 2009 19:33:29
Откуда: Молдова, Кишинев

Сообщение BCluster »

В коде вы проверяете установку бита. А кнопка у вас на землю замыкает при нажатии, т.е. бит снимается.
Контактная информация:
Ответить

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