Программы
Книги
Статьи

Микроконтроллеры AVR. Вводный курс
Скачать книгу  

Описание

ОГЛАВЛЕНИЕ

Благодарности................................................................9

Предисловие................................................................ 10

Глава 1. Введение........................................................... 11

Краткое замечание для пользователей PIC...................................... 13

Системы счисления.......................................................... 14

Сложение в двоичной системе................................................. 17

Отрицательные числа......................................................... 17

8-битный RISC FLASH-микроконтроллер?..................................... 19

Первые шаги................................................................20

Выбор модели...............................................................20

Блок-схема алгоритма........................................................23

Написание программы........................................................24

Ассемблирование............................................................25

Регистры....................................................................25

Команды....................................................................29

Шаблон программы..........................................................30

-5-

Оглавление

Глава 2. Основные операции в AT90S1200 и TINY12..........................37

Программа А. Светодиод (LEDon).............................................37

AVR Studio — трансляция с языка ассемблера...............................39

Проверка..............................................................40

AVR Studio — симуляция.................................................40

Эмуляция..............................................................41

Аппаратное обеспечение.................................................42

AVR Studio — программирование.........................................45

Конфигурационные ячейки..............................................46

Программы В и С. Кнопка....................................................47

Семисегментные индикаторы и косвенная адресация........................49

Программы D и Е. Счетчик...................................................55

Формирование временных интервалов.....................................60

Программа F. Бегущий огонек.................................................63

Формирование временных интервалов без таймера?.........................69

Счетчик команд и подпрограммы.........................................71

Программа G. Счетчик (версия 3.0)............................................75

Программа Н. Светофор......................................................77

Логические элементы....................................................83

Программа I. Симулятор логических элементов..................................85

SREG — регистр состояния..............................................91

Сторожевой таймер.....................................................91

Спящий режим.........................................................93

Остальные команды.....................................................94

Программа J. Частотомер.....................................................95

Глава 3. Знакомство с остальными моделями семейства.......................111

Глава 4. Дополнительные возможности......................................118

Прерывания................................................................ 118

Программа К. Измеритель скорости реакции................................... 120

Случайное распределение............................................... 123

Аналоговый компаратор................................................  128

Программа L. 4-битный аналого-цифровой преобразователь..................... 129

Аналого-цифровой преобразователь (АЦП)............................... 132

Программа М. Инвертор напряжения.........................................  136

EEPROM.............................................................  140

Таймер/счетчик 1 (16-битный)...........................................  142

Функция захвата.......................................................  143

Функция сравнения.................................................... 146

Главная программа N. Музыкальный автомат.................................. 146

Глава 5. Продвинутые возможности..........................................152

ШИМ — широтно-импульсная модуляция.....................................  152

UART.....................................................................  154

Программа О. Конвертер клавиатуры.......................................... 160

-6-

Последовательный интерфейс SPI........................................ 163

Нестандартный Таймер 1 модели Tinyl5.................................. 167

Сокращение объема кода................................................ 170

Обзор семейства Mega.................................................. 171

Заключительная программа Р. Робот, управляемый компьютером................. 172

Заключение................................................................ 178

Приложение А. Основные параметры некоторых моделей AVR..................180

Приложение В. Цоколевка некоторых моделей AVR..........................181

Приложение С. Обзор системы команд......................................182

Приложение D. Справочник команд........................................186

Приложение Е. Таблица векторов прерываний...............................195

Приложение F. Преобразование шестнадцатеричных чисел...............   ___197

Приложение G. Таблица кодов символов ASCII..............................198

Приложение N. Если ничего не получается, прочтите это......................199

Приложение I. Контактная информация н дополнительная литература...........200

Приложение J. Полные тексты учебных программ............................201

Ответы к упражнениям.....................................................244

Предметный указатель.......................................................265

Предметный указатель...................................................... 1001

Джон Мортон кроконтроллеры AVR

Вводный курс

ЮАЭКА

(%) Newnes

John Mortem

AVR

Am Introductory Course

(%) Newnes

Серия «МИРОВАЯ ЭЛЕКТРОНИКА»

Джон Мортон

Микроконтроллеры

AVR

Вводный курс

Перевод с английского

Москва

Издательский дом «Додэка-ХХГ»

2006

ш

ОДЭКА

John Моф&Щ       Джон Морт

AVR

An Ilitroductory Course

($j Newnes

Серия «МИРОВАЯ ЭЛЕКТРОНИКА»

ОН

Микроконтроллеры

AVR

Вводный курс

Перевод с английского

Москва

Издательский дом «Додэка-ХХГ»

2006

ш

ОДЭКА

УДК621.316.544.Ште1 ББК 31.264

М80

Мортон Дж.

М 80 Микроконтроллеры AVR. Вводный курс. /Пер. с англ. — М.: Издательский дом «Додэка-ХХ1», 2006. — 272 с: ил. (Серия «Мировая электроника»).

ISBN 5-94120-096-Х

Данное издание представляет собой практическое руководство, с помощью которого вы сможете изучить, а впоследствии и использовать микроконтроллеры AVR компании Atmel.

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

Для демонстрации различных возможностей AVR Джон Мортон использует простые устройства и программы. В отличие от книг, в которых ихпагается голая теория либо просто воспроизводится фирменная техническая документация, такой подход (обучение в процессе использования) предлагает быстрое и интуитивное изучение возможностей микроконтроллеров AVR.

В общей сложности, в книге рассмотрены 16 проектов, охватывающих все наиболее популярные микроконтроллеры AVR, включая модели семейства Tiny.

Предназначена для разработчиков радиоэлектронной аппаратуры, инженеров, студентов технических вузов и радиолюбителей.

УДК621.316.544.1А1те1 ББК 31.264

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

Процесс разработки программ состоит из пяти основных этапов:

1.   Выбор  конкретного  микроконтроллера  и  составление  блок-схемы, программы.

2.   Написание программы (с помощью Блокнота, AVR Studio или любой другой подходящей программы).

3.   Ассемблирование программы (преобразует написанный вами текст в форму, понятную микроконтроллеру).

4.   Симуляция   или   Эмуляция   программы,   чтобы   убедиться    в   ее работоспособности (или неработоспособности).

5.   Программирование AVR. На этом этапе написанное вами заносится в реальный микроконтроллер.

А теперь рассмотрим некоторые из этих этапов более подробно.

Выбор модели

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

AT90S1200---------    Код объема ОЗУ; 0 - ОЗУ отсутствует

'------------    № модели ЦПУ — О

'--------------    Код объема EEPROM; 2 — 64 байта

'----------------    1 Кбайт FLASH-памяти программ

Коды объема памяти:

Коды                0123456789АВ

Объем [байт]    0     32    64    128   256   512   1К   2К   4К   8К   16К 32К

-20-

Выбор модели

Значения использованных терминов могут быть вам незнакомы, однако не волнуйтесь — мы скоро их рассмотрим. Следует заметить, что микроконтроллеры семейств Tiny и Mega имеют немного другую систему обозначений. Краткие сведения о характеристиках некоторых микроконтроллеров AVR приведены в Приложении А.

@S  УПРАЖНЕНИЕ 1.8. Определите объем различных областей памяти *^   микроконтроллера AT90S8515.

Одним из наиболее важных параметров микроконтроллеров, не нашедший, к сожалению, отражения в обозначении модели, является число входов и выходов. Модель 1200 имеет 15 контактов ввода/вывода (т.е. 15 выводов, которые могут использоваться как входы или выходы), а модель 8515 — целых 32 контакта ввода/вывода!

0 ПРИМЕР 1.10. Необходимо разработать устройство, которое будет считать количество нажатий на кнопку и отображать это число на одном семисегментном индикаторе (при достижении значения 9 он будет сбрасываться).

1.  Для управления семисегментным индикатором требуется семь выходов.

2. Для кнопки требуется один вход.

Таким образом, для такого устройства потребуется в общей сложности 8 контактов ввода/вывода. В данном случае вполне можно использовать 1200, поскольку это одна из самых простых моделей, имеющая достаточное количество выводов.

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

0 ПРИМЕР 1.11. Необходимо разработать счетчик, который прибавляет число от 1 до 9 к текущему двухзначному значению. Соответственно, в устройстве будет 9 кнопок и 2 семисегментных индикатора. На первый взгляд, для решения поставленной задачи потребуется достаточно много входов и выходов:

1.  Для каждого семисегментного индикатора требуется семь выходов, итого 14.

2. Для каждой кнопки требуется один вход, итого 9.

-21-

Глава 1. Введение

Таким образом, в общей сложности требуется 23 вывода, что влечет за собой необходимость использования «большого» микроконтроллера, такого как 8515 (имеющего 32 контакта ввода/вывода); однако на самом деле использовать такой «большой» микроконтроллер нет никакой необходимости, поскольку требуемое число выводов можно значительно уменьшить.

При использовании стробирования состояния всех кнопок можно будет прочитать с помощью шести выводов, а для управления двумя семисег-ментными индикаторами потребуется всего девять выводов. Итого получается 15 контактов ввода/вывода, имеющихся в микроконтроллере 1200. Соответствующая схема приведена на Рис. 1.2.

PDO PD1 PD2/INT0 PD3 PD4/T0 PD5 PD6

AT90S1200

Рис. 1.2. Схема стробирования

При подаче на вывод РВО лог. 1 (+5 В), а на выводы РВ1 и РВ2 — лог. 1 (0 В) разрешается обработка кнопок 1, 4 и 7. После этого состояние каждой из них можно узнать, проверив напряжение на одном из выводов РВЗ...РВ5. Таким образом, подавая последовательно на выводы РВ0...РВ2 лог. 1, можно проверить состояние всех кнопок. Чтобы определить, какое количество выводов потребуется для обслуживания массива из X кнопок, найдите пару сомножителей числа X, имеющих наименьшую сумму (например, для числа 24 сомножителями с наименьшей суммой являются числа 6 и 4, поэтому для контроля 24 кнопок потребуется 6 + 4 = 10 контактов ввода/вывода). Лучше сделать меньшее число выводов (конечно, если эти числа не равны) выходами, а большее число — входами. В этом случае опрос всех строк матрицы кнопок займет меньше времени.

22-

Блок-схема алгоритма

Стробирование семисегментных индикаторов заключается в кратковременном отображении числа на одном индикаторе и последующем выключении этого индикатора на время отображения другого числа на другом индикаторе. На выводы PD0...PD6 выдается код числа для обоих индикаторов, а подавая лог. 1 на вывод РВ6 или РВ7, вы можете включать соответствующий индикатор. Хотя в действительности индикаторы мерцают с большой частотой, кажется, что они светятся непрерывно. Требования к программированию подобных узлов мы рассмотрим позже.

@S  УПРАЖНЕНИЕ 1.9. С помощью Приложения А определите, какую ^^   модель микроконтроллера AVR можно использовать для реализации 4-разрядного калькулятора с кнопками для цифр от 0 до 9 и пяти операций: +, -, х, -=- и =.

Блок-схема алгоритма

После того как вы определили требуемое количество контактов вво-ka/вывода и, таким образом, выбрали конкретный микроконтроллер, можно приступать к следующему этапу, который заключается в создании блок-схемы программы. В принципе, на этом этапе формируется основа Программы, а написать программу, имея перед собой блок-схему, гораздо ;легче, чем с нуля.

k Блок-схема должна отображать основные этапы функционирования микроконтроллера, а также прояснять структуру программы. Представьте, |что ваша программа является растительным лабиринтом. В этом случае рлок-схема будет представлять собой грубую карту, обозначающую основные участки лабиринта. При создании блок-схемы вы должны иметь в ви-*ду, что лабиринт не может выходить к обрыву (т.е. программа не может просто взять и закончиться), так как в противном случае AVR перешагнет через край и разобьется. Вместо этого AVR вынужден постоянно бродить по лабиринту (хотя вы можете усыпить его!). Простой пример блок-схемы программы приведен на Рис. 1.3.

0    ПРИМЕР 1.12. Блок-схема программы, которая включает светоди-од (СИД), если нажата кнопка.

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

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

Рис. 1.3, Блок-схема программы, включающей и выключающей СИД

Объем кода, соответствующий каждому конкретному элементу блок-схемы, может быть самым разным и, вообще говоря, не важен. Идея блок-схемы заключается в выделении основных этапов выполнения программы, а также в создании диаграммы, которую мог бы понять любой человек, даже совершенно незнакомый с программированием. В дальнейшем вы поймете, что гораздо легче писать программу на основе блок-схемы, поскольку в этом случае можно заниматься каждым блоком по отдельности, совершенно не задумываясь об общей структуре программы.

ф^ УПРАЖНЕНИЕ 1.10. Повышенной сложности! Нарисуйте блок-схе-^^ му программы устройства сигнализации с тремя кнопками. При обнаружении устройством сигнала от датчика необходимо в течение 10 с нажать в правильном порядке три кнопки, иначе сработает сигнализация. Если кнопки нажаты вовремя, устройство возвращается в состояние, в котором находилось до получения сигнала от датчика. Если набран неправильный код, включается сирена. (Сложность ответов может быть различной, однако для ориентировки намекну, что мой вариант ответа состоит из 13 блоков.)

Написание программы

Следующим этапом после разработки блок-схемы является загрузка шаблона (подобного предложенному на стр. 31) и написание на его основе своей программы. Это можно сделать с помощью любого простейшего текстового редактора, например Блокнота (эта программа входит в состав Windows®), или специализированной среды разработки, такой как AVR Studio.

-24-

Ассемблирование

Ассемблирование

Чтобы написанную вами программу можно было записать в микросхе-

!у, ее необходимо ассемблировать. Данная операция преобразует текст рограммы в последовательность чисел, которая может быть помещена в LASH-память программ микроконтроллера. Эта последовательность чи-;л называется шестнадцатеричным кодом, или hex-файлом — и файл будет меть расширение .hex. Ассемблер проверяет вашу программу строку за строкой и пытается преобразовать каждую строчку в соответствующий шестнадцатеричный код. Если он не может распознать, что написано в какой-либо строке, он регистрирует в этой строке ошибку (error). Ошибкой является то место в программе, которое ассемблер однозначно считает неправильным, т.е. он не может понять, что там написано. Также ассемблер может сгенерировать предупреждение (warning) — если встретилось что-то, что возможно неверно, т.е. написанное выглядит необычно, но не обязательно неправильно. Все сказанное станет гораздо понятнее, когда мы приступим к ассемблированию нашей первой программы.

Регистры

Одним из наиболее важных аспектов программирования AVR и микроконтроллеров вообще являются регистры. Чтобы было понятнее, представьте себе, что в микроконтроллере AVR имеется шкаф с большим количеством ящиков, в каждом из которых хранится 8-битное число (один байт). Эти ящики и являются регистрами — точнее, мы называем их регистрами ввода/вывода (РВВ). Кроме этих регистров ввода/вывода, у нас есть 32 «рабочих» регистра. Они отличаются от регистров ввода/вывода, поскольку не являются частью шкафа. Представьте себе, что рабочие регистры являются служащими, а вы — их начальником. Если вы хотите положить что-нибудь в шкаф, вы отдаете это служащему и приказываете ему положить это в шкаф. Точно так же программист не может поместить число непосредственно в регистр ввода/вывода. Вместо этого он должен записать число в рабочий регистр, а затем скопировать рабочий регистр в регистр ввода/вывода. Вы можете также попросить служащих выполнить какую-либо операцию над имеющимися у них числами, т.е. вы можете складывать числа, находящиеся в рабочих регистрах. На Рис. 1.4 показаны регистры модели 1200.

Из рисунка видно, что каждому регистру соответствует уникальный номер. Рабочие регистры обозначаются как R0, R1, ..., R31. Заметим, однако, что регистры R30 и R31 немного отличаются от остальных. Они образуют сдвоенный регистр Z ~ регистр, который может содержать 16-битное значение (называемое словом). К этим регистрам можно обращаться

Заметим, что такое объединение используется только в некоторых командах. Будем считать, что команда не может использовать сдвоенный регистр, пока это не указано явно.

26

Регистры

Как вы понимаете, удобнее давать рабочим регистрам названия (по той же причине, по которой вы не называете своих служащих по их учетным номерам), и такая возможность у вас имеется. Целесообразно давать регистрам имена, соответствующие характеру хранящихся в них чисел. Например, если регистр R5 используется для хранения числа прошедших минут, его можно назвать Minutes. Каким образом можно назначать регистрам имена, вы узнаете, когда мы будем рассматривать шаблон программ. Чуть позже мы с вами также увидим, что рабочие регистры R16...R31 имеют больше возможностей, чем остальные.

Регистрам ввода/вывода тоже присвоены номера (0...63 десятичные, или S0...S3F шестнадцатеричные). Каждый из этих регистров выполняет специфические функции (например, считает ход времени, управляет последовательным портом и т.п.), и в течение курса мы рассмотрим функции всех этих регистров. Однако я отдельно выделю регистры PortB, PortD, PinB и PinD. Эти регистры ввода/вывода представляют собой порты (В и D соответственно) — основное средство связи микроконтроллеров AVR с окружающим миром. И не удивляйтесь, пожалуйста, отсутствию портов А и С. Все четыре порта (А, В, С и D) имеются в более развитых моделях (например, 8515); более простые микроконтроллеры AVR (например, 1200) имеют только два порта. Эти порты соответствуют портам В и D более развитых микроконтроллеров, поэтому так и называются.

На Рис. 1.5 приведено расположение выводов (цоколевка) микроконтроллера 1200. Обратите внимание на выводы, обозначенные РВО, РВ1, ..., РВ7, — это выводы порта В. Соответственно, выводы PD0...PD6 являются выводами порта D. Состояние этих выводов можно считать (как входов) или изменить (как выходов). Если порт функционирует как вход, то двоичное число, содержащееся в регистрах PinB или PinD, покажет нам состояние выводов, при этом вывод РВО будет соответствовать биту 0 регистра PinB и т.д. Если на выводе присутствует напряжение ВЫСОКОГО уровня, то соответствующий бит установлен в 1, и наоборот. Обратите внимание, что порт D содержит всего семь битов, а не восемь.

0 ПРИМЕР 1.15. Все выводы РВ0...РВ7 используются в качестве входов. К ним подключены кнопки, которые другими выводами подключены к шине питания +5 В. Когда все кнопки нажаты, в регистре PinB находится число Ob 11111111, или 255 в десятичной системе. Когда нажаты все кнопки, кроме кнопки, подключенной к выводу РВ7, в регистре PinB находится число 0Ь01111111, или 127 в десятичной системе.

Аналогичным образом, если вывод является выходом, его состояние контролируется соответствующим битом регистра Portx. Вывод может обеспечивать втекающий или вытекающий ток до 20 мА и, таким образом, способен напрямую управлять светодиодными индикаторами (СИД).

0

м?

ПРИМЕР 1.16. Все выводы РВ0...РВ7 являются выходами и подключены к СИД. Другие выводы СИД подключены через резисторы к общему проводу. Для включения всех СИД в регистр PortB записывается число 0Ы1111111. Для выключения двух центральных СИД в регистр PortB записывается число Ob 111 00 111.

УПРАЖНЕНИЕ 1.11. Воспользуемся примером, приведенным выше, в котором все выводы РВ0...РВ7 подключены к СИД. Мы хотим получить «бегущую дорожку» из восьми светодиодов (как показано на Рис. 1.6) и собираемся для создания этого эффекта поочередно записывать в регистр PortB соответствующие числа. Что это должны быть за числа (в двоичной, десятичной и шестнадцатеричной системе счисления)?

1. ОООООООФ

2. ООООООФО

3. ОООООФОО

4. ООООФООО

5. ОООФОООО е. ООФООООО 7. ОФОООООО е. ФООООООО э. ОООООООФ

и т.д. Рис. 1.6. «Бегущая дорожка»

-28-

Команды

@s УПРАЖНЕНИЕ 1.12. К выводам PDO, PD1 и PD2 подключены *^ кнопки, которые другими выводами подключены к шине питания +5 В. Эти кнопки используются в пульте для телевикторины. Какие числа в регистре PinD указывают на то, что одновременно нажато более одной кнопки (в двоичной, десятичной и шестнадцатеричной системе счисления)?

Команды

С этого момента мы начнем изучать команды. Полностью все команды микроконтроллеров AVR приведены в Приложении С в конце книги. В общей сложности микроконтроллеры AVR поддерживают около сотни команд. На первый взгляд это звучит довольно устрашающе, но не беспокойтесь — большинство из них просто дублируют друг друга. На самом деле имеется всего около 40 команд, которые действительно нужно запомнить. Причем большинство этих команд очень легко запомнить, поскольку они имеют знакомые названия, как, например, add или jmp. К тому же имеется несколько общих правил, которые могут помочь расшифровать неизвестную команду. Если в названии команды встречается буква i, это означает, как правило, наличие непосредственного операнда (immediate), т.е. числа, которое указывается прямо в команде, или в регистре ввода/вывода (I/O). Буква b часто означает бит (bit) или переход (branch). Давайте взглянем на формат строки с командой.

Г-?Г     ПРИМЕР 1.17

(Метка:)   sbi  portb.O                                     ; Включить СИД

Первым элементом строки (необязательным) является метка. Она позволяет перейти на эту строку из другого места программы. Замечу, что метка не может начинаться с числа и не должна совпадать с названием команды или регистрового файла. Метка всегда заканчивается символом двоеточия (при написании программы двоеточие легко пропустить, и, если вы будете невнимательны, метки могут стать частым источником ошибок). Причем метка необязательно должна находиться на той же строке, что и команда. Например, следующий фрагмент совершенно корректен:

Метка:

sbi  portb,3                                     ; Включить СИД

После метки располагается собственно команда: sbi, т.е. указание, что именно мы делаем, а затем — над чем мы это делаем: portb,0 (эти символы называются операндами). Последним и тоже немаловажным элементом   строки   является   точка   с   запятой,   за   которой   располагается

-29-

Глава 1. Введение

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

Подчеркну, что очень важно объяснять каждую написанную вами строчку, как это сделано в предыдущих примерах. И для этого есть множество причин. Во-первых, то, что вы написали, может быть понятно вам сейчас, но после некоторого перерыва, через неделю или через месяц, вы будете смотреть на строку и думать: «Черт возьми, для чего я это написал!» Во-вторых, у вас может возникнуть необходимость показать программу другим людям для консультации. Мне часто присылают программы, в которых, к сожалению, очень мало комментариев либо их вообще нет.

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

Г"*     ПРИМЕР 1.18

sbi  PortB, 0                                ; Установить бит 0 регистра PortB

Комментарий, подобный приведенному выше, абсолютно лишен смысла, поскольку он не объясняет, почему вы устанавливаете бит О регистра PortB, а просто констатирует этот факт. Если вы хотите получить общее представление обо всех имеющихся командах, внимательно изучите Приложение С. Это поможет вам понять, каким образом группируются различные команды. Со всеми этими командами мы будем постепенно знакомиться при написании учебных программ.

Шаблон программы

Большинство программ имеют одинаковую структуру, кроме того, во всех программах присутствуют некоторые общие элементы, необходимые для их нормальной работы. Поэтому, чтобы облегчить себе жизнь, мы можем написать некоторый шаблон, сохранить его и загружать всякий раз, когда начинаем писать новую программу. Шаблон, которым я обычно пользуюсь, приведен на Рис. 1.7.

-30-

Start:

<Разместите здесь текст вашей программы>

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

Рис. 1.7. Шаблон программы

Рамка из звездочек, располагающаяся в самом начале шаблона, представляет собой заголовок профаммы (звездочки здесь набраны исключительно для красоты). Заголовок заполняется таким образом, чтобы можно

; Первая выполняемая команда

Определяем входы и выходы порта В Определяем входы и выходы порта D

Включаем подтяжку для входов порта В и задаем начальные состояния выходов Включаем подтяжку для входов порта D и задаем начальные состояния выходов

-31-

Глава 1. Введение

было легко понять, что это за программа, не просматривая ее целиком. Также благодаря заголовку можно убедиться, что вы работаете с последней версией программы. Заметьте, что содержимое этого блока совершенно не влияет на реальную работу программы, поскольку в начале каждой строки стоит точка с запятой. В строке «Тактовая частота:» указывается частота источника тактовых сигналов (например, кварцевого резонатора), подключенного к микроконтроллеру. Микроконтроллеру AVR необходим стабильный сигнал, указывающий, когда следует переходить к выполнению следующей команды; таким образом, микроконтроллер выполняет команды в каждом периоде тактовых импульсов (или такте). Соответственно, если к микроконтроллеру подключен резонатор с частотой 4 МГц, то микроконтроллер будет выполнять около 4 миллионов команд в секунду. Заметьте, что я говорю около 4 миллионов, поскольку некоторые команды (как правило, используемые для переходов внутри программы) выполняются за два такта. В строке «Для AVR:» указывается, для какой конкретной модели микроконтроллера предназначена программа.

После заголовка начинаются строки, действительно выполняющие какие-либо функции. Слово .device является директивой (командой для ассемблера), которая сообщает ассемблеру, для какой модели микроконтроллера должна транслироваться программа. Например, если вы пишете программу для модели 1200, строка с этой директивой должна иметь вид:

.device  at90sl200

Другой важной директивой является директива .include, которая позволяет ассемблеру использовать так называемые включаемые файлы. Они выполняют для ассемблера роль словаря. Ассемблер поймет большинство написанных вами выражений, а для остальных ему может потребоваться найти перевод. Например, все имена регистров ввода/вывода и их адреса хранятся во включаемых файлах, поэтому, вместо того чтобы писать адрес S3F, вы можете указать символическое имя регистра SREG. При установке программы ассемблера на компьютер включаемые файлы для различных моделей микроконтроллеров помещаются в определенную папку. В программах я буду указывать путь, имеющийся на моем компьютере, однако на вашем компьютере этот путь может быть другим. Итак, если предполагается использовать модель 1200, полная строка будет иметь вид:

.include        "СЛРгодгал Files\Atnel\AVR Studio\Appr.otes\1200def.ir.c"

В заключение я хотел бы сказать несколько слов о директивах .nolist и .list. Когда ассемблер обрабатывает написанный вами код, он генерирует файл листинга, который содержит копию вашей программы с комментариями ассемблера. Вообще говоря, вам совсем не нужно, чтобы в файле листинга оказался довольно объемный текст включаемого файла. Для этого

-32-

Шаблон программы

достаточно поставить перед директивой .include директиву .nolist, которая указывает ассемблеру прекратить копирование считываемых данных в файл листинга. После строки с директивой .include поставьте директиву .list, чтобы вновь разрешить- ассемблеру вывод данных в файл листинга. Таким образом, строки с директивами .list и .nolist совершенно не влияют на функционирование программы, однако благодаря им можно значительно уменьшить размер файла листинга. Мы ознакомимся с файлом листинга более подробно, когда будем писать нашу первую программу.

После заголовка обычно размещаются различные объявления (declarations). Они являются вашими собственными дополнениями к словарю ассемблера — вы можете присвоить используемым регистрам осмысленные названия. Я, например, всегда использую рабочий регистр, называемый temp, для временного хранения данных, и назначаю это имя регистру R16. Имена рабочих регистров задаются с помощью директивы .def, как это показано в шаблоне. Другим типом объявления, которое может использоваться для присваивания числового значения идентификатору, является директива .equ. Она, в частности, может использоваться для задания своих имен регистрам ввода/вывода. Например, я собираюсь подключить семисегментный индикатор к порту В и хочу при обращении к регистру PortB писать DisplayPort. Регистр PortB является регистром ввода/вывода с номером 0x18, так что после объявления я смогу писать в программе DisplayPort и это слово будет интерпретироваться ассемблером как PortB:

.equ      DisplayPort = PortB

ИЛИ

.equ  DisplayPort = Cxl8

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

В следующей после объявлений строке располагается первая команда, выполняемая микроконтроллером при включении питания. В этой строке я советую поместить команду перехода к секции, помеченной меткой Init, в которой выполняются все начальные настройки AVR. Для этого используется команда rjmp:

rjmp Init

-33-

Глава 1. Введение

Это команда относительного перехода (relative jump). Другими словами, она указывает микроконтроллеру перейти на участок программы, который вы пометили меткой Init. Причина, по которой переход называется относительным, связана с тем, каким образом ассемблер интерпретирует эту команду, и, вообще говоря, не особо важна для понимания. Пусть, например, секция Init располагается через 40 команд от команды rjmp Init. В этом случае ассемблер интерпретирует эту команду как «перепрыгнуть вперед через 40 команд», т.е. перейти относительно текущей команды. Однако гораздо проще считать, что микроконтроллер просто переходит к метке Init.

В первой части секции Init задается, какие из выводов будут работать как входы, а какие — как выходы. Это осуществляется при помощи регистров ввода/вывода DDRB и DDRD (регистры направления передачи данных). Каждый бит этих регистров соответствует одному из выводов микроконтроллера. Например, бит 4 регистра DDRB соответствует выводу РВ4, а бит 2 регистра DDRD — выводу PD2. Установка соответствующего бита регистра DDRx в 1 делает вывод выходом, а сброс бита в 0 делает вывод входом.

Если мы сконфигурируем вывод как вход, мы сможем задать, будет ли к этому выводу подключен внутренний подтягивающий резистор или нет. Это может избавить нас от необходимости использовать внешние резисторы. Чтобы включить подтяжку входа, необходимо установить в 1 соответствующий бит регистра Portx; однако, если вам этого не требуется, убедитесь, что вы ее отключили, сбросив соответствующий бит регистра Portx в 0. Что же касается выходов, то при включении микроконтроллера они должны находиться в определенном начальном состоянии (например, все выключены). Поэтому следует установить или сбросить соответствующие биты регистров Portx в зависимости от того, в какое состояние мы хотим установить выходы при старте. Поясним сказанное на примере.

0 ПРИМЕР 1.19. Используется микроконтроллер 1200, выводы PB0, РВ4 и РВ7 подключены к кнопкам. Мы хотим, чтобы подтяжка была только на входах РВ4 и РВ7. Выводы PD0...PD6 подключены к семисегментному индикатору; остальные выводы оставлены неподключенными. При включении питания все выходы должны быть выключены. Какие значения необходимо записать в регистры DDRB, DDRD, PortB и PortD, чтобы выводы микроконтроллера функционировали описанным образом?

Прежде всего, разберемся с входами и выходами. Выводы PB0, РВ4 и РВ7 — входы, остальные не задействованы (поэтому сделаем их выходами).   Соответственно,   в  регистр  DDRB   необходимо  записать  число

-34-

Шаблон программы

ObOl 101110. Все выводы порта D являются выходами или не используются, поэтому в регистре DDRD должно быть число 0Ы1111111.

Для включения подтяжки на выводах РВ4 и РВ7, установим биты PortB,4 и PortB,7 в 1. Все выходы при включении питания должны быть выключены, поэтому в регистр PortB необходимо записать число 0Ы0010000. Все выходы порта D должны быть выключены, поэтому в регистр PortD необходимо записать число 0Ь00000000.

Мы не можем записать эти значения непосредственно в регистры ввода/вывода, вместо этого мы должны сначала записать их в рабочий регистр (например, temp), а затем переслать содержимое рабочего регистра в регистр ввода/вывода. Эту операцию можно выполнить несколькими способами:

ldi  register,number                         ;

Эта команда загружает непосредственное значение (load immediate) в регистр. Необходимо отметить, что эта команда может работать только с регистрами R16...R3I (именно поэтому мы можем использовать temp, поскольку он соответствует регистру R16). Если же число, которое мы собираемся записать в регистр, равно 0 или 255/0xFF/0bl 1111111, можно воспользоваться другими командами:

clr  register                                        ;

Эта команда сбрасывает содержимое регистра (clear register), т.е. записывает в него 0. Заметим, что в отличие от команды ldi эта команда может использоваться со всеми рабочими регистрами. И наконец,

seP     register                                ;

Эта команда заполняет содержимое регистра (set register), т.е. записывает в него число 255/0xFF/0bl 1111111. Как и команда ldi, эта команда может работать толь к о с регистрами R16...R31.

Теперь нам необходимо переслать temp в регистр ввода/вывода, используя следующую команду:

out  ioreg,reg                                     ;

Эта команда выводит (out) содержимое регистра reg общего назначения в регистр ввода/вывода ioreg. Обратите внимание на порядок операндов команды — сначала адрес регистра ввода/вывода, потом рабочий регистр — их очень легко перепутать! Теперь вам должно быть понятно, что в восьми строках секции Init осуществляется загрузка регистров DDRB, DDRD, PortB и PortD посредством регистра temp.

-35-

Глава 1. Введение

ж? УПРАЖНЕНИЕ 1.13. Используется микроконтроллер 1200, вывод РВО подключен к датчику давления, а выводы PBI, РВ2 и РВЗ управляют красным, желтым и зеленым светодиодами соответственно. На выводы PD0...PD3 выдается сигнал для ИК передатчика, а с выводов PD4...PD6 принимается сигнал с ИК приемника. Остальные выводы не подключены. При включении питания все выходы должны быть выключены, а на входе РВО должна быть разрешена подтяжка. Напишите восемь строк, которые будут составлять секцию Init этой программы.

После секции Init начинается основное тело программы, обозначенное меткой Start. Начиная с этого места, будет располагаться основная часть кода. Обратите внимание, что программа заканчивается строкой rjmp Start. Необязательно возвращаться именно к метке Start, однако переход куда-нибудь должен быть, поэтому вам может потребоваться изменить эту последнюю строку программы соответствующим образом. В конце программы можно поместить директиву .exit, которая приказывает ассемблеру прекратить трансляцию файла, однако это делать не обязательно, поскольку при достижении конца файла трансляция прекратится в любом случае.

-«-

Глава 2. ОСНОВНЫЕ ОПЕРАЦИИ BAT90S1200HTTNY12

Лучшим способом обучения является внимательное изучение примеров и самостоятельное написание программ. На протяжении остальной части книги мы рассмотрим ряд учебных проектов, причем большую часть кода для многих из них вы будете писать самостоятельно. Чтобы обучение было наиболее эффективным, желательно на практике исследовать работу этих программ, полностью набирая их по мере рассмотрения в Блокноте или какой-либо другой программе. Если же в настоящий момент вы не располагаете специализированным программным обеспечением для AVR, можно пока просто набирать программы в Блокноте, а проверить их позже.

Прежде чем приступить к изучению, наберите шаблон, описанный в предыдущей главе, внесите в него необходимые изменения и сохраните в файле template.asm. Расширение .asm означает, что в файле содержится исходный текст на ассемблере, т.е. именно то, что потом будет ассемблировано. Если вы пользуетесь Блокнотом, убедитесь, что при сохранении в поле «Тип файла» (File Туре) вы выбрали значение «Все файлы» (All Files).

Программа А. Светодиод (LEDon)

• Управление выходами

Первые несколько программ, которые мы рассмотрим, предназначены для модели 1200. Загрузите шаблон и, выбрав в меню Файл (File) пункт Сохранить как... (Save as...), сохраните его в файле ledon.asm (исходный файл шаблона при этом останется неизменным). Внесите в текст необходимые изменения, относящиеся к модели микроконтроллера (заголовок, директивы .device и .include). Наша первая программа будет просто включать СИД и удерживать его в этом состоянии. Прежде всего, необходимо определить входы и выходы. В этом проекте нам потребуется только один выход, в качестве которого будем использовать вывод RB0. Вторым этапом при разработке программы является создание блок-схемы алгоритма (см. Рис. 2.1). После этого мы можем приступить к написанию собственно программы. Первый прямоугольник (Инициализация) соответствует сек-

-37-

Глава 2. Основные операции в AT90S1200 и TINY12

ции Init. Эту часть программы вы вполне можете написать самостоятельно (помните, если вывод не используется в схеме, его необходимо делать выходом).

Инициализация

Включить СИД

I                             ________

Рис. 2.1. Блок-схема программы А

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

sbi  ioreg,bit                                     ;

Эта команда устанавливает бит с номером bit в регистре ввода/вывода ioreg (set bit in an I/O register). Хотя мы и не можем загружать числа непосредственно в регистры ввода/вывода, мы можем устанавливать и сбрасывать индивидуальные биты в некоторых из них. Мы не можем устанавливать и сбрасывать отдельные биты в регистрах ввода/вывода 32...63 ($20...$3F). К счастью, регистр PortB ($18), а также все регистры Portx и Pinx могут управляться описанным образом. Аналогичная команда для сброса бита имеет следующий формат:

cbi      ioreg,bit

Эта команда сбрасывает бит в регистре ввода/вывода (clear bit in an I/O register), однако не забывайте, что она применима только к регистрам ввода/вывода 0...31. В нашем конкретном случае мы хотим установить 0-й бит регистра PortB в 1. Воспользуемся для этого следующей командой, пометив ее меткой Start:

Start:

sbi   PortB,0                                   ; Включаем СИД

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

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

Таким образом, микроконтроллер будет выполнять бесконечный ЦИК&, постоянно включая СИД. Теперь программа готова для ассемблировании,

-38-

Программа А. Светодиод (LEDon)

В том, что вы все написали правильно, можно убедиться, сравнив написанное с текстом программы, приведенным в Приложении J {Программа А). Тексты всех остальных программ, которые мы с вами будем писать, тоже приведены в этом приложении. Теперь приступим к ассемблированию программы, а если у вас нет соответствующего программного обеспечения, просто прочитайте следующий параграф. Среду разработки AVR Studio !) можно совершенно бесплатно скачать с сайта компании Atmel (www.atmel.com). Она позволяет транслировать программы с языка ассемблера и отлаживать их, а при наличии соответствующей аппаратуры и программировать микроконтроллеры AVR.

AVR Studio — трансляция с языка ассемблера

После запуска AVR Studio создайте новый проект, выбрав в меню Project команду New Project. В появившемся окне в поле Project Name введите название проекта (например, LEDon), в поле Location укажите подходящее расположение, а в списке Project Туре выберите тип проекта «Atmel AVR Assembler». Здесь же можно указать на необходимость создания основного (входного) файла для проекта (флажок Create initial File), а также на необходимость создания отдельной папки для проекта (флажок Create Folder). В проект могут входить ассемблерные и другие файлы. Написанная вами программа является ассемблерным файлом (.asm), и его необходимо добавить в проект2'. Для этого в окне Workspace (вкладка Project) щелкните правой кнопкой мыши на группе Assembler и выберите пункт Add existing file. Найдите созданный вами файл LEDon.asm и выберите его двойным щелчком мыши. Название файла должно появиться в дереве проекта. Теперь нажмите клавишу F7 или выберите пункт Build в меню Project, в результате чего начнется трансляция программы. Будем надеяться, что трансляция вашего файла пройдет без ошибок. В противном случае будет полезно просмотреть файл листинга (*.lst). Откройте его в Блокноте или каком-либо другом текстовом редакторе и поищите сообщения об ошибках 3). Поскольку наша программа чрезвычайно проста, эти сообщения будут вызваны скорее всего орфографическими ошибками. Устраните все замечания и переходите к проверке программы.

]) В книге рассматривается работа со средой AVR Studio 4. — Примеч. пер.

21 Если при создании проекта указывается имя существующего файла, он добавляется в проект автоматически. — Примеч. пер.

3) Сообщения об ошибках выводятся также в окне Output (вкладка Build). Для локализации ошибок достаточно дважды щелкнуть левой кнопкой на сообщении об ошибке. При этом курсор в окне редактора будет установлен на строку, вызвавшую сообщение об ошибке. — Примеч. пер.

-39-

Глава 2. Основнщдщрию »АИОБПОО и TtNYU

Проверка

Существует три основных метода, позволяющих проверить работоспо*

собность программы:

1. Симуляция.

2. Эмуляция.

3. Программирование микроконтроллера и проверка его в реальной схеме.

Первый из этих методов, симуляция, является полностью программным. Используется специальное программное обеспечение, которое симулирует деятельность микроконтроллера и показывает вам, что происходит внутри него во время выполнения программы, в частности как изменяется состояние его регистров. Вы можете также симулировать изменение входных сигналов, вручную изменяя, например, содержимое регистра PinB. С помощью этого метода можно легко убедиться в работоспособности (или, напротив, в неработоспособности) ключевых идей, лежащих в основе программы. С другой стороны, вы не сможете проверить реакцию программы на некоторые реальные воздействия, такие как дребезг контактов. Симулятор микроконтроллеров AVR входит в состав среды разработки AVR Studio.

AVR Studio — симуляция

Теперь мы приступим к симуляции программы LEDon. После ассемблирования программы (файла с расширением .asm) необходимо переключить AVR Studio в режим отладки, для чего следует нажать кнопку ^ . Через некоторое время откроется окно с текстом программы (если оно не было открыто), первая строка которой (rjmp Ink) будет отмечена желтой стрелкой. Одновременно станут доступными некоторые кнопки на панелях инструментов, находящихся в верхней части экрана. Для пошагового выполнения программы используются три из них. Наиболее полезная из

них, v), называется Step Into. При нажатии на эту кнопку выполняется текущая строка программы. При помощи этой кнопки (или соответствующей «горячей» клавиши F11) можно по шагам выполнить программу. Назначение остальных кнопок пошаговой отладки мы рассмотрим немного позже, при изучении подпрограмм. Чтобы получить от симуляции хоть какую-нибудь пользу, нам нужно посмотреть, каким образом изменяется состояние регистров ввода/вывода (в частности, бита 0 регистра PortB). Для этого перейдите к вкладке I/O окна Workspace и раскройте группу I/O AT90S1200. Вы увидите, что регистры ввода/вывода сгруппированы по функциональным блокам микроконтроллера. Раскрыв блок PortB, вы увидите три регистра: PortB, DDRB и PinB. Также вы можете просмотреть со-

-40-

Программа А. Светодиод (LEDon)

держимое рабочих регистров, выбрав в меню View пункт Register. В данном случае мы будем наблюдать за регистром R16 (temp). Еще одной полезной

кнопкой является кнопка сброса   Ь (Shift+F5).

Продолжайте пошаговое выполнение программы. Обратите внимание, как в регистре temp появляется число OxFF (ОЫ1111111), которое затем записывается в регистры DDRB и DDRD. После этого регистр temp, а вслед за ним и регистры PortB и PortD сбрасываются в 00. Затем 0-й бит регистра PortB устанавливается в 1, что индицируется закрашиванием соответствующего квадратика в окне Workspace. Заметьте, что эта операция приведет также к автоматической установке на следующем шаге 0-го бита регистра PinB. Не забывайте, в чем отличие между этими двумя регистрами, — регистр PortB представляет данные, которые вы собираетесь передать через порт, а регистр PinB представляет реальные значения напряжений на выводах порта. Например, если вы попытаетесь установить вход в состояние ВЫСОКОГО уровня в то время, когда он случайно замкнут на общий провод, то в регистре PortB соответствующий бит установится в 1, а в регистре PinB этот бит будет сброшен в 0, поскольку вывод подключен к шине 0 В.

Эмуляция

Эмуляция позволяет получить гораздо больше информации о реальном функционировании программы и может быть намного полезнее при отыскании ошибок в программе. При эмуляции к компьютеру подключается зонд (probe) с разъемом, соответствующим конкретной модели AVR. Под управлением программы эмулятора зонд начинает функционировать точно так же, как и реальный микроконтроллер, выполняющий вашу программу Работа устройства под управлением эмулятора ничем не отличается от работы под управлением реального микроконтроллера, однако, используя эмулятор, вы можете замедлить выполнение программы, а также просмотреть состояние внутренних узлов микроконтроллера (регистров и т.п.). При использовании этого метода проверяется работоспособность программы, корректность разводки печатной платы, а также их совместная работа. К сожалению, эмулятор является довольно дорогим удовольствием. В качестве примера упомяну фирменный внутрисхемный эмулятор ICE (In-Circuit Emulator).

Если у вас нет эмулятора (либо после завершения эмуляции), вам нужно будет запрограммировать реальный микроконтроллер AVR и установить его в устройство или на макетную плату. Одним из важнейших достоинств микроконтроллеров AVR является наличие у них FLASH-памяти программ, что позволяет многократно программировать одну и ту же микросхему. Так что вы можете спокойно запрограммировать микроконтрол-

-41-

Ггава 2. Основные операции в AT90S1200 и TINY12

лер, посмотреть, работаетли он, внести в программу необходимые исправления и запрограммировать его снова.

Для того чтобы воспользоваться этими двумя последними методами тестирования, вам, очевидно, понадобится какая-либо схема или отладочная плата. Если вы разрабатываете собственное устройство, позаботьтесь о правильной разводке определенных выводов микроконтроллера. Этот вопрос мы рассмотрим в следующем разделе.

Аппаратное обеспечение

На Рис. 2.2 приведена цоколевка микросхемы 1200. Вы уже знакомы с выводами РВх и PDx, однако в микроконтроллере есть и другие выводы, имеющие специальное назначение. Вывод Vcc — это вывод положительного полюса источника питания; для модели 1200 напряжение питания может быть от 2.7 до 6.0 В. Допустимый диапазон напряжения питания зависит от модели, тем не менее для любого микроконтроллера напряжение 4...5 В будет безопасным. Вывод GND — это общий вывод (0 В). Также в микроконтроллере имеется вывод аппаратного сброса RESET. Черта над названием вывода означает, что он является выводом с активным НИЗКИМ уровнем. Другими словами, чтобы сбросить микроконтроллер, необходимо подать на этот вывод напряжение НИЗКОГО уровня (на время не менее 50 не). Соответственно, если нам требуется кнопка сброса, мы можем подключить ее согласно схеме, подобной приведенной на Рис. 2.3.

Между включением и появлением на выходе источника питания стабильного напряжения, очевидно, должно пройти какое-то время. Аналогично кварцевый генератор сможет сформировать стабильный тактовый сигнал только по истечении некоторого времени после включения. Поэтому необходимо сделать так, чтобы между подачей напряжения питания на AVR и началом выполнения программы прошло определенное время. К счастью, в микроконтроллерах AVR уже имеется узел, формирующий эту задержку (длительностью около 11 мс); однако, если вашему источнику питания или генератору требуется задержка большей длительности, можно использовать схему, подобную приведенной на Рис. 2.4. Увеличение задержки достигается увеличением емкости конденсатора С1.

И наконец, выводы XTAL1 и XTAL2, как видно из их названия, предназначены для подключения кварцевого или керамического резонатора, формирующего импульсы стабильной частоты, которые необходимы для

      10    -----1            ------      OB —о

Рис. 2.4. Схема для увеличения длительности задержки

нормальной работы AVR. Чем выше частота резонатора, тем быстрее микроконтроллер будет выполнять программу, однако различные модели имеют разные значения максимальной частоты тактового сигнала. Обычно максимальная частота находится в диапазоне от 4 до 8 МГц, а используемая в этой главе модель 1200 может работать при частотах до 12 МГц! Замечу, что в некоторых микроконтроллерах (в частности, в моделях Tiny и 1200) имеется встроенный генератор частоты 1 МГц, при использовании которого внешний резонатор не требуется. Встроенный генератор построен на базе RC-цепочки и поэтому менее точен, более чувствителен к изменению температуры и т.д. Однако, если вам не требуется высокая точность,

-43-

Глава 2. Основные операции в AT90S1200 и TINY12

имеет смысл его использовать, освободив тем самым место на печатной плате. На Рис. 2.5 показано, как кварцевый или керамический резонатор подключается к выводам XTAL1 и XTAL2.

+5 В

Рис. 2.5. Схема подключения резонатора

Если требуется синхронизировать микроконтроллер с другим устройством или в схеме уже имеется линия синхронизации с сигналом высокой частоты, то в качестве тактового сигнала можно воспользоваться внешним сигналом. Для этого нужно подключить выход генератора к выводу XTAL1, а вывод XTAL2 оставить неподключенным. На Рис. 2.6 показано, каким образом можно синхронизировать два микроконтроллера AVR, используя буфер типа НС (быстродействующая КМОП-технология).

....                    OB 1---------0

Рис. 2.6. Схема синхронизации двух микроконтроллеров AVR

-44-

Программа А. Светодиод (LEDon)

|^VR Studio — программирование

Чтобы проверить работоспособность запрограммированного микро-сонтроллера, нужна печатная плата устройства или же макетная плата. Са-иым очевидным решением является изготовление печатных плат по мере 4еобходимости, однако гораздо проще и удобнее изготовить собственную пакетную плату, пригодную для реализации всех проектов, рассматриваемых в книге. Схема, соответствующая программе LEDon, приведена на Рис. 2.7.

СИД

ов

Рис. 2.7. Схема для проверки программы LEDon

Если у вас уже есть макетная плата, проверьте, как на ней распаяны светодиоды. В нашей схеме предполагается, что выводы будут играть роль источников тока для светодиодов (т.е. чтобы включить СИД, надо установить на выходе ВЫСОКИЙ уровень). Если же имеющаяся у вас плата разведена таким образом, что выводы микроконтроллера являются потребителями тока светодиодов, необходимо внести изменения в программу микроконтроллера. При этом сигнал лог. 0 будет включать СИД, а сигнал лог. 1 — выключать его. Поэтому вместо обнуления регистра PortB в начале секции Init потребуется записать в него число Obi 1111111 (чтобы выключить все светодиоды). Кроме того, для включения СИД нужно будет не устанавливать 0-й бит регистра PortB, а сбрасывать его. Для этого достаточно вместо команды sbi использовать команду cbi.

Замечу также, что, хотя эта программа была написана в расчете на модель 1200 (как самую простейшую), она совместима со всеми другими моделями микроконтроллеров AVR. Поэтому, если в вашем распоряжении имеется, например, микроконтроллер модели 8515 (используемый в некоторых из имеющихся в продаже комплектов разработки), просто измените

-45-

Глава 2. Основные операции в AT90S1200 и TINY12

в программе аргументы директив .device и .include, и она должна будет заработать.

Теперь приступим к программированию микроконтроллера, воспользовавшись для этого стартовым набором разработчика STK500 (действия, которые необходимо выполнять при использовании других программаторов, не должны очень сильно отличаться от указанных). Для программирования микросхемы вставьте ее в соответствующую панельку на плате. Кроме того, может потребоваться изменить положение перемычек для выбора требуемой модели кристалла. В среде AVR Studio выберите в меню Tools -> Program AVR -> Connect...; в открывшемся окне выберите используемый программатор (STK500 или AVRISP), порт компьютера, к которому он подключен, и нажмите кнопку Connect. В открывшемся окне диалога выберите соответствующую модель микроконтроллера (AT90S1200). Нам требуется запрограммировать FLASH-память программ. Если перед этим вы отлаживали программу в симуляторе, т.е. она еще находится в его памяти, достаточно будет выделить пункт Use current Simulator/Emulator Flash Memory, а затем нажать кнопку Program. Если же программа отсутствует в памяти симулятора, просто загрузите ее в компьютер, ассемблируйте и запустите симуляцию. В результате этих действий программа будет загружена в память симулятора.

Конфигурационные ячейки

Вы, очевидно, заметили, что в окне программирования имеется несколько вкладок. В частности, на вкладке Fuses вы можете задать некоторые параметры аппаратной конфигурации программируемого микроконтроллера. Набор этих конфигурационных ячеек изменяется от модели к модели. В модели 1200, например, имеется всего две таких ячейки — RCEN и SPIEN. Ячейка RCEN должна быть установлена, если в качестве источника тактовых сигналов вы используете внутренний RC-генератор микроконтроллера. Если же вы используете внешний источник тактовых сигналов, например кварцевый резонатор (как в данном проекте), эта ячейка должна быть сброшена. Вторая конфигурационная ячейка — SPIEN разрешает считывание кода программы из кристалла. Если вы хотите сохранить код своей программы в секрете и не хотите, чтобы другие люди могли считать его из микросхемы, убедитесь, что эта ячейка сброшена.

Конечно, довольно глупо разбираться во всех этих тонкостях только для того, чтобы посмотреть, как включается СИД, однако впереди нас ждут более сложные задачи!

-4«-

Программы В и С. Кнопка

Программы В и С. Кнопка

•  Контроль входов

•  Управление выходами     *¦

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

1.   Проверить конкретный бит в регистре Pinx, используя команды sbic или sbis.

2.   Считать значение из регистра Pinx в рабочий регистр с помощью команды in.

Кнопку мы подключим к выводу PD0 и шине О В, а СИД подключим к выводу РВО. Блок-схема соответствующей программы приведена на Рис. 1.3, а принципиальная схема

Рис. 2.8. Принципиальная схема устройства для контроля состояния входов (программы В и С)

Вы уже знаете достаточно, чтобы самостоятельно написать секцию Init программы. Обратите внимание: поскольку на схеме отсутствует внешний подтягивающий регистр, необходимо включить внутреннюю подтяжку на входе PD0. В самом начале программы необходимо проверить, не нажата ли кнопка. Для этого в нашем распоряжении имеется две команды:

sbic  ioreg.bit

-47-

Глава 2. Основные операции в AT90S1200 и TINY12

Эта команда проверяет бит в регистре ввода/вывода и пропускает следующую команду, если бит сброшен (skip if bit in I/O is clear). Аналогично команда

sbis  ioreg,bit                                  ;

проверяет бит в регистре ввода/вывода и пропускает следующую команду, если бит установлен (skip if bit in I/O is set). Обратите внимание, что эти две команды, как и команды sbi и cbi, работают только с регистрами ввода/вывода 0...31 ($0...$1F). К счастью, адрес регистра, который мы будем проверять, PinD, находится как раз в этом диапазоне (номер $10). Таким образом, чтобы проверить состояние кнопки (при ее нажатии на выводе PD0 появляется напряжение НИЗКОГО уровня), мы должны написать:

sbis  PinD,0                                         ; Прозерим кнопку

Эта команда заставит AVR пропустить следующую команду, если на выводе PD0 присутствует напряжение НИЗКОГО уровня. Соответственно, команда в следующей строке будет выполнена только в том случае, если кнопка не нажата. В этой строке необходимо выключить СИД, поэтому прикажем микроконтроллеру перейти к секции LEDoff:

rjmp  LEDoff                                         ; Переходим к секцу.-л  LEDoff

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

as  УПРАЖНЕНИЕ 2.1. Напишите две строки, в первой из которых ^^   включается СИД, а во второй осуществляется возврат к метке Start для повторной проверки состояния кнопки.

Теперь нам осталось разобраться с секцией LEDoff.

yg?^  УПРАЖНЕНИЕ 2.2. Напишите две строки, в первой из которых вы-™^   ключается СИД, а во второй осуществляется возврат к метке Start для повторной проверки состояния кнопки.

Вот и вся программа. Удостовериться в том, что все написано правильно, можно, посмотрев на текст, приведенный в Приложении J {Программа В). Теперь можно протестировать программу и записать ее в микроконтроллер точно так же, как мы это делали при разработке предыдущей программы. Во время симуляции вы можете симулировать нажатие кнопки простым выделением квадратика, соответствующего биту 0 регистра PinD на вкладке I/O окна Workspace.

-48-

Программы В и С. Кнопк:

Иногда имеет смысл снова вернуться к задаче и посмотреть на нее с другой стороны. Вместо того чтобы обрабатывать кнопку и СИД как отдельные биты двух портов, давайте посмотрим, как связаны их состояни? и числа, находящиеся в регистрах портов. Когда кнопка нажата, в регистре PinD находится число ОЬОООООООО, а светодиод должен быть включен (т.е. i регистр PortB необходимо записать число ОЬОООООООО). Когда кнопка не нажата, в регистре PinD находится число ObOOOOOOOl, и соответственно i регистр PortB необходимо записать число ObOOOOOOOl. Поэтому вместо того, чтобы проверять отдельные биты, мы будем использовать содержимо« порта, сохраняя его в регистровом файле. Таким образом, единственное задачей программы будет пересылка числа из регистра PinD в регистр PortD. Мы не можем напрямую перемещать числа между регистрами ввода/вывода, поэтому сначала мы прочитаем содержимое регистра PinD с помощью следующей команды:

in    register,ioreg

Эта команда копирует содержимое регистра ввода/вывода в рабочие регистр. Чтобы переслать число из рабочего регистра обратно в регистр ввода/вывода, мы воспользуемся командой out. Таким образом, основное тело программы будет выглядеть следующим образом:

Start:

in             temp,PinD                              ;   Считываем состояние кнопки

out           PortB, ten-.p                            ;    Вк-/выкл СИД

rj:r.p        Start                                         ;    Зозврадаемся к началу

Полный текст этой программы приведен в Приложении J {Программа С),

Семисегментные индикаторы и косвенная адресация

Используя для управления семисегментными индикаторами микроконтроллер AVR, а не отдельную микросхему дешифратора, вы сможете отображать на них все, что вам угодно. Очевидно, что на индикаторе можно отображать не только любые числа, но и многие латинские буквы: А, Ь, с, С, d, Е, F, G, h, Н, i, I, J, 1, L, п, о, О, Р, г, S, t, u, U, у и Z.

Все выводы семисегментного индикатора желательно подключить к одному порту в любом порядке (это может облегчить разводку печатной платы). Незадействованный вывод порта можно использовать для управления десятичной точкой индикатора. Запомните, какому биту порта соответствует каждый из сегментов (а, Ь, си т.д.). Общепринятые обозначения сегментов семисегментного индикатора показаны на Рис. 2.9.

-49-

Глава 2. Основные операции в AT90S1200 и TINY 12

О

Рис. 2.9. Обозначения

выводов семисегментного

индикатора

0    ПРИМЕР 2.1. Порт В используется следующим образом: бит 7 — d, бит 6 — а, бит 5 — с, бит 4 — g, бит 3 — Ь, бит 2 — f, бит 1 — е.

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

Если индикатор подключен так, как указано в Примере 2.1, то число, помещаемое в регистр PortB, должно иметь вид dacgbfe- (состояние 0-го бита не имеет значения, поскольку этот вывод порта не подключен к индикатору). Значение каждого бита этого числа соответствует требуемому состоянию вывода, соединенного с конкретным сегментом.

Итак, если вы используете дисплей с общим катодом (т.е. для включения сегмента надо подать на него напряжение ВЫСОКОГО уровня, см. Рис. 2.10) и хотите отобразить, например, букву А, то вам потребуется включить сегменты а, Ь, с, е, f и g.

Вернемся к Примеру 2.1, в котором сегменты подключены к порту В в порядке dacgbfe-. В этом случае для отображения буквы А в регистр PortB следует записать число 0Ь01111110. Бит 0 сброшен, поскольку соответствующий вывод порта не подключен к дисплею.

0 ПРИМ ЕР 2.2. Если сегменты дисплея с общим катодом подключены к порту В в порядке dacgbfe-, какие числа следует записать в регистр PortB для отображения букв С и Е?

Буква С состоит из сегментов a, d, е и f, поэтому число, записываемое в регистр PortB, должно быть равно 0Ы1000110. Буква Е включает сегменты a, d, е, fng, поэтому число должно быть равно Obi 1010110.

У

-50-

Программы В и С. Кнопка

Общий катод

44-

Общий

Рис. 2.10. Дисплеи с общим катодом и общим анодом

faS УПРАЖНЕНИЕ 2.3. Если сегменты индикатора подключены к пор-/^~^   ту В в порядке abcdefg-, то какие числа необходимо загрузить в регистр PortB для отображения символов 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, А, Ь, с, d, Е и F?

Преобразование числа в код для семисегментного индикатора можно выполнить различными способами, однако наиболее простой из них заключается в использовании таблицы перекодировки (look-up table). Основной идеей, лежащей в основе таблиц перекодировки, является косвенная адресация. До сих пор мы использовали только прямую адресацию, т.е., если нам необходимо было прочитать содержимое регистра с номером 4, мы просто считывали содержимое этого регистра. Косвенная же адресация заключается в таком способе чтения числа из регистра с номером X, при котором значение X определяется содержимым другого регистра, называемого Z (2-байтный регистр, образованный объединением регистров R30 и R31). Эта операция по своей сути напоминает отсылку письма, причем письмо соответствует содержимому рабочего регистра (R0...R31), а адрес задается числом, находящимся в регистре Z.

0    ПРИМЕР 2.3. Необходимо загрузить число 00 в рабочие регистры с R0 по R29.

-51-

Глава 2. Основные операции в AT90S1200 и TINY12

Вместо того, чтобы писать:

clr   R0                                      ; Очищаем R0

clr   R1                                      ; Очищаем R1

clr   R2                                      ; Очищаем R2

clr   R29                                    ; Очищаем R29

мы можем воспользоваться косвенной адресацией и выполнить эту задачу меньшим числом команд. Первым регистром, в который мы хотим записать число, является R0 (адрес 0), поэтому следует записать в регистр Z число 00 (задавая адрес письма равным 0). Как вы помните, регистр Z состоит из двух регистров — ZL и ZH (старший и младший байты), поэтому необходимо очистить их оба:

cir   ZL                                      ; Очищаем ZL

clr   ZH                                      ; Очищаем ZH

После этого нам необходимо инициализировать какой-либо регистр нулем, чтобы мы могли послать его содержимое «по почте» другому регистру. У нас уже есть регистр, содержащий 0 (ZH), его мы и будем использовать.

st         register,Z

Эта команда косвенно пересылает (store) содержимое рабочего регистра register по адресу, хранящемуся в регистре Z. Соответственно, команда

st          ZH,Z

пошлет число, находящееся в регистре ZH (0), по адресу, задаваемому регистром Z (тоже 0), и, таким образом, очистит регистр R0. Теперь мы хотим очистить регистр R1, поэтому инкрементируем регистр Z, чтобы он указывал на адрес 01 (т.е. R1). После этого программа должна вернуться обратно, образуя цикл, в котором постепенно будут сброшены все регистры. Причем для этого потребуется гораздо меньше команд, чем в случае использования прямой адресации. Все, что нам нужно, — это контролировать регистр ZL для определения момента, когда его содержимое станет равным 30, так как это значение превышает адрес последнего регистра, который мы собираемся очистить.

А как узнать, что регистр ZL стал равен 30? Мы просто вычтем из регистра 30 и проверим, равен результат нулю или нет. Если ZLpaeen 30, то результат вычитания будет равен 0. Однако мы не собираемся действительно вычитать 30 из ZL! Вместо этого мы воспользуемся одной из команд сравнения:

ср   register,register

-Я-f-

Программы В и С. Кнопка

Эта команда сравнивает (compares) содержимое двух регистров общего назначения (на самом деле микроконтроллер вычитает один регистр из другого, оставляя неизменным их содержимое). После этой команды мы должны узнать, равен ли результат нулю. Это можно сделать, посмотрев состояние флага нуля. Регистр ввода/вывода SREG ($3F) содержит набор флагов, которые автоматически устанавливаются и сбрасываются в зависимости от результата выполнения ряда операций. Флаг нуля устанавливается, если результат операции равен нулю. Проверить состояние этого флага можно двумя способами. Первый из них:

brbs  label,bit                           ;

По этой команде микроконтроллер переходит в другое место программы, если заданный бит в регистре SREG установлен (branches if a bit is set). Флаг нуля является 1-м битом, поэтому операнд bit должен быть равен 1. Обратите внимание, что между командой и меткой label не может быть больше 63 команд. Аналогично

brbc  label,bit                           ;

По этой команде микроконтроллер переходит в другое место программы, если бит в регистре SREG сброшен (branches if a bit is clear). И здесь мы впервые встречаемся с избыточностью системы команд, которая заключается в том, что наряду с общими командами проверки состояния битов регистра SREG в микроконтроллерах AVR имеются команды, соответствующие каждому конкретному биту. В частности, для флага нуля:

breq      label

Эта команда означает «переход, если равно» (branch if equal) (а конкретно — переход, если флаг нуля установлен). Обратная по смыслу команда:

brne      label

означает «переход, если не равно» (branch if not equal) (а конкретно — переход, если флаг нуля сброшен). Полный набор избыточных/второстепенных команд приведен в Приложении С вместе с их эквивалентными командами. Для сравнения содержимого регистра с константой, а не с содержимым другого регистра используется команда:

cpi  register,number     ;

Пожалуйста, обратите внимание на то, что эта команда работает только с регистрами R16...R31, однако, поскольку ZL соответствует R30, мы в данном случае можем воспользоваться этой командой. Таким образом, фрагмент программы, выполняющий очистку регистров R0...R29, выглядит следующим образом:

-53-

Глава 2. Основные операции в AT90S1200 и TINY12

clr        ZL                                  ;   Очищаем ZL

clr        ZH                                  ;   Очищаем ZH ClearLoop:

st          ZH,Z                              ;   Косвенно очищаем регистр

inc        ZL                                  ;   Переходим к следующему адресу

cpi        ZL,30                            ;   Сравниваем ZL с 30

brne      ClearLoop                    ;   Переходим к ClearLoop, если ZL о 30

Имеет смысл разместить эти шесть команд в секции Init для сброса большого числа рабочих регистров при старте программы. Вы можете задавать начальный и конечный адреса, изменяя начальное значение регистра ZL и конечное значение, с которым он сравнивается. Однако заметьте, что вы не можете в этом же цикле сбрасывать регистр ZL (т.е. адрес не может быть больше 30), поскольку в противном случае программа войдет в бесконечный цикл (поразмыслите над этим).

gfS  УПРАЖНЕНИЕ 2.4. Повышенной сложности! Напишите фраг-^^   мент из шести команд, при выполнении которого в регистры R0, R1,..., R15 будут занесены числа 0, 1,..., ^соответственно.

Косвенным может быть не только запись, но и чтение:

Id         register,Z                ;

Эта команда косвенно загружает (load) в рабочий регистр register значение, которое находится по адресу, указываемому регистром Z. Таким образом, если сформировать таблицу значений, занимающих последовательные ячейки памяти, то, изменяя значение Z, можно считывать различные значения. Допустим, к примеру, мы храним коды для отображения на се-мисегментном индикаторе цифр от 0 до 9 в регистрах R20...R29. Тогда запишем в регистр Z число 20 («обнулим» его, чтобы он указывал на начало таблицы) и прибавим к регистру то число, которое хотим преобразовать. Используя после этого операцию косвенного чтения, мы получим семи-сегментный код, соответствующий этому числу:

ldi      ZL,20                            ;   Устанавливаем ZL на R20

add      ZL,digit                      ;   Прибавляем цифру к ZL

Id        temp,Z                          ;   Читаем Rx в temp

out      PortB,temp                  ;   Выводим temp в порт В

Приведенный выше фрагмент преобразует число, хранящееся в регистре digit, в семисегментный код, который затем выставляется на выводы порта В. Не забудьте, что перед этим необходимо занести все коды в соответствующие регистры:

ldi   R20,  ,0ЫШ1100    ;  Код для  0

ldi   R21,  ,0Ь01100000 ;  Код для  1

ldi   R29,  ,0Ы1110110  ;  Код для  9

-54-

Программы D и Е. Счетчик

В заключение замечу, что рабочие регистры очень редко используются для хранения таблиц. Более того, такое использование регистров слишком расточительно, но, поскольку в модели 1200 нет других областей памяти данных, у нас не остается выбора. В моделях, имеющих ОЗУ, можно его использовать для хранения таблиц перекодировки. Кроме того, в других моделях микроконтроллеров имеется команда 1pm, которая позволяет использовать для хранения таблиц память программ. Подробнее об этой возможности мы поговорим при разработке симулятора логических элементов (см. стр. 85).

Программы D и Е. Счетчик

•  Контроль входов

•  Управление семисегментными индикаторами

Нашим следующим проектом будет счетчик, который подсчитывает количество нажатий на кнопку, от 0 до 9. После 10 нажатий (когда значение счетчика превысит 9) он должен сброситься в 0. Семисегментный дисплей будет подключен к выводам РВ0...РВ6, а кнопка — к выводу PD0. Принципиальная схема устройства приведена на Рис. 2.11; обратите внимание на то, в каком порядке выводы индикатора подключены к порту. Блок-схема программы приведена на Рис. 2.12.

           

                        AT90S1200 -------------------------------1     10 t------------------------ ------------------------------1                0B \--------------------O

Рис. 2.11. Принципиальная схема счетчика

Секцию Init вы вполне можете написать самостоятельно, не забудьте только включить подтяжку на входе, к которому подключена кнопка. Начальное состояние выводов порта В должно соответствовать 1 на индикаторе. Для хранения числа нажатий мы будем использовать регистр, назы-

-55-

Глава 2. Основные операции в AT90S1200 и TINY12

Инициализация

      ---------------р. у'        Кнс    <---------- пка         — ата?        ^ ДА     НЕТ                 

v.            наж                             

1              Инкрементировать 1                       счетчик                              

/                                 

^/          Счетчик      ^v. V.         больше 9?      ^S              ДА          Обнулить счетчик    

                                  

\.          НЕТ                    

                                  

      Изменить число на индикаторе                        

                                  

Рис. 2.12. Блок-схема программы «Счетчик»

ваемый Counter, поэтому вам необходимо в секции объявлений присвоить это имя рабочему регистру R17. Мы задействуем именно R17 по той причине, что регистры RI6...R31 являются «исполнительными ассистентами» — более мощными регистрами, над которыми можно выполнять большее число операций. Поэтому сначала мы «забиваем» регистры R16...R31 и только потом, если их не хватило, используем R0...R15. В регистры с R20 по R29 в секции Init загрузите коды семисегментного индикатора для цифр от 0 до 9. (ПОДСКАЗКА: Если это сделать до настройки порта, то для его инициализации можно будет просто скопировать R20 в регистр PortB. Кроме того, не забудьте сбросить в секции Init регистр Counter.)

-56-

Программы D и Е. Счетчик

jgS  УПРАЖНЕНИЕ 2.5. С помощью каких трех команд можно прове-*^~*   рить состояние кнопки и вернуться обратно для повторной проверки? Если кнопка нажата, программа должна выходить из цикла и инкрементировать Counter.

После этого мы должны определить, не стал ли Counter больше 9. Воспользуемся командой cpi для сравнения и командой brne для перехода, если Counter не равен 10. В противном случае регистр Counter должен быть сброшен в 0. При использовании команд brne и аналогичных может оказаться полезным следующий прием. Часто встречаются ситуации, когда по результату сравнения вместо перехода «куда-то» мы хотим просто пропустить следующую команду (как в случае команд sbis и sbic). Чтобы осуществить пропуск при использовании команд перехода, напишите в качестве операнда вместо метки слово PC + 2, тогда при переходе будет пропущена одна команда (т.е. программа перейдет на две команды вперед). Буквы PC означают «счетчик команд» (Program Counter), который более подробно будет рассматриваться на стр. 71.

fo<? УПРАЖНЕНИЕ 2.6. С помощью каких трех строк можно прове-^^   рить, не равен ли Counter десяти, и сбросить его, если это так? Возможно, вам пригодится прием с использованием счетчика команд (PC + 2).

Теперь нам необходимо отобразить на индикаторе содержимое регистра Counter. Для этого мы, как и прежде, загрузим в ZL адрес регистра R20, после чего прибавим к нему Counter.

/gS  УПРАЖНЕНИЕ 2.7. С помощью каких пяти строк можно вывести '^   содержимое регистра Counter на индикатор, подключенный к порту В, и вернуться к метке Start?

Полный текст получившейся программы приведен в Приложении J {Программа D). Я рекомендую вам собрать это устройство. Поработав с ним, вы заметите, что наша программа имеет ряд недостатков.

Основной проблемой является то, что мы не ждем отпускания кнопки, в результате чего инкрементирование регистра Counter происходит многократно, в течение всего времени, пока кнопка нажата. Если предположить, что длительность нажатия на кнопку равна 0.1 с, а частота резонатора равна 4 МГц, то, учитывая время выполнения фонового цикла программы (приблизительно 14 тактов), при каждом нажатии на кнопку инкрементирование регистра Counter будет выполнено примерно 4 000 000/(14 х 10) = = 28 600 раз! То есть мы получили довольно неплохой генератор случайных чисел (отступая от темы, замечу, что создание генератора случайных чисел

-57-

Глава 2. Основные операции в AT90S1200 и TINY12

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

Новая блок-схема приведена на Рис. 2.13. В конец программы следует внести изменения, позволяющие перед возвратом к началу основной: цикла дождаться отпускания кнопки.

/gS  УПРАЖНЕНИЕ 2.8. Напишите две  новые строки, необходимые "^   для устранения описанной проблемы, и укажите место, куда их необходимо вставить. (ПОДСКАЗКА: этому циклу нужно будет присвоить имя.)

Проверьте работу новой программы (полный ее текст приведен в Приложении J — Программа Е). Наверняка вы заметите еще один недостаток, степень проявления которого зависит от качества использованной кнопки. Вы увидите, что при нажатии на кнопку состояние счетчика изменяется скачками (например, с 1 на 4). Это происходит из-за явления, которое называется дребезгом контактов. Оно заключается в многократном неконтролируемом размыкании и замыкании контактов при нажатии или отпускании кнопки, как показано на Рнс. 2.14.

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

ПРИМЕР 2.4. Чтобы избавиться от дребезга контактов, мы могли бы подождать после отпускания кнопки 5 с, прежде чем повторно проверять ее состояние. Но в таком случае, если нажать на кнопку через 3 с после предыдущего нажатия, то этот сигнал не будет зарегистрирован. Такая задержка наверняка предотвратит влияние дребезга, однако приведет к тому, что минимальный интервал между нажатиями станет слишком большим.

ПРИМЕР 2.5. В качестве альтернативы для подавления дребезга контактов мы могли бы подождать 0.1 с после отпускания кнопки перед повторной проверкой ее состояния. Однако длительность дребезга может быть больше 0.1 с, так что такая задержка будет неэффективной.

-58-

0 0

Риа«2.0с»о№Неов«<мившвЛТ9051200иТПЧУ12

------------о+5 В

Кнопка

Д1 ЮОк

Кнопка нажата            Кнопка отпущена                                        I----------------о О В

Рис. 2.14. Дребезг контактов

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

Формирование временных интервалов

Если вы вспомните список регистров ввода/вывода (не помешает лишний раз взглянуть на стр. 26), то обнаружите регистр TCNT0 ($32) — счетный регистр таймера/счетчика 0 (rimer/Counter 0). Это встроенный таймер микроконтроллера, который может автоматически считать в прямом направлении с заданной скоростью, сбрасываясь в 0 после достижения значения 255. Мы можем использовать этот таймер для выполнения различных операций, связанных со временем (например, для формирования секундной задержки). В более развитых микроконтроллерах имеется несколько таймеров, часть из которых являются 16-битными. Причина, по которой в названии таймера присутствует слово «счетчик», заключается в том, что он может также использоваться для подсчета числа импульсов на определенном выводе микроконтроллера (вслучае модели 1200 это вывод 8 — PD4). В данном проекте мы будем использовать таймер/счетчик 0 в качестве таймера, поэтому я буду называть его Таймер 0 или, для краткости, Т/СО.

Прежде чем использовать Таймер 0, его необходимо соответствующим образом сконфигурировать (в частности, задать работу в режиме таймера, а не счетчика). Для конфигурирования таймера используется регистр TCCR0 ($33) - регистр управления Т/СО (Т/СО Control Register). Каждый бит этого регистра отвечает за определенный аспект функционирования Т/СО. В модели 1200 используются только биты 2...0 (Рис. 2.15).

Биты 3...7 не используются, однако, изменяя состояние битов 0...2 определенным образом, можно задать требуемое нам поведение Т/СО. Если мы вообще не собираемся использовать Т/СО, то все три бита должны быть

-60-

Программы D и Е. Счетчик

TCCR0 — регистр управления Т/СО ($33)

Бит        стающему фронту на выводе ТО

Рис. 2.15. Регистр управления Т/СО

сброшены. Если мы собираемся использовать его в качестве таймера, необходимо выбрать одну из пяти возможных в этом режиме конфигураций. И наконец, если мы хотим использовать его для счета внешних импульсов (на выводе PD4), мы можем воспользоваться двумя последними конфигурациями. Различные конфигурации, доступные нам при использовании Т/СО в качестве таймера, определяют скорость его счета. Понятно, что значение тактовой частоты (СК) слишком велико (несколько МГц) — это частота кварцевого резонатора, подключенного к микроконтроллеру, — поэтому для отсчета интервалов порядка секунд нам придется существенно уменьшить это значение. Уменьшить скорость счета Таймера 0 можно не более чем в 1024 раза. Соответственно, если подключить к микроконтроллеру резонатор с частотой 2.4576 МГц (это действительно очень распространенное значение частоты резонатора), то частота счета Таймера 0 будет равна 2 457 600/1024 = 2400 Гц. Таким образом, даже при максимально возможном замедлении таймера его состояние будет изменяться 2400 раз в секунду.

0 ПРИМЕР 2.6. Какое число следует загрузить в регистр TCCR0, чтобы с наибольшей эффективностью использовать Т/СО для подсчета числа прошедших секунд?

Биты с 3-го по 7-й всегда равны нулю.

Счет осуществляется по внутреннему тактовому сигналу с наименьшей частотой, равной СК/1024.

-61-

Глава 2. Основные операции в AT90S1200 и TINY12

Соответственно число, записываемое в регистр TCCR0, равно ObOOOOOlOl.

rgS  УПРАЖНЕНИЕ 2.9. Какое число должно быть записано в регистр ^^   TCCR0, если Т/СО используется для подсчета нажатий на кнопку, подключенную между выводом PD4 и +5 В?

Чтобы записать число в регистр TCCR0, мы должны сначала загрузить его в регистр temp, а затем воспользоваться командой out, как и в случае других регистров ввода/вывода. Поскольку в дальнейшем вы вряд ли будете изменять конфигурацию таймера, имеет смысл задавать ее в секции Init и больше не обращать на это внимание.

Для получения секундных и минутных интервалов дальнейшее деление частоты вам необходимо осуществлять самостоятельно. Воспользуемся для этого так называемым маркером и некоторым количеством счетных регистров. Это рабочие регистры, которые мы будем использовать для формирования требуемых временных интервалов. Основная идея заключается в том, чтобы подсчитать, сколько раз Таймер 0 достигает определенного значения. Например, для формирования секундной задержки мы должны подождать, пока Таймер 0 отсчитает 2400 раз. Это аналогично тому, как если бы Таймер 0 достиг значения 80 в общей сложности 30 раз, поскольку 30 х 80 = 2400. Эту задачу можно было бы выполнить, используя и другие сомножители числа 2400, меньшие 256.

Чтобы проверить, не равно ли содержимое Таймера 0 числу 80, напишем следующие строки:

out   TCNTO,temp      ; Копируем TCNT0 в temp

cpi   temp,80                      ; Сравниваем temp с 80

breq  Equal                          ; Переходим к Equal, если temp = 80

В этом фрагменте проверяется равенство Таймера 0 числу 80 и осуществляется переход к метке Equal, если это так. Проблема заключается в том, что нам требуется сравнивать состояние Таймера 0 с различными значениями, а не только с числом 80. Сначала мы возьмем это число, однако в следующем цикле мы будем сравнивать Таймер 0 с числом 160, затем с числом 240 и т.д. Поэтому мы будем использовать регистр (я называю его маркером), в который первоначально записывается 80, а при каждом достижении Таймером 0 значения маркера последний увеличивается на 80. В микроконтроллерах AVR отсутствует команда сложения константы с регистром, однако имеется команда вычитания константы из регистра. Очевидно, что вычитание отрицательного числа эквивалентно сложению.

subi  register,number  ;

-62-

Программа F. Бегущий огонек

Эта команда вычитает константу (subtracts immediate) из регистра общего назначения. Обратите внимание: команда работает только с регистрами R16...R31.

Итак, нам удалось определить момент достижения Таймером 0 заданного значения (80). Для формирования секундного интервала нам нужно, чтобы это событие наступило 30 раз. Возьмем регистр, запишем в него число 30 и будем уменьшать содержимое этого регистра каждый раз, когда значение Таймера 0 будет становиться равным 80.

dec    register     ;

Эта команда декрементирует, т.е. уменьшает на единицу (decrements) содержимое регистра. Равенство регистра нулю означает, что описанное событие произошло 30 раз. Собрав все описанное вместе, получим фрагмент программы, необходимый для формирования секундной задержки.

; Начальное значение счетчика 30 ; Начальное значение маркера 80

;   Сохраняем состояние Таймера 0 в temp

;   Сравниваем temp с Mark80

;   Если не равны, возвращаемся

;   к началу цикла

;   Прибавляем 80 к Mark80

; Уменьшаем Count30 на единицу ; Если Count30 о 0, возвращаемся ; к началу цикла

Первые две команды загружают в регистры счетчика и маркера ipcoyc-мые значения. Затем содержимое регистра TCNT0 копируется в регистр temp, который в свою очередь сравнивается с маркером. Если они не равны, программа переходит к началу цикла (TimeLoop). В противном случае значение маркера увеличивается на 80, счетчик уменьшается на единицу, и, если последний не равен нулю, программа переходит к метке TimeLoop. Не забудьте определить регистры Mark80 и Count30 в секции объявлений (это должны быть регистры из диапазона R16...R31).

Программа F. Бегущий огонек

•  Формирование временных интервалов

•  Считывание входных сигналов

•  Управление выходами

Нашим следующим проектом будет устройство, формирующее огонек, «бегущий» по ряду светодиодов. Этот эффект достигается поочередным

- 63 -

ldi   Count30,30

idi   Mark80,80

TimeLoop:  

out   TCNTO,temp

со    temp,Mark80

erne  TimeLoop

subi  Mark80,-80

dec   Count30

brne  TimeLoop

Глава 2. Основные операции в AT90S1200 н TINY12

включением светодиодов. Скоростью движения огонька мы будем управлять с помощью двух кнопок: при нажатии на одну из них скорость будет увеличиваться, а при нажатии на другую — уменьшаться. По умолчанию каждый светодиод будет включаться на 0.5 с; это значение можно будет изменять от 0.1 до 1 с.

Светодиоды мы подключим к порту В, а кнопки — к выводам PD0 и PD1. Принципиальная схема устройства и блок-схема алгоритма приведены на Рис. 2.16 и Рис. 2.17 соответственно.

П____| 2.4576 МГц

Д|...Яв 320

Рис. 2.16. Принципиальная схема устройства «Бегущий огонек»

Секция, соответствующая блоку инициализации, должна быть достаточно простой; не забудьте только о том, что в этой секции имеет смысл сконфигурировать и Таймер 0 (поскольку мы будем оперировать интервалами порядка секунды, придется использовать Т/СО в режиме таймера, работающего на минимально возможной частоте). Обратите также внимание, что на входах PD0 и PD1 потребуется включить подтяжку, а у порта В при старте программы должен быть включен только один выход (например, РВО).

Теперь подумаем о том, как мы будем формировать задержку, длительность которой может изменяться от 0.1 до 1с. Минимальная задержка (0.1 с) может быть сформирована при значении маркера, равном 240 (2400/240 = 10 Гц), — предполагается, что в схеме используется кварцевый резонатор с частотой 2.4576 МГц, а период тактового сигнала Таймера 0 равен СК/1024. В этом случае, чтобы изменять длительность общей задержки от 0.1 до 1 с, достаточно будет изменять начальное значение счетчика от 1 до 10. Таким образом, нам понадобится регистр маркера Mark240 и регистр счетчика Counter. По умолчанию Counter равен 5 (0.5 с), но может

-64-

Программа F. Бегущий огонек

НЕТ

Инициализация

НЕТ

НЕТ

Сменить СИД

Увеличить длительность свечения

ДА

Уменьшить длительность свечения

Рис. 2.17. Блок-схема программы «Бегущий огонек»

быть установлен и в другое значение, задаваемое регистром Speed. Не забудьте определить эти регистры в секции объявлений в начале программы. В следующем после инициализации блоке проверяется состояние кнопки уменьшения скорости. Назначим эту функцию кнопке, подключенной к выводу PD0. Ее состояние мы будем проверять с помощью команды sbic. Если кнопка не нажата (т.е. на выводе порта ВЫСОКИЙ уровень), то будет выполняться следующая после sbic команда — команда

-65-

Глава 2. Основные операции в AT90S1200 и TINY12

перехода к секции проверки состояния кнопки увеличения скорости (назовем эту секцию UpTest).

Если же кнопка нажата, нам нужно увеличить на единицу значение регистра Speed (замедлить движение огонька). Эту операцию можно выполнить с помощью следующей команды:

inc    register     ;

Эта команда инкрементирует (increment) содержимое регистра, т.е. увеличивает его на 1. Нам не требуются задержки больше 1 с, поэтому мы должны следить за тем, чтобы значение Speed не стало больше 10 (таким образом, число 11 означает, что задержка больше допустимой). Мы можем легко выполнить такую проверку, используя уже рассмотренную команду сравнения регистра с константой, cpi. Если регистр Speed не равен 11, мы спокойно можем перейти к метке ReleaseDown и ждать отпускания кнопки. Если же содержимое регистра равно 11, мы должны уменьшить его на единицу (используя команду dec).

Соответственно, первые строки программы будут следующими:

; Проверяем кнопку уменьшения скорости ; Не нажата, переходим

; Уменьшаем длительность

; Speed стало равно 11?

; Если не^,  перейдем к ReleaseDown

; Уменьшаем Speed на 1

; Ждем отпускания кнопки

В блоке UpTest мы выполняем те же самые операции с кнопкой увеличения скорости, подключенной к PD1, только вместо перехода к метке UpTest мы будем переходить к следующей секции, которую назовем Timer. Если кнопка увеличения скорости нажата, то мы должны декрементиро-вать регистр Speed, а вместо проверки его на равенство 11 должны проверить, не равен ли он нулю (и увеличиваем его, если это так). Конечно, можно было бы написать команду cpi Speed,0, однако это лишнее, так как флаг нуля изменяется в результате выполнения команды dec. Таким образом, если в результате декрементирования регистр станет равным нулю, то мы сможем, как и раньше, воспользоваться командой Ьгпе.

/gS  УПРАЖНЕНИЕ 2.10. Напишите семь строк программы, выполня-'^^   юших описанные выше действия.

Start:     

sbic  PinD,0

rjrap UpTest

inc   Speed

cpi   Speed,11

brne  ReleaseDowa

dec   Speed

ReleaseDown:     

sbis  PinD,0

rjmp  ReleaseDown

-66-

Программа F. Бегущий огонек

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

Помимо всего прочего, мы должны разместить в секции Init строки, в которых задаются начальные значения маркера и счетчика. В регистр Магк240 необходимо загрузить 240, а в регистры Speed и Counter — 5. Имея инициализированные таким образом регистры, мы можем перейти непосредственно к циклу отсчета времени.

Tinier:

in           temp,TCNT0            ;   Загружаем содержимое Таймера 0 в temp

ср           temp,Mark240       ;   Сравниваем с маркером

brne       Start                       ;   Если не равны, возвращаемся к Start

subi  Mark240,-240 

 
Сайт управляется системой uCoz