Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Методичка MCU

.pdf
Скачиваний:
18
Добавлен:
23.02.2015
Размер:
1.03 Mб
Скачать

S1, S2, S3 — это регуляторы («переменные резисторы», «3-х выводные реостаты», «потенциометры»); 0-F — это клавиши. Терминал и ЖК-дисплей также появятся в этом окне.

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

Следующее действие: написание управляющей программы на языке C.

21

написание управляющей программы на языке C.

1)Перейдите в окно с заголовком Code. Target file: my_idea.hex

2)В окне уже есть некоторый текст программы: некоторые необходимые конструкции (#include <...>), примеры объявления переменных и функций. Важнее всего здесь пример объявления обработчика прерывания. В стандарте С нет такого понятия, как обработчик прерывания, поэтому каждый разработчик системы программирования считает своим долгом придумать что-то свое. В GNU C обработчик прерывания определяется так:

3)Запишите в этом окне пример программы «светофор» и нажмите кнопку компиляции, чтобы ее запустить.

4)Программа не заработает. О причинах этого можно узнать из окна «Messages» - «сообщения:». На самом деле, критичными ошибками являются только те, что выделены красным «!» - так называемый

Errors. Некоторые ошибки проявляют себя и как Errors, и как «предупреждения» - «Warnings» - это как раз наш случай. Здесь Warning: implicit declaration, а

Error: undefined reference... . («пойди туда, не знаю куда») - вызывать-то функцию вызываем, а вот как она действует — не определили. Аккуратно дописываем перед функцией main:

void delay()

{

long l;

for (l = 0; l < 100; l ++) asm («nop»);

}

5)Запускаем снова и видим, что все заработало (в окне Messages выделение должно перейти на строчку «success» — успешно. Рядом с ней — изображение медальки. Возможно, придется «прокрутить» окно до этого сообщения). В этом же окне GNU C сообщает, какая часть памяти для программ и памяти для данных была использована. Наши ресурсы достаточно сильно ограничены, поэтому стоит следить за их расходом: в ATMEGA162 есть только 16КБ памяти команд и 1КБ памяти данных. Никаких дискет, «сетевых дисков» и пр.

6)Чтобы видеть при симуляции результат работы программы, разверните окна «Controls» и «Scope».

7)Запустите программу на выполнение в симуляторе,

нажмите на «светофор»:

8)Вот, что означают остальные кнопки этой группы:

«step over» - перешагнуть. Программа выполняется пошагово, но

если встречается вызов функции — функция выполняется вся за 1

шаг

«step into» - «шагнуть В» - то же, что и «step over», но если встретился вызов функции — то «шагнуть внутрь» этой функции.

«step out» - выполнять, пока не выйдем из функции, которая запущена сейчас.

22

Кроме того, с целью отладки можно назначать так называемый «BREAKPOINT»'ы — точки останова. После того, как программа откомпилировалась, около некоторых строк (а именно — около строк, содержащих исполнимые команды) появились зеленые квадратики. Щелкнув по такому квадратику, можно переключить его в красный знак «stop». Как раз это и будет определением точки останова в этом месте.

На данном снимке экрана мы видим:

рабочие окна программы;

точки останова около строк «state=0;...» и «delay()» в окне «Code»

реакцию на точку останова в окне «Messages»

Изображение диаграмм сигнала в окне «Scope»

Запустив программу, увидим в окне «Control panel», как работает наш «светофор» :-) (для наглядности настройте переключатели осциллографа, чтобы на экране помещался весь цикл работы светофора).

Возможно, нажимать на «светофор» придется несколько раз. Это происходит из-за того, что во время выполнения программы могут возникать подозрительные с точки зрения VMLAB события, например, обращение к регистру ввода-вывода (PORTB, PINB, DDRB) не по абсолютному адресу, а через косвенную адресацию (Indexed write to a I/O space register?...). При этом VMLAB останавливает выполнение и выдает предупреждение. Это связано с машинным кодом, в который переводится наша программа. В данном случае, ничего сделать нельзя — так устроен компилятор. Нажимайте «светофор».

23

Выберите в меню «View» - «Program memory» (Вид — память программы), на экране появится след. окно:

В этом окне можно увидеть команды процессора в том виде, в котором они выполняются. Слева направо:

колонка возможных «BREAKPOINT»'ов

адрес ячейки памяти (1 команда занимает 2 байта = 16 бит)

код команды (как раз эти 2 байта)

возможное наличие «метки» («label»), на которую можно сделать переход

ассемблерная мнемоника инструкции («Disassemble») - можно понять, что команда делает

«Coverage» - «покрытие» - симулятор подсчитывает, сколько раз выполнилась каждая команда. Иногда это полезно. Именно Coverage (в логарифмическом масштабе) закрашивает строчки программы желтым цветом во время выполнения. Реальный процессор этого, естественно не делает.

«Source code line» - строка исходного кода — здесь можно увидеть, из которой нами написанной строчки при компиляции появились эти ассемблерные команды.

Можно также посмотреть и память данных: «View — Data memory»:

24

Видно, что и здесь симулятор предоставляет бОльшую функциональность и удобство, чем просто процессор: для контроля за использованием памяти, чтобы, например, избежать ситуации, когда мы из ячейки считываем какие-то данные, но мы их туда не записывали (это может привести к нестабильной работе программы при каждом включении устройства), симулятор хранит для каждой ячейки MCU информацию, как мы к ней обращались. Легенду можно увидеть в верхней правой части окна («A8 Position read...»).

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

Понятно, что после включения реальный процессор в каждой ячейке хранит какие-то случайные данные (составленные из логических 1 и 0), а шестнадцатеричных цифр «??» не существует. В каждом месте будут «00», «FF», «F0», «3D» или что-то еще. Опираться на эти данные нельзя! Всегда нужно явно инициализировать переменные и массивы до использования!

25

Таймер. Прерывание.

Проблема в том, что наш светофор не привязан к реальному времени. Для того, чтобы работа светофора стала более практичной, сделаем эту привязку.

Для генерации нормированных задержек применяется специальное периферийное устройство (оно встроено внутрь нашего процессора, так что никакой пайки не потребуется) Таймер. На самом деле, в нашем процессоре их 3 шт., но в целом они похожи, поэтому рассмотрим только 1 из них. Схемная реализация таймера (согласно datasheet) следующая:

Кроме собственно схем таймера, в его работе принимает участие так называемый «Prescaler» - предделитель.

26

Задача предделителя: взять тактовую частоту процессора (от встроенного генератора, например, 1-2-4-8 МГц или от внешнего «кварца» - 12 МГц, 11.0592 МГц и т.п.) и подготовить ее для дальнейшей работы. Видно, что прескалер имеет один 10-битный счетчик и 3 «мультиплексора» - управляемых переключателя, позволяющие выбирать, с какого выхода счетчика будем снимать сигнал для подачи его на таймер.

Допустим, минимальным интервалом времени, к которому будет привязана работа нашего светофора, станет ½ секунды — полупериод горения/негорения зеленого, когда он находится в режиме мигания. Имея кварцевый резонатор на 11.0592 МГц, определим, что нужно эту частоту разделить на 11 059 200 / 2 = 5529600.

Прескалер не может выбирать частоту для деления произвольным образом. Это связано с его устройством: он реализован из 10 каскадов, каждый из которых может делить частоту входного сигнала в 2 раза. Таким образом, можно было бы получить 5.5296 МГц, 2.76480 МГц, 1.3824 МГц, 691.2 Кгц, 345.6 Кгц, 172.8 Кгц, 86.4 Кгц, 43.2 Кгц, 21.6 Кгц, 10.8 Кгц, но для упрощения некоторые выводы не задействованы, и возможны только коэффициенты деления 8, 16, 32, 64, 256, 1024, таким образом, остаются только подчеркнутые частоты. Выберем самый большой коэффициент деления 1024. На выходе clk_T0 будут прямоугольные импульсы с частотой 10.8 Кгц. Далее они поступают непосредственно в таймер.

Таймер имеет схему управления и 8-битный счетный регистр. Схема управления может реализовать множество режимов работы таймера, но мы воспользуемся самым простым:

Каждый импульс, пришедший с prescaler'a будет увеличивать счетный регистр на 1.

Как только насчитает 54 таких импульса, таймер вызовет программное прерывание (SIG_OVERFLOW0).

Проще всего прерывание генерировать по так называемый переполнению таймера — когда его значение было равно 0xFF, следующий пришедший импульс должен бы был перевести его в 0x100, но поскольку таймер 8-битный, эту единичку записывать некуда. Чтобы она не пропала, таймер в этот момент может прервать работу основной программы и выполнить некоторую специальным образом написанную функцию (так называемый «обработчик прерывания»), которая уже учтет эту единичку и/ или выполнит какие-то действия.

Логика работы таймера задается при помощи «регистров конфигурации»:

Нас интересуют биты:

WGM (установим их всех в 0: генерировать сигналы на ножках нам пока не нужно),

27

COM2 (их тоже в 0, функциональность «Output compare», позволяющая измерять время между внешними событиями пока тоже не нужна)

CS2 указывает, к какому выводу «прескалера» подключать вход нашего таймера. Имеется следующая таблица:

Наш выбор: 1 1 1 — то есть, частоту нашего «кварца» сначала делим на 1024.

Теперь возьмите на листе в клетку 8 смежных клеток и заполните их 0 и 1 в соответствии с битами регистра. Переведите это двоичное число в 16ричное. Это и будет слово инициализации таймера. Таймер нужно «инициализировать» перед использованием, в функции main:

// Init hardware: Timer0

TCCR0 = 3; // FCLK/1024 – настройка предделителя TIMSK |= (1 << TOIE0); // разрешить прерывание

TCNT0 = 255-39; // Счет ведем до 256-40 = 216 импульсов. (11 059 200 Гц / 1024 / 216 = 50 Гц)

Кроме регистра конфигурации, имеется возможность прямого доступа на запись и на чтение к счетному регистру из любого места программы. Нам это потребуется, чтобы заставить его считать до 37, а затем вызывать прерывание.

Далее, нам потребуются 2 глобальные переменные:

volatile char flags, csec;//«флаги» и санти-секунды (10мс=1сс).

модификатор volatile нужен, чтобы указать компилятору, что не следует

пользоваться регистрами процессора, даже если он все еще «помнит» значения этих переменных, а нужно просто их считывать из предназначенной для них области памяти. Это связано с тем, что выполнение основной программы может быть прервано в любом месте, и то,

28

что процессор «помнил» может стать не соответствующим действительности.

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

Чтобы не писать явно числовые константы, а использовать читаемые обозначения, определим бит №3 переменной flags как «флажок» того, что прошло пол-секунды:

#define HALFSEC 0x08

Далее, пишем собственно обработчик прерывания:

SIGNAL(SIG_INTERRUPT0) {

TCCR0 = 255 - 39;

if (csec++ > 24)

{

csec = 0;

flags |= HALFSEC;

}

}

Теперь вместо вызова функции delay(); будем постоянно

просматривать переменную flags, и как только появится бит-флажок, свидетельствующий о том, что прошло пол-секунды, будем продолжать выполнение программы:

while (!(flags & HALFSEC));//«пока не флажок» -> пустой цикл flags&=~HALFSEC;//сбрасываем флажок и продолжаем выполнение.

Еще раз: эти строки будут выполняться, пока не пройдет пол-секунды.

Часто во время выполнения основного цикла необходимо делать некоторые действия постоянно (например, переходить в режим сбережения энергии: asm(«sleep»)), а некоторые — раз в секунду (например, опросом

датчиков и выводом информации на ЖК-дисплей). В этом случае можно пользоваться флагом HALFSEC:

while(1) // вечный цикл

{

asm («sleep»); // «засыпаем» до очередного прерывания

if ((flags & HALFSEC)) //если видим, что прошло полсекунды

{

flags &= ~HALFSEC; //сбрасываем флажок T = getT(); // измеряем температуру lcdGotoXY(1,1); // позиционируем курсор

29

print(«T=»); //выводим на экран подсказку pnum(T,1); // выводим значение температуры

}

}

Самостоятельная работа:

1)Модифицировать светофор так, что переключение выключателя SA1 переведет его в «мигающий желтый», а обратное переключение переведет его в нормальный режим начиная с «красного». Для этого нужно перевести вход, к которому подключен выключатель SA1 на «ввод» и соответствующим образом модифицировать switch();

2)Модифицировать процедуры инициализации и обработки прерывания таймера так, чтобы интервал стал не ½, а ¼ секунды.

30