Логотип

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

Программирование микроконтроллеров. Урок 5
(Переменные и арифметические операции)

Урок 5 (Переменные и арифметические операции)

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

Что бы наш светодиод начал моргать необходимо записать условие переключения его работы и выглядеть оно будет так:

		if(PORTB.0) PORTB.0 = LED_ON;   	// зажигаем светодиод
		else PORTB.0 = LED_OFF;         	//гасим светодиод 

Если вы вдумчиво читали учебники, то знаете о том, что в языке «Си» если результатом условия оператора ветвления будет единица, то условие считается истина, если результат «0», то лож. Поэтому запись if (PORTD.0) при выключенном светодиоде будет истина т.к. в нулевой ячейке регистра порта D будет единица. В этом случае нам необходимо включить светодиод записав туда ноль. А если светодиод включен и в ячейки порта ноль, то будет выполнен оператор else и в ячейку будет записана единица. LED_ON и LED_OFF не что иное как псевдонимы для «0» и «1». Если мы просто запишем единицу и ноль, то через какое-то время не очень будет понятно к чему эти цифры. А так все наглядно и более того если потребуется поменять начальные значения, то менять их надо будет только в блоке псевдонимов, а не выискивать по всей программе.

Блок схема этих строк будет выглядеть так.

Блок-схема 1

Листинг нашей программы тогда будет вот такой.

Листинг 1

И если вы скомпилируете эту программу и запустите ее на МК, то будет видно, что светодиод горит постоянно, но не в полную яркость. Этот эффект получается потому, что частота переключения светодиода очень велика и глаз не может уследить за моментом переключения. На этом принципе основана работа светодиодной матрицы, работу которой мы будем изучать чуть позже. Вывод напрашивается сам собой. Необходимо сделать задержку, что бы замедлить процесс переключения светодиода. Сразу на ум приходит идея организовать некий цикл, после переключения который бы на какое-то время переключил на себя работу контроллера. Например, такой который показан на блок-схеме.

Блок-схема 2

Как мы видим сразу после переключения, включается счетчик «J», который закольцовывает программу и отнимает от заданного в начале цикла числа «J=100» по единичке «J=J-1». Как только переменная станет нулем «J=0» программа организует выход из цикла, светодиод переключится, и счетчик начнет считать сначала. Да, такие задержки иногда используют, но только в очень специфических местах, там, где надо «наглухо» задержать работу программы или небольшая задержка не критична по отношению к работе всего устройства. Дело в том, что пока счетчик считает проходящие циклы, контроллер не выполняет никакой полезной работы. Поэтому применение таких задержек весьма ограничено. Но как же тогда быть?

Если мы присмотримся к блок-схеме нашей программы, то увидим, что у нас уже имеется один бесконечный цикл «W». Вот его то мы и будем использовать, поместив в него счетчик.

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

Для хранения и обработки текущих данных как в компьютерах, так и в МК используется оперативная память. Она представляет собой множество ячеек, называемых битами. Бит информации можно сравнить с пустой коробкой из-под обуви. Если коробка пуста, то мы будем считать, что в коробке ноль «0», а если в коробку положить пирожок, то в коробке будет единица «1». При этом два пирожка в коробку не влезет. Вот из таких «коробок», которые имеют свой уникальный номер, состоит память компьютера и в них либо ничего нет, либо лежат «пирожки». Для того чтобы было легче обращается к этим «коробкам» их организовали в ячейки по 8 штук и назвали эти ячейки байтами. Один байт может в себя вместить число 255, для больших чисел используют 2…3…4 байта. Например, 3 байта вмещает в себя число 16 777 215.

Необходимо указать на три особенности которые имеют байты. Первая из них в том, что биты в байте считаются с права на лево. Т.е. крайне правый бит будет самым младшим. Вторая особенность в том, что биты считают не от единицы, а от нуля. Поэтому хоть мы и говорим, что в байте восемь бит, самый старший из них седьмой. Ну и третья особенность в том, что в байте, как я уже писал, умещается максимальное число равное 255, но байт может принимать 256 значений. Не забываем про ноль. Краткую историю байта можно почитать в Википедии.

Так вот почти все переменные, за исключением битовых, состоят из таких байтов. А так как данные могут быть различные, это и цифры (дробные, со знаком, целые до 256 или очень большие) и код обозначающий какой-то символ и разного рода флаги, то для разных типов данных отведено разное количество байт. Например, для переменного типа «char» (символ) всего один байт, а для «float» (вещественный тип) четыре байта.

О переменных вы почитаете самостоятельно позже, а сейчас надо запомнить то, что для микроконтроллеров лучше всего использовать целые числа без знака.

Для МК их всего три:
    unsigned char – (8 бит диапазон 0…255);
    unsigned int – (16 бит диапазон 0…65 535);
    unsigned long int – (32 бита диапазон 0…4 294 967 295).

И то, переменную типа unsigned long int без необходимости использовать не стоит. Если вдруг вам понадобилась переменная с плавающей точкой, то выбор не большой. Для МК такой тип данных именуется float – (32 бита диапазон ±1.18*10^-38…±3.4*10 ^38). Этот тип данных необходимо использовать только когда без него ну уж ни как нельзя. Обработка типов long int и float отнимают очень много процессорного времени, а их обработчики занимают большой объем flash памяти. Намного проще представить число Пи в виде 31416х10^-4 степени. А после всех арифметических операций с целыми частями при выводе на экран результата поставить точку в нужном месте с учетом получившихся степеней и округления до необходимых значений (сильно позже я покажу как это сделать). Про числа со знаком вообще говорить не буду. Когда вам понадобится их использовать, я уверен, программировать вы будите лучше, чем просто на пять.

Что касается арифметических операций, то тут есть только одна особенность. Т.к. большинство операций будут производится над целыми числами без знака, то делений будет два. Одно деление, обозначаемое «/» косой чертой будет возвращать целую часть без остатка, а деление, обозначаемое «%» знаком процента будет возвращать остаток от деления. Например, разделим число 105 / 10 получим 10, а если 105 % 10, то получим 5. Вот так.

При чтении литературы прошу обратить внимание на области видимости переменных, а также на то, что если вы объявляете переменную в подпрограмме или цикле, то при выходе из подпрограммы или цикла переменная прекращает свое существование. Если же необходимо сохранить переменную до следующего входа в цикл или подпрограмму, то при объявлении необходимо указать что переменная статична написав перед типом данных ключевое слово static. Например, static unsigned int.

Вот всё, что необходимо знать на первых парах о переменных, типах данных и арифметических операциях.

Теперь вернемся к программе. Для организации счетчика необходимо будет объявить переменную типа unsigned int. Кстати, как вы понимаете, работа такого счетчика напрямую связана с частотой работы контроллера и чем она выше, тем больше число необходимо будет установить для нормальной работы нашего «маячка». О внутреннем генераторе частоты мы поговорим чуть позже. 65 000 циклов между переключениями светодиода при рабочей частоте 1МГц, установленной для МК по умолчанию, должно хватить чтобы наш светодиод начал моргать с приемлемой частотой. При объявлении переменной ей необходимо присвоить уникальное имя, а также хорошим тоном считается проинициализировать её, записав начальное значение или ноль. Я присоединяюсь к рекомендациям монстров программирования давать осмысленные имена переменным и писать их целиком в нижнем регистре. Более того я для себя разделяю переменные по назначению - данные, счетчики и флаги. И поэтому перед именем переменной ставлю букву «d» data, «c» counter (счетчик), «f» flag. Например, «с_pause_led».

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

Блок-схема 3

Описание блок-схемы.
   1. Описываем программу и вставляем файл с псевдонимами регистров.
   2. Объявляем и инициализируем максимальным значением переменную счетчика «c_pausa_led».
   3. Настраиваем вывод МК со светодиодом как выходной.
   4. Цикл W
   5. Сравниваем значения счетчика. Если (c_pausa_led ==0) переходим к п.6. если не равна переходим к п. 7.
   6. Проверяем состояние светодиода. Если светодиод не горит (PORTB.0 = =1) переходим к п.9, если PORTB.0 == 0 переходим к п.8.
   7. Декремент переменной c_pausa_led. Переходим к п.4.
   8. Выключаем светодиод. Переходим к п.10.
   9. Включаем светодиод.
   10. Установка максимального значения счетчика. Переходим к п.4.

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

Листинг 2


Теперь как обычно компилируем и прошиваем нашу программу в МК. И конечно наслаждаемся работой маячка. Как вы уже догадались при изменении начальных значений счетчика изменяется и частота переключения светодиода. Если частота работы МК 1МГц (настройки по умолчанию) то наш маячок будет работать вот так.

В качестве домашнего задания нужно будет ответить на простой вопрос. Изменится ли частота переключения светодиода если мы включим в цикл W полезный код? И если измениться, то как и почему?


К следующему уроку необходимо почитать все о переменных и об операциях с ними:
   М.Б. Лебедев «CodeVisionAVR пособие для начинающих» 2008г. Страницы 175…183, 198…209.
   Ю.А. Шпак «Программирование на языке С для AVR и PIC микроконтроллеров» 2006. Страницы 111…116.
   Майк МакГрат «Программирование на С для начинающих» 2016. Страницы 21…35, 53…57.
   К. Поляков «Программирование на языке Си» 1995-2012. Страницы 7…11.


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


01.05.18


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




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