Страница 1 из 1
Программирование на "C"
Добавлено: Пн авг 05, 2013 14:56:51
gerrus
Добрый день!
Начал на днях заниматься Микроконтролерами и их программированием на "C".
Хочу для тренировки сделать программу для Светофора.

Светофор 1 и 2 сделал. переключаются как надо.но вот как быть с светофорами 3 и 4?они должны срабатывать только тогда когда кнопка S1 или 2 нажата будет.
Моя проблема в том что у меня в while (1) много "delay_ms", это значит что мне надо держать кнопку так долго пока программа не начнет цикл заново.
подскажите как можно осуществить,что бы s1 запоминался и после одного цикла ставил светофор 1 и 2 на красный , 3 и 4 на зелöный?
надо ли начинать изучать таймеры или все таки можно еще как то без них обойтись?
Спасибо!!
Re: Программирование на "C"
Добавлено: Пн авг 05, 2013 15:06:22
eess9
А внешние прерывания не пробовали?
Re: Программирование на "C"
Добавлено: Пн авг 05, 2013 15:17:43
Аlex
Зачем они нужны ? Достаточно периодичного прерывания от таймера, в нём опрашиваем входы кнопок и ставим соответствующие флаги. Эти флаги уже обрабатываем в main в любое удобное время.
Собственно, тема про кнопки уже есть, даже с примерами.
Re: Программирование на "C"
Добавлено: Пн авг 05, 2013 15:21:50
gerrus
Вы наверное про Interrupts?
К сожалению ,так же как и таймеры, они мне еще мало известны .
Почему то на надеялся на то, что можно как то с "if" или "switch" или "while" (петля?

) сделать?!
А разве Interrupt не прерывает программу сразу?Именно этого не хотелось. Хотелось что бы "зеленый цикл" для машин до горел нормально, а потом долгий "красный цикл" плюс пешеходный светофор.
Re: Программирование на "C"
Добавлено: Пн авг 05, 2013 15:22:43
gerrus
Аlex писал(а):Зачем они нужны ? Достаточно периодичного прерывания от таймера, в нём опрашиваем входы кнопок и ставим соответствующие флаги. Эти флаги уже обрабатываем в main в любое удобное время.
Собственно, тема про кнопки уже есть, даже с примерами.
Не подскажите как найти?

Re: Программирование на "C"
Добавлено: Пн авг 05, 2013 15:25:32
Аlex
Re: Программирование на "C"
Добавлено: Пн авг 05, 2013 15:29:37
Аlex
А разве Interrupt не прерывает программу сразу?Именно этого не хотелось.
Ну он же вернёт потом программный счётчик "на место".
Re: Программирование на "C"
Добавлено: Пн авг 05, 2013 16:55:08
eess9
Re: Программирование на "C"
Добавлено: Чт авг 08, 2013 00:45:19
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);
}
}
}
Re: Программирование на "C"
Добавлено: Чт авг 08, 2013 11:20:12
eess9
А как вы определили что прерывание выполняется сразу при старте контроллера?
Это очень странно. Согласно коду не должно.
А вы уверены, что у вашей схемы не возникает на прерывании спадающий фронт при старте?
Возможно причина в том, что вы прерывания определили раньше чем направления портов. Сделайте инициализацию прерывания непосредственно перед глобальным разрешением.
Re: Программирование на "C"
Добавлено: Чт авг 08, 2013 12:29:25
gerrus
Определил очень просто.Я в функции "ISR" включал "левый" LED на пару секунд,как тест.
Да Вы и сами можете испробовать в том же Proteus.Он так же как и Hardware (в данном случае) работает.
А вы уверены, что у вашей схемы не возникает на прерывании спадающий фронт при старте?
Не уверен.Вот именно в этом наверное и проблема.но не в схеме дело.там не чего сложного нет.Кнопка на массу.
Возможно причина в том, что вы прерывания определили раньше чем направления портов. Сделайте инициализацию прерывания непосредственно перед глобальным разрешением.
А вот за это огромнейшее Спасибо!!Скорее всего именно в этом и проблема!Дома буду попробую местами поменять!Как же сам не додумался:)
Хотя я и так уже сперва global interrups отключал
(
)
не помогло.поэтому думал что всё дело в микроконтроллере самом.
Спасибо
Re: Программирование на "C"
Добавлено: Чт авг 08, 2013 12:59:42
eess9
Вы запретили прерыванию войти в обработчик, но не установить свой флаг.
Как только вы прерывание разрешили (а флаг уже стоит), он сразу в него вывалился (в обработчик прерывания).
Еще вариант: перед разрешением прерываний, сбросьте все флаги всех прерываний, которые вы разрешаете.
Re: Программирование на "C"
Добавлено: Чт авг 08, 2013 13:36:44
a_skr
Да, по крайней мере, в протеусе по включению устанавливаются флаги прерываний INTF0 и INTF1 в регистре GIFR. Так что, когда прерывания станут разрешенными - сразу сработает обработчик. Как выше сказали, перед разрешением прерываний сбросить INTF1:
GIFR |= 1<<INTF1;
Еще, по коду:
В начале прерывания запрещены, так что, cli() - лишнее.
//PORT A als Ausgang
PORTC = 0x00; - наверное, опечатка
но, все-равно, не имеет значения, т.к. там нули.
DDRD = 1<<PD3;
это неправильно, т.к. порт на вход - убрать.
для этого:
SREG |= (1<<7);
есть функция sei() ( по аналогии с cli() )
И все-равно придется программу переделать, если хотите немедленной реакции на кнопку.
Re: Программирование на "C"
Добавлено: Чт авг 08, 2013 15:57:06
kolobok0
gerrus писал(а):...прочитал что в аземблере надо просто первый адрес памяти перепрыгнуть.....
это если компилятор ничего не знает о структуре программы(читай прошивки). думаю что Ваш компилятор на сях (если ему выставить правильный камень) - сделает это на автомате(в зависимости что юзаете. если специализированную среду разработки под МК - так и будет).
проверить (очень часто такое бывает при затыках на ровном месте - чтоб отсечь лишнии сомнения) можно заглянув в промежуточные файлы которые создаёт компилятор и линкер. Для этого надо указать им необходимые флажки при вызове. Если компиляете в среде - то ищите в пропертях проджэкта или солюшена. типа генерить аут файл(ы). обычно создаётся листинг на азме, карта линковки всех файлов и ышо пару вспомогательных форматов. заглянув туда и убедившись что попадает у вас на таблицу векторов и на вектор сброса - картина станет понятней.
удачи вам
(круглый)
Re: Программирование на "C"
Добавлено: Чт авг 08, 2013 18:09:59
gerrus
@ eess9
Поменял местами!Теперь всё как положено:)
@ a_skr
Спасибо за регистор GIFR.буду знать!
cli() убрал,действительно лишнее.
PORTA опечатка,но я им не пользуюсь

DDRD = 1<<PD3;
это неправильно, т.к. порт на вход - убрать.
Почему не правильно?Я подтянул PULL-UP,что бы потом на массу замкнуть?!
Программа работает иммено так как я и за думал!Ибо хочу что бы зеленый для пешеходов сработал только при следуйшей красной фазе для машин!
----------
Другой вопрос:
хотелось бы "delay" убрать и заменить таймерами.Но пока не пойму как.Логический не пойму. Может кто пнёт в правильную сторону?Код не надо,а вот диаграммку не большую не плохо было бы.ну или может есть где что интересное почитать?
Спасибо Большое за Помощь!
Re: Программирование на "C"
Добавлено: Чт авг 08, 2013 19:01:06
eess9
Re: Программирование на "C"
Добавлено: Чт авг 08, 2013 19:20:22
a_skr
gerrus писал(а):DDRD = 1<<PD3;
это неправильно, т.к. порт на вход - убрать.
Почему не правильно?Я подтянул PULL-UP,что бы потом на массу замкнуть?!
DDRD - это регистр направления: выход (1) или вход (0) для каждого пина.
Если нужна подтяжка на вход, то нужно записать единицу в соотв. бит регистра PORTD, как у Вас и сделано следующей командой:
PORTD = 1<<PD3;
Re: Программирование на "C"
Добавлено: Чт авг 08, 2013 20:44:53
gerrus
DDRD - это регистр направления: выход (1) или вход (0) для каждого пина.
Если нужна подтяжка на вход, то нужно записать единицу в соотв. бит регистра PORTD, как у Вас и сделано следующей командой:
PORTD = 1<<PD3;
Вы совершенно правы!! Перепутал..
но к моему удивлению всё равно работало.
@eess9 Благодарю! Буду читать.
Хочу перекрёсток сделать. то есть тоже самое X2.и противоположно.Если когда нибудь бамбино появится,сделаю светофоры ему

Re: Программирование на "C"
Добавлено: Пн авг 12, 2013 20:43:34
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: Да и вообще любым советам только рад!!Может вообще ерунду пишу и есть другая дорога?легче?надежней?
Спасибо!