Логотип

Если Вы хотите обучаться программированию микроконтроллеров, но попали, сюда не прочитав предыдущих уроков, то советую начать изучение материала с самого начала.

Программирование микроконтроллеров. Урок 4
(IF ELSE)

Урок 4 (IF ELSE)

На прошлом занятии мы написали первую программу, которая зажгла для нас светодиод, а сегодня мы подключим кнопку к нашему контроллеру и всё узнаем об операторе ветвления «if». Это самый универсальный и часто используемый оператор. В союзе с «goto» может организовывать любые циклы. Но т.к. оператор «goto» у программистов не в почете и употреблять его нужно с большой осторожностью (для этого действительно имеются веские причины), в языке «Си» достаточно разных конструкций для того, чтобы обойтись без него.

Но перед тем как мы начнем изучать тонкости применения оператора «if else» нам необходимо повторить предыдущий урок, а точнее структуру программы. Как было указано ранее минимальная программа на «Си» выглядит вот так.

Структура программы на Си

Для удобства я с помощью комментариев указал места, в которых мы будем располагать разные элементы нашей будущей программы. Т.к. все программы на Си строго детерминированы, то для каждого элемента программы отведено определенное место.

Раздел 1 (Описание). В самых первых строках программы обычно помещают небольшое описание. Что за программа, для чего предназначена, кто и когда ее написал, а также какова ее версия и т.д. Для компилятора эти строки не значат ничего, но вот для программиста они очень важны. Конечно, набивать описание по началу жуть как не охота, но потом понимаешь, что без него обойтись невозможно.

Раздел 2 (Апноты). Далее идет строка #include "mega8.h". Если вы внимательно читали литературу, то знаете, что оператором препроцессора # include прикрепляются сторонние файлы, которые могут содержать необходимую для данной программы информацию или ее части (подпрограммы). В данном файле содержатся псевдонимы всех регистров и их разрядов. Для компилятора надпись PIND ничего не значит. А в этом файле указано, что PIND это порт, находящийся по адресу (0x10) (запись, начинающаяся с 0х… является числом в шестнадцатеричной системе счисления). Этот файл включается в текст программы автоматически в зависимости от указанного МК при генерации нового проекта.

Раздел 3 (Псевдонимы и макросы). Затем в нашей программе идет место для объявления псевдонимов и макросов. С помощью оператора препроцессора #define можно творить чудеса, а также сильно облегчать себе работу по настройке программы и поиску ошибок. Возьмите себе за правило, что любое предопределенное число будь то разряд вывода порта, начальное значение переменной или какие-нибудь другие установки, всё это НЕОБХОДИМО записывать через псевдонимы. Т.к. если, по каким-то причинам при настройке программы понадобится скорректировать такое число, или изменить выводы порта, то искать и исправлять эти данные по всей программе работа не благодарная, которая обязательно будет сопряжена с головной болью. Намного проще изменить данные в одном месте при объявлении псевдонима. Прошу также обратить внимание на то, что имена псевдонимов я пишу только большими буквами. Такой подход позволяет без труда определить в программе где используются псевдонимы, а где переменные.

Раздел 4 (Глобальные переменные). В этом разделе создают глобальные переменные, которые будут «видны» из любой части программы. О переменных мы поговорим позже. Здесь я только скажу о том, что увлекаться глобальными переменными не стоит. В основном они нужны для работы с прерываниями и функциями на уровне программы в целом.

Раздел 5 (Процедуры и функции). Здесь должны размешаться или быть объявлены процедуры и функции (подпрограммы). Забегая вперед скажу, что функция от процедуры отличается тем, что первая может возвращать некое значение и передавать ее в переменную, а процедура предназначена только для выполнения определенных действий. Хотя на мой взгляд это отличие условное.

Раздел 6 (Обработчики прерывания). Этот раздел я выделил отдельно. Он последний перед функцией «main». Сюда вы будете прописывать обработчики прерываний, когда дело дойдет до них. А почему именно сюда. Все очень просто, если обработчик прерывания имеет внутри себя исполняемую функцию, то эта функция должна быть объявлена до обработчика.

Раздел 7 (Рабочие переменные и настройки). Этот раздел уже в пределах функции «main» с этого места начинается исполнение программы. Любой оператор прописанный после «main» будет исполнен. Поэтому в начале этого раздела прописывают переменные, которые будут видны только в рамках функции «main», а затем прописывают начальные настройки МК перед выполнением цикла «while (1){}».

Раздел 8 (Тело программы). В бесконечном цикле «while (1){}» помещается непосредственная программа, которая будет исполняться по кругу пока не наступит условие для выхода из цикла. Самое распространённое условие для выхода из этого цикла, прерывание. Обработав его, МК опять вернется к исполнению основной программы с того места где она была прервана.

Теперь, когда нам понятно, что куда пишем, необходимо подключить к нашему контроллеру кнопку.

Схема подключения кнопки к МК Реаллизация подключения кнопки к МК

Схема подключения кнопки к МК

Реаллизация подключения кнопки к МК

Ну вот теперь можно написать самый примитивный обработчик, который нам поможет понять, как работает оператор ветвления IF. Вот листинг нашей программы.

Структура программы на Си

Скомпилируйте ее и прошейте в МК. При запуске контроллера загорится светодиод. Но как только вы нажмете кнопку, он потухнет. При отпускании кнопки светодиод загорится снова.
   Оператор

   if (условие) {исполняемый код}
   else {исполняемый код};

   По русский можно прочитать как:

   Если (условие выполняется) {то делаем эту часть программы и переходим в конец оператора}
   А если условие не выполняется {то выполняется эта часть программы};

Блок схема нашей программы будет выглядеть так.

Блок-схема

Блок-схема

Описание работы блок-схемы:
   Блок 3 Устанавливаем начальные настройки вывода для подключения светодиода.
   Блок 4 вход в цикл «W»
   Блок 5 проверка нажатия кнопки. Если на выводе PINС.5 = 1 (кнопка НЕ нажата), то выполняется ветвь «НЕТ» блок 6.
     Если на выводе PINС.5 = 0 (кнопка нажата), то выполняется ветвь «ДА» блок 7.
   Блок 6 Записываем в регистр порта "В" ноль (зажигаем светодиод). Возвращается к началу цикла «W».
   Блок 7 Записываем в регистр порта "В" единицу (гасим светодиод). Возвращается к началу цикла «W».


Конечно это самый примитивный обработчик нажатия кнопки и никто таким не пользуется, зато он наглядно показывает работу оператора IF. И если мы говорим об этом операторе, то не должны забывать и о логических отношениях и операциях, которые с ним используются:
     >   больше;
     <   меньше;
     >=  больше либо равно;
     <=   меньше либо равно;
     ==   равно;
     !=   не равно.
     ||   ИЛИ (операция). Когда при сравнении двух условий результат истина (1) будет в том случае если хотя бы одно из условий истина.
     &&   И (операция). Когда при сравнении двух условий результат истина (1) будет в том случае, если оба условия истина.
     !   НЕ (операция). Отрицание условия. Т.е. если А == В то применив отрицание будет !(А != В).


Операции || и && используются, если необходимо сравнить сразу несколько условий. Например, if (A>x1 || B>x2) если хотя бы одно из условий (A>x1) или (B>x2) является истиной, то и всё условие будет считаться как истина.

Вот небольшое видео о том, как это должно работать.

Для следующего занятия у нас все есть. На нем мы заставим светодиод моргать, а заодно разберемся с переменными.


К следующему уроку необходимо почитать:
   М.Б. Лебедев «CodeVisionAVR пособие для начинающих» 2008г. Страницы 216…217, 209…216.
   Ю.А. Шпак «Программирование на языке С для AVR и PIC микроконтроллеров» 2006. Страница 126.
   Майк МакГрат «Программирование на С для начинающих» 2016. Страницы 58…63, 73…75.
   К. Поляков «Программирование на языке Си» 1995-2012. Страницы 14…17.


На сегодня всё. Удачи.


25.05.18


Если вдруг найдете в статье неточности или заблуждения. Напишите мне об этом. Я подправлю.




Приложение:
Схема подключения, блок-схема, проект CV_AVR.