Программирование на "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»