Си для микроконтроллеров и чайников. Часть 5. Структура программы: инициализация, фон, прерывания.. Раздел: Начинающим.

Eugene's MCU

Пятница, 04.10.2013, 13:35

Главная | | Мой профиль | Выход | RSS
Меню сайта
Категории каталога
Рекламный блок





Рекомендовать этот сайт:

Главная » Статьи » » Начинающим


Си для микроконтроллеров и чайников. Часть 5. Структура программы: инициализация, фон, прерывания.
Краткое предисловие для начинающих: прочитать  
      
       Очевидно, что любая программа представляет собой совокупность действий, описанных в соответствии с правилами языка программирования и предназначенных для исполнения конкретным устройством, в данном случае микроконтроллером. Каков порядок выполнения этих действий на практике, мы и попытаемся уяснить в данном разделе.
   
 
§ > Общая структура простейшей программы. Инициализация, фон.
 
       При рассмотрении программы на уровне языка Си можно сказать, что она начинает свою работу с первой строки функции main (строка 001 на рисунке):
       Далее последовательно выполняются строки 002, 003, 004, объёдинённые одним общим свойством: программа проходит по ним только один раз, при запуске микроконтроллера. Эту часть программы принято называть инициализационной.  Инициализационная часть - законное место для размещения действий по подготовке периферии микроконтроллера к работе с заданными параметрами - настройки портов на вход или выход, начальной инициализации таймеров, задания скорости и формата кадра UART и так далее для всего, что планируется использовать в дальнейшем.
  
       Поскольку любая программа предназначена для непрерывной работы, нормальный режим её функционирования - это безостановочное повторение по кругу содержимого бесконечного цикла. На практике такой цикл чаще всего реализуется с помощью конструкции while(1) { }, предназначенной для многократного выполнения действий, размещённых внутри её фигурных скобок. Содержимое бесконечного цикла программы называется фоном. Именно здесь происходит основная часть работы по проверке состояния аппаратной части и соответствующее воздействие на неё для получения нужного результата.
 
       Рассмотрим описанную структуру программы на простейшем примере. Пусть необходимо: отправлять по шине UART символ *, пока кнопка на выводе PA0 находится в нажатом состоянии (нулевой уровень сигнала). Программа в данном случае (без лишних процедур по подавлению дребезга кнопки и прочего) может выглядеть так:
       
 void main (void)
       {
              PORTA|=(1<<0); // Притянуть вход кнопки PORTA.0 внутренним pull-up резистором.
       
       UBRRL=51; // Скорость UART – 9600 bps.
       
       UCSRB = (1<<RXEN)|(1<<TXEN); // Разрешить выводы RX, TX.
              while (1)
       
       {
       
             if ( ! (PINA & (1<<0)) ) // Если кнопка нажата...
     
              {
     
                    while( ! (UCSRA & (1<<UDRE)) ) { } // Ждать освобождения UDR.
     
                    UDR = ' * '; // Отправить *.
     
              }
                    // другие команды фона:
                    00N
                    00N+1
                    ...
               }
       }
     
       Здесь конструкция if (...), расположенная в фоне программы проводит бесконечные опросы входного регистра PINA и проверку вывода PA0 на наличие низкого уровня. Далее выполняются другие действия фонового процесса, обозначенные строками 00N, 00N+1 и так далее.
 
       Какие факторы, применительно к данной программе, определяют самые важные параметры её работы - надёжность и быстродействие?
 
       Из примера видно, что частота опроса входа PA.0 определяется длительностью выполнения команд фона, ведь прежде чем в очередной раз опросить кнопку, микроконтроллер должен выполнить следующие за этим строки 00N, 00N+1 и т. д. Очевидно, что надёжность фиксации внешнего события (нажатия на кнопку) в данном случае будет зависеть от соотношения длительности воздействия этого события к периоду его детектирования. Длительность фона в данной программе наверняка будет во много раз меньше длительности удержания кнопки, которое на практике составляет несколько десятков миллисекунд. Однако при разрастании фоновой части программы и малом времени внешнего воздействия, надёжность его отслеживания в определённый момент резко снизится. Что бы этого не произошло, а также для снижения времени реакции программы на внешнее событие, используется система прерываний.

§ > Прерывания.
 
       Как работает механизм прерываний? Очень просто, особенно на уровне языка Си!
  
       В архитектуру микроконтроллеров AVR, впрочем как и любых других, на аппаратном уровне заложена способность отслеживать определённые "интересные состояния железа” и устанавливать при этом соответствующие биты-признаки. Такие состояния называются условиями возникновения прерываний, а устанавливаемые признаки - флагами прерываний. В процессе работы, микроконтроллер непрерывно отслеживает состояние этих флагов. При обнаружении любого установленного флага прерывания, при условии, что оно разрешено включением соответствующего бита, а также установлен бит глобального разрешения прерываний (№7 в регистре SREG для AVR), выполнение основной части программы будет временно приостановлено (прервано).
   
       Поскольку прерывание может возникнуть при выполнении любой произвольной команды фона, её адрес запоминается в так называемом программном стеке. После чего, выполнение предается на часть программы, специально написанную разработчиком для реакции на событие, вызвавшее данное прерывание. Эта небольшая часть программы называется обработчиком прерывания. Когда обработчик будет выполнен до конца, программа, воспользовавшись адресом, сохранённым в программном стеке, вернётся в то место, откуда была вызвана для обработки данного прерывания.
       Какова роль программиста в этом процессе? При разработке на Си она сведена к минимуму.
       Часть действий, как то отслеживание флагов прерываний реализованы на аппаратном уровне. Другую часть, например, защиту от изменений в обработчике важного для программы регистра статуса SREG, сохранение адреса программы в стеке и многое другое компилятор берёт на себя.  
       Единственное, в чём остаётся необходимость это:
  
       1. Разрешить использование прерываний в программе.
       2. Разрешить вызов интересующего нас прерывания специальным битом в соответствующем регистре. Каким именно и где подскажет описание на микроконтроллер.
       3. Создать условия для возникновения прерывания, например, если это переполнение таймера, то банально запустить его. Если это прерывание по изменению состояния внешнего вывода то задать нужные условия для этого (фронт, срез или нулевой уровень).
       4. Разместить в программе обработчик прерывания, оформив его в соответствии с требованиями компилятора.

       Применительно к нашему примеру организовать отправку в UART по низкому уровню на входе кнопки, можно используя так называемое внешнее прерывание INT0. Данное прерывание вызывается по фронту, срезу или нулевому уровню на выводе INT0. 
 
       Перенесем кнопку на вывод PD.2 с альтернативной функцией INT0. В инициализационной части программы разрешим прерывания глобально и INT0 конкретно. Микроконтроллер по-умолчанию настроен на формирование прерывания INT по низкому уровню входного сигнала, поэтому дополнительных настроек не потребуется. Остаётся объявить за пределами функции main обработчик INT0, отправляющий в UART символ *:
  
       void main (void)
       {
              PORTD|=(1<<2); // Притянуть вход кнопки PORTD.2 внутренним pull-up.
              UBRRL=51; // Скорость UART – 9600 bps.
              UCSRB = (1<<RXEN)|(1<<TXEN); // Разрешить RX,TX.
              SREG|=(1<<7); // Разрешить прерывания.
              GICR|=(1<<INT0); // Разрешить прерывание INT0.

              while (1){}
       }

       #pragma vector=INT0_vect // Обработчик прерывания INT0/
       __interrupt void INT0_INTPT()
       {
              if ( ! (PIND & (1<<2)) ) {while( ! (UCSRA & (1<<UDRE)) ) {} UDR = '*';}  
       }


       Здесь обработчик прерывания объявлен в формате компилятора IAR. Принципиально в нём только имя вектора прерывания - INT0_vect, компилятор заменяет его на адрес памяти программ, на который передаётся выполнение программы при возникновении данного прерывания. Имя самого обработчика INT0_INTPT выбирается произвольно. Названия векторов всех возможных прерываний для данного МК описаны в h-файлах.
 
       Теперь время реакции на нажатие кнопки не зависит от длительности фона программы и составляет несколько тактов микроконтроллера, а вероятность пропустить это событие равна нулю. Таким образом, прерывание – отличный способ реакции на событие, требующее немедленной обработки. Это и есть его главное предназначение.
 
       Хочется сразу упомянуть одно негласное правило относительно обработчиков прерываний, хоть это и достаточно узкий вопрос. В них следует размещать только то, что на самом деле необходимо для быстрой реакции на прерывание. Все остальные действия, которые можно отложить, необходимо размещать в фоне.
       С чем это связано?
       Если события, вызывающие прерывание, происходят достаточно часто, то на момент возникновения следующего прерывания слишком длинный обработчик может не успеть выполниться до конца. А это чревато неприятными последствиями в виде потери данных и нарушения нормальной последовательности действий. Например, если необходимо принять по UART некий массив байтов, то в обработчике, который вызывается после приёма каждого из них, не следует заниматься пристальным изучением принятых данных, а только переписывать их с заранее заготовленный массив. А уже после приёма последнего из них, в обработчике можно выставить соответствующий признак (мол, всё принято) и в фоне, обнаружив его, спокойно заняться исследованием всего принятого массива.
  


При использовании материалов сайта ссылка на данный источник обязательна.

Категория: Начинающим | Добавил: eugenemcu (02.11.2009) | Автор:

Просмотров: 21678 | Комментарии: 9 | Рейтинг: 4.7/63 |
Всего комментариев: 9
0
* 1 (27.06.2010 21:03) [Материал]
Отличное описание. Добавьте, пожалуйста, ссылку на эту статью в предыдущей статье. Не сразу видно, что есть эта статья.

0
* 2 (12.12.2010 03:10) [Материал]
Очень доходчиво, практически то что нужно. Лаконическая графика очень дополняет понимание процесса работы программы. Огромное спасибо. Продолжайте, если будет вдохновенние и желание

0
* 3 (21.01.2011 02:37) [Материал]
Отличный материал!
Если будет что-то новенькое по МК - прысылайте мне на почту: [email protected]

0
* 4 (25.01.2011 23:59) [Материал]
Спасибо за статьи. Приятно читать доходчивый материал, человека умеющего чисто излагать мысли. Это особенно приятно в мире Интернета. С удовольствием буду следить за будущими публикациями по теме программирования AVR. Пытаюсь чтобы программирование для меня стало вторым хобби.

0
* 5 (21.03.2011 13:44) [Материал]
А вообще продолжение то будет или как? очень хочу научиться программировать микроконтроллеры)) и если будет продолжение то мне интересно когда и с какой переодичностью приблизительно выходят новые части

0
* 6 eugenemcu (21.03.2011 21:48) [Материал]
Переодичности никакой нет.
Пишу по настроению )

0
* 7 (22.03.2011 03:48) [Материал]
Большое спасибо. Читается на одном дыхании, как роман!

0
* 8 (08.05.2011 01:32) [Материал]
Да не иссякнет этот источник Знания!

0
* 9 (19.10.2011 10:49) [Материал]
Отлично написано и очень доходчиво!!!
Все, что здесь написано помогло быстро вкурить в программирование железок! Раньше был какой-то страх. Сейчас пришло понимание. СПАСИБО и ждем продолжения!!!

Статистика

Наш магазин




Какую среду разработки Вы используете?

[ Результаты · Архив опросов ]
Всего ответов: 1328



Им нужна Ваша помощь: