ТОР 5 статей: Методические подходы к анализу финансового состояния предприятия Проблема периодизации русской литературы ХХ века. Краткая характеристика второй половины ХХ века Характеристика шлифовальных кругов и ее маркировка Служебные части речи. Предлог. Союз. Частицы КАТЕГОРИИ:
|
Структура программы на языке Си
Программа на Си имеет определенную структуру: 1) заголовок; 2) включение необходимых внешних файлов; 3) ваши определения для удобства работы; 4) объявление глобальных переменных (глобальные переменные объявляются вне какой-либо функции, т.е. не после фигурной скобки {, доступны в любом месте программы, значит можно читать их значения и присваивать им значения там, где требуется); 5) описание функций-обработчиков прерываний; 6) описание других функций, используемых в программе; 7) функция main (это единственный обязательный пункт).
Функция имеет { "тело" } в фигурных скобках. Тело – это код на Си, определяющий то, что делает функция. Знак; после функции не ставится. Программа на Си начинает работу с функции main(), по необходимости из main() вызываются другие функции программы, по завершении работы функции программа возвращается в main() в то место, откуда функция была вызвана. main(){ вызов функции_1; //программа перейдет в функцию_1 строка программы; // будет выполняться после возврата } Функции могут вызываться не только из main(), но и из других функций. Кроме того, описанный выше ход программы может нарушаться прерываниями. Приведем пример программы на Си с описанной выше структурой (текст в рамке). По мере надобности программа будет разрываться обычным текстом, а затем продолжаться.
Перед использованием переменной в программе на Си её необходимо объявить, т.е. указать компилятору, какой тип данных она может хранить и как она называется.
[<storage modifier>] <type definition> <identifier>;
[<storage modifier>] – необязательный элемент, extern – если переменная объявляется во внешнем файле, например, в хидере delay.h, приведенном выше;
Пример:
static – если переменная локальная, т.е. объявлена в какой-либо функции и должна сохранять свое значение до следующего вызова этой функции.
eeprom – разместить переменную в EEPROM. Значение таких переменных сохраняется при выключении питания и при перезагрузке МК. Пример: Если это первая переменная в EEPROM, то её младший байт будет помещен в ячейку 1 EEPROM, а старший в ячейку 2. Необходимо помнить, что запись в EEPROM длительный процесс - 8500 тактов процессора.
Глобальные переменные объявляются до появления в тексте программы какой либо функции. Глобальные переменные доступны в любой функции программы. Локальные переменные объявляются в самом начале функций, т.е. сразу после фигурной скобки {. Локальные переменные доступны только в той функции, где они объявлены!
<type definition> - тип данных, которые может хранить переменная. Наиболе часто используемые типы данных: unsigned char - хранит числа от 0 до 255 (байт); Вместо unsigned char можно писать просто char, так как компилятор по умолчанию считает char беззнаковым байтом. А если вам нужен знаковый байт, то объявляйте его так: signed char imya_peremennoi;
<identifier> – имя переменной - некоторый набор символов по вашему желанию, но не образующий зарезервированные слова языка Си. Выше был уже пример идентификатора – имени переменной: imya_peremennoi. Желательно давать осмысленные имена переменным Например, так: moya_peremennaya, __vasha_funkziya. Глобальные переменные, а также локальные с модификатором static - при старте и рестарте программы равны 0, если вы не присвоили им (например, оператором =) иное значение при их объявлении или по ходу программы.
Вот несколько примеров объявления переменных: unsigned char my_peremen = 34; Пример массива, содержащего три числа или элемента массива. Нумерация элементов начинается с 0, т.е. элементы данного массива называются mas[0], mas[1], mas[2] и в них хранятся деся-тичные числа 11, 22 и 33. Где-то в программе вы можете написать: mas[1] = 120; Теперь в mas[1] будет храниться число 120. Можно не присваивать значений элементам массива при объявлении, но только при объявлении вы можете присвоить значения всем элементам массива сразу. Потом это можно будет сделать только индивидуально для каждого элемента.
Строковая переменная или массив, содержащий строку символов. Например: Элемент строки stroka[1] содержит число 101, которому по таблице ASCII соответствует символ 'e'. Элемент stroka[4] содержит число 111, которому соответствует символ 'o'. Элемент stroka[5] содержит число 0, которому соответствует символ 'NUL', его еще обозначают вот так '\0'. Строковая переменная может быть "распечатана" или выведена в USART MK вот так: printf("%s\n", stroka); flash и const ставятся перед объявлением констант, неизменяемых данных, хранящихся во flash-памяти программ. Они позволяют использовать не занятую программой память МК. Обычно для хранения строковых данных – различных информационных сообщений, либо чисел и массивов чисел.
flash int integer_constant=1234+5;
Очень информативна следующая строка программы: PORTB = (unsigned char) ~(ADCW>>2); Нужно присвоить значение выражения справа от оператора присваивания той переменной, что указана слева от него. Значит, нужно вычислить выражение справа и поместить его в переменную PORTB. ADCW – это двухбайтовая величина (так она объявлена в файле mega16.h, в котором CodeVisionAVR сохраняет 10-битный результат АЦП, а именно в битах 9_0 (биты с 9-го по 0-й), т.е. результат выровнен обычно – вправо.
VMLAB имеет только 8 светодиодов – значит нужно отобразить 8 старших бит результата - т.е. биты 9_2. Для этого мы сдвигаем все биты слова ADCW вправо на 2 позиции: ADCW >> 2. Теперь старшие 8 бит результата АЦП переместились в биты 7_0 младшего байта (LowByte - LB) слова ADCW.
>> n означает сдвинуть все биты числа вправо на n позиций. << n означает сдвинуть все биты числа влево на n позиций.
Светодиоды загораются (показывая "1") при "0" на соответствующем выводе МК – значит, нам нужно выводить в PORTB число, в котором "1" заменены "0" и наоборот. Это делает операция побитного инвертирования. Результатом выражения ~(ADCW >>2) будут инвертированные 8 старших бит результата АЦП, находящиеся в младшем байте двухбайтового слова ADCW. В Си в переменную можно помещать только тот тип данных, который она может хранить. Так как PORTB – это байт, а ADCW – это два байта, то прежде чем выполнить оператор присваивания (это знак =) нужно преобразовать слово (слово - word - значит два байта) ADCW в беззнаковый байт. Пишем...(unsigned char) ~(ADCW>>2). Результат этой строки – один байт и мы можем поместить его в PORTB. Если в регистре DDRB все биты равны "1" – т.е. все ножки порта_B выходы, мы безусловно увидим старшие 8 бит результата АЦП горящими светодиодами.
Разберем еще одну строчку: ADCSRA|=0x40; /* результат поразрядного ИЛИ с маской 01000000 поместить обратно в регистр АDCSRA, т.е. установить бит 6. Обратите внимание на необходимость ставить в конце
Так делаются вставки ассемблерных инструкций: #asm("инструкция на ассемблере"). Обратите внимание – точки с запятой нет. На Си можно управлять всеми программно изменяемыми битами в регистрах МК, но часто используются такие строки: #asm("sei") // Разрешить ГЛОБАЛЬНО все прерывания #asm("cli") // Запретить ГЛОБАЛЬНО все прерывания #asm("nop") // Пауза в 1 такт процессора #asm("wdr") // Сбросить сторожевой таймер
Теперь программа будет работать так. По завершении цикла АЦП будет возникать прерывание и программа будет перескакивать в функцию обработчик прерывания adc_isr (). При этом будут автоматически запрещены все прерывания. В конце adc_isr () запускается новое АЦ преобразование и при выходе из обработчика прерывания снова разрешаются глобально прерывания, а программа возвращается опять в бесконечный цикл while (1). Светодиоды будут высвечивать 8-ми битный код АЦ преобразования напряжения на ножке PA0. Не нашли, что искали? Воспользуйтесь поиском:
|