Программирование на "C"

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

Программирование на "C"

Сообщение gerrus »

Добрый день!

Начал на днях заниматься Микроконтролерами и их программированием на "C".

Хочу для тренировки сделать программу для Светофора.

Изображение

Светофор 1 и 2 сделал. переключаются как надо.но вот как быть с светофорами 3 и 4?они должны срабатывать только тогда когда кнопка S1 или 2 нажата будет.

Моя проблема в том что у меня в while (1) много "delay_ms", это значит что мне надо держать кнопку так долго пока программа не начнет цикл заново.
подскажите как можно осуществить,что бы s1 запоминался и после одного цикла ставил светофор 1 и 2 на красный , 3 и 4 на зелöный?

надо ли начинать изучать таймеры или все таки можно еще как то без них обойтись?

Спасибо!!
Реклама
Аватара пользователя
eess9
Вымогатель припоя
Сообщения: 672
Зарегистрирован: Ср фев 29, 2012 01:58:32
Откуда: Харьков, Украина

Re: Программирование на "C"

Сообщение eess9 »

А внешние прерывания не пробовали?
Реклама
Аватара пользователя
Аlex
Модератор
Сообщения: 4614
Зарегистрирован: Чт мар 18, 2010 23:09:57
Откуда: Планета Земля
Контактная информация:

Re: Программирование на "C"

Сообщение Аlex »

Зачем они нужны ? Достаточно периодичного прерывания от таймера, в нём опрашиваем входы кнопок и ставим соответствующие флаги. Эти флаги уже обрабатываем в main в любое удобное время.

Собственно, тема про кнопки уже есть, даже с примерами.
gerrus
Родился
Сообщения: 14
Зарегистрирован: Пн авг 05, 2013 14:12:40

Re: Программирование на "C"

Сообщение gerrus »

Вы наверное про Interrupts?
К сожалению ,так же как и таймеры, они мне еще мало известны .
Почему то на надеялся на то, что можно как то с "if" или "switch" или "while" (петля? :) ) сделать?!

А разве Interrupt не прерывает программу сразу?Именно этого не хотелось. Хотелось что бы "зеленый цикл" для машин до горел нормально, а потом долгий "красный цикл" плюс пешеходный светофор.
Реклама
Эиком - электронные компоненты и радиодетали
gerrus
Родился
Сообщения: 14
Зарегистрирован: Пн авг 05, 2013 14:12:40

Re: Программирование на "C"

Сообщение gerrus »

Аlex писал(а):Зачем они нужны ? Достаточно периодичного прерывания от таймера, в нём опрашиваем входы кнопок и ставим соответствующие флаги. Эти флаги уже обрабатываем в main в любое удобное время.

Собственно, тема про кнопки уже есть, даже с примерами.
Не подскажите как найти? :oops:
Реклама
Аватара пользователя
Аlex
Модератор
Сообщения: 4614
Зарегистрирован: Чт мар 18, 2010 23:09:57
Откуда: Планета Земля
Контактная информация:

Re: Программирование на "C"

Сообщение Аlex »

Реклама
Аватара пользователя
Аlex
Модератор
Сообщения: 4614
Зарегистрирован: Чт мар 18, 2010 23:09:57
Откуда: Планета Земля
Контактная информация:

Re: Программирование на "C"

Сообщение Аlex »

А разве Interrupt не прерывает программу сразу?Именно этого не хотелось.
Ну он же вернёт потом программный счётчик "на место".
Аватара пользователя
eess9
Вымогатель припоя
Сообщения: 672
Зарегистрирован: Ср фев 29, 2012 01:58:32
Откуда: Харьков, Украина

Re: Программирование на "C"

Сообщение eess9 »

gerrus
Родился
Сообщения: 14
Зарегистрирован: Пн авг 05, 2013 14:12:40

Re: Программирование на "C"

Сообщение gerrus »

С interrupt я вроде как бы разобрался.(покрайней мере с hardware-interrupt)
но вот только одна проблема.после каждой прошивки и после hardware-reset всегда исполняется функция interrupt (ISR) (первым делом.то есть сразу с начало она,потом void main)
прочитал что в аземблере надо просто первый адрес памяти перепрыгнуть.
А можно такое и на "С" как то сделать?
или может есть другой способ изменить программу что бы контроллер после reset не исполнял сначала Interrupt-Функцию?
Спойлер

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

/*
 * Ampel.c
 *
 * Created: 07.08.2013 10:36:06
 *  Author: ad
 */ 

#define F_CPU 1600000UL

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

// eigene Bezeichnungen
#define		rot		PC0
#define		gelb	PC1
#define		gruen	PC2
#define		rotf		PC4
#define		gruenf	PC5
#define		ein		1
#define		aus		0

volatile unsigned char _taster=0;


ISR (INT1_vect)
{
	/*_delay_ms(80);
	PORTC	 |= (1 << PC4);
	_delay_ms(1000);
	PORTC &=~(1<<PC4);*/
	//unsigned char temp;
	//temp=_taster;
	_delay_ms(80);
	_taster=1;
	
	
}
void rotf_schalten(unsigned int i)
{
	if (i==ein){PORTC	 |= (1 << rotf);}   // Setzen
	else {PORTC &= ~(1 << rotf);}  // Rücksetzen
	//_delay_ms(1000);
	
	
};
void gruenf_schalten(unsigned int i)
{
	if (i==ein){PORTC	 |= (1 << gruenf);}   // Setzen
	else {PORTC &= ~(1 << gruenf);}  // Rücksetzen
	//_delay_ms(1000);
	
	
};
void rot_schalten(unsigned int i)
{
	if (i==ein){PORTC	 |= (1 << rot);}   // Setzen
	else {PORTC &= ~(1 << rot);}  // Rücksetzen
	//_delay_ms(1000);
	
	
	};

void gelb_schalten(unsigned int i)
{
	if (i==ein){PORTC	 |= (1 << gelb);}   // Setzen
	else {PORTC &= ~(1 << gelb);}  // Rücksetzen
	//_delay_ms(1000);
	
	
};


void gruen_schalten(unsigned int i)
{
	if (i==ein){PORTC	 |= (1 << gruen);}   // Setzen
	else {PORTC &= ~(1 << gruen);}  // Rücksetzen
	//_delay_ms(1000);
	
	
};


int main(void)
{	cli();

	 GICR |= (1 << INT1);               
	 MCUCR |= (1 << ISC11);
	//PORT A als Ausgang
	PORTC	=	0x00;
	DDRA	=	0xFF;
	
	//PORT C als Ausgang
	PORTC	=	0x00;
	DDRC	=	0xFF;
	
	//PORT D als Eingang
	
	DDRD	=	1<<PD3;
	PORTD	=	1<<PD3;
	SREG |= (1<<7);
	
    while(1)
    {
		
		if (_taster==1)
		{
			_taster=0;
			rot_schalten(ein);
			rotf_schalten(ein);
			_delay_ms(1300);
			rotf_schalten(aus);
			gruenf_schalten(ein);
			_delay_ms(3000);
			gruenf_schalten(aus);
			rotf_schalten(ein);
			_delay_ms(1100);
			gelb_schalten(ein);
			_delay_ms(1600);
			rot_schalten(aus);
			gelb_schalten(aus);
			gruen_schalten(ein);
			_delay_ms(3000);
			gruen_schalten(aus);
			gelb_schalten(ein);
			_delay_ms(1600);
			gelb_schalten(aus);
		} 
		else
		{
		rotf_schalten(ein);
		rot_schalten(ein); 
		_delay_ms(3000);
		gelb_schalten(ein);
		_delay_ms(1600);
		rot_schalten(aus);
		gelb_schalten(aus);
		gruen_schalten(ein);
		_delay_ms(3000);
		gruen_schalten(aus);
		gelb_schalten(ein);
		_delay_ms(1600);
		gelb_schalten(aus);
		}
		
    }
}

Аватара пользователя
eess9
Вымогатель припоя
Сообщения: 672
Зарегистрирован: Ср фев 29, 2012 01:58:32
Откуда: Харьков, Украина

Re: Программирование на "C"

Сообщение eess9 »

А как вы определили что прерывание выполняется сразу при старте контроллера?
Это очень странно. Согласно коду не должно.
А вы уверены, что у вашей схемы не возникает на прерывании спадающий фронт при старте?
Возможно причина в том, что вы прерывания определили раньше чем направления портов. Сделайте инициализацию прерывания непосредственно перед глобальным разрешением.
gerrus
Родился
Сообщения: 14
Зарегистрирован: Пн авг 05, 2013 14:12:40

Re: Программирование на "C"

Сообщение gerrus »

Определил очень просто.Я в функции "ISR" включал "левый" LED на пару секунд,как тест.

Да Вы и сами можете испробовать в том же Proteus.Он так же как и Hardware (в данном случае) работает.


А вы уверены, что у вашей схемы не возникает на прерывании спадающий фронт при старте?
Не уверен.Вот именно в этом наверное и проблема.но не в схеме дело.там не чего сложного нет.Кнопка на массу.
Возможно причина в том, что вы прерывания определили раньше чем направления портов. Сделайте инициализацию прерывания непосредственно перед глобальным разрешением.
А вот за это огромнейшее Спасибо!!Скорее всего именно в этом и проблема!Дома буду попробую местами поменять!Как же сам не додумался:)

Хотя я и так уже сперва global interrups отключал
( )
не помогло.поэтому думал что всё дело в микроконтроллере самом.

Спасибо
Аватара пользователя
eess9
Вымогатель припоя
Сообщения: 672
Зарегистрирован: Ср фев 29, 2012 01:58:32
Откуда: Харьков, Украина

Re: Программирование на "C"

Сообщение eess9 »

Вы запретили прерыванию войти в обработчик, но не установить свой флаг.
Как только вы прерывание разрешили (а флаг уже стоит), он сразу в него вывалился (в обработчик прерывания).

Еще вариант: перед разрешением прерываний, сбросьте все флаги всех прерываний, которые вы разрешаете.
a_skr
Вымогатель припоя
Сообщения: 630
Зарегистрирован: Пн июн 14, 2010 13:07:29
Откуда: Жуковский

Re: Программирование на "C"

Сообщение a_skr »

Да, по крайней мере, в протеусе по включению устанавливаются флаги прерываний INTF0 и INTF1 в регистре GIFR. Так что, когда прерывания станут разрешенными - сразу сработает обработчик. Как выше сказали, перед разрешением прерываний сбросить INTF1:

GIFR |= 1<<INTF1;

Еще, по коду:
В начале прерывания запрещены, так что, cli() - лишнее.
//PORT A als Ausgang
PORTC = 0x00; - наверное, опечатка
но, все-равно, не имеет значения, т.к. там нули.

DDRD = 1<<PD3;
это неправильно, т.к. порт на вход - убрать.

для этого:
SREG |= (1<<7);
есть функция sei() ( по аналогии с cli() )

И все-равно придется программу переделать, если хотите немедленной реакции на кнопку.
kolobok0
Грызет канифоль
Сообщения: 296
Зарегистрирован: Ср дек 30, 2009 09:55:39

Re: Программирование на "C"

Сообщение kolobok0 »

gerrus писал(а):...прочитал что в аземблере надо просто первый адрес памяти перепрыгнуть.....
это если компилятор ничего не знает о структуре программы(читай прошивки). думаю что Ваш компилятор на сях (если ему выставить правильный камень) - сделает это на автомате(в зависимости что юзаете. если специализированную среду разработки под МК - так и будет).

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

удачи вам
(круглый)
gerrus
Родился
Сообщения: 14
Зарегистрирован: Пн авг 05, 2013 14:12:40

Re: Программирование на "C"

Сообщение gerrus »

@ eess9
Поменял местами!Теперь всё как положено:)

@ a_skr
Спасибо за регистор GIFR.буду знать!

cli() убрал,действительно лишнее.

PORTA опечатка,но я им не пользуюсь :)
DDRD = 1<<PD3;
это неправильно, т.к. порт на вход - убрать.
Почему не правильно?Я подтянул PULL-UP,что бы потом на массу замкнуть?!

Программа работает иммено так как я и за думал!Ибо хочу что бы зеленый для пешеходов сработал только при следуйшей красной фазе для машин!

-------------

Другой вопрос:

хотелось бы "delay" убрать и заменить таймерами.Но пока не пойму как.Логический не пойму. Может кто пнёт в правильную сторону?Код не надо,а вот диаграммку не большую не плохо было бы.ну или может есть где что интересное почитать? :oops:

Спасибо Большое за Помощь!
Аватара пользователя
eess9
Вымогатель припоя
Сообщения: 672
Зарегистрирован: Ср фев 29, 2012 01:58:32
Откуда: Харьков, Украина

Re: Программирование на "C"

Сообщение eess9 »

a_skr
Вымогатель припоя
Сообщения: 630
Зарегистрирован: Пн июн 14, 2010 13:07:29
Откуда: Жуковский

Re: Программирование на "C"

Сообщение a_skr »

gerrus писал(а):
DDRD = 1<<PD3;
это неправильно, т.к. порт на вход - убрать.
Почему не правильно?Я подтянул PULL-UP,что бы потом на массу замкнуть?!
DDRD - это регистр направления: выход (1) или вход (0) для каждого пина.
Если нужна подтяжка на вход, то нужно записать единицу в соотв. бит регистра PORTD, как у Вас и сделано следующей командой:
PORTD = 1<<PD3;
gerrus
Родился
Сообщения: 14
Зарегистрирован: Пн авг 05, 2013 14:12:40

Re: Программирование на "C"

Сообщение gerrus »

DDRD - это регистр направления: выход (1) или вход (0) для каждого пина.
Если нужна подтяжка на вход, то нужно записать единицу в соотв. бит регистра PORTD, как у Вас и сделано следующей командой:
PORTD = 1<<PD3;
Вы совершенно правы!! Перепутал..
но к моему удивлению всё равно работало.


@eess9 Благодарю! Буду читать.

Хочу перекрёсток сделать. то есть тоже самое X2.и противоположно.Если когда нибудь бамбино появится,сделаю светофоры ему :) 8)
gerrus
Родился
Сообщения: 14
Зарегистрирован: Пн авг 05, 2013 14:12:40

Re: Программирование на "C"

Сообщение gerrus »

Товарищи помогите новичку.

Перешел на таймер.Вроде бы как работает.

Но вот одно но.Не могу "уловить" кнопку.

Засунул проверку уже чуть ли не в каждую вторую строку.всё равно не работает толком.

Не уж то не обойтись Interrupt`ом ? может всё таки где то что то не доглядел?

Принцип всё тот же: Светофор работает ТОЛЬКО для машин(void schalten_ohne (void) ).до тех пор пока кнопка PD3 не будет нажата. Тогда ,при след ушей красной фазе для машин, загорается зеленый для пешеходов(void schalten_mit (void)).
Спойлер

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

/*
 * Ampel_mit_Timer.c
 *
 * Created: 10.08.2013 10:05:38
 *  Author: Gerrus
 */ 

#define F_CPU 4000000UL //Takt auf 4Mhz festlegen





#include <avr/io.h>
#include <avr/interrupt.h> //Include fürs INterrupt

// eigene Bezeichnungen
#define		rot		PC0
#define		gelb	PC1
#define		gruen	PC2
#define		rotf	PC4
#define		gruenf	PC5
#define		ein		1
#define		aus		0

volatile  int sec,igr;
void rotf_schalten(unsigned int i)
{
	if (i==ein){PORTC	 |= (1 << rotf);}   // Setzen
	else {PORTC &= ~(1 << rotf);}  // Rücksetzen
	
	
	
};
void gruenf_schalten(unsigned int i)
{
	if (i==ein){PORTC	 |= (1 << gruenf);}   // Setzen
	else {PORTC &= ~(1 << gruenf);}  // Rücksetzen
	
	
	
};
void rot_schalten(unsigned int i)
{
	if (i==ein){PORTC	 |= (1 << rot);}   // Setzen
	else {PORTC &= ~(1 << rot);}  // Rücksetzen
	
	
	
};
void gelb_schalten(unsigned int i)
{
	if (i==ein){PORTC	 |= (1 << gelb);}   // Setzen
	else {PORTC &= ~(1 << gelb);}  // Rücksetzen
	
	
	
};
void gruen_schalten(unsigned int i)
{
	if (i==ein){PORTC	 |= (1 << gruen);}   // Setzen
	else {PORTC &= ~(1 << gruen);}  // Rücksetzen
	//_delay_ms(1000);
	
	
};
void schalten_ohne (void)
{
	if (!(PIND &(1<<PD3)))
	{
		sec=1;
	}
	if (igr==0)
	{
		rot_schalten(ein);
		
	}
	if (!(PIND &(1<<PD3)))
	{
		sec=1;
	}
	if (igr==76)
	{
		gelb_schalten(ein);
	}
	if (!(PIND &(1<<PD3)))
	{
		sec=1;
	}
	if (igr==107)
	{
		rot_schalten(aus);
		gelb_schalten(aus);
		gruen_schalten(ein);
	}
	if (!(PIND &(1<<PD3)))
	{
		sec=1;
	}
	if (igr==183)
	{
		gruen_schalten(aus);
		gelb_schalten(ein);
	}
	if (!(PIND &(1<<PD3)))
	{
		sec=1;
	}
	if (igr==213)
	{
		gelb_schalten(aus);
		igr=0;
	}
	
}
void schalten_mit (void)
{
	if (!(PIND &(1<<PD3)))
	{
		sec=1;
		
	}
	
	if (igr==0)
	{
		rot_schalten(ein);
		rotf_schalten(ein);
	}
	if (igr==15)
	{
		rotf_schalten(aus);
		gruenf_schalten(ein);
	}
	if (igr==61)
	{
		gruenf_schalten(aus);
		rotf_schalten(ein);
	}
	if (igr==76)
	{
		gelb_schalten(ein);
	}
	if (igr==107)
	{
		rot_schalten(aus);
		gelb_schalten(aus);
		gruen_schalten(ein);
	}
	if (igr==183)
	{
		gruen_schalten(aus);
		gelb_schalten(ein);
	}
	if (igr==213)
	{
		rotf_schalten(aus);
		gelb_schalten(aus);
		igr=0;
		sec=0;
	}

	
}




ISR (TIMER0_OVF_vect)					// Die Funktion die beim Overflow aufgerufen wird
{
	
	 igr ++;
	if (!(PIND &(1<<PD3)))
	{
		sec=1;
	}
}






int main(void)
{
	//PORT C als Ausgang
	PORTC	=	0x00;
	DDRC	=	0xFF;
	
	//PORT D als Eingang
	
	DDRD	=	0<<PD3;
	PORTD	=	1<<PD3;
	
	
	
	TCCR0 |= (1<<CS02)|(1<<CS00);	//Einstellen Von Preteiler 1/1024. Datasheet Seite 85
	TIMSK |= (1<<TOIE0);			//Interrupt auslösen beim Overflow Datasheet Seite 85
	TCNT0 = 0;						//den timer selber reseten.Eine Null reinschreiben
	sei();							//Interruos aktivieren
	
	
	while(1)
    {	
		if (!(PIND &(1<<PD3)))
	    {
		    sec=1;
	    }
		if (sec==0)
		{
			schalten_ohne();		//Ampelschaltung ohne fussgaenger
			
		}
		if (!(PIND &(1<<PD3)))
		{
			sec=1;
		}
		if (sec==1)
		{
			schalten_mit();			//Ampelschaltung mit fussgaenger
		}
		
		

	}
}
P.S: Да и вообще любым советам только рад!!Может вообще ерунду пишу и есть другая дорога?легче?надежней?

Спасибо!
Ответить

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