Микроконтроллеры AVR: начало. Составление схемы и симуляция в Proteus

Ни для кого не секрет, что микроконтроллеры находят все большее и большее применение в различных электронных устройствах. И уважающему себя айтишнику-радиолюбителю стыдно не знать их архитектуру, принцип работы, возможности использования. Поводом для написания этой статьи стало отсутствие каких-либо знаний о микроконтроллерах, а самое главное — отсутствие железа, на котором можно поучиться-поиграться со светодиодами/кнопками/индикаторами (всякие эти ваши «одурины» не в счет, банально нет ни макетницы, ни контроллера, ни желания все это собирать/разбирать/паять/перепаивать). Вывод напрашивается сам — необходимо использовать программу для дизайна схем в совокупности с эмулятором. Как нельзя кстати подошел пакет Proteus VSM: имеется редактор схем, конечно же библиотека компонентов, редактор PCB с автотрассировщиком, 3D-визуализатор готовой платы, различные измерительные приборы, и самое главное — компиллятор, дебаггер и симулятор.

В качестве подопытного образца мы возьмем Attiny2313 — 8-битный RISC FLASH-микроконтроллер. (Почему AVR? Как говорят некоторые дамы — «Потому что!»).  Про FLASH-память, EEPROM и RAM говорить пока не буду, потому что имеющихся ресурсов контроллера хватит для того, чтобы поиграться со светодиодами/кнопками.

Выглядит он вот так (В корпусе DIP20):

attiny2313-20pu_dip20
Рис. 1

Расположение выводов

ATtiny2313_pins
Рис. 2

 

Контроллер может работать с восьмибитными числами, старшее из которых 11111111 (в двоичной системе счисления) или FF (в шестнадцатеричной системе счисления) или 255 (в десятичной системе). Для различия двоичных и шестнадцатеричных чисел в коде их записывают соответственно 0b11111111 и 0xFF (или $FF).

При работе с входами и выходами микроконтроллера обычно используют двоичную систему счисления, при этом каждый входной или выходной контакт соответствует конкретному биту. Бит, установленный в 1, соответствует состоянию, называемому логическая единица. Это означает, что напряжение на выводе микроконтроллера равно напряжению питания
(например, +5 В). Бит, сброшенный в 0, соответствует состоянию логического нуля, или 0 В. Для входных сигналов порогом между состояниями логического 0 и логической 1 является половина напряжения питания (например, +2.5 В).

Входы/выходы контроллера объединяются в порты ввода/вывода. Для данного контроллера выводы PB0…PB7 — выводы порта B, PD0…PD6 — выводы порта D (порт урезанный — вместо восьми ног у него семь).  Для того, чтобы передать программе (или из программы) информацию о состоянии портов ввода/вывода (банально — на какой ноге порта какое напряжение) используются регистры ввода/вывода PortB, PortD, PinB, PinD — для порта B и  D соответственно. В общей сложности у данного контроллера их 64 (с номерами 0x00…0x3F). Каждый из регистров выполняет специфические функции (например, считает ход времени, управляет последовательным портом и т.п.). Но сейчас нам нужны PortB, PortD, PinB и PinD. Если порт B настроен на вход, то число, содержащееся в регистре PinB показывает состояние выводов (замкнут на общий провод — логический ноль, на шину питания — логическая единица). При этом младший разряд (нулевой бит) соответствует ноге PB0, старший — ноге PB7.

Пример №1. Выводы PB0…PB7 подключены к шине питания (Рис. 3)

PinB_ff
Рис. 3

Если прочесть содержимое регистра PinB, то мы увидим там число 0xFF или 0b11111111. Если отсоединить вывод PB0 от шины питания (Рис. 4), то в PinB мы увидим число 0xFE или 0b11111110.

PinB_fe
Рис. 4

Аналогичным образом, если порт B настроен на выход, его состояние контролируется регистром PortB.

Пример №2. Все выводы РВ0…РВ7 являются выходами и подключены к светодиодам, другие выводы светодиодов подключены через резисторы к общему проводу. Для включения всех светодиодов в регистр PortB необходимо записать число 0b1111111 или 0xFF. Для выключения двух центральных светодиодов в регистр PortB необходимо записать число 0b11100111 или 0xE7.

Помимо регистров ввода/вывода, у микроконтроллера есть 32 рабочих регистра R0…R31. Основное отличие их от регистров ввода/вывода в том, что в регистр ввода/вывода нельзя напрямую загрузить число, а рабочие как раз для этого и предназначены.

Практикум №1. Необходимо зажечь светодиод, подключенный к общему проводу, и удерживать его в зажженном состоянии.

Итак, запускаем Proteus 8.1 SP1, идем в «File» => «New project», вводим имя, выбираем куда сохранять, жмем «Next». На следующем шаге выбираем «Create a schematic from the selected template», по умолчанию выбрано «Portrait A4», нам хватит, жмем «Next». PCB Layout нам не нужен, оставляем «Do not create…», жмем «Next». Тут начинается интересное: на следующем шаге выбираем «Create Firmware Project», в «Family» выбираем AVR, в «Controller» — ATtiny2313, компилятор используем встроенный в Proteus, жмем «Next» и «Finish». Открываются вкладки «Schematic Capture» и «Source Code» со схемой и исходным кодом программы контроллера соответственно (Рис. 5).

proteus
Рис. 5

Переходим к схеме. Контроллер уже добавлен в список компонентов и размещен на схеме при создании проекта. За светодиодами идем в библиотеку (Рис 6.)

led_lib
Рис. 6

В поле «Keywoards» вводим LED, далее все как по скриншоту, и ОК. Компонент добавился в список. Выбираем его оттуда, ставим курсор-карандашик на поле со схемой, кликаем раз, целимся, кликаем два — компонент установлен. Тянем карандашиком провод от 12 ноги контроллера к аноду светодиода (Автотрассировку проводников можно отключить). Землю берем, кликая по кнопке «Terminals Mode» terminal_mode на левой панели инструментов, подключаем к катоду светодиода.

Рис. 7
Рис. 7

Двойным кликом на светодиоде открываем окно его свойств, и в «Model Type» ставим Digital (Рис. 8).

Рис. 8
Рис. 8

На этом аппаратная реализация заканчивается, переходим к написанию программы. Чтобы написанную программу можно было записать в микросхему, ее необходимо ассемблировать. Данная операция преобразует текст программы в последовательность чисел, которая может быть помещена в FLASH-память программ микроконтроллера. Эта последовательность чисел называется шестнадцатеричным кодом, или hex-файлом — и файл будет иметь расширение .hex. Ассемблер проверяет программу строку за строкой и пытается преобразовать каждую строчку в соответствующий шестнадцатеричный код. На вкладке «Source Code» (если закрыли — кнопка на верхней панели view_source ) уже имеется скелет файла программы, созданный мастером новых проектов.

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

Рис. 9
Рис. 9

В процессе инициализации необходимо указать, каким образом будет использоваться вывод PB0 порта B — на вход или на выход. В данном случае PB0 используется на выход. Осуществить данную операцию поможет регистр DDRB — регистр направления передачи данных. Каждый бит этих регистров соответствует одному из выводов микроконтроллера.
Например, бит 4 регистра DDRB соответствует выводу РВ4, а бит 2 регистра DDRD — выводу PD2. Установка соответствующего бита регистра DDRx в 1 делает вывод выходом, а сброс бита в 0 делает вывод входом. Если вывод микроконтроллера никуда не подключен — его необходимо сделать выходом. Таким образом, для порта B в регистр DDRB необходимо загрузить число 0b11111111 или 0xFF. Порт D не используется, поэтому в DDRD также надо загрузить 0xFF.

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

ldi R16,0xFF ; Команда загружает непосредственное значение в регистр. Работает только с регистрами R16...R31
out DDRB,R16 ; Команда выводит содержимое регистра общего назначения R16 в регистр ввода/вывода DDRB.

Если в рабочий регистр необходимо записать 0x00, вместо команды ldi можно воспользоваться командой

clr register ; Команда сбрасывает содержимое регистра. Работает со всеми рабочими регистрами.

Если в рабочий регистр необходимо записать 0xFF, можно воспользоваться командой

ser register ; Команда устанавливает все биты регистра в 1. Работает только с регистрами R16...R31.

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

ldi R16,0x00
out PortB,R16 ; Задаем начальное состояние выводов порта B
out PortD,R16 ; Тоже самое для порта D

Таким образом, секция Init выглядит следующим образом:

Init:
 ldi R16,0xFF
 out DDRB,R16 ; Устанавливаем DDRB как выход
 out DDRD,R16 ; Устанавливаем DDRD как выход

 ldi R16,0x00
 out PortB,R16 ; Задаем начальное состояние выводов порта B
 out PortD,R16 ; Задаем начальное состояние выводов порта D

Далее зажигаем светодиод. Чтобы его включить, нужно на вывод RB0 подать напряжение ВЫСОКОГО логического уровня, для чего 0-й бит регистра PortB устанавливается в 1. Для выполнения этой операции можно загрузить число в рабочий регистр, а затем переслать его в регистр PortB; однако это можно сделать гораздо проще. Можно использовать следующую команду:

sbi PortB,0 ; Эта команда устанавливает в 1 нулевой бит в регистре ввода/вывода PortB

Хоть и нельзя загружать числа непосредственно в регистры ввода/вывода, можно устанавливать и сбрасывать индивидуальные биты в некоторых из них. Нельзя устанавливать и сбрасывать отдельные биты в регистрах ввода/вывода 32…63 ($20…$3F). К счастью, регистр PortB ($18), а также все регистры Portx и Pinx могут управляться описанным образом. Аналогичная команда для сброса бита имеет следующий формат:

сbi PortB,0 ; Эта команда сбрасывает нулевой бит в регистре ввода/вывода PortB. Применима только к регистрам ввода/вывода 0...31

В нашем конкретном случае мы хотим установить 0-й бит регистра PortB в 1. Воспользуемся для этого следующей командой, пометив ее меткой Start:

Start:
 sbi PortB,0 ; Включаем светодиод

В следующей строке напишем:

rjmp Start ; Возвращаемся к метке Start

Таким образом, микроконтроллер будет выполнять бесконечный цикл, постоянно включая светодиод. Теперь программа готова. Готовый исходник можно скачать отсюда. Чтобы посмотреть, что получится, необходимо проранить весь проект в режиме симуляции (кнопка simul_start на панели управления симуляцией). При удачной компиляции программа переходит на вкладку со схемой, на которой отображаются логические уровни на выводах микроконтроллера и все, что с ними связано (в данном случае горит светодиод — рис. 10).

Рис. 11
Рис. 11

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

В первом случае для того, чтобы включить светодиод, необходимо было установить на выходе PB0 ВЫСОКИЙ уровень, т.е. предполагалось, что выводы будут играть роль источников тока для светодиодов.  Во втором случае светодиод подключен к источнику питания (рис. 11), и для того, чтобы его зажечь, необходимо наличие логического нуля на выводе контроллера, т.е. выводы микроконтроллера являются потребителями тока светодиодов.

Рис. 12
Рис. 12

Поэтому вместо обнуления регистра PortB в начале секции Init потребуется записать в него число 0b11111111 (чтобы выключить все светодиоды).

Таким образом, секция Init будет выглядеть так:

Init:
 ser R16 ; устанавливаем в R16 0xFF для загрузки в DDRB, DDRD и PortB
 out DDRB,R16 ; Устанавливаем DDRB как выход
 out DDRD,R16 ; Устанавливаем DDRD как выход
 out PortB,R16 ; Инициализация PortB как потребителя тока

; PortD не используется, поэтому загружаем в него 0x00
 clr R16 ; Сбрасываем R16 для загрузки в PortD
 out PortD,R16 ; Задаем начальное состояние выводов порта D

Кроме того, для включения светодиода нужно будет не устанавливать 0-й бит регистра PortB, а сбрасывать его. Для этого достаточно вместо команды sbi использовать команду cbi. Секция Start будет иметь вид:

Start:
 cbi PortB,0 ; Делаем логический 0 на ножке PD0, зажигая светодиод
 rjmp Start ; Зацикливаемся

Готовый исходник можно посмотреть здесь. Запускаем симуляцию, должно получиться как на рис. 13.

Рис. 13
Рис. 13

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

Литература, настоятельно рекомендуемая к прочтению: 

1. Джон Мортон — Микроконтроллеры AVR. Вводный курс. 2006 г. Статья писалась, опираясь на знания, полученные от прочтения этой книги, брались некоторые цитаты. Книжка немного устаревшая ( частности контроллер AT90S1200 можно уже не найти), но в ней очень хорошо дается материал, ее интересно читать, особенно когда проделываешь всё на практике (пусть и в симуляторе).

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *