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

Методичка MCU

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

Изучаемый микроконтроллер ATMEL ATMEGA162.

Почему выбран именно он? - Потому, что по назначению ножек («pin-to- pin compatibility» без программной совместимости) он совпадает с применявшимся в наших стендах, но морально устаревших AT89S52.

Есть и более перспективная архитектура для изучения — процессоры ARM. Они достаточно недорогие, но быстродействующие, например, чтобы кодировать-декодировать MP3 или WMA «на лету», имеют много памяти и развитую архитектуру. Правда, есть одна проблема: Они выпускаются только в корпусах, которые очень неудобно паять вручную. особенно, не имея достаточно опыта. А в серии контроллеров AVR (в том числе ATMEGA162) есть достаточно много контроллеров, выпускающихся в корпусах DIP.

Тем не менее, все изучаемое в данном курсе является достаточно общим практически для всех типов микроконтроллеров, так что у читателя не возникнет проблем с освоением новых микроконтроллеров. Кроме того, при использовании языка C можно переносить уже написанные программы из одного семейства MCU на другое практически без модификации. Это называется «портируемость программы».

AVR – Это большое семейство микорконтроллеров, производимых фирмой ATMEL. Различаются они объемом памяти программ/данных (от 1К ПЗУ/ 128 байт ОЗУ в AT90S1200) до 128Кб/8Кб (ATMEGA128), типом корпуса (самый маленький 3х3мм, 8 ножек, а самый большой – 144 ножки TQFP), а так же набором встроенной периферии. Мы выберем ставший уже классикой MEGA16. Но, что удобно, для перехода на «старшую» или «младшую модель» достаточно поменять только несколько (1-5) строчек конфигурации и подправить схемотехнику, все используемые программы – и компилятор C, и симулятор – останутся теми же самыми.

Вот схематическое изображение нашего микроконтроллера. Не обращайте пока внимания на указанные в скобках названия (OC0/T0 и т.п.):

Ключ(key) – выемка на корпусе. Место, от которого против часовой стрелки отсчитываются номера выводов корпуса.

Ключ

Описание назначения выводов:

GND - «общий» для всех сигналов. Соединяется с «-» батарейки или иного источника питания.

VCC - «+» источника питания. Обычно напряжение питания составляет 5 В, но все активно переходят на 3.3 и даже 1.8 вольта.

XTAL1, XTAL2 — выводы для подключения кварцевого генератора — стабильного источника тактовых импульсов.

PA0-PA7, PB0-PB7, PC0-PC7, PD0-PD7, PE0-PE2 - это 35 выводов, использовать которые мы можем по своему усмотрению*. Из программы, которую скоро

11

напишем, можем считывать уровень сигнала с любой ножки; принимать на его основе какое-то решение; выводить уровень сигнала на любую ножку микросхемы.

Здесь «уровень сигнала» - это так называемый логический уровень — либо 0, либо 1, что соответствует напряжениям 0-0.5 В и 1.5-5 В. Вопрос: а что с интервалом 0.5В-1.5В?

Ножки, которыми можно управлять сгруппированы для удобства по 8 штук в «Порты»: A, B, C, D, E — это порт, а номер от 0 до 7 — это номер бита, который отвечает в данном порте за эту ножку.

Очевидно, что состояние ножек одного порта можно представить в виде 1 байта, то есть, например, числа от 0 до 255. или от 00000000 до 11111111 в двоичной системе.

Например, представим, что на ножках 1,2,5,6,7,8 будут логические 0, а на 3,4 — лог. 1. Тогда это число будет 00001100 (BIN) = 0xC0 (HEX) = 12 (DEC).

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

Каждому порту Px (x — это A, B, C, D, E) соответствуют 3 переменных, доступных из любого места программы на С: DDRx, PORTx, PINx.

PINx — переменная, которую имеет смысл только считывать. Она отражает реальные состояния ножек вне зависимости от того, кто является источником сигналов.

DDRx — переменная, задающая, какие ножки порта будут работать на выход (соответствующий бит должен быть установлен в 1) или на вход ( лог. 0). Например, если к ножкам 1,2,3 подключены кнопки, а к 4,5,6,7,8 — светодиоды, то команда задания соответствующего режима порта будет такой: DDRB = 0xF8; // F8(HEX)=11111000 (BIN)

PORTx - переменная, явно задающая состояние ножек, если они находятся в режиме вывода. Если ножка находится в режиме ввода, то лог.1 в соотв. позиции включает «подтяжку» к 5V.

Вот схема, поясняющая работу вывода порта:

Таким образом, если мы хотим «поморгать» светодиодом, подключенным к 3й ножке процессора, сначала нужно перевести его в

состояние «вывод»:

DDRB |= 4; // 4 = 00000100 в двоичной системе.

12

А затем собственно «моргание»:

while(1) // «моргаем вечно»

{

PORTB |= 4; //вывести в порт (B:2) 1:включить светодиод

delay();

//задержка, хотя бы

пол-секунды

PORTB &= ~4;//вывести в порт (B:2) 0:выключить светодиод

delay();

// задержка, хотя бы

пол-секунды

}

 

 

Если мы хотим узнать, нажата или не нажата кнопка, нужно перевести

соответствующую линию в режим ввода:

DDRB |= 40; // 40 = 010000000 в двоичной системе. PORTB |= 40; // включаем «подтяжку» (см. далее) if ( !( PINB & 0x40 ) ) {PORTB &= ~0x07;}

Рассмотрим подробнее, что здесь происходит.

PINB & 0x040 — Вспоминая таблицу истинности операции «логическое И» (а & - это именно она), понимаем, что в соотв. бите будет «1» только если она есть и в первом и во втором операндах на одном месте. Во втором операнде (назовем его «маской») «1» есть только в 3-м бите. Таким образом, чтобы результат выражения стал ненулевым, необходимо, чтобы 3-й бит «выражения» (PINB) был = 1.

Далее, поскольку нас интересует нажатая клавиша, а значит, соединенная с GND, то активный уровень на этой ножке - «0», таким образом, используя !(0), получим привычный активный «лог. 1».

Далее. Если 0х07 — это битовая 00000111, то ~0x07 — это 11111000 (меняем единички на нолики и наоборот — операция побитового «НЕ»). А если мы возьмем переменную Y (Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 — это ее биты), проведем с ней и с маской (~0x07) побитовые «И», то получим (Y7 Y6 Y5 Y4 Y3 0 0 0). Затем этот результат записывается в исходную переменную (вспоминаем «сокращенные операции»).

Таким образом, мы «обнулили» биты, которые были указаны в маске.

Что такое и для чего нужна «подтяжка».

Допустим, есть провод. Пусть он подключен только к вольтметру. Замыкаем на землю, размыкаем — никакой разницы по напряжению. А вот если мы подключим его через источник тока, например, к +5V, то различие будет: разомкнутый покажет 5V, замкнутый — 0V. Простейший источник тока

— резистор. таким образом, имея 5 кнопок, нужно ставить 5 резисторов. Для удобства, к каждой ножке нашего процессора можно подключить внутренний источник тока. Для этого ножка должна быть в режиме ввода (соотв. бит в DDR = 0), и соотв. бит в PORT должен быть равным 1.

Кроме кнопок, подтяжка может использоваться для других датчиков, в том числе оптических. Ток, создаваемый источником, имеет значение порядка 100-200 мкА.

Вопрос: какой будет зависимость напряжения на ножке сразу после размыкания кнопки?

13

Помимо «Портов» ввода-вывода имеется огромное разнообразие периферийных устройств, уже встроенных в процессор, но о них чуть позже.

Сейчас у нас достаточно знаний, чтобы написать программу «Светофор». Хотим, чтобы светофор переключался так:

1.красный

2.красный+желтый

3.зеленый

4.мигающий зеленый

5.желтый

6.(переход на 1)

Реализуем светофор в виде конечного автомата при помощи оператора множественного ветвления switch(state). Обратите внимание на его

синтаксис.

Мигание зеленого реализуем «вручную», состояния автомата будут меняться просто по времени (переменная t)

int main(void)

{

int t;

int TrafL[5]={10,3,5,5,5};//красн.—10сек.,желт.3 и т.д. char state;

DDRB |= 7; // настроим линии на вывод.

state=0;t = TrafL[state]; //зададим начальное состояние. while(1) // бесконечный цикл

{

delay();

switch(state)

{

case 0: PORTB = ~0x01; break; // красный

case 1: PORTB = ~0x03; break; // красный+желтый case 2: PORTB = ~0x04; break; // зеленый

case 3: if (t & 1)

// мигающий зеленый

PORTB = ~0x04;

 

else

 

PORTB = ~0x00; break;

case 4: PORTB = ~0x02; break; // желтый default: state = 0;

}

if(t-- == 0) // Смена состояния системы.

{

state ++;

if (state >4) state = 0; t = TrafL[state];

}

}}

14

«Дребезг контактов» Практически у всех механических контактов имеется следующая

особенность: прежде, чем надежно соединиться, за счет упругости материала контактов может произойти несколько соединений/разъединений. Таким образом, график напряжения на входной ножке микроконтроллера будет таким:

Это явление называется «дребезг контактов». Это — вредное явление, поэтому от него стараются избавиться путем и механических методов, и электронных, но проще всего сделать это программным методом. Вообще говоря, удобно опрос клавиатуры выделить в отдельную функцию, чтобы иметь достаточно абстрактную и портируемую реализацию. Кроме того, централизовано будет реализовано декодирование клавиш.

Я опрашиваю клавиатуру в обработчике прерывания таймера, которое, как правило, вызывается 200-500 раз в секунду. Проверяя соответствующий бит в смежные моменты времени, программа реагирует только на неизменный сигнал: было 1 и осталось 1; было 0 и осталось 0. Можно также различать события нажатия и отпускания кнопок.

Вот участок обработчика прерывания:

SIGNAL(SIG_OVERFLOW0)

{

...

K_DDR &= ~K_MASK;

K_PORT |= K_MASK;

...

keys = ((~K_PIN) & K_MASK); K_PORT &= ~K_MASK; K_DDR |= K_MASK;

if ((keys == K_NONE) || (oldkeys != keys))// none key pressed or smth. changed

{

krepeat = 0; kptimes = 0;

KF &= ~(KFAST | KPROG | KMULT | KULTRAFAST);

}

15

else

{

 

if (krepeat<255) krepeat++;

 

if (keys & K_REPEATABLE)

 

if ((KF & KULTRAFAST) || (krepeat == ((KF&KFAST)?15:40)))

 

{

 

krepeat=5;

 

if (kptimes < 255) kptimes++;

 

if (kptimes==7) KF |= KFAST;

 

if (kptimes==20) KF |= KULTRAFAST;

 

}

 

if (krepeat == 5)

 

{

 

kresult = keys;

 

KF |= KREADY;

//

kprocess(keys); // process an event of keypressing

 

}

}

oldkeys = keys;

Здесь oldkeys — глобальная volatile char переменная, хранящая состояние клавиатуры во время предыдущего вызова прерывания.

16

Организация памяти процессора.

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

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

Для полноты картины можно упомянуть еще о командах управления выполнением: они меняют последовательность выполнения команд либо безусловно, либо при выполнении какого-то условия (различные переходы).

Сейчас нас интересует организация хранения данных и программ. В универсальных процессорах (тот же самый Pentium и вообще x86) области хранения программ и данных принципиально ничем не отличаются — только соответствующей настройкой специальных регистров процессора. Такая архитектура называется «Принстонской» (или Фон-Неймановской арх.). Удобство такой архитектуры в том, что мы можем произвольно распределять имеющуюся память под программы и данные в произвольном соотношении

возрастает универсальность процессора.

Внашем процессоре реализована так называемая «Гарвардская архитектура»: области для хранения различных типов данных принципиально различны и реализованы различными «проводками». Зато обращение к командам и к данным производится независимо. Кроме того, команды и данные могут иметь различный размер. В нашем ATMEL AVR MCU данные хранятся побайтово, а размер команды — 16 бит (2 байта).

Вx86 есть команды, занимающие аж до 6 байт, для этого приходится идти на различные конструктивные ухищрения. А вот в AVR одна команда — одно программное слово. Даже если команда использует данные — они помещаются в эти же 16 бит. Гарвардская архитектура упрощает реализацию процессора; увеличивает его быстродействие при сравнимой сложности устройства; позволяет более оптимально использовать адресное пространство. В то же время процессор получается менее универсальным: как определено на этапе разработки, что «вот здесь у нас будет 16 КБ постоянной памяти команд», а «вот здесь — 1 КБ оперативной памяти данных» — и ничего с этим не сделать.

Еще уточнение: память команд у нас является постоянной и программируется 1 раз после компиляции («прошивка» процессора). И во время работы процессора изменять память команд практически невозможно (есть, конечно, команды lpm/spm, но у них несколько другое назначение).

А вот память для хранения данных менять можно (и нужно), хоть по 1010 раз в день, но при пропадании питания все данные в ней теряются.

17

Программа-симулятор VMLAB (Виртуальная микропроцессорная лаборатория).

1) Запустите рабочего стола или из меню «Пуск» - «Программы» - «VMLAB» - «VMLAB»:

2) Выберите в «главном меню» программы пункт «Project» -

«New» (Проект — Новый)

3) В «STEP 1» нажмите «Enter name/browse/create dir.», в появившемся диалоге выбора файла перейдите на Ваш сетевой диск (K:), создайте папку проекта (как в Delphi) (название папки должно отражать примерную цель Вашего проекта. Пусть в нашем случае это будет TrafLight - «Светофор».

4)Название файла проекта «my_idea.prj» можно оставить без изменений.

5)«STEP 2»: выберите целевой микроконтроллер. В нашем случае это ATMEGA 162.

6)В «STEP 3» Выберите 2й пункт: GNU C Compiler, далее замените

C:\WinAVR на C:\WinAVR-20081205 (это связано с использованием более свежей WinAVR). Спросите у преподавателя, возможно, WinAVR снова обновилась.

7)В «STEP 4» укажите название файла с исходным кодом: trafl.c и нажмите «<--Add this». Если имеются дополнительные файлы (типа библиотеки работы с ЖК-дисплеем или библиотеки вспомогательных функций) — скопируйте их в папку с проектом и добавьте при помощи кнопки «Browse+add»

8)Нажмите «Ок». Первый проект создан. Приступим к настройке параметров симуляции.

18

Настройка параметров симуляции в VMLAB

1)Переместите окно проекта на передний план:

Вэтом окне редактируется файл, непосредственно содержащий информацию:

1)тип целевого процессора

2)местонахождение файлов с исходным кодом и правила их компиляции

3)некоторые служебные параметры (.TRACE)

4)условия электропитания процессора (.POWER VDD=5 VSS=0 ; Power nodes)

5) условия тактования процессора (.CLOCK 1meg

; Micro clock)

6)Кроме этого, здесь можно задавать, к каким «ножкам» виртуального процессора будут подключены виртуальные кнопки, регуляторы, светодиоды и «терминалка». Делается это следующим образом:

K0 VSS PC0

K1 VSS PC1

Так описываются простейшие кнопочки: мышкой нажал => выводы PC0 и VSS (он же GND, он же «земля», он же «общий») соединились. Отпустил : разъединились.

Функциональность кнопки можно улучшить: сделать ее «с защелкиванием»:

K4 VSS PC5 LATCHED

Именно так следует описывать тумблеры.

Виртуальные светодиоды подключаются так: D1 VDD PB1 ; RED

D2 VDD PB2 ; YELLOW

D3 VDD PB3 ; GREEN

Как Вы уже могли сообразить, после знака «;» здесь пишется комментарий.

19

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

.plot V(PD0) V(PD1) V(PB0)

Эта строчка означает, что мы желаем смотреть напряжения одновременно в точках PD0, PD1, PB0 (это ножки нашего виртуального контроллера)

Какую-либо из ножек контроллера можно явно подключить к выводу питания +5V (VDD), «земле» (VSS), или источнику прямоугольных импульсов частотой 10 кГц (например):

R2 PD7 VDD 1K

R3 PD6 VSS 1K

V1 PC1 VSS PULSE(0 5 40u 0 0 50u 100u); (v_initial v_final t_delay t_rise t_fall t_width t_period)

Здесь R2, R3 — виртуальные «сопротивления»; параметры генератора расшифровываются так: v_initial — начальное напряжение;

v_final — конечное напряжение; t_delay — время задержки

t_rise — время нарастания импульса («передний фронт») t_fall - время спада импульса («задний фронт»)

t_width — длительность «вершины» импульса t_period — период повторения.

буква «u» означает греческую «μ» - сокращение от «μS» - интервал задан в микросекундах. Такое обозначение встречается часто, например, мкФ — это

«uF» («μF») просто раньше печатные машинки не имели греческого шрифта, и дорисовывать черточку приходилось вручную.

А вот так задаются виртуальные ЖК-дисплей и виртуальный терминал. Что это такое, поговорим несколько позже.

X1 LCD(20 4 270K) PD2 PD3 PB0 PD7 PD6 PD5 PD4 nc3 nc2 nc1 nc0 X8 TTY(115200 8) PD0 PD1

Почти все устройства, добавленные нами в симулятор, собраны в одном окне:

Вертикальный ряд D1-D8 — это светодиоды, к сожалению, все красные.

20