Пункт 4. Объявление переменных
Перед использованием переменной в программе на Си её необходимо объявить - т.е. указать компилятору какой тип данных она может хранить и как она называется. Наиболее подробно об этом было сказано в предидущих параграфах. Ниже сжато - самое главное:
Формат объявления переменной таков:
[<storage modifier>] <type definition> <identifier>;
[<storage modifier>]- необязательный элемент, он нужен только в некоторых случаях и может быть:
extern - если переменная может использоваться в других файлах исходного кода программы, например объявляется во внешнем файле - хидере delay.h приведенном выше, а используется в основном файле программы.
volatile - ставьте если нужно предотвратить возможность повреждения содержимого переменной в прерывании, и не позволить компилятору попытаться выкинуть её при оптимизации кода.
пример: volatile unsigned char x;
static - если переменная локальная т.е. объявлена в какой либо функции после скобки { и должна сохранять свое значение до следующего вызова этой функции.
register - разместить переменную в регистрах AVR - это может ускорить доступ к ней. Компилятлор по-умолчаниюразмещает переменные в регистрах до их заполнения.
eeprom - разместить переменную в EEPROM. Это энергонезависимая память - значение таких переменных сохраняется при выключении питания и при перезагрузке МК.
пример: eeprom unsigned int x;
<type definition> - тип данных которые может хранить переменная.
наиболе часто используемые типы данных:
unsigned char - хранит числа от 0 до 255 (байт) unsigned int - хранит числа от 0 до 65535 (слово == 2 байта) unsigned long int - хранит от 0 до 4294967295 двойное слово == 4 байта)
Вместо unsigned char - можно писать просто char, так как компилятор по-умолчанию считает char без знаковым байтом.
А если вам нужен знаковый байт то объявляйте его так:
signed char imya_peremennoi;
<identifier> - имя переменной - некоторый набор символов по вашему желанию, но не образующий зарезервированные слова языка Си.
Выше был уже пример идентификатора - имени переменной: imya_peremennoi.
Принято использовать маленькие буквы, а для отличия имен переменных от названия функций - имена переменных можно например начинать с буквы, а названия функций (кроме main конечно) с символа подчеркивания.
Например так: moya_peremennaya _vasha_funkziya
Глобальные переменные, а также локальные с модификатором static - при старте и рестарте программы равны 0 если вы не присвоили им (например оператором =) иное значение при их объявлении или по ходу программы.
Вот несколько примеров объявления переменных:
unsigned char my_peremen = 34; unsigned int big_peremen = 34034;
Это объявлены две переменные и им присвоены значения.
Первая my_peremen - символьного типа - это 1 байт, она может хранить число от 0 до 255. В данном случае в ней хранится число 34.
Вторая big_peremen - целого типа, два байта, в ней может хранится число от 0 до 65535, а в примере в неё поместили десятичное число 34034.
КОНСТАНТЫ.
flash и const ставятся перед объявлением констант - неизменяемых данных хранящихся во флэш памяти программ. Они позволяют вам использовать не занятую программой память МК. Обычно для хранения строковых данных - различные информационные сообщения, либо чисел и массивов чисел. Примеры объявления констант
flash int integer_constant=1234+5; flash char char_constant=’a’; flash long long_int_constant1=99L; flash long long_int_constant2=0x10000000; flash int integer_array1[ ]={1,2,3}; flash int integer_array2[10]={1,2}; flash int multidim_array[2][3]={{1,2,3},{4,5,6}}; flash char string_constant1[ ]=”This is a string constant”; const char string_constant2[ ]=”This is also a string constant”;
пункт 5 Описание функций-обработчиков прерываний.
/* В ЭТОЙ программе - есть только одно прерывание и значит одна функция обработчик прерывания.
Программа будет переходить на неё при возникновении прерывания: ADC_INT - по событию " окончание АЦ преобразования " */
interrupt [ADC_INT] void adc_isr(void) { PORTB=(unsigned char) (~(ADCW>>2)); /* отобразить горящими светодиодами подключенными от питания МК через резисторы 560 Ом к ножкам порта_B старшие 8 бит результата аналого-цифрового преобразования Сделаем паузу 127 МСек - просто как пример пауз */ delay_ms(127);
В реальных программах старайтесь не делать пауз в прерываниях! Обработчик прерывания должен быть как можно короче и быстрее. */
/* начать новое АЦПреобразование, установив в 1 бит 6 в регистре ADCSRA*/ ADCSRA|=0x40; } // закрывающая скобка обработчика прерывания
Эта строка означает следующее: - берем значение переменной ADCSRA (это регистр МК - значит программа прочитает его, возьмет число из него)
- выполняем с этим числом операцию обозначаемую вертикальной черточкой | (это поразрядная операция ИЛИ) с числом 0x40
- присвоим или поместим результат поразрядного ИЛИ обратно в переменную ADCSRA - т.е. запишем результат в регистр ADCSRA
0x40 это в двоичном виде: 0100 0000
так как в результате поразрядного ИЛИ биты в ADCSRA напротив нулей не изменятся, а вот бит_6 в ADCSRA оказывается напротив "1" и теперь он станет "1" не зависимо от того каким он был до этого!
т.е. смысл рассматриваемой строки программы
ADCSRA|=0x40;
"установить" (т.е. сделать "1") бит_6 в регистре ADCSRA (флаг запуска АЦП)
Функция обработчик прерывания может быть названа вами роизвольно - как и любая функция кроме main.
Здесь она названа: adc_isr
При каком прерывании ее вызывать - компилятор узнает из строчки: interrupt[ADC_INT]
По первому зарезервированному слову - interrupt - он узнаёт, что речь идет об обработчике прерывания, а номер вектора прерывания (адрес куда физически, внутри МК перескочит программа при возникновении прерывания) будет подставлен вместо ADC_INT препроцессором компилятора перед компиляцией - этот номер указан в подключенном нами ранее заголовочном файле ("хидере") описания МК - mega16.h - это число сопоставленное слову ADC_INT.
Очень информативна следующая строка программы:
PORTB = (unsigned char) (~(ADCW >> 2));
Проанализируем как она работает.
Нужно вычислить выражение справа и поместить его в переменную PORTB. Вычислим что справа от оператора присваивания.
ADCW - это переменная слово (двухбайтовая величина - так она объявлена в файле mega16.h) в котором компилятор AVR сохраняет 10-битный результат АЦП - а именно в битах9_0 (биты с 9-го по 0-й) т.е. результат выровнен обычно - вправо.
Схема имеет только 8 светодиодов - следовательно нужно отобразить 8 старших бит результата - т.е. биты_9_2 - для этого мы сдвигаем все биты слова ADCW вправо на 2 позиции
ADCW >> 2 /* биты 1 и 0 выходят вправо из числа, бит_9 перемещается в позицию бит_7, бит_8 в позицию бит_6 и так далее до бит_2 становится бит_0 */
Теперь старшие 8 бит результата АЦП встали в биты 7_0 младшего байта слова ADCW.
>> n означает все биты числа сдвинуть вправо на n позиции. Это равносильно делению на 2 в степени n;
<< nозначает сдвинуть все биты влево на n позиции умножению на 2 в степени n.
Светодиоды загораются (показывая “1”) при «0”на соответствующем выводе МК - следовательно нужно выводить в PORTB число в котором "1" заменены "0" и наоборот. Это выполняет операция побитного инвертирования (~).
Следовательно результатом выражения
~(ADCW >> 2)
будут инвертированные 8 старших бит результата АЦП находящиеся в младшем (правом) байте двух байтового слова ADCW.
Так как PORTB это байт, а ADCW - это два байта, то прежде чем выполнить оператор присваивания нужно преобразовать слово (слово - это два байта) ADCW в без знаковый байт.
Преобразование типов данных - делают так:
перед тем что надо преобразовать записывают в скобках () тип данных к которому нужно преобразовать.
Пишем...
(unsigned char) (~(ADCW>>2))
Результат этой строки - один байт и мы можем поместить его в PORTB
Если в регистре DDRB все биты равны "1" - т.е. все ножки порта_B выходы, мы безусловно увидим старшие 8 бит результата АЦП горящими светодиодами.
Управление отдельными битами в переменной или регистре.
Как изменить только некоторые биты не изменяя остальные.
Для обнуления нужных бит используют обозначаемое знаком & поразрядное логическое И - только "1" и "1" дает "1"
PEREM &=(~0x04); // обнулить бит_2 в переменной PEREM
А вот так более понятно:
PEREM &=(~(1 << 2)); // обнулить бит_2 в переменной PEREM
Обнулить биты 5, 3 и 0 в переменной PEREM PEREM &=(~ ((1 << 5)|(1 << 3)|(1 << 0))); конечно здесь вместо (1 << 0) можно написать просто (1)
"Установить" - сделать "1" - биты 7, 5 и 3 в переменной PEREM PEREM |=(1 << 7)|(1 << 5)|(1 << 3);
Например (1 << 4) означает: взять число 1 и все его биты сдвинуть в лево на 4 позиции - в итоге мы получим двоичное 10000. Эти вычисления компилятор сделает сам и в программе заменит всё, что правее = на число-результат.
Вместо номеров битов вы можете использовать их названия
WDTCR |= (1 << WDTOE) | (1 <<WDE);
эта строка программы "Установит" - сделает "1" биты WDTOE и WDE в регистре WDTCR.
/* Пункт 6 Функции используемые в программе */
// их может быть столько сколько вам нужно.
// у нас будет одна, кроме main и // обработчика прерывания.
/* ================================= Это будет функция в которой описано начальное конфигурирование МК в соответствии с поставленной задачей.
Удобно над функцией сделать заголовок подробно поясняющий назначение функции!
(void)_init_mk(void) { /* Вначале любой функции объявляются ЛОКАЛЬНЫЕ ПЕРЕМЕННЫЕ - если конечно они вам нужны */
/* void - означает пусто. Перед названием функции - void - означает что функция не возвращает ни какого значения. А в скобках после названия означает что при вызове в функцию не передаются никакие значения. */
// инициализация Port_B DDRB=0xFF; // все ножки сделать выходами PORTB=0xFF; // вывести на все ножки "1"
/* настройка АЦП - производится записью определенного числа в регистр – ADCSRA в соответствне с его описанием.
- Включить модуль АЦП;
- Установить допустимую частоту тактирования АЦП при частоте кварца 3.69 МГц - мы выберем коэф. деления 64 - это даст частоту такта для процессов в АЦП 57.656 КГц;
- Включить прерывание по завершению АЦ преобразования.
По описанию для этого нужно записать в регистр ADCSRA число: 1000 1110 или 0х8E */
ADCSRA=0x8E;
/* Теперь выбираем вход АЦП ADC0 (ножка PA0) и внешнее опорное напряжение (это напряжение код АЦП которого будет 1023) с ножки AREF
/* для этого в регистр мультиплексора (выбора входа) АЦП ADMUX нужно записать 0 (он там по-умолчанию)*/
ADMUX=0;
/* Разрешаем ГЛОБАЛЬНО все прерывания разрешенные индивидуально*/
#asm("sei") //это ассемблерная вставка глобального разрешения прерывания
} // скобка закрывающая для функции _init_mk()
|
|
|
| /* Пункт 7 Главная функция main() - обязательная! Си программа начинает выполнятся с нее! */
void main(void){ /* Вначале любой функции объявляются (если нужны) ЛОКАЛЬНЫЕ ПЕРЕМЕННЫЕ */
init_mk(); /*Вызываем функцию инициализации, настроийки аппаратуры МК. Выполнив ее программа вернется сюда и будет выполнять следующую строку */
// запускаем АЦП ADCSRA|=0x40;
// бесконечный цикл в ожидании прерываний while(1);
/* Программа будет крутится на этой строчке постоянно проверяя истинно ли условие в скобках после while а так как там константа 1 - то условие будет истинно всегда! // функция main закончена } // скобка закрывающая для функции main()
Эта программа на Си будет работать так:
По завершении АЦП будет возникать прерывание и программа будет перескакивать в функцию обработчик прерывания adc_isr ()
При этом будут автоматически запрещены все прерывания ГЛОБАЛЬНО!
В конце adc_isr () запускается новое АЦ преобразование и при выходе из обработчика прерывания снова разрешаются глобально прерывания, и программа возвращается опять в бесконечный цикл while (1). Цикл будет продолжаться пока есть питание МК и не будет сброса. Светодиоды будут высвечивать 8-ми битный код АЦП напряжения на ножке PA0.
| |
|