Микроконтроллеры AVR: от простого к сложному

Голубцов М. С.

Глава 6

Практические примеры применения микроконтроллеров AVR

 

 

6.1. 10-разрядный светодиодный индикатор на

AT90S1200

Данное устройство предназначено для замены ЖК-индикатора со встроенным контроллером, аналога НТ1611, применяемого в телефонных аппаратах, на светодиодный индикатор с общим катодом. При применении десяти знаков ЖК-индикатора реализуется полная совместимость с НТ1611 (за исключением режима часов и таймера). Частота мерцания индикатора близка к 100 Гц, что практически незаметно даже боковым зрением. Ток через сегмент ограничен 20 мА самим микроконтроллером, что позволяет выровнять свечение при разном количестве зажженных сегментов. При программировании контроллера есть возможность постоянно зажечь запятые в нужных разрядах и исключить неиспользуемый знак при применении 9-знакового ЖКИ (который показан на схеме).

Сигналы Clk и Data должны соответствовать питанию 5 В, т. е. логическая единица должна быть 3…5 В. После проверки правильности монтажа подаем и проверяем питание +5 В на 20-м выводе панельки под DD1. Вставляем микроконтроллер — на экране после включения питания появятся 1234567890, что записано в программе. Если нет — проверить колебания с частотой 4,000—4,005 МГц на выводе 4. При подаче внешних сигналов Clk и Data индицироваться будет входящая информация. Общее потребление — 65…250 мА. Микросхему AT90S1200-12PC можно заменить на AT90S1200-12PI, а также AT90S1200-4PC и AT90S1200-4PI. Светодиодный индикатор — AJIC318A, ТОТ — 3361 AG-1 (три штуки), от АОНа или любой другой с общим катодом, можно набрать из одиночных, сдвоенных, строенных и т. п.

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

Программа для AT90S1200: lcd.zip, находится на компакт-диске, прилагаемом к книге.

Автор: Дергаев Э. Ю., UA4NX (E-mail: ).

 

6.2. Управление синтезатором частоты радиостанции «Маяк»

Данное устройство позволяет управлять частотой радиостанции «Маяк» в диапазоне 144,5—146,0 МГц. В режиме репитера и антирепитера индицируется частота передачи. Программа хранит в энергонезависимой памяти 63 частоты каналов и одну VFO, включая репитерный разнос +600 кГц, антирепитерный разнос — 600 кГц, с шагом перестройки 25 кГц. Запись частот в каждую ячейку памяти гарантируется 100 000 раз. В режиме SCAN происходит сканирование с 53-го по 63-й канал памяти, в режиме DUAL — сканирование между любым каналом памяти и VFO. При понижении напряжения источника питания на индикаторе отображаются прочерки. При выключении питания или нажатии клавиши CLOCK индикатор переходит в режим часов. Подтверждением нажатия клавиш служит короткий звуковой сигнал высокого тона.

Принципиальная схема на рис. 6.2. Микроконтроллер DD1 AT90S2313 управляет работой синтезатора радиостанции, передавая в блок приемопередатчика на DD4 и DD5 последовательный код частоты, анализирует состояние клавиатуры, нажатие тангенты РТТ, состояние шумоподавителя BUSY, напряжение питания радиостанции и передает информацию на жидкокристаллический 10-разрядный индикатор DD3 НТ1611. Питание стабилизировано DD2 78L05, напряжение — 5 В. Для питания ЖКИ используется отдельный источник питания+1,5 В, (для установки и коррекции времени используются кнопки S1 и S2). Питание на DD4 и DD5 подается с блока синтезатора +9 В, туда же желательно установить и плату с ними. Вариант 1 соответствует синтезатору на трех К561ИЕ11. Вариант 2 — синтезатор с дополнительной платой на К155РЕЗ. Если у вас другой синтезатор, установите перемычками 125,000 МГц и подключите остальные шины по «весу». Сигналы с контроллера подаются по кабелю, соединяющему пульт управления и приемопередатчик, используются свободные А0, A1, А2 (последним можно продублировать «землю»). Сам контроллер встраивается в пульт управления, питание +13,8 В. Сигнал BUSY снимается с D16 K561ЛA7 вывод 4, сигнал TX/RX с переключателя S8 (выводы на схеме 55). Выход SOUND подключается к собственному динамику (телефон из трубки) или УНЧ-приемника, регулируется подбором R6 (не забывайте, что УНЧ блокируется шумоподавителем). Кнопки управления — любые. Индикатор — любой аналог НТ1611, используемый в АОНах. Так как индикатор имеет горизонтальное положение, то устанавливать его придется на корпус сверху.

Режим VFO. На индикаторе F145025

Нажимая Н или L, на один шаг увеличиваем или уменьшаем частоту.

При их удержании частота начинаег изменяться с шагом 100 кГц.

Нажимая S/D, включаем режим DUAL.

Рис. 6.2. Электрическая принципиальная схема управления синтезатором частот на AVR микроконтроллере фирмы АТМЕ2

Нажимая F, переходим в режим SERVIS.

Прц выключении питания частота VFO сохраняется.

Режим MEMORY. На индикаторе 1Р 145750

Нажимая Н или L, увеличиваем или уменьшаем канал памяти на один.

При удержании клавиш — непрерывное изменение номера канала.

Нажимая S/D, включаем режим SCAN.

Нажимая дважды F, записываем в память частоту, набранную в VFO.

Режим SERVIS

Нажимая Н или L, переходим между:

S — режим частота RX — ТХ совпадает;

S Р — режим репитера;

S Р — режим антирепитера.

При включении питания устанавливается режим совпадения RX — ТХ. Для включения режима репитера необходимо выйти в SERVIS, выбрать режим, например репитер, и выйти из SERVIS, нажав F. То есть при нажатии на F выбранная на экране информация записывается в память.

Изменение частоты

Перейдем в режим VFO. Нажимая Н или L, на один шаг увеличиваем или уменьшаем частоту на заданный шаг. Затем можно включить режим репитера, антирепитера, перейти в режим MEMORY, выбрать канал и, нажав F, два раза записать содержимое VFO в память.

Режим SCAN

Сканируются каналы памяти 53–63. При появлении несущей сканирование приостанавливается, а при пропадании — через 3 секунды возобновляется. Для выхода из режима надо нажать любую клавишу или РТТ.

Режим DUAL

Сканируются канал памяти и VFO. При появлении несущей сканирование приостанавливается, а при пропадании — через 3 секунды возобновляется. Для выхода из режима надо нажать любую клавишу Или РТТ.

Режим LOCK

В режиме, передачи нажимая на Н, блокируем клавиатуру. Для снятия блокировки нажимаем на «L» в режиме передачи.

Настройка

После проверки правильности установки всех деталей подается питание. Уровень срабатывания индикации пониженного напряжения — R5, на выводе 12 DD1 порог около 2,5 В.

Возможная замена деталей

DD1 AT90S2313-4РС, AT90S2313-4PI, AT90S2313-10PC, AT90S2313-10PI. Перечисленные типы микроконтроллеров отличаются максимальной тактовой частотой (соответственно 4 и 10 МГц). Так как в схеме применен кварцевый резонатор на частоту 4 МГц, для схемы годится любой из них. VD1 — любой красный светодиод, можно уменьшить R1 до 470…910 Ом и вывести светодиод на переднюю панель как индикатор питания. VD2, VD3 — любые диоды. Кварц в пределах 2000…6000 кГц, но желательно ближе к 4 МГц, резисторы с допуском 20 %.

Программа для AT90S2313 находится на компакт-диске, прилагаемом к книге.

Пособие по отладке

1. Монтируем плату микроконтроллера (без 561ИР2). Достаем из панельки контроллер, если он был вставлен. Включаем «Маяк». Измеряем напряжение тестером. На ножке 1 должно быть 3,45…3,5 В. Светодиод должен быть красным. На ножке 20 должно быть 5 В. Выключаем питание. Вставляем в панельку контроллер. Включаем питание. Слышим телефонную трель в излучателе, подключенному к Sound. На индикаторе появляются 0-00 — это таймер, а затем первый канал памяти (1Р 145125, например). Проверяем все кнопки (на них должно быть 5 В, а при нажатии — 0 В). Проверяем работу программы, набирая кнопками частоты, и т. п.

2. Если все нормально, монтируем плату с 561ИР2, соединяем с контроллером и синтезатором. Питание на 561ИР2 должно быть 9,0 В, а лучше его понизить на 0,7 В, подав питание через диод в прямом включении. В некоторых синтезаторах напряжение на плате существенно выше, так что придется поставить несколько диодов или светодиод. Далее можно пройти по всем частотам и убедиться в правильности распайки синтезатора.

3. Если что-то не так, проверьте на 4-м выводе контроллера высокоомным щупом с делителем осциллографа наличие 4 МГц прямоугольников.

4. Если вы прошиваете микроконтроллер в плате — отключите светодиод на время программирования с компьютера.

Автор: Дергаев Э. Ю., UA4NX (E-mail: ).

 

6.3. Синтезатор частоты для УКВ ЧМ-радиостанции

Данное устройство позволяет управлять частотой радиолюбительской радиостанции в диапазоне 144… 146 МГц. В режиме репитера и антирепитера индицируется частота передачи. Программа хранит в энергонезависимой памяти 31 частоту каналов и одну VFO, включая репитерный разнос +600 кГц, антирепитерный разнос -600 кГц, с шагом перестройки 5, 10 и 25 кГц. Запись частот в каждую ячейку памяти гарантируется 100 000 раз. В режиме SCAN происходит сканирование с 21-го по 31-й канал памяти, в режиме DUAL — сканирование между любым каналом памяти и частотой VFO. При понижении напряжения источника питания на индикаторе отображаются прочерки. Для индикации «захвата» синтезатора служит светодиод VD4. При выключении питания или нажатии клавиши CLOCK индикатор переходит в режим часов. Подтверждением нажатия клавиш служит короткий звуковой сигнал высокого тона. Возможна блокировка клавиатуры.

Принципиальная схема на рис. 6.3.

Рис. 6.3. Электрическая принципиальная схема синтезатора частот УКВ радиостанции

Микроконтроллер DD2 AT90S1200 управляет работой синтезатора DD1 1508ПЛ1, переключает приемный (VT2) и передающий (VT5) генератор, управляемый напряжением (ГУН), анализирует состояние клавиатуры, нажатие тангенты РТТ, состояние шумоподавителя BUSY, напряжение питания радиостанции и передает информацию на жидкокристаллический 10-разрядный горизонтальный индикатор DD4 НТ1611. Питание стабилизировано DD3 78L05, напряжение — 5 В. Для питания ЖКИ используется отдельный источник питания +1,5 В (для установки и коррекции времени используются кнопки S1 и S2). В режиме передачи ГУН ТХ работает на частоте 144,5…145,8 МГц, модуляция подается на вход MOD, а в режиме приема ГУН RX — на частоте 133,3…135,3 МГц, для ПЧ — 10,7 МГц. В случае другой ПЧ необходимы изменения в программе (обращайтесь к автору).

Через буферные каскады VT4 и VT6 сигнал с ГУНов поступает| далее. Напряжение на них подается через 1 кОм для приемного^ (VT4) и 330 Ом для передающего (VT6) с соответствующих коммутируемых шин питания приемника и передатчика, сигнал снимается через емкость 47…68 пФ.

Остановимся на работе программы. Существует два основных режима работы — VFO и MEMORY. Они переключаются соответствующей клавишей V/M.

Режим VFO. На индикаторе F 145025

Нажимая Н или L, на один шаг увеличиваем или уменьшаем частоту.

При их удержании частота начинает изменяться с шагом 100 кГц.

Нажимая S/D, включаем режим DUAL.

Нажимая F, переходим в режим SERVIS.

При выключении питания частота VFO сохраняется.

Режим MEMORY. На индикаторе 1Р 145750

Нажимая Н или L, увеличиваем или уменьшаем канал памяти на один.

При удержании клавиш — непрерывное изменение номера канала.

Нажимая S/D, включаем режим SCAN.

Нажимая дважды F, записываем в память частоту, набранную в VFO.

Режим SERVIS

Нажимая Н или L, переходим между:

S — режим частота RX — ТХ совпадает;

S Р — режим репитера;

S Р — режим антирепитера;

S 5 — шаг 5 кГц;

S 10 — шаг 10 кГц;

S 25 — шаг 25 кГц.

При включении питания устанавливается шаг 25 кГц и режим совпадения RX — ТХ. Для изменения шага и включения режима репитера необходимо выйти в SERVIS, выбрать шаг, например 5 кГц, выйти из SERVIS, нажав F, затем снова зайти в SERVIS, выбрать режим, например репитер, и выйти из SERVIS, нажав F. То есть при нажатии на F выбранная на экране информация записывается в память.

Изменение частоты

Перейдем в режим VFO. Нажимая Н или L, на один шаг увеличиваем или уменьшаем частоту на заданный шаг. Затем можно включить режим репитера, антирепитера, перейти в режим MEMORY, выбрать канал и, нажав F два раза, записать содержимое VFO в память.

Режим SCAN

Сканируются каналы памяти 21–31. При появлении несущей сканирование приостанавливается, а при пропадании — через 3 секунды возобновляется. Для выхода из режима надо нажать любую клавишу или РТТ.

Режим DUAL

Сканируются канал памяти и VFO. При появлении несущей сканирование приостанавливается, а при пропадании — через 3 секунды возобновляется. Для выхода из режима надо нажать любую клавишу или РТТ.

Режим LOCK

В режиме передачи, нажимая на Н, блокируем клавиатуру. Для снятия блокировки нажимаем на L в режиме передачи.

Настройка

После проверки правильности установки всех деталей подается питание. Подключается частотомер к выводу 7 DD1. Конденсатором С13 выставляется частота 4 000 000 Гц, с точностью до герца. Подключаются выходы буферов ГУНов к радиостанции и к частотомеру.

Контурами L1 и L2 ГУНы вгоняются в диапазон, емкостями С5, С17 достигается перекрытие 2,0…2,8 МГц. Ка коллекторе VT4 желательно иметь 2,5…3 В — R12. Уровень модуляции — R39. Уровень срабатывания индикации пониженного напряжения — R25, на выводе 12 DD2 порог около 2,5 В. Выход SOUND подключается к УНЧ-приемника, регулируется R33, при данных номиналах R33, R34 — размах 1,5 В. Уровни сигналов:

BUSY — «1» — несущая в канале, «0» — нет несущей;

RX/TX — «1» — режим приема, «0» — режим передачи.

Уровни, подаваемые на BUSY и RX/TX, не должны превышать 5 В!!! Катушки L1 и L2 бескаркасные, содержат 4…6 витков провода ПЭВ или ПЭЛ диаметром 0,4…0,8 мм и намотаны на оправке диаметром 4,5 мм. После настройки они заливаются прозрачным термоклеем. Подстройка после заливки производится паяльником, разогревая и сдвигая крайний виток. Также заливаются С5—С9, С17—С21, Cl 1, С12, С14 и кварц.

Замена DD1 — 1508ПЛ11, 1508ПЛ11А; DD2 — AT90S1200-4PI, AT90S1200-12РС, AT90S1200-12PI, а также AT90S2313-4PC, — 4PI, — 10РС, — 10PI, у последней число каналов памяти 63+1 VFO, КВ 109А — на КВ 132.

Плата должна экранироваться, для чего паяется коробочка из жести высотой 20–30 мм.

Данное устройство встраивается в радиостанцию, поэтому не имеет собственных защит от переполюсовки и перенапряжений, будьте внимательны!!!

Программы для AT90S1200 и для AT90S2313 находятся на компакт диске, прилагаемом к книге.

Автор: Дергаев Э. Ю., UA4NX (E-mail: ).

 

6.4. Телеграфный манипулятор

Для передачи азбуки Морзе в небольших объемах (10–20 групп), можно применить данный манипулятор. Он может манипулировать CW-передатчик и передавать звуковой тон в SSB- и FM-передатчиках. Рассмотрим принципиальную схему на рис. 6.4.

Рис. 6.4. Электрическая принципиальная схема телеграфного манипулятора

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

1. CW-key (прямой телеграфный выход) — для включения передатчика;

2. CW-key (инверсный телеграфный выход) — для выключения передатчика;

3. SOUND — подключение динамика или УНЧ;

4. LED — подключение светодиода на корпус, ток до 10 мА задается R4.

Общее потребление — 10… 15 мА.

Микросхему AT90S1200-4PC можно заменить на AT90S1200-4PI, AT90S1200-12PC, AT90S1200-12PI. При необходимости больших объемов можно использовать AT90S2313-4PC, AT90S2313-4PI, AT90S2313-10PC, AT90S2313-10PI.

Данное устройство работает: на «Маяке» UA4NX/a (CW-пepeдатчик 144,199 МГц), на «Маяке» UA4NM (CW-передатчик 28,302 МГц), в карте DX-связей на Станции юных техников. Возможности можно существенно расширить, например переключать мощность передатчика и сообщать об этом, переключать антенны и т. п.

Программы для AT90S1200 и для AT90S2313 имеются на компакт-диске, прилагаемом к книге.

Автор: Дергаев Э. Ю., UA4NX (E-mail: ).

 

6.5. Частотомер до 7200 кГц

Предназначен данный прибор для измерения частот до 7,2 МГц с точностью 10, 1 и 0,1 Гц. Рассмотрим принципиальную схему на рис. 6.5.

Рис. 6.5. Электрическая принципиальная схема частотомера

Для согласования уровней до 1,5 В используются делители на R2…R7. Кварц на частоту 16000 кГц. Индикатор — аналог НТ1611, применяемый в АОНах. К сожалению, эти индикаторы производятся для горизонтального положения. Запятые наклеиваются переводным шрифтом или ставятся черным маркером. При замыкании клавиши F появляется значение грубой коррекции частоты от 0 до 255, что соответствует частоте кварца примерно +120 кГц, точно подстраивают С2. Для этого подают на вход формирователя (на схеме не показан) сигнал 5000 кГц от поверенного промышленного частотомера, подбирают коррекцию и подстраивают С2. Значение коррекции записывается в EEPROM автоматически. Нажимая S1 или S2, изменяют предел измерения в сторону увеличения или уменьшения соответственно (одну кнопку можно не ставить).

Перспективы: переход на универсальный индикатор МЭЛТ МТ10Т7-7, появится возможность устанавливать его вертикально, что сделает возможным доработку до частотомера — шкалы с записью ПЧ, увеличения верхней частоты до 40 МГц (добавлением одной микросхемы), возможностью подключения внешних делителей с учетом их коэффициентов деления.

Общее потребление— 10…15 мА.

Микросхему AT90S1200-4PC можно заменить на AT90S1200-4PI, AT90S1200-12РС, AT90S1200-12PI.

Программа для AT90S1200 имеется на компакт диске, прилагаемом к книге (файл).

Автор: Дергаев Э. Ю., UA4NX (E-mail: ).

 

6.6. Частотомер (шкала для UW3DI)

Данное устройство предназначено для измерения частоты ГПД (5500…6000 кГц) и отображения реальной частоты трансивера с учетом 1-го и 3-го гетеродинов. Частота измеряется 10 раз в секунду. Точность измерения — 20 Гц, причем программным способом полностью устранено «перескакивание» младшего и других знаков. Для переключения диапазонов (их может быть 12 плюс режим частотомера) соответствующие выводы микроконтроллера замыкаются на корпус галетным переключателем (рис. 6.6).

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

Монтаж производится на макетной плате, под микроконтроллер устанавливается панелька. Плата помещается в полностью экранированный корпус, например из жести. Все соединяющие провода — экранированные (в том числе и идущие на переключатель диапазонов). Транзистор VT2 (вместе с R11, R12 и С8) выносится к ГПД. Так как индикатор предназначен для горизонтального положения, на переднюю панель его разместить проблематично.

Настройка

Проверяется напряжение на 20-м выводе контроллера, должно быть +5 В, на первом +3,5 В (±0,2 В), на VDD индикатора +1,5…1,6 В. Напряжение на коллекторе VT1 должно быть 2,5 В, подбирается резистором R9. Вставляем контроллер, если нет ошибок в монтаже и исправен кварц — шкала должна работать. Если нет проверить колебания частотой 16,000—16,012 МГц на выводе 4. Подстроечный конденсатор С2 должен быть в среднем положении. Далее подаем на вход формирователя шкалы сигнал частотой 5000 кГц, с любого промышленного частотомера, предварительно прогрев его и шкалу. Переводим шкалу в режим частотомера. Нажимая клавиши Up и Down, добиваемся показаний как можно ближе к истине, затем точно корректируем подстроечным конденсатором С2. (Если показания неустойчивые — регулируем усиление резистором R10). Отключаем генератор. Подключаем вход формирователя к выходу ГПД. Уровень сигнала с ГПД регулируется резистором R10. На коллекторе VT1 добиваемся синусоиды размахом 3…4,5 В, во всем диапазоне перестройки. Далее подстраиваем частоты на всех диапазонах: подключаем промышленный частотомер к выходу передатчика, слабо связав его с антенной или через нагрузку и аттенюатор. Даем несущую и, сравнивая показания шкалы и частотомера, нажатием клавиш Up и Down добиваемся совпадения показаний. И так на всех диапазонах. (Можно использовать «не круглые» кварцы в первом гетеродине). Детали: VD1 любой красный светодиод, падение на нем должно быть 1,5 В. Диоды VD2…VD9 — любые, монтируются прямо на переключателе навесным монтажом, если данных диапазонов в трансивере нет, то и ставить их не надо. С1 — желательно марки КМ, КД, КТ1; С2 — КТ4-21. Кварц на 16 ООО кГц, а не 16 МГц, последний, скорее всего, гармониковый, может запустится на 1-й гармонике. Кварц жестко закрепить или приклеить силиконовым термоклеем. Микросхему AT90S1200-12PC можно заменить на AT90S1200 — 12PI. Кнопки S1 и S2 — любые, расположены прямо в корпусе шкалы, так как требуются только для корректировки.

Микросхему AT90S1200-4РС можно заменить на AT90S1200-4PI, AT90S1200-12РС, AT90S1200-12PI.

Так как практически первый гетеродин не работает ниже 7 МГц, то в диапазоне 10 МГц используется не верхняя боковая (кварц 4 МГц), а нижняя, для телеграфного диапазона это не существенно.

Общее потребление — 10…15 мА.

Программа для AT90S1200 находится на компакт-диске, прилагаемом к книге.

Автор: Дергаев Э. Ю., UA4NX (E-mail: ).

 

6.7. Книгочей

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

Устройство предназначено для прочтения текстовой информации, записанной по COM-порту во flash-память AT49F040, на алфавитно-цифровом индикаторе с контроллером HD44780 фирмы Hitachi и русифицированным знакогенератором. Размер памяти 1 Мбайт, это позволяет записать 8 книг с возможностью оставить закладку на каждую книгу. Также предусмотрена возможность пролистывания книги — на случай, если закладка не была положена, а питание устройства было отключено. Выбор функций и чтение происходит по нажатию трех клавиш: выбор, вверх и вниз. Размер дисплея 40 х 2 строчки, что, как показала практика, вполне достаточно для восприятия смысла написанного.

Схема устройства приведена на рис. 6.7.

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

«Сердцем» его является микроконтроллер AT90S8515, управляющий шинами адреса-данных, принимающий команды от компьютера и выводящий на индикатор. Для защелкивания младших 8 битов адреса служит регистр КР1533ИРЗЗ (аналог 74ALS573). Для преобразования уровней RS-232C служит также хорошо известная микросхема ADM202 фирмы Analog Devices.

Не связанное с компьютером устройство может только считывать записанную в него книгу. Для стирания памяти либо записи в нее надо выбрать пункт меню «Связь с ЭВМ», непосредственно затем послать либо число 0х5е, либо 0хе5. В первом случае устройство перейдет в командный режим, во втором — в отладочный, когда принимаемый от компьютера символ будет просто отображаться на экране.

Команды, подаваемые на устройство, таковы:

10 — стереть первую МС памяти;

20 — стереть вторую МС памяти;

30 — записать в память данные;

40 — считать из памяти начиная с текущего адреса;

50 — выставить текущий адрес.

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

На компакт диске, прилагаемом к книге, имеется файл resource.zip, в котором находятся следующие файлы:

reader.asm — содержит собственно код для AT90S8515;

rus.inc — содержит эквиваленты русских букв (AVR Studio традиционно некорректно работает с неанглийскими символами);

reader.hex — откомпилированный код, прошивка того, что получилось;

reader.cpp — пример программы для компьютера, написанной на языке Си под DOS, позволяющей записывать текстовые файлы; reader.exe — это соответственно откомпилированный reader.cpp; reader.ini — файл настроек для программы. Настроек, собственно, две: это на какой порт подключено устройство (в примере поддерживаются только СОМ 1 и COM2, впрочем, легко добавить в исходник программы и остальные порты) и кодировка записываемого текста (WIN или DOS). Само устройство отображает текст, который записан в него только в кодировке WIN, но поскольку очень много текстов в Интернете, доставшихся в наследство еще от FIDO, имеют кодировку DOS, то программа позволяет перекодировать при записи, на лету, освобождая пользователя от этой предварительной процедуры. Также программа отфильтровывает «лишние» пробелы, получившиеся при форматировании текста, убирает переносы, оставляя нетронутыми дефисы и тире перед фразами речи. Для программы при запуске существуют два возможных параметра — либо reader.exe е, означающий стирание памяти, либо reader.exe w <имя_файла>, позволяющее записать файл. При записи программа попросит ввести имя автора и название книги, причем для удобства выведет первые строчки текста (что, однако, удобно только для DOS-файлов. В случае win-кодировки на экран выведется бессмыслица).

И напоследок немного о кнопках. Это действительно тяжелая проблема, поскольку когда автор конструкции поставил «красивые» разноцветные кнопочки, они не выдержали интенсивного нажимания (все-таки двустрочный дисплей, нажимать где-то раз в секунду приходится), сдав за две недели, поэтому пришлось поставить произведение отечественной «оборонки» — кнопки на основе концевиков — КМ1-1. И в итоге ни дребезга, ни залипания. Уже полгода успешно работают в довольно интенсивном режиме.

Автор: Александр Корниленко (E-mail: ).

 

6.8. Преобразование DTMF-сигнала в импульсный

Устройство предназначено для детектирования введенного DTMF-методом номера телефона и последующего его набора пульсовым методом. Пример: с радиотелефона входим в телефонную линию, послав в качестве телефона цифру 8. База набирает «8» и подключает трубку. После подсоединения к междугородней телефонной станции абонент набирает код города и телефонного абонента методом DTMF, нажимает «*». Нажатие «#» приводит к сбросу набранного номера. Устройство набирает телефон пульсовым методом и засыпает.

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

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

Устройство не допускает набора более 12 цифр.

Если набран код, содержащий ведущую «8», то после нее делается пауза 4 секунды, что позволяет выходить на межгород, просто набрав телефон и установив метод набора dtmf.

В случае, если активность не наблюдается в течение 10 секунд, устройство засыпает до следующего hung-up.

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

Залипание телефонной линии, с точки зрения автора, невозможно, так как используется нормально замкнутое оптоуправляемое реле и при пропадании питания реле замкнуто.

Качество определения dtmf достаточно неплохое.

Автор схемы: Алексей Кургузов (E-mail: ).

 

6.9. Многоточечный термометр

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

В качестве управляющего был использован микроконтроллер AT90S2313, индикация — ЖК-индикатор.

Применение преобразователя напряжения позволило создать автономное устройство с единственным источником питания 1,5 В. Большую часть времени устройство спит, просыпаясь на небольшие промежутки времени, чтобы найти новые устройства на шине, вывести информацию на индикатор, измерить температуру.

Устройство способно обслуживать до 16 датчиков типа DS18B20.

Рис. 6.9. Внешний вид конструкции

Рис. 6.10. Электрическая принципиальная схема цифрового термометра

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

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

Измерение потребляемой прибором мощности не производилось. Расчетно — 750 мкВт (ток около 500 мкА).

Устройство имеет защиту от КЗ.

Файлы, относящиеся к проекту, находятся на компакт диске, прилагаемом к книге.

Автор: Алексей Кургузов (E-mail: ).

 

6.10. Ключ для защиты от копирования

Описываемая здесь схема предназначена для защиты программ от нелегального копирования. Ее использование позволяет практически полностью исключить эту возможность, так как часть защищаемой программы можно хранить в микроконтроллере с установленным битом защиты, не позволяющим ее считать, а значит, и скопировать. Ключ подключается к параллельному порту и может работать одновременно с принтером. Сразу замечу, что эта схема имеет два ограничения. Во-первых, программа микроконтроллера ничего не делает, кроме получения данных от компьютера, их хранения и отправки обратно. Этого достаточно, чтобы программно определить наличие или отсутствие ключа, но, если в микроконтроллер записать часть программы или перед возвращением данных их шифровать, степень защиты значительно увеличится. Я думаю, границей между двумя этими вариантами является цена защищаемой программы в несколько сотен долларов. И во-вторых, эта схема не всегда позволяет работать принтеру, подключенному к тому же порту. Epson Stylus Color 600 прекрасно работал и даже автоматически определился, а Epson LX 1050+ нет. Для исправления этого надо немного переделать схему. Но и этот вариант можно использовать с некоторыми принтерами или, если принтер нужен и он не работает, подключить переключатель порта DataSwitch. Если принтер не подключен, схема устойчиво работает.

Схема конструкции показана на рис. 6.11.

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

Комментарии к схеме

В целях уменьшения размера и стоимости устройства использован микроконтроллер AT90S1200 с внутренним RC-генератором. Это позволяет спокойно разместить всю собранную схему внутри

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

Питание берется с того же порта, при работающем принтере микросхема почти всегда находится в режиме Power Down и потребляет меньше 1 мА, для ее питания достаточно единицы на одном из выводов шины данных. В активном режиме на выводы данных должны быть программно выставлены единицы, благодаря чему обеспечивается ток, достаточный для питания микроконтроллера. Желательно использовать германиевые диоды, так как падение напряжения на них меньше. Есть два исполнения микросхемы AT90S1200 с максимальными частотами 4 или 12 МГц и минимальными напряжениями питания соответственно 2,7 и 4 В. Лучше применять первое, так как из-за существующего разброса параметров параллельных портов на разных компьютерах может оказаться, что на питание микросхемы будет подано напряжение менее 4 В. Например, AT90S1200A-4PC.

Программа микроконтроллера

.INCLUDE "1200def.inc"; AT90S1200 @ 1 МГц

.CSE6

.DEF Byte=r16

.DEF LoopCounter=r18

.DEF Byte0=r19

.DEF Byte1=r20

.DEF Byte2=r21

.DEF Byte3=r22

.DEF Byte4=r23

.DEF Byte5=r24

.DEF Byte6=r25

.DEF Byte7=r26

.ORG 000

          rjmp RESET ; Reset Handler.

.ORG 001

          rjmp EXT_INT0 ; IR00 Handler

RESET:

          ; Настраиваем направление работы портов

         ; Все линии портов после сброса настроены на рабoту в качестве входов; а на неиспользуемых включены pull-up резисторы

          cli

          ldi r31,0

          out DDRB,r31

          out DDRD,r 31

          ldi r31,$ff

          out PORTB.r31

          ldi r31,$72

          out P0RTD,r31

          ; Ждем прихода импульса на вход INTO, находясь в режиме Power Down

          ldi r31.$40

          out GIMSK,r31 ldi

          r31,$30

          out HCUCR,r31

UnLoop: sei

          sleep

          rjmp UnLoop

EXT_INT0:

          ; Если PD3=0, данные относятся к принтеру

          sbis PIND,3

          reti

          ; Включение ключа

          ldi r31,$02

          out DDRD,r31

          ldi Byte0,$31

          ldi Byte1,$32

          ldi Byte2.$33

          ldi Byte3,$34

          ldi Byte4,$35

          ldi Byte5,$36

          ldi Byte6,$37

          ldi Byte7,$38

MainLoop:

          rcall

          Byte8Exchange

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

          sbis PIND.3

          rjmp RESET; Работа с ключом завершена, переполнение стека допустимо

          rjmp MainLoop

Byte8Exchange:

         mov Byte,Byte0

         rcall ByteExchange

         mov Byte0,Byte

         mov Byte,Byte1

         rcall ByteExchange

         mov Bytel,Byte

         mov Byte,Byte2

         rcall ByteExchange

         mov Byte2,Byte

         mov Byte,Byte3

         rcall ByteExchange

         mov Byte3,Byte

         mov Byte,Byte4

         rcall ByteExchange

         mov Byte4,Byte

         mov Byte,Byte5

         rcall ByteExchange

         mov Byte5,Byte

         mov Byte,Byte6

         rcall ByteExchange

         mov Byte6.Byte

         mov Byte,Byte7

         rcall ByteExchange

         mov Byte7.Byte

         ret

ByteExchange: ;Обмен одним байтом данных

         ldi LoopCounter,8

         ; Обработка положительного фронта сигнала синхронизации

Loop8: sbrs Byte,7 ; Вывод бита

         cbi PORTD,1

         sbrc Byte,7

         sbi PORTD, 1

Wait1: sbls PIND,2 ; Ждем прихода положительного фронта;

         rjmp Waltl sec; c=1

         sbis PIND,0

         clc;c=0

         rol Byte

Wait0: sbic PIND,2 ; Ждем прихода отрицательного фронта

         rjmp Wait0

         ; Цикл для 8 битов байта

         dec LoopCounter

         brne Loop8

         ret

EXIT

Программа PC

Исходник на С примера, проверяющего наличие ключа.

#include

#include

#include

char SendByte(char ByteOut)

{

  int i.j; long li;

  unsigned char ByteOutCpy, Byteln = 0;

  ByteOutCpy = ByteOut ;

  for (i=0; i<B; i++)

  {

    Byteln = (Byteln <<1) + ((inportb(0x379)&0x80)==0);

    outportb(0x37A,0x021(((128&Byte0utCpy)==0)));

    for (li=0; li<20001; li++);

    outportb(0x37A,0x001(((12B&Byte0utCpy)==0)));

    for (li=0; li<20001; li++);

    outportb(0x37A,0x021(((128&Byte0utCpy)==0)));

    ByteOutCpy = ByteOutCpy <<1;

    for (li=0; 1К20001; li++);

  }

  return Byteln;

}

void KeyOn(void)

{

  int i;

  long li;

  for (i=0; i<64; i++)

  {

    outportb(0x37A,0x00);

    for (li=0; li<200001; li++);

    outportb(0x37A.0x03);

    for (li=0; li<200001; li++);

  }

  delay(100);

}

void KeyOff(void)

{

  int i;

  long li;

  for (i=0; i<128; i++)

  {

    outportb(0x37A,0x0B);

    for (li=0; li<20001: li++):

    outportb(0x37A.0x08);

    for (li=0; 1K20001: li++);

  }

}

void Riain()

{

  Key0n(); // Включение ключа

  printf("%02X",SendByte(0)); // Отправка 8 байтов.

  printf("%02X",SendByte(1))

  printf("%02X",SendByte(2));

  printf("%02X",SendByte(3));

  printf("%02X",SendByte(4));

  printf("%02X",SendByte(5));

  printf("%02X",SendByte(6));

  printf("%02X",SendByte(7));

  printf("\n");

  printf(''%02X",SendByte(7)); // Отправка следующих 8 байтов и одновременное

  printf(”%02X",SendByte(6));  // получение байтов, отправленных раньше

  printf("%02X",SendByte(5));

  printf("%02X",SendByte(4));

  printf("%02X",SendByte(3));

  printf("%02X",SendByte(2));

  printf("%02X",SendByte(1));

  printf(:%02X".SendByte(0)):

  printf("\n");

  printf("%02Х''.SendByte(0xF0)); // Отправка следующих 8 байтов и одновременное

  ргintf("%02Х",SendByte(0xF1)); // получение байтов, отправленных раньше

  printf("%02Х",SendByte(0xF2));

  printf("%02X",SendByte(0xF3));

  printf("%02X",SendByte(0xF4));

  printf("%02X",SendByte(0xF5));

  printf("%02X",SendByte(0xF6));

  printf("%02X", SendByte(0xF7));

  printf("\n");

  printf("\n');

  KeyOff(); // Отключение питания ключа

Автор: Сафонников В. В. (E-mail: ).

 

6.11. Кодовый замок

1. Назначение и описание устройства

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

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

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

При проигрывании мелодии останавливается вывод данных во внешний компьютер, поэтому, если шел прием данных и был нажат звонок, потребуется дополнительное время на исправление возникшей ошибки. При проигрывании простого сигнала такого не происходит. Кроме того, для правильного воспроизведения мелодии желательно использовать внешний кварц вместо внутреннего RC-генератора микросхемы.

Микроконтроллер имеет 64 байта энергонезависимой памяти (EEPROM), которая используется для записи происходящих событий. Есть 3 типа событий: включение питания, правильный набор кода и неправильный набор кода. Одновременно с событием записывается время, прошедшее после предыдущей записи. Точность записи времени около 8 минут. Если время между записями превысило 50 суток, дальнейший счет времени останавливается. Естественно, что для точного счета времени надо использовать кварцевый, а не встроенный RC-генератор.

Содержимое энергонезависимой памяти постоянно побитно, с частотой 3815 Гц выводится на один из выводов микроконтроллера и может быть считано для контроля внешним компьютером. Объем памяти позволяет хранить 30 записей по 2 байта. Число циклов записи в EEPROM ограничено, поэтому для увеличения срока службы используется специальный формат записи, разобраться в нем можно по исходному коду программы. После включения питания секретный код равен числу 23232. Для его изменения надо: 1) набрать старый код, при этом сработает соленоид, открывающий замок; 2) в момент отключения соленоида должна быть нажата кнопка 2 — это переведет устройство в режим ввода нового пароля; 3) ввести новый пароль, при ошибке можно пользоваться клавишей сброса.

Все нажатия на клавиши и некоторые другие действия озвучиваются короткими звуковыми сигналами.

2. Схема

Рис. 6.12. Электрическая принципиальная схема кодового замка

3. Комментарии к схеме

Питание на микроконтроллер подается постоянно, поэтому желательно использовать сетевой блок питания, а не батарейки.

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

4. План

Управляющую программу для чтения информации из EEPROM автор не написал, но возможность чтения заложена в программе микроконтроллера, так что, если найдутся желающие, автор будет только рад.

5. Программа

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

Текст программы:

.INCLUDE "1200def.inc"; AT90S1200 @ 1 МГц

.CSEG

.EQU evReset=1

.EQU evPassed=3

.EQU evDenied=5

.EQU KeyRing=1 ; Код клавиши звонка

.EQU KeyEnter=9 ; Код клавиши сброса

.EQU NewPassKey=2 ; Код клавиши, нажимаемой для ввода нового пароля

.EQU KeyLen=5; Длина секретного кода не должна быть больше 5 цифр

.EQU FreqKeyPressed=50 ; Константа частоты для выдачи писка при нажатии кнопок

.EQU Note_1=227 ; Константы частот и длительности нот

.EQU Note_la0 = Note_1

.EQU Note_2=202

.EQU Note_ci0 = Note_2

.EQU Note_3=191

.EQU Note_do1 = Note_3

.EQU Note_4=170

.EQU Note_re1 = Note_4

.EQU Note_5=152

.EQU Notejni.1 = Note_5

.EQU Note_6=143

.EQU Note_fa1 = Note_6

.EQU Note_7=128

.EOU Note_sol1 = Note_7

.EOU Note_8=114

.EOU Note_la1=Note_8

.EOU Note9=101

.EOU Sote_ci1=Note_9

.EOU Note_10=96

.EOU Note_do2 = Note_10

.EGU Note_11=85

.EOU Note_re2 = Note_11

.EOU Note_12=76

.EOU Note_mi2 = Note_12

.EOU Note_13=72

.EOU Note_fa2 = Note_13

.EOU Note_14=64

.EOU Note_sol2 = Note_14

.EOU DURATION = 350

.EOU Time_1=1000/Note_1

.EOU Time_2=1000/Not e_2

.EOU Time_3=1000/Note_3

.EOU Time_4=1 000/Not e_4

.EOU Tinve_5=1000/Note_5

.EOU Time_6=1000/Note_6

.EOU Time_7=1000/Note_7

.DEF KeyPass=r0

.DEF tmp=r0

.DEF KeyPass1=r1

.DEF KeyPass2=r2

.DEF KeyPass3=r3

.DEF KeyPass4=r4

.DEF KeyPass5=r5

.DEF SSRE6=r6

.DEF DelayVar=r7

.DEF IntUse0=r8

.DEF LastKey=r9

.DEF ZerReg=r10

.DEF Time0=r16 ; Время, увеличивается 3815 раз в секунду

.DEF Time1=r17 ; Время, увеличивается раз в 67 секунд

.DEF Time2=r18 ; Время, увеличивается раз в 47721 час,

; насыщается за 50 суток

.DEF EEadr=r20 ; Адрес для записи в EEPROM, равен 255.

; если в текущий момент запись не производится

.DEF EventType=r21

.OEF BeepVar=r21

.DEF TimerEEByte=r22

.DEF TimerEEBit=r23

.DEF Key=r24

.DEF BeepDuration=r25

.DEF FreqConst=r26

.DEF IntUse1=r27

;.DEF PassLoopCounter=r28

.DEF Flags=r29

; bit 0 — Password accepted

.ORG 000

          clr ZerReg

          rjmp RESET

.ORG 002

;======================

; Здесь оказываемся при возникновении прерывания от таймера 3 раза в секунду

;======================

; Таймер используется как RTC и для вывода информации EEPR0N

; Увеличение счетчика времени

         in SSREG.SREG

         subi Time0,1

         adc Time1,ZerReg

         абс Time2,ZerReg

         brcc TimeOk

         com Time1 ; Если проело переполнение, счетчик времени остановлен

         com Time2

TimeOk:

;Чтение данных из EEPROM и выдача на внешний порт.

;Процедура обработки прерывания будет вызвана не раннее, чей через 300-3∙4 мс

;после последнего обращения к EEPROM, поэтому проверка готовности EEPROM не

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

         andi TimerEEByte,$3F

         out EEAR,TiirerEEByte

         sbi EECR,EERE ;set EEPROH Read strobe

         sbi EECR,EERE ;set EEPROH Read strobe 2nd time

         in IntUse0,EEDR ;get data

         andi TimerEEBit,7

         brne Bitlop

         inc TimerEEByte

Bitlop: inc TimerEEBit ; =1.8

         mov IntUsel, TimerEEBit

         rol IntUse0

BitShift :; Выбираем посылаемый бит

         ror IntUse0 ; Используется как текущий считанный EEPROM байт

         dec IntUse1 ; Используется как счетчик

         brne BitSbift

         in IntUse1,PORTD

         bst IntUse0,0

         bid IntUse1,0

         out PORTD,IntUsel

         out SREG.SSREG

         reti

;===============

;Здесь оказываемся при включении питания схемы

;===============

RESET:

;Настройка микросхемы и программы.

         Idi r31,$8F ; Настраиваем направление работы портов

         out DDRB,r31

         ldi r31,$03

         out DDRD,r31

         ldi r31,$05; Настраиваем таймер

         out TCCRO,r31

         ldi r31,$02

         out TIMSK,r31 ; Разрешаем прерывания при переполнении таймера

         ldi r31.2

         mov KeyPass1,r31 ; Загружаем пароль по умолчанию

         ldi r31,3

         mov KeyPass2,r31

         ldi г31.2

         mov KeyPass3,r31

         ldi r31,3

         mov KeyPass4,r31

         ldi r31,2

         clr Time 1; Сбрасываем счетчик времени

         clr Time2

         rcall WrireEEPROM

         sei ; Разрешаем прерывания

Mainloop:

         ; Подготавливаемся к вводу пароля.

         clr r30,0

         clr r31,0

         ori Flags,1 ; При предъявлении пароля пока ошибок не было

         clr KeyPass

         clr LastKey

Passloop:; Цикл ввода пароля

         rcall GetKey ; Записывает в переменную Key код нажатой клавиши

         brtc Passloop ; и устанавливает бит Т, если клавиша нажата

         cp Key,LastKey

         breq Passloop ; Если предыдущая клавиша еще не отпущена

         cpi Key,KeyEnter

         breq MainLoop ; Если нажат сброс, начинаем ввод строки с начала

         ldi FreqConst,FreqKeyPressed

         ldi BeepDuration,5

         rcall Beep ; Выдача короткого звукового сигнала

         mov LastKey,Key

         inc r30

         ld KeyPass.Z

         cpse Key,KeyPass; Сравниваем введенную цифру и цифру пароля из ОЗУ.

         andi Flags, ~ 1 ; если ошибка, сбрасываем бит успешности пароля.

         cpi r30,Keylen ; Длина секретного кода.

         brne PassLoop

         ; Вывод бита на внешний вывод порта и запись события в EEPROM

         ldi EventType,evDenied

         bst Flags,0

         brtc PasswordDenied

         sbi PORTD,1 ; Если пароль совпал, включаем соленоид

         ldi EventType,evPassed

PasswordDenied:

         rcall WrireEEPROW

          ; Открытие двери при совпадении кода или цикл ожидания при ошибке

          ldi Key,5

Openloop:

          rcall Delay100

          ldi BeepDuration,Time_7/2

          ldi FreqConst,Note_7

          rcall Beep

          dec Key

      sbic PIND,2 ; Если сработал концевик (открылась дверь), то выходим

          brne Openloop ; или выходим из цикла по тайм-ауту

          cbi PORTD,1; Отключаем соленоид

          ; Нажатие на клавишу NewPassKey в этой месте приводит к загрузке

          rcall GetKey; нового секретного кода

          brtc NainLoop

          cpi Key,NewPassKey

          brne Mainloop

          sbrs Flags,0 ; Если старый был предъявлен верно

          rjmp Mainloop

;Ввод нового, секретного кода,

          ldi FreqConst,Note_3 ;Проигрывание "приглашения" для изменения пароля

          ldi BeepDuration,20

          rcall Beep

          ldi FreqConst,Note_5

          ldi BeepDuration,20

          rcall Beep

          ldi FreqConst.Note_7

          ldi BeepDuration,20

          rcall Beep

          rcall Delay100

NewPassword:

          clr LastKey

          clr r30

          clr r31

NewPassLoop:

          rcall GetKey

          brtc NewPassLoop; Ожидаем нажатия клавиши

          cp Key,LastKey

          breq NewPassLoop ; Если клавиша еще не отпущена, продолжаем цикл

          cpt Key,KeyEnter

          breq NewPassword ; Если сброс, начинаем ввод строки с начала

          mov LastKey,Key

          ldi BeepDuration,2*10

          ldi FreqConst,2*FreqKeyPressed

          rcall Beep ; Удлиненный звуковой сигнал

          inc r30

          St Z,Key ; Сохраняем введенную цифру

          cpi r30,Keylen ; Длина секретного кода

          brne NewPassLoop

          rcall Delay100

          ldi FreqConst,Note_7 ; Проигрывание мелодии, говорящей об

          ldi BeepDuration,20 ; успешной изменении пароля

          rcall Beep

          ldi FreqCoost, Note_5

          ldi BeepDuration,20

          rcall Beep

          ldi FreqConst,Note_3

          ldi BeepDuration,20

          rcall Beep

          rjmp MainLoop

;==============

;Дальше идут процедуры, вызываемые из основной программы

;==============

Веер:; Выдача звукового сигнала

          ldi BeepVar,20

Beep1: mov DelayVar.FreqConst

Delay2: rjmp NopJmp1; 2 +

NopJmp1:dec DelayVar;  1 + = 5 мкс

          brne Delay2;2 +

          sbi P0RTB.7

          mov DelayVar.FreqConst

Delay3: rjrep NopJmp2

NopJmp2:dec DelayVar

          brne Delay3

          cbi P0RTB.7

          dec BeepVar

          brne Beep1

          dec BeepDuration

          brne Beep

          sei

          ret

;================

; Задержка на время порядка секунды

Delay100:

          ldi BeepOuration,3

D100_3: clr FreqConst

D100_2: clr DelayVar

D100_1: dec DelayVar

          brne D100_1

          dec FreqConst

          brne D100_2

          dec BeepDuration

          brne D100_3

          ret

;================

; проверка приема данных от клавиатуры

GetKey:

          clt; Сброс признака, что была нажата клавиша

          ldi r31,$0Е ; Младшая тетрада — запрос в порт, старшая -

          rcall GetKeyAnswer ; смещение ответа

          brne KeyPressed

          ldi r31,$3D

          rcall GetKeyAnswer

          brne KeyPressed

          ldi r31,$6B

          rcall GetKeyAnswer

          brne KeyPressed

          ldi r31,$97

          rcall GetKeyAnswer

          brne KeyPressed

          ret

KeyPressed:

          set

          swap r31

          andi r31,$0F

          swap Key

          andi Key,$07

CCFind: inc r31 ; Поиск, какой конкретно бит установлен

          lsr Key

          brcc CCFind

          breq NoMu1

          clt ; Если нажато несколько клавиш, то не нажата ни одна

NoNul: mov Key,r31

; Проверка, нажата ли кнопка Ring, если нажата, то будет выдан звуковой сигнал

; без сообщения об этом вызывающей функции

          cpi Key,KeyRiog

          breq Ring

          rjmp NoRing

Ring: sbrc KeyPass1,0 ; Выбор типа мелодии в зависимости от четности

          rjmp SimpleRing ; старей цифры секретного кода

          rcall Delay100

;Проигрываем сложную мелодию (Отговорила роща золотая)

;1 ми 1/8

          ldi FreqConst,Note_mi1

          ldi BeepDuration,2*DURATI0N/Note_mi1

          rcall Beep_Del

;1 ми 1/8

          ldi F reqConst,Note_mi1

          ldi BeepDuration,2*DURATION/Note_mi1

          rcall Beep_Del

; 1 ми 1/8

          ldi FreqConst,Notejnii

          ldi BeepDuration.2*DURATION/Note_mi1

          rcall Beep_Del

;1 фа 3/8

          Idi FreqConst,Note_fa1

          ldi BeepDuration,6*DURATI0N/Note_fa1

          rcall Beep_Del

;1 ми 1/8

          ldi F reqConst,Note_mi1

          ldi BeepDuration,2*DURATION/Note_mi1

          rcall Beep_Del

;1 фа 3/8

          Idi FreqConst,Note_fa1

          ldi BeepDuration,6*DURATI0N/Note_fa1

          rcall Beep_Del

;1 ми 1/16 (1/8)

          ldi FreqCoost,Note_mi1

          ldi BeepDuration,2*DURATI0N/Note_mi1

          rcall Beep_Del

;1 ре 1/8

          ldi FreqConst,Note_re1

          ldi BeepDuration,2*DURATlON/Note_re1

          rcall Beep_Del

;1 до 1/8

          ldi FreqConst,Note_do1

          ldi BeepDuration,2*DURATION/Note_do1

          rcall Beep_Del

;1 си 1/4

          ldi FreqConst,Note_ci0

          ldi BeepOuration,4*DURATION/Note_ci0

          rcall Beep_Del

; 1 фа 3/8

          ldi FreqConst.Note_fa1

          ldi BeepDuration,6*DIRATION/Note_fa1

          rcall Beep_Del

;1 Фа 1/8

          ldi FreqConst,Note_fa1

          ldi BeepDuration,2*DURATION/Note_fa1

          rcall Beep_Del

; 1 ми 3/8

          ldi FreqConst.Note_mi1

          ldi BeepDuration,6*DURATIOIN/Note_mi1

          rcall Beep_Del

;1 ми 1/6

          ldi FreqConst.Note.mil

          ldi BeepDoration. 3*DURATI0N/Note_mi1

          rcall Beep_Del

; 1 ми 1/8

          ldi FreqConst.Note_mi1

          ldi BeepDuration,2*DURATIOIN/Note_mi1

          rcall Beep_Del

;1 Фа 1/8

          ldi FreqConst,Note_fa1

          ldi BeepDuration,2*DURATION/Note_fa1

          rcall Beep_Del

;1 си 1/8

          ldi FreqConst,Note_ci0

          ldi BeepOuration,2*DURATION/Note_ci0

          rcall Beep_Del

;1 ре 1/8

          ldi FreqConst,Note_re1

          ldi BeepDuration,2*DURATlON/Note_re1

          rcall Beep_Del

;1 Фа 1/8

         ldi FreqConst,Note_fa1

         ldi BeepDuration,2*DURATION/Note_fa1

         rcall Beep_Del

; 1 ми 5/8

         ldi FreqConst.Note_mi1

         ldi BeepDuration,10*DURATIOIN/Note_mi1

         rcall Beep_Del

         rjmp EndRing

SimpleRing :; Проигрываем простую мелодию

         ldi F reqConst,Note_7

         ldi BeepDuration,1

         rcall Beep

         ldi FreqConst,Note_5

         ldi BeepDuration,1

         rcall Beep

         ldi FreqConst,Note_3

         ldi BeepDuration,1

         rcall Beep

         ldi FreqConst,Note_5

         ldi BeepDuration,1

         rcall Beep

EndRing:

         clt ; Сбрасываем флаг нажатия клавиши

NoRing:

         ret

;===================

Beep_Del:; Короткая пауза перед нотой и сама нота

         ldi BeepVar,50

Del2: clr DelayVar

Del1: dec DelayVar

         brne Del1

         dec BeepVar

         brne Del2

         cli

         rjmp Beep

;===================

; Сканирование клавиатуры: установка на выходных линиях заданного кода и прием

; ответа вызывается из функции GetKey

GetKeyAnswer:

        mov Key,r31

        andi Key,$0F

        out PORTB,Key

        clr DelayVar

Delay1: dec DelayVar

        brne Delay1

        in Key,PINB

        com Key

        andi Key,$70 ;Если что-то было найдено, флаг Z не нулевой

        ret

;===================

; Запись события EventType в текущую ячейку EEPR0M памяти и обнуление времени

WrireEEPROM:

        ; Поиск последней записи

        clr tmp

        ldi EEadr,2

FincNextAdr:

        out TCNTO,ZerReg ; Пока не сработал таймер, читающий из EEPROM (через 300 мс). все операции должны быть завершены

        subi EEadr, — 2

        cpi EEadr,62

        brne EERead

        ldi EEadr,4; Если дошли до 64-ro адреса, начинаем с начала

EERead

        sbic EECR,EEWE ; Ждем обнуления бита EEWE

        rjmp EERead

        out EEAR,EEadr ; Подготавливаем адрес

        sbi EECR,EERE ; Устанавливаем бит для синхронизации

        sbi EECR,EERE ; Дважды

        in r31,EEDR ; Читаем данные

        dec tirp; Если было 256 неудачных попыток найти бит

       breq NoZav ; признак последней записи, значит, его нет вообще, значит, пишем по любому адресу

        sbrs r31,0

        rjmp FindNextAdr

NoZav: andi r31,$FE

EEWrite0:; Запись дня сброса флага, указывающего на последнюю запись

        sbic EECR,EEWE

        rjmp EEWrite0

        out EEAR,EEadr

        out EEDR,r31

        sbi EECR,EEWE

        inc EEadr

EEWrite1:; Запись старшего байта времени

        sbic EECR,EEWE

        rjmp EEWrite1

        out EEAR,EEadr

        out EEDR,Time2

        sbi EECR,EEWE

        inc EEadr

EEWrite2:; Запись времени, типа события и флага последней записи

        sbic EECR,EEWE

        rjmp EEWrite2

        out EEAR,EEadr

        mov r31,Time1

        andi r31,SF8

        or r31,EventType

        out EEDR,r31

        sbi EECR,EEWE

        clr Time1 ; Счетчик считает время между записями в EEPROM,

        clr Time2 ; поэтому сбрасываем его

        ret

.EXIT

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

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

; PORTB,7 — вывод на динамик

; PORTB,0…3 — выходы на сканирование клавиатуры

; PORTB,4…6 — входы от 12-клавишной клавиатуры, подключены к +5 В через резисторы

; Порт D используется для управления замком, выдачи информации и проверки двери

; PORTD.0 используется для вывода информации из ЕЕРАОН, на нем с частотой

; 3815 Гц (@ 1МГц) побитно выводится все ее содержимое

; PORTD.1 Подключен на электромагнит или двигатель, единица разрешает открытие

; PORTD.2 Подключен на концевику, срабатывающему (=0) при открытии

Автор: Сафонников В. В. (E-mail: ).

 

6.12. Музыкальный звонок

1. Краткое описание

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

2. Схема

Рис. 6.13. Электрическая принципиальная схема музыкального звонка

Благодаря использованию микроконтроллера схема предельно упрощается, необходимо только подключить питание (от 2,7 до 6 В), кварцевый резонатор и, если необходимо, усилитель низкой частоты, собранный на транзисторе КТ815. Если предполагается, что ток через нагрузку не будет превышать 20 мА (например, при использовании пьезоизлучателя), можно обойтись без усилителя.

Следует заметить, что 20 мА — это максимальный ток логического нуля, т. е. второй вывод звукового излучателя следует подключать к плюсу питания.

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

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

Частоту кварца можно выбрать практически любую в диапазоне от 32 768 кГц до 10 МГц. Схема была проверена на частоте 10 МГц. Если вы хотите использовать кварц на меньшую частоту (при этом уменьшится и потребляемый ток схемы), необходимо пропорционально уменьшить коэффициенты таблицы SoundTab и длительности звучания всех нот.

3. Программа

Эта программа написана на ассемблере для AT90S2313, таблица нот рассчитана для работы процессора на максимальной частоте 10 МГц, вместо мелодии используется проигрывание гаммы от более низких нот к более высоким и обратно. Текст программы:

.INCLUDE "2313def.inc"

.DEF Step=r20

.DEF Freqlndex=r21

.DEF FreqDelay=r22

.DEF SSREG=r23

.CSEG

.ORG 0

         rjmp Reset

.ORG OVFOaddr

         in SSREG,SREG

         dec FreqDelay

         out SREG,SSREG

         reti

Reset:

         ldi r30,$DF

     out SPL,r30

         ; Настройка направления работы линий порта В

         ldi r30,$08

         out DDRB,r30

         ; Режим работы таймера 0 с максимальным предварительным делением

         ; Здесь же разрешаем прерывания

         ldi г30,$05

         out TCCR0,r30

         ldi r30,$02

         out TIMSK,r30

         sei

         ; Режим работы таймера 1 на переключение внешнего вывода,

         ; выбор коэффициента предварительного деления тактовой частоты 1:1

         ; и автоматический сброс таймера при совпадении

     ldi r30,$40

     out TCCR1A,r30

     ldi r30,$09

     out TCCR1B,r30

         ; Установка номера шага на начало

         clr Step

ReadNote:; Чтение длительности и номера одной ноты

         ldi r31,High(2*ProgramTab); Freqlndex = Lo ProgramTab[Step]

         ldi r30,Low(2*ProgramTab); FreqDelay = Hi ProgramTab[Step]

         mov r0,Step ; Step++

         lsl r0

         add r30,r0

         inc Step

         lpm

         mov Freqlndex,r0

         inc r30

         lpm

         sbrc r0,7

         rjmp SleepReset; Если старший бит установлен, то

         mov FreqDelay,r0; это означает, что мелодия закончилась

SetFreq:; Настройка таймера 1 на вывод частоты текущей ноты

         ldi r31,High(2*SoundTab) ; OCR1A = SoundTab[FreqIndex]

         ldi r30,Low(2*SoundTab)

         lsl Freqlndex

      add r30,FreqIndex

      lpm

      out OCR1AL,r0

         inc r30

       lpm

       out OCR1AH,r0

Wait: tst FreqDelay ; Ожидаем заданное время, пока проигрывается

          brne Walt ; текущая нота

          rjmp ReadNote

SleepReset:

      ldi r30,$3F ; Подготовка к переходу в режим Power Down

     out MCUCR,r30

         sleep ; Отключение микроконтроллера

         rjmp Reset ; Эта команда в данной версии программы не должна исполняться никогда

.ORG $100

SoundTab:

; Таблица констант соответствующим нотам

; Желательно выровнять таблицу по границе 256 байтов, чтобы упростить программу, отказавшись от операций с 16-битовыми словами.

.DW 11364,10292,9322,8443,7647,6926,6273 ; 440 Гц — нота "ля"

.DW 5682,5146,4661,4222,3824,3463,3137 : вторая октава

.DW 2841,2573,2330,2111,1912,1732,1568 ; третья октава

; При необходимости таблицу можно продолжить

ProgramTab:

; Таблица последовательности нот,

; формат таблицы: байт длительности/кода операции, байт номера частоты

; В этом примере записано проигрывание гаммы

.DW $1001, $1002, $1003, $1004, $1005, $1006, $1007

.DW $1008, $1009, $100А, $100В, $10 °C, $100D, $100Е

.DW S200D, $2006, $2009, $2007, $2005, $2003, $2001

.DW $8000

EXIT

Автор: Сафонников В. В. (E-mail: ).

 

6.13. Универсальный расширитель последовательного порта

Назначение

Схема предназначена для управления с помощью компьютера различными устройствами: бытовыми электроприборами, шаговыми двигателями, электромагнитами и т. д. Можно ее также использовать как основу для программирования микросхем памяти, микроконтроллеров или других разработок. Для подключения к внешним устройствам имеются 24 универсальных выхода, совместимых с TTL, состояние и режим работы каждого из них можно задать с помощью управляющей программы. Она может быть написана на любом языке, поддерживающем технологию COM (ActiveX), например Delphi, MS Visual C++, MS Visual Basic, JavaScript, VBS и т. д.

Основу схемы составляет микроконтроллер AT90S8515, он подключен к компьютеру через последовательный порт, а для управления внешними устройствами используются выводы трех свободных портов — А, В и С. Для упрощения схемы данные передаются только в одну сторону — от компьютера к микроконтроллеру, никаких подтверждений не возвращается, запросить состояния выводов компьютер тоже не может, хотя программа микроконтроллера поддерживает такой режим работы.

Рис. 6.14. Нагрузочные характеристики выводов микросхемы

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

Уровни последовательного порта преобразуются в TTL с помощью схемы на транзисторе VT1. Если вы собираетесь использовать эту схему для управления устройствами, работающими с высоким напряжением, вместо транзистора следует использовать оптопару.

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

После включения питания все порты переводятся в третье состояние.

Комплект программ для этой конструкции находится на компакт диске, прилагаемом к книге.

Автор: Сафонников В. В. (E-mail: ).

 

6.14. Таймер

1. Краткое описание и назначение прибора

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

2. Схема конструкции показана на рис. 6.16.

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

3. Программа

Для компилирования программы использовался AVR macro assembler version 1.21 и файл 2313def.inc с описанием периферии микроконтроллера, оба они доступны на сайте Atmel. Программа может быть перенесена для работы с другими микроконтроллерами семейства AVR. Для этого замените файллпс, а константе ENDJDATAAREA присвойте новое значение в соответствии с размером доступной памяти.

Текст программы:

INCLUDE "2313def.inc"; @ 8 МГц

.DEF tmp=r0

.DEF SSREG=r1

.DEF tem=r25

.DEF temp=r26

.DEF TheByte=r27

.DEF Time0=r16

.DEF Time1=r17

.DEF Time2=r18

.DEF TimeA=r19

.DEF TimeB=r20

.DEF Flags=r21

.EQU END_DATA_AREA=$DB ; 41 запись общей длиной 123 байта

.CSEG

.ORG 0

         rjmp Reset

.ORG OC1addr ; $0004

         nop

.ORG OVF1addr ; $0005

         in SSREG,SREG

         inc Time0

         brne NoOvf

         inc Time1

         brne NoOvf

         inc Time2

NoOvf: ldi r30,$60

         ldi r31,0

LoopCheck:; Loop of verify time for all records

         set ; Флаг T указывает на совпадение текущего и заданного

         Id tmp,Z+ ; Чтение младшего байта времени

         cp tmp,Time0

         breq E1

         clt

Е1:    Id tmp,Z+ ; Чтение среднего байта времени

         cp tmp,Time1

         breq E2

         clt

Е2:    Id tmp,Z+ ; Чтение последней тетрады времени (биты 0–3)

         brtc NoMath ; и типа события (биты 4–7)

         mov tem,tmp

         eor tem,Time2

         andi tem,15

         brne NoMath

          mov tem,tmp ; Время совпало, выполняем заданное действие

          bndi tem,$F0

          cpi tem,$00

          brne Off0

          cbi PORTB,0 ; Отключение канала 0

Off0:  cpi tem,$10

          brne On0

          sbi PORTB,0 ; Включение канала 0

On0:   cpi tem,$20

          brne Off1

          cbi PORTB,1 : Отключение канала 1

Off1:  cpi tem,$30

         brne On1

         sbi PORTB,1

On1:  cpi tem,$40

         brne Off2

         cbi PORTB,2

Off2: cpi tem,$50

         brne On2

         sbi PORTB,2

On2:  cpi tem,$60

         brne Off3

         cbi PORTB, 3

Off3: cpi tem,$70

         brne On3

         sbi PORTB,3

On3:  cpi tem,$80

         brne Off4

         cbi PORTB,4

Off4: cpi tem,$90

         brne On4

         sbi PORTB,4

On4:  cpi tem,$A0

         brne Off5

         cbi PORTB,5 ; Отключение канала 5

Off5: cpi tem,$60

         brne On5

         sbi PORTB, 5 ; Включение канала 5

On5:  cpi tern,SC0

         brne Off6

         andi Flags,$FE ; Отключение канала 6 (используется как будильник)

Off6: cpi tem,$D0

         brne On7

         ori Flags,1 ;Включение канала 6

On7:  cpi tem,$E0

         breq TimeReset ; Обнуление счетчика времени

         cpi tem,$F0

         brne NoMath

TimeReset: ;Если биты 4–7 равны 1, то сбрасываем счетчик времени

         clr Time0

         clr Time1

         clr Time2

NoMath:

         cpi r30,END_DATA_AREA

         breq Loop_Check

         rjmp LoopCheck

Loop_Check:

         ; Включение секундного светодиода

         clr TimeB

         sbi PORTB,7

         out SREG,SSREG

         reti

Reset:

         ; Установка указателя стека на максимальный адрес SRAM

         ldi r30,$DF

    out SPL,r30

         ; Настройка направления работы линий порта В

         ldi r30,$FF

         out DDRB,r30

         ldi r30,$7Е

         out DDR0,r30

         ; Режим работы таймера 1 со сбросом и прерыванием при совпадении

         ; Частота прерываний равна 8 000 000 / 256 / 31250 (7А12) = 1 Гц

         ldi r30,12

         ut TCCR1B,r30

         ldi r30,$7A

         out OCR1AH,r30

         ldi r30,$12

         out OCR1AL,r30

         ldi r30,$CO

         out TIMSK,r30

         ; Настройка WDT на 2 секунды

         ldi r30,S0F

         out WDTCR,r30

         ; Настройка UART на прием с параметрами 9600 8N1

         ldi r30,$18

         out UCR,r30

         ldi r30,$33

         out UBRR,r30

         ; Обнуление счетчика времени

         clr Time0

         clr Time1

         clr Time2

; Задание тестовых точек включения (после отладки схемы убрать)

         ldi r30,$05

         sts $60,r30

         ldi r30,$00

         sts $61,r30

         ldi r30,$10

         sts $62,r30

         ldi r30,$07

         sts $63,r30

         ldi r30,$00

         sts $64,r30

         ldi r30,$00

         sts $65,r30

         ldi r30,$0F

         sts $66,r30

         ldi r30,$00

         sts $67,r30

         ldi г30,$10

         sts $68,r30

         clr Flags

         sei

UnlessLoop:

         rcall GetByte; Прием сообщения говорящего о начале данных

         cpi TheByte, 'T'

         brne UnlessLoop

         rcall GetByte

         cpi TheByte,'i'

         brne UnlessLoop

         rcall GetByte

         cpi TheByte,'m'

         brne UnlessLoop

         rcall GetByte

         cpi TheByte,'e'

         brne UnlessLoop

         rcall GetByte

         cpi TheByte,'r'

         brne UnlessLoop

         clr Time0 ; Обнуление счетчиков времени

         clr Time1

         clr Time2

         ldi r28,$60

         ldi r29,0

LoopRX:

         rcall GetByte: ; Чтение байта данных

         St Y+,TheByte ; Сохранение считанного байта

         cpi r28,END_DATA_AREA

         brlo LoopRX

         rjmp UnlessLoop

         ser Time0 ; Обнуление счетчиков времени

         ser Time1

         ser Time2

     ldi r28,$10

     out TCNT1L,r28

     ldi r28,$7A

     out TCNT1H,r28

; Процедура чтения байта с UАRТ

; Программа все свое свободное время находится внутри нее

GetByte:

         wdr ; Сброс сторожевого таймера

         inc TimeA ; Увеличение асинхронного счетчика времени

         brne Early

         inc TimeB

         brne Early

         cbi PORTB,7 ; Выключение секундного светодиода

Early:

         sbrs Flags,0

         rjmp NoBell

         mov temp,TimeB ; Звуковой сигнал включен

         andi temp.$E7

         breq NoBell

         sbis PORTB,6 ; Инвертирование состояния вывода PB.6

         rjmp NoCBI

         cbi PORTB,6

         rjmp NoBell

NoCBI: sbi PORTB.6

NoBell:

         sbis USR,RXC

         rjmp GetByte

         in TheByte,UOR

         ret

; Протокол управления таймером

; Формат загрузки расписания управления каналами от компьютера

; * Настройки порта 9600 8N1

; * Загрузка производится одним блоком, состоящим из 41-й записи

; * Перед блоком с записями надо отправить строку Timer

; * Каждая запись состоит из 3 байтов, сначала передается младший байт времени

; включения, затем средний, затем байт, состоящий из двух частей: младшие

; 4 бита — это самая старшая тетрада времени, бит 4 — это состояние, в которое

; будет переведен канал, и оставшиеся 3 бита — это номер канала. Если номер

; канала равен 7, то будет обнулен внутренний счетчик времени таймера и

; выполнение программы начнется сначала

; * Время считается с момента загрузки новой программы в секундах

. EXIT

4. Технические характеристики

Максимальное количество нагрузок, поддерживаемое программой — 7, но оно может быть меньше, в зависимости от конкретной реализации схемы. Седьмой канал, в отличие от остальных, при срабатывании выдает не постоянное напряжение единицы, а импульсы звуковой частоты. Это позволяет использовать его, например, в качестве будильника. Допустимая мощность нагрузок тоже определяется схемой, например если используются симисторы КУ208, установленные на радиатор, то она составляет 1 кВт на каждый канал.

Память микроконтроллера, установленного внутри таймера, позволяет хранить расписание, состоящее из 41-го (для AT90S2313) события. Максимальное время от загрузки расписания до выполнения события не может быть больше 220 секунд (немного больше 7 дней). Точность задания времени события 1 секунда.

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

5. Управляющая программа и инструкция (рис. 6.17)

Рис. 6.17. Вид окна управляющей программы

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

для хранения некоторых настроек используется системный реестр, расписание хранится в файле Timer.sav в текущем каталоге (текстовый файл).

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

Комплект программ для проекта находится на компакт диске, прилагаемом к книге.

Автор: Сафонников В. В. (E-mail: ).

 

6.15. Универсальный параллельный адаптер

1. Краткое описание

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

2. Схема

Рис. 6.18. Электрическая принципиальная схема адаптера

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

Схема состоит из трех регистров и одного мультиплексора. Все регистры включены по одинаковой схеме, за исключением третьего, его выходы могут быть переведены в высокоомное состояние, поэтому к нему также подведен управляющий сигнал разрешения включения выходов ОЕ. Информационные входы всех регистров объединены и подключены к соответствующим выходам параллельного порта компьютера, так как используется ТТЛШ серия, то допустимо нагружать один выход порта на несколько входов микросхем. Для стробирования используются управляющие линии порта, подключенные ко входам С регистров. Для увеличения количества входов используется мультиплексор D4.

3. Подключение к компьютеру и внешнему устройству

Схема подключается к параллельному порту, также необходимо подвести питание +5 В к микросхемам, лучше всего для этого использовать блок питания компьютера. В моем варианте собранная схема находится внутри компьютера, подключается к внутреннему разъему LPT-порта на системной плате, для питания использует 4-контрактный разъем, а рабочие выходы выведены на 32-контактный разъем, вмонтированный в заглушку от отсека 5,25 на передней панели. На этот же разъем выведены напряжения питания +5, +12 В.

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

Для подключения к внешнему устройству используются цепи O1—O24, из них O1…O16 являются обычными выходами, а O17…O24 могут использоваться как входы или выходы. Цепь O16 параллельно используется для внутренних нужд.

4. Программирование

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

Практически любой язык программирования имеет функции, позволяющие записать число по заданному адресу в порт ввода-вывода. Управление схемой осуществляется через вызовы таких функций. Для записи 8-битового числа в буферный регистр схемы необходимо записать его в регистр данных параллельного порта, затем записать в регистр управления любое число, имеющее в соответствующем разряде единицу (соответствующем выводу С выбранного буферного регистра), и затем в него же ноль. Для чтения 4 битов данных достаточно просто прочитать регистр состояния порта, для чтения остальных битов предварительно измените состояние линии 016. Необходимо учитывать, что некоторые входные и выходные линии порта проинвертированы.

Адреса регистров для LPT1 приведены в таблице (верно для большинства компьютеров, но для корректного определения адресов следует использовать данные BIOS):

Регистр данных… 378h

Регистр состояния… 379h

Регистр управления… 37Ah

Автор: Сафонников В. В. (E-mail: ).

 

6.16. Электронные часы с будильником на

AT90S2313-10PI

В данном проекте описаны электронные часы с будильником, выполненные на AVR-микроконтроллере типа AT90S2313-10PI. Часы имеют светодиодный индикатор отображения текущего времени и встроенный аккумулятор для поддержания хода при пропадании сетевого напряжения, что очень актуально в условиях непрекращающегося экономического кризиса. Устройство содержит минимум комплектующих и имеет несложную электрическую схему. Часы были испытаны автором на протяжении нескольких месяцев, что показало их надежность и работоспособность.

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

Краткое описание устройства часов

Предлагаемый вашему вниманию проект был выполнен «по ходу дела» при освоении автором микроконтроллеров семейства AVR фирмы Atmel. Один из этих «жучков» и был использован при разработке часов. Выбор именно AT90S2313-10PI объясняется широкой доступностью и невысокой ценой этого кристалла, а также наличием в нем памяти программ объемом 2 Кб и программно реализованного стека.

Как видно, микроконтроллер является основной и единственной микросхемой, используемой в данной разработке. Для задания тактовой частоты контроллера используется кварцевый резонатор на 10 МГц, но управляющую программу очень легко переделать и для резонаторов с другими частотами. В качестве устройства отображения использованы два индикатора красного цвета свечения с общим анодом, каждый индикатор состоит из двух цифр с десятичными точками. Цифры имеют отдельные аноды. Можно применить любые индикаторы с общим анодом, лишь бы ток сегмента не превышал 20 мА и каждая цифра имела бы собственный анод. Рекомендуется выбирать индикаторы с большими цифрами, тогда часы будут хорошо видны в темноте.

Индикация текущего времени осуществляется динамически, в данный конкретный момент времени отображается лишь одна цифра, что позволяет значительно снизить аппаратные затраты. Происходит это так. Аноды каждой из четырех цифр обоих индикаторов являются раздельными, что позволяет в данный момент времени подключить к источнику питания только один анод и отобразить одну цифру. Для этого часы имеют четыре транзисторных ключа, выполненных на транзисторах типа КТ361Е (VT1…VT4), и резисторах (R2…R9). Ключи управляются микроконтроллером, причем соответствующий ключ открыт, если на выводе контроллера присутствует логический ноль. Одноименные сегменты всех четырех цифр соединены вместе и через токоограничивающие резисторы R21…R27 подключены к выводам порта В (выводы РВ.0…РВ.6). Десятичная точка не используется, она «принесена в жертву» добавленному в часы будильнику и всегда выключена. Управляющая программа один за другим подключает цифры к источнику питания, и одновременно на соответствующие выводы порта В выставляется код отображаемой цифры. Поскольку сканирование индикатора происходит очень быстро, мерцание цифр становится незаметным.

Для перевода минут, часов и установки будильника используются кнопки S1…S3. Обновление показаний индикатора происходит каждую секунду. При нажатии более чем на одну кнопку управляющая программа игнорирует нажатие кнопок. Для установки будильника следует нажать кнопку ALARM и подождать двукратного звукового сигнала (не более секунды) для входа в режим будильника. Для перехода обратно в режим часов кнопку ALARM нужно удерживать до подачи однократного сигнала. В качестве выхода для сигнала будильника используется вывод РВ.7 порта В, а в качестве усилителя — схема на транзисторе VT5. В роли сигнализатора применен звонок от импортных часов сопротивлением около 15 Ом. Для отключения будильника используется выключатель S4 (лучше всего — кнопка с фиксацией).

Питаются часы от стабилизированного источника напряжением 6 В. Причем индикация работает только при работе часов от сети. Ток потребления при наличии индикации — около 80 мА. При работе от аккумуляторов (четыре аккумулятора типа Д-0,26) индикация отключается, но часы продолжают идти и функционирует будильник. Диоды D5…D7 обеспечивают правильное использование источников питания при работе от сети и от аккумуляторов, сами же аккумуляторы при работе часов от сети заряжаются через резистор R10. Поскольку при отсутствии индикации часы потребляют ток около 6 мА, предложенный UPS способен поддерживать работу часов более суток, что чрезвычайно удобно. Лично я не люблю наводить часы всякий раз после броска сетевого напряжения. Кстати, это явилось одной из движущих сил данного проекта.

Краткое описание управляющей программы

Управляющая программа часов написана на ассемблере.

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

Основой программы являются два обработчика прерываний от таймеров. Обработчик прерывания от таймера-счетчика Т/С0 используется для динамической индикации, а обработчик от таймера-счетчика Т/C1 считает время. Естественно, можно было бы организовать сканирование индикатора и без привлечения прерывания от таймера и применить более дешевый кристалл, например AT90S1200. Но непривычность работы с аппаратным стеком (автор много лет до этого занимался 80X86), недостаточный объем памяти и незначительное отличие между этими контроллерами в цене говорят явно не в пользу такого решения.

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

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

Резюме

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

Файлы, относящиеся к проекту, находятся на компакт-диске, прилагаемом к книге.

Автор: Игорь Коваль (E-mail: ).

 

6.17. Подключение внешнего статического оперативного запоминающего устройства

Разработчики устройств на микроконтроллерах постоянно сталкиваются с задачей временного хранения данных, однако объема внутреннего SRAM (Static Random Access Memory — статическое запоминающее устройство с произвольным доступом, далее — память) зачастую недостаточно.

Для облегчения решения такой задачи микроконтроллер AT90S8515 (AT90LS8515) снабжен интерфейсом для подключения внешней памяти объемом до 64 Кбайт.

Интерфейс для подключения внешней памяти

Интерфейс включает в себя:

• порт А: шина младшего байта адреса / шина данных;

• порт С: шина старшего байта адреса;

• контакт ALE (Address Latch Enabled): разрешение фиксации адреса;

• контакты RD и WR: стробы записи и считывания.

Для инициализации работы интерфейса надо установить (в единицу) бит SRE (Static RAM Enable — разрешение работы с внешней памятью) регистра MCUCR (MCU Control Register — регистр управления микроконтроллера). Теперь при использовании стандартных команд работы с памятью (команды LD, LDD, LDS, ST, STD и STS) микроконтроллер может обращаться к ячейкам памяти, адреса которых лежат в диапазоне $0060…$FFFF (знак «$» используется в тексте для обозначения шестнадцатеричных чисел, так же, как это должно быть записано в программе на Ассемблере для AVR Studio; указанный диапазон адресов в десятичной форме будет иметь вид 96…65535).

Свободно размещать данные в памяти микроконтроллера AT90S8515 можно, начиная с адреса $0060, как при работе с внутренней оперативной памятью, так и при использовании интерфейса внешней памяти, поскольку адреса оперативной памяти $0000…$001F заняты регистрами общего назначения r0…r31 (десятичное число 31 равно шестнадцатеричному числу $001F), адреса $0020…$005F отведены для регистров ввода-вывода.

Замечание.

Если до инициализации интерфейса линии порта А, порта С и контакты PD6 (WR) и PD7 (RD) порта D были запрограммированы как линии ввода или вывода, установка бита SRE перепрограммирует эти линии для работы с внешней памятью.

Если бит SRE сбросить (в ноль), работа с внешней памятью прекращается, устанавливается обычный режим работы портов А, С и контактов PD6 и PD7 порта D, а область оперативной памяти, к которой может обращаться микроконтроллер AT90S8515 ограничивается диапазоном S0060…S025F (всего 512 байт).

Примечание. При работе в диапазон адресов, например, S2220…S222F количество адресуемых ячеек не 15 ($222F-$2220=$F), а 16 ($F+1), так как обращение происходит и к ячейке с адресом $2220.

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

Для фиксации младшего байта адреса необходимо использовать дополнительный элемент хранения с записью положительным уровнем. Для этого подойдет 8-разрядный регистр 74HCT573N. При высоком уровне сигнала на контакте ALE младший байт адреса, сформированный на восьми линиях порта А, записывается в регистр. При низком уровне на контакте ALE через порт А передаются данные. Сигналы на контактах WR и RD микроконтроллера активны только во время обращения к внешней памяти.

Обращение к внутренней памяти происходит за три цикла микроконтроллера.

Обращение к внешней памяти происходит за три цикла микроконтроллера, однако можно удлинить время обращения на один дополнительный цикл, установив бит SRW (External SRAM Wait State — состояние ожидания внешней памяти) в регистре MCUCR.

Пример подключения внешней оперативной памяти к микроконтроллеру AT90S8515

Фрагмент схемы, в котором реализовано подключение внешней памяти 8Кх8 (8 Кбайта) к микроконтроллеру, приведен на рис. 6.20. Для подключения с минимальным количеством дополнительных элементов пригодны микросхемы памяти, имеющие двунаправленную 8-разрядную шину данных (D0…D7), шину адреса и инверсные входы управления записью и считыванием (WE, ОЕ).

На фрагменте схемы к микроконтроллеру AT90S8515 (DD2) подключена микросхема внешней памяти НМ62256 (DD5) с организацией 32Кх8, из которых используется только 8Кх8, что требует использования тринадцати линий адреса (А0…А12) из пятнадцати имеющихся. Свободные контакты А13 и А14 шины адреса микросхемы DD5 соединены с общим проводом. Замечу, что эти контакты можно было соединить и с линией питания, что никак не сказалось бы ни на работе, ни на программе, физически использовалась бы другая область ячеек микросхемы памяти.

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

Рис. 6.20. Подключение внешней памяти

Установка адреса

По высокому уровню сигнала на контакте ALE микроконтроллера в регистр 74НСТ573 (DD3) производится запись младшего байта адреса, сформированного микроконтроллером на линиях A/D0…A/D7. Записанный в регистр младший байт адреса появляется на выходах регистра, соединенных с адресными входами микросхемы памяти (линии А0…А7).

На контактах порта С микроконтроллера формируется старший байт адреса (линии А8…А15).

Регистр 1533HP33 является функциональным аналогом регистра 74НСТ573, однако они выполнены по разной технологии. Опыт использования регистра 1533ИРЗЗ для фиксации младшего байта при работе микроконтроллера с внешней памятью показал, что при записи — считывании массива (0, 1…., 254, 255) появляются сбои, число которых сокращается при подключении конденсатора (десятки пикофарад) между контактом С регистра и общим проводом, использование же регистра 74НСТ573 полностью решало проблему и без конденсатора. Поэтому следует аккуратно относиться к заменен регистра.

Для качественного тестирования памяти удобно использовать подключение контроллера к компьютеру, используя UART микроконтроллера (Universal asynchronous receiver-transmitter — универсальный асинхронный приемопередатчик).

Считывание данных из внешней памяти

Если производится считывание данных из внешней памяти, то после того, как адрес установлен, линии порта А переводятся в режим ввода данных в микроконтроллер, на линии RD микроконтроллер формирует импульс отрицательной полярности. По сигналу RD шина данных D0…D7 микросхемы памяти DD5 переводится в режим вывода данных, и по линиям A/D0…A/D7 из адресуемой ячейки в порт А микроконтроллера передаются хранимые данные.

Запись данных во внешнюю память

Для записи данных во внешнюю память, после того, как адрес установлен, линии порта А переводятся в режим вывода данных из микроконтроллера, на линии WR микроконтроллер формирует импульс отрицательной полярности. По сигналу WR шина данных D0…D7 микросхемы памяти DD5 переводится в режим ввода данных, и по линиям A/D0…A/D7 из порта А микроконтроллера в адресуемую ячейку внешней памяти передаются данные для хранения.

Программный доступ к оперативной памяти

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

Кроме того, в любую программу обязательно включается файл, определяющий регистры и имена битов микроконтроллера, для которого пишется программа. Для микроконтроллера AT90S8515 — это файл 8515def.inc, для AT90S4433 — файл 4433def.inc. Все файлы с расширением. inc на момент написания главы хранились в самораспаковывающемся файле avr000.exe.

Найти ссылки на файл для установки AVR Studio 3.хх и на файл avr000.exe можно по адресу: .

Инструкции и картинки, размещенные далее, соответствуют версии AVR Studio 3.51.

Создайте директорию, в которой будут размещаться файлы вашего проекта, например, C: \Avr\Try. Запустите скачанный файл avr000.exe, а появившийся в результате распаковки файл 8515def.inc скопируйте в созданную директорию C: \Avr\Try.

Запустите AVR Studio, на экране появится окно AVR Studio (обрамленное сверху синей полосой окно с надписью AVR Studio).

Создадим новый проект: Project | New.

Запись вида «Project | New» обозначает, что надо установить указатель мышки на меню Project, расположенном в первой строке окна AVR Studio, в открывшемся окне установить указатель мышки на слове New и нажать левую клавишу мышки. Далее вместо слов «нажать левую клавишу мышки» будем употреблять слова «щелкнуть» или «выбрать»; если требуется нажатие правой клавиши мышки — будем использовать слова «щелкнуть правой клавишей мышки».

Начальная строка окна содержит его наименование (AVR Studio…), только следующая строка доступна при работе, ее будем считать первой строкой окна.

В появившемся окне Select new project (рис. 6.21):

• введите имя проекта в строке ввода Project name (например, Memory);

• впишите вручную созданную в Windows директорию для проекта C: \Avr\Try в строку ввода Location или найдите эту директорию в дереве каталога, щелкнув по кнопке справа от строки ввода;

• выберите строку AVR Assembler для определения типа проекта в окне Project type;

• щелкните мышкой по кнопке ОК.

Рис. 6.21. Окно Select nez project

Теперь на экране активно окно Project: Memory (рис. 6.22).

Рис. 6.22. Окно Project

Создадим новый файл, который будет содержать программу на Ассемблере:

• щелкните правой клавишей мышки в окне Project: Memory;

• в появившемся окне выберите строку Create New File;

• в строку Name открывшегося окна Create new file (рис. 6.23) введите имя файла программы, например, SRAM.asm, затем щелкните по кнопке ОК.

Рис. 6.23. Окно Create new file

В появившемся окне SRAM.asm наберите следующую программу, не игнорируя знаков препинания в начале строк.

Команды лучше писать строчными буквами, тогда они выделяются цветом. В тексте программы команды и метки выделены прописными буквами только для снижения вероятности ошибок при наборе программы. В AVR Studio 3.51 нет различия между прописными и строчными буквами. Между меткой, командой и ее операндами удобно вставлять знак табуляции вместо пробела, тогда программа лучше читается.

; Программа SRAM.asm

; Обращение к внешней памяти

; =================

.INCLUDE "8515def.inc"

; Вставка содержимого файла 6515def.inc в нашу программу

; Файл 8515def.inc не обязательно располагать в директории

; проекта тогда следует указать и путь к нему, например:

;.include "d:\Def_dir\8515def.inc"

.DEF tmp =r16 ; Регистру r16 присвоить имя tmp

; (значение переменной tmp будет храниться в

; регистре общего назначения r16)

.DEF cnt =r16 ; Переменная cnt — счетчик цикла

; Замечание: одному регистру в программе могут присваиваться

; разные имена, хотя в данном случае в этом и не было

; необходимости, микроконтроллер имеет 32 регистра, но; переменных в программе обычно гораздо больше

.EQU ArSize =10

; Константа ArSize=10 — используем эту константу

; для определения размера массива, записываемого в память

.EOU aArBgn =$0170

; Используем константу аАгВhn как адрес начальной ячейки; для хранения массива

RESET: IN tmp,HCUCR

; Ввод содержимого регистра MCUCR в регистр tmp. Физические

; адреса регистров MCUCR,ZL,ZH и константы SRE и SRW,

; используемые в программе, определены в файле 8515def.inc

; если по директиве. include файл 8515def.inc не будет найден,

; команды с операндами MCUCR,ZL,ZH,SRE и SRW дадут ошибки

          ORI tmp,(1<<SRE)+(1<<SRW)

; Операция V (логическое ИЛИ) содержимого регистра и константы.

; Для работы с внешней памятью надо установить

; биты SRE и SRW в регистре MCUCR (установка SRW — если

; нужно продлить состояние ожидания; для "медленной" микросхемы памяти).

; Открыв файл 8515def.inc, мы обнаружим, что SRE=7, SRW=6,

; значит величина, которую надо занести; в регистр MCUCR в двоичном коде: 11bb bbbb,

; Ь — это значения битов, которые мы не должны изменять; запись (1<<SRE)+(1<<SRW) равна сумме

; единицы, сдвинутой влево SRE (или 7) раз (=1000 0000),

; и единицы, сдвинутой влево SRW (или 6) раз (0100 0000)

; Сумма 1000 0000+0100 0000=1100 0000

; операция V (bbbb bbbb VI100 0000 =11bb bbbb)

; дает нужный результат

          OUT MCUCR,tmp;

Вывод содержимого регистра tmp в регистр MCUCR

          LDI ZL,low(aArBgn)

          LDI ZH,high(aArBgn)

; LDI — операции загрузки в регистр однобайтной константы

; аАгВgn — двухбайтная константа — должна быть загружена

; в пару однобайтных регистров ZH: ZL, которые составляют

; двухбайтный регистр Z. Регистр Z и аналогичные ему двухбайтные

; регистры X и Y используются в операциях с памятью (st,Id….).

; aArBgn=$0170, в ZL загрузится low($0170), т. е., младший байт $70

; в ZH загрузится high($0170), то есть, старший байт $01

; в Z (или в ZH: ZL) образуется двухбайтное слово $01:$70.

; то есть, адрес, выбранный для начальной ячейки массива

           LDI cnt,ArSize; Загрузить в cnt размер массива

NEXT: ST Z+,cnt

; ST — операция записи содержимого регистра cnt в ячейку памяти,

; адрес которой — в регистре Z, знак "+" после Z — значит

; с последующим увеличением адреса в регистре Z на единицу.

; В первом цикле данные занесутся по адресу $0170, a Z = $0171:

; во втором цикле — по адресу $0171, Z = $0172 и т. д.

; в память пишется состояние счетчика циклов

            DEC cnt

; Уменьшить содержимое регистра cnt на единицу

            NOP ; Команда добавляет один пустой цикл

            NOP ; То же

            BRNE NEXT

; Если бит (флаг) Z в регистре состояния процессора SREG

; (не путать бит Z регистра SREG с регистром адреса Z)

; не установлен (не равен) — перейти на команду с меткой NEXT:

; последняя команда, воздействующая на бит Z

; регистра SREG — DEC cnt. Команды N0P введены для

; демонстрации отсутствия их влияния на бит Z

; если вместо них вставить, например, команду INC tmp, влияющую

; на состояние бита Z, работа программы будет нарушена

; Далее — блок считывания данных из памяти:

           LDI ZL,low(aArBgn)

           LDI ZH.high(aArBgn)

           LDI cnt.ArSize

RD_BLK:

           LD tmp, Z+

; Здесь какие-то действия с tmp, например,

; передача в компьютер через UART

           DEC cnt

           BRNE RD_BLK

           RJMP RESET ; Перейти на команду с меткой RESET:

Вы заметили, что имена регистров и константы, определенные во включаемом в программу файле 8515def.inc довольно длинны? Не думайте, что, сокращая используемые имена до одной-двух букв, можно сэкономить время: через пару недель вы не разберетесь в собственной программе! А вот имена MCUCR, SRE, являясь сокращениями английских наименований MCU control register и Static RAM Enable, быстро запоминаются. Те же рекомендации можно предложить и для определяемых вами имен: и в чужой программе можно понять, что ArSize это Array Size (размер массива), a aArBgn — начальный адрес массива.

Отладка программы

Ассемблируем программу: Project|Assemble. Обращайте внимание на наличие горячих клавиш: на строке Assemble указана горячая клавиша F7. Так как эта операция повторяется часто, удобнее пользоваться именно клавишей F7.

При первом ассемблировании автоматически открывается окно Simulator options. В строке Device этого окна надо выбрать микроконтроллер, на который ориентирована программа, в нашем случае это AT90S8515 with external SRAM. Затем нажмите кнопку ОК.

Замечание: для микроконтроллеров с программно-аппаратной поддержкой подключения внешней памяти симулятор программируется как для поддержки внешней памяти (выбрать AT90S8515 with external SRAM), так и для работы только с внутренней памятью (выбрать AT90S8515). Последовательность команд в программе, задающая режим работы с внешней памятью, достаточна лишь для самого микроконтроллера; на работу в режиме отладки в AVR Studio влияет именно выбор устройства в окне Simulator options.

В окне Simulator options следует установить частоту кварцевого резонатора, использующегося вместе с микроконтроллером. Частоту можно либо выбрать из предлагаемого списка, либо ввести вручную, если надо задать точное значение, например, 6,425 МГц. Это позволит симулятору корректно выводить время работы программы не только в циклах микроконтроллера, но и в единицах времени.

Вызвать окно Simulator options вновь можно только после успешного ассемблирования в режиме отладки (переход в режим отладки — по нажатию клавиши F11).

В результате ассемблирования появится окно Project output.

Если программа набрана без ошибок, а файл 8515def.inc находится в рабочей директории проекта — в последней строке окна Project output будет сообщение об отсутствии ошибок (Assembly complete with no errors).

Интересной информацией в этом окне является:

• сообщение о включении файла 8515def.inc в программу;

• предостережение: C: \Avr\Jry\SRAM.asm(15): warning: Register already defined by the.DEF directive — предупреждение о том, что в строке (15) программы директивой. DEF назначается имя регистру, которому уже назначено другое имя; хотя программа невелика, считать строки нет необходимости, достаточно щелкнуть мышкой на любой строке, чтобы увидеть ее номер, а также номер колонки, в правом нижнем углу всего окна AVR Studio: Lnl5, Со15;

• общий объем памяти, занимаемый программой (Total). Обратите внимание на то, что объем занимаемой памяти указывается в словах (words), каждое слово в памяти программы микроконтроллера двухбайтное, поэтому размер программы в байтах будет в два раза больше.

Займемся пошаговой отладкой программы: Debug|Trace into (F11).

Замечание. При отладке (смотрите меню Debug) удобно пользоваться горячей клавишей F11, когда требуется пошаговая отладка каждой команды, если Вы уже отладили часть программы в предыдущих сеансах работы, установите курсор на команду, с которой хотели бы продолжить отладку и нажмите Ctrl+F10.

На левой границе окна против команды, которая будет выполняться, устанавливается стрелка — указатель выполнения команд.

Первая команда:

in tmp.MCUCR

Для просмотра содержимого MCUCR выполнить: View|New IO view, в открывшемся окне IO выполнить: CPU | Control register (щелкнуть по знаку «+» слева в строке CPU, затем щелкнуть по знаку «+» слева в открывшейся строке Control register).

В строке Control register индицируется состояние этого регистра 0x00 и адрес этого регистра в оперативной памяти микроконтроллера 0x35 (это одна из форм записи шестнадцатеричного числа, соответствующая записи $35). Ниже можно увидеть состояние интересующих нас битов SRE и SRW, а при необходимости установить состояние какого-либо бита прямо здесь, например, бита SM (sleep mode), состояние которого не влияет на работу этой программы. Для этого установите галочку (щелкните) в квадратике, помеченном цифрой 4, находящемся правее слова SM. Заметьте, состояние регистра (строка Control register) изменилось на 0x10.

Для проверки состояния регистра tmp выполним:

• Watch|Add watch;

• в строке ввода появившегося окна Watch введите tmp.

Дважды щелкнув по величине, хранимой в регистре (0x00), можно ввести с клавиатуры новое значение. Щелкнув правой клавишей мышки в окне Watch на имени переменной tmp, можно изменить формат вывода состояния регистра на десятичный, добавить новую переменную для просмотра или удалить выделенную переменную.

Теперь, когда отладка началась, можно изменить опции симулятора: Options|Simulator options. После их изменения программу надо вновь ассемблировать (F7) и перейти к пошаговой отладке (F11).

Выполним команду, нажав кнопку F11 на клавиатуре. Состояние переменной tmp (окно Watches) стало равным состоянию регистра MCUCR (0x10). Если окно Watches развернуть, то можно увидеть тип переменной, а также в каком регистре она хранится.

Следующий шаг (нажать F11 — далее просто нажать F11) — в переменной tmp величина 0xD0 или 1101 0000 в двоичном представлении, два старших бита установлены, остальные биты остались без изменения.

Нажать F11 — теперь в MCUCR установлены биты SRE и SRW, работа с внешней памятью разрешена.

Добавим две переменные ZH и ZL в окно переменных (Выполнить Watches|Add (или в открытом ранее окне Watches щелкнуть правой клавишей мышки, в появившемся окне выбрать Add watch), ввести имя ZH, повторить процедуру, введя имя ZL).

Откроем окно просмотра состояния процессора: View | Processor.

Дважды нажать F11, наблюдая за изменением переменных ZH и ZL в окне Watches. В окне Processor наблюдайте состояние Z-register.

Замечание. В окне Watches удобно группировать переменные в соответствии с размещением их в программе. Для одной части программы переменные можно разместить в группе, выбрав страницу окна Watch 1 (смотрите нижнюю строку окна Watches), для другой части программы разместите переменные на странице Watch2 и т. д.

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

Добавить переменную cnt в окно Watches, нажать F11.

Ввод имени константы в окно Watches в AVR Studio 3.51 может привести к неустранимой ошибке (например, константа aArBgn из нашей программы). То же может произойти, если Вы будете удалять переменные, пользуясь клавишей Del. Это внутренние ошибки AVR Studio 3.51.

Следующая часть программы — это цикл записи элементов массива в память. Откроем окно просмотра содержимого памяти (выполнить View|New memory view). В появившемся окне Memory будем просматривать оперативную память (выбрать режим Data в первой строке окна слева). В первой строке окна справа установлен адрес ячейки оперативной памяти 0x0060, расположенной в первой строке области просмотра памяти. Наша программа должна записывать массив, начиная с адреса 0x0170, для того, чтобы в окне Memory было удобно наблюдать занесение данных в память, переместим просматриваемую область памяти так, чтобы область просмотра памяти начиналась адресом 0x0170. Для этого в адресе 0x0060 в первой строке окна справа заменим 0060 на 0170, либо воспользуемся вертикальным скроллером окна Memory.

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

Выбор размера окна, например, для окна Watches, начнем с уменьшения ширины колонок. Установите указатель мышки в первой строке окна на вертикальной разграничительной линии между наименованиями колонок Watch и Value. При попадании на разделительную линию форма курсора изменится, приобретя вид жирной вертикальной линии с горизонтальными стрелками, направленными вправо и влево, как на рис. 6.24. Нажмите левую клавишу мышки и, не отпуская нажатую клавишу, перемещайте вместе с мышкой вертикальную разделительную линию влево, пока колонка не сузится до минимально необходимой ширины. На рисунке третья колонка, не представляющая интереса в данной программе, максимально сужена. Установите указатель мышки на любую стенку или любой угол окна (при этом должны появиться стрелки, направленные в противоположные стороны), манипулируя мышкой так же, как при перемещении разделительной линии, измените размер окна. Если окно развернуто полностью, изменить его размеры можно после нажатия кнопки «Восстановить» (в строке наименования окна средняя кнопка справа, помечена квадратами).

Рис. 6.24. Вид окна

На рис. 6.25 представлены окна, использующиеся при отладке программы SRAM.asm. Размеры и размещение окон позволяют одновременно наблюдать происходящие в них изменения.

Рис. 6.25. Вид окна

Все до сих пор выполнявшиеся команды занимали по одному циклу микроконтроллера, мы выполнили 6 команд, что соответствует состоянию счетчика циклов (Cycle counter 00000006) в окне Processor.

Нажмите F11. В ячейку памяти 0x0170 записалось число $0А (окно Memory). Состояние счетчика циклов увеличилось лишь на два (окно Processor). Для команды ST такое увеличение соответствует обращению лишь к внутренней памяти микроконтроллера. В нашем случае обращения к внешней памяти количество циклов должно было увеличиться на четыре, так как один дополнительный цикл требуется для обращения к внешней памяти и еще один цикл, так как мы задали режим работы с дополнительным циклом ожидания установкой бита SRW в регистре MCUCR.

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

Нажмите F11 четыре раза, наблюдая за состоянием счетчика циклов. При выполнении условия команды BRNE (если бит Z в окне Processor | Flags сброшен) состояние счетчика циклов увеличивается на два. Обратите на это внимание, так как в описании для этой команды указано число циклов 1/2 (один или два). Один цикл потребуется при невыполнении условия команды (смотрите ниже).

Теперь мы перешли ко второму циклу записи массива в память. Ничего нового в каждом отдельном цикле мы не увидим, поэтому воспользуемся сочетанием клавиш Ctrl+F10 (нажать клавишу Ctrl, не отпуская ее, нажать F10).

По каждому нажатию Ctrl+F10 контролируйте переменную cnt в окне Watches. Число нажатий — пока переменная cnt не станет равной единице. При каждом нажатии можно также следить за записью данных в память (окно Memory), адресом, по которому производится запись (окно Processor|Z-register и переменные ZH, ZL в окне Watches).

Если в окне Processor | Stop watch нажать кнопку Clear, то после следующего нажатия Ctrl+F10 правее кнопки Clear появится информация о длительности одного цикла.

Теперь, когда мы снова остановились на команде ST, a cnt=1, воспользуемся клавишей F11.

Дважды нажать F11. Выполнение команды DEC привело к нулевому результату в регистре cnt, поэтому бит Z установлен (окно Processor | Flags).

Дважды нажать F11. Состояние бита Z не изменилось, так как команды NOP не влияют на него.

Нажать F11. Так как бит Z установлен, выполнение команды BRNE не приводит к переходу на метку NEXT, выполняется переход к следующей команде. Состояние счетчика циклов при невыполнении условия команды BRNE (когда бит Z в окне Processor|Flags установлен) увеличивается на единицу.

Замечание. Команду BRNE лучше ставить непосредственно за командой, определяющей, как работать команде BRNE (в нашем случае правильнее было бы разместить перед ней команду DEC cnt).

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

Замечу лишь, что блок будет отлаживаться нормально, так как в память записывалось состояние счетчика циклов cnt. Попробуйте перед началом отладки блока считывания из памяти занесенное в ячейку 0x0170 значение 0А заменить вручную на значение 01 и отладить программу. Выполнится лишь один цикл считывания, так как переменные cnt и tmp хранятся в одном регистре r16.

До начала цикла в регистр r16 командой LDIcnt,ArSize записывается начальное состояние счетчика циклов (10 или $0А). Затем в тот же регистр r16, как в переменную tmp, из памяти считывается значение 01 (команда LDtmp,Z+), далее содержимое регистра r16, как переменной cnt, декрементируется (DECcnt), становясь равным нулю и приводя к завершению цикла.

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

Завершая работу, закрываем окно AVR Studio, в ответ на запрос «Сохранить изменения в проекте Метогу. арr?» нажимаем кнопку Да.

Теперь в директории C: \Avr\Try\ находится файл проекта Метогу. арг, файл программы SRAM.asm, включенный в проект, файл 8515def.inc, а также файл SRAM.hex, который может быть загружен с помощью программатора и соответствующего программного обеспечения во флеш-память микроконтроллера.

При следующем запуске AVR Studio наш проект можно вызвать из меню File. Здесь можно выбрать один из файлов, с которыми мы уже работали. Для нашего проекта следует выбрать строку C: \Avr\Try\Memory.apr. Другой вариант — выполнить Project|Open и в появившемся каталоге выбрать необходимый файл проекта (Метогу. арг). В открывшемся окне Project дважды щелкните на имени файла программы SRAM.asm. Теперь все готово к новому сеансу работы с нашим проектом.

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

В предыдущем примере рассматривалось подключение внешней памяти к AT90S8515. Из четырех портов микроконтроллера порты А и С, а также два контакта порта D заняты обслуживанием внешней памяти.

Как быть, если в дополнение к подключению внешней памяти необходимо организовать передачу полутора — двух десятков управляющих сигналов от микроконтроллера к внешним устройствам? Возможный вариант — использование необходимого количества 8-разрядных регистров (буферов) с обращением к ним, как к ячейкам памяти, расположенным в общем адресном пространстве внешней памяти. На рис. 6.26 — фрагмент схемы, реализующий такое подключение. Данный фрагмент и фрагмент, изображенный на рис. 6.20, являются частями одной схемы, а шина BUS1 является общей для обоих рисунков.

Три адресные линии А13…А15 остались свободными при подключении внешней памяти объемом 8Кх8, используем их для записи данных в три 8-разрядных регистра DD6…DD8.

Использование дополнительной микросхемы дешифратора адреса А13…А15 позволило бы подключить до восьми (23) регистров.

Входные контакты D0…D7 регистров DD6…DD8 подключены к порту А микроконтроллера (линии A/D0…A/D7). К выходным контактам 00…07 регистров можно подключить до 24-х линий внешних устройств (линии В0…В23). Выходы регистров всегда активны и не переводятся в третье состояние, поэтому контакты ЕО регистров соединены с общим проводом.

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

Запись данных в регистры происходит при появлении высокого уровня на их контактах С. Микроконтроллер же формирует низкий уровень записи во внешнюю память (сигнал WR). В схеме для формирования сигнала записи высокого уровня применены элементы ИЛИ-HE (микросхема DD4).

В этой схеме сигнал высокого уровня на линии WB3 для записи данных в регистр DD6 образуется при низких уровнях сигналов на линиях А15 и WR. То есть, чтобы запись произошла в один регистр DD6, необходимо, чтобы на линиях WR и А15 был низкий уровень, на линиях А13 и А14 — высокие уровни.

Одновременное присутствие низких уровней на всех четырех линиях (WR, А13…А15) вызовет параллельную запись данных в три регистра.

Приведенная схема подключения регистров требует тщательного подхода к выбору адресов при написании программы.

Запись в регистр DD6 должна происходить при А15=0, А14=1 и А13=1, то есть, в двоичном коде адрес

регистра DD6: 011х хххх хххх хххх,

регистра DD7: 101х хххх хххх хххх,

регистра DD8: 110х хххх хххх хххх.

Для того, чтобы данные не записывались в регистры, адрес должен иметь вид:

111х хххх хххх хххх, где х — состояние бита (0 или 1) не имеет значения.

Хотя состояния битов х не имеют значения, они должны быть определены. Это значит, что при записи в какой-либо регистр на контактах А0…А12 микросхемы памяти (рисунок bvn_Pic1.gif) будет сформирован адрес, а в ячейку с этим адресом будет произведена запись того же значения, которое посылается в регистр. Надо позаботиться, чтобы ячейка с таким адресом не использовалась для хранения данных.

Удобно определить все биты х равными единице. Тогда адреса регистров примут вид:

адрес DD6: 0111 1111 1111 1111 или $7FFF,

адрес DD7: 1011 1111 1111 1111 или SBFFF,

адрес DD6: 1101 1111 1111 1111 или SDFFF,

а в ячейке памяти с адресом xxx1 1111 1111 1111 нельзя хранить данные, линии адреса А13…А15, соответствующие битам ххх в этом адресе, к микросхеме памяти не подключены.

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

Для хранения данных доступны ячейки памяти с адресами

1110 0000 0000 0000… 1111 1111 1111 1110 или $E000…$FFFE.

В разделе «Интерфейс микроконтроллера AT90S8515 для подключения внешней памяти» была обозначена нижняя граница доступной для безопасного хранения данных области памяти: адрес $0060.

С учетом этого ограничения для безопасного хранения данных доступны ячейки памяти с адресами $E060…$FFFE.

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

Выберем для регистров следующие адреса:

адрес DD6: 0111 1111 1111 1101 или $7FFD,

адрес DD7: 1011 1111 1111 1110 или $BFFE,

адрес DD6: 1101 1111 1111 1111 или $DFFF.

Теперь для безопасного хранения данных доступны ячейки памяти с адресами $E060…$FFFC, а из ячеек с адресами $FFFD, $FFFE и $FFFF можно считать данные, которые были записаны в регистры DD8, DD7 и DD6 соответственно.

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

Состояние выходов регистров может не соответствовать считанным из соответствующих ячеек памяти данным

• из-за неисправности регистров;

• из-за возможного соединения выхода регистра с общим проводом во внешнем устройстве.

Создадим новую директорию C: \Avr\Buff\. В AVR Studio создадим новый проект, назовем его Buffers, воспользуемся уже созданной директорией C: \Avr\Buff\, в проекте создадим новый файл BUFSRAM.asm, в котором напишем следующую программу.

; Программа BUF_SRAM.asm

; обращение к внешней памяти, запись данных в буферы

; ========================

.INCLUDE "С: \Avr\Try\8515def.inc”

; Вставка содержимого файла 8515def.inc, находящегося

; в директории C: \Avr\Try в нашу программу

.DEF tmp=r16 ; Регистру r16 присвоить имя tmp

; (значение переменной tmp будет храниться в

; регистре общего назначения г16)

.DEF cnt = r17 ; Переменная cnt — счетчик цикла

RESET:   IN tmp,MCUCR

             ; Ввод содержимого регистра MCUCR в регистр tmp

             ORI tmp,(1<<SRE)

             ; Установка бита SRE (режим работы с внешней памятью)

             ; Бит SRW не устанавливаем; (работа без дополнительного цикла ожидания)

             OUT MCUCR,tmp

             ; Вывод содержимого регистра tmp в регистр MCUCR

;=========ЗАГРУЗКА ДАННЫХ В БУФЕРЫ

.EQU wrBuf1=$7FFD

             ; Адрес для записи данных в буфер 1 (по схеме — элемент DD6)

.EQU wrBuf2=$BFFE

             ; Адрес для записи данных в буфер 2 (DD7)

.EQU wrBuf3=$DFFF

             ; Адрес для записи данных в буфер 3 (DD8)

.EQU rdBuf1=$FFFD

             ; Адрес для считывания данных из ячейки, соотв. буферу 1

.EQU rdBuf2=$FFFE

             ; Адрес для считывания данных из ячейки, соотв. буферу 2

.EQU rdBuf3=$FFFF

             ; Адрес для считывания данных из ячейки, соотв. буферу 3

             LDI tmp,$AA ; Загрузка константы в tmp

             STS wrBuf1, tmp

; Сохранение содержимого регистра tmp в буфере 1

             LDI tmp, $55 ; Загрузка константы в tmp

             STS wrBuf2, tmp

             LDI tmp,$71 ; Загрузка константы в tmp

             STS wrBuf3, tmp

;======ЧТЕНИЕ / ИЗМЕНЕНИЕ ДАННЫХ В БУФЕРАХ

            LDS tmp, rdBuf1

                  ; Передача данных из ячейки с адресом rdBuf1 в регистр tmp

            ORI tmp, (1<<7)+(1<<6)+(1<<5)+(1<<4); Установка битов 4.. 7

            STS wrBuf1, tmp

; Сохранение содержимого регистра tmp в буфере 1

            LDS tmp, rdBuf2

            ORI tmp, 0b11110000

                  ; Установка тех же битов, что и для буфера 1,

                  ; только константа представлена в двоичной форме,

                 ; для обозначения двоичной константы 1111 0000; перед ней ставят символы "ноль” и “Ь”

            STS wrBuf2, tmp

            ; Сохранение содержимого регистра tmp в буфере 2

            LDS tmp, rdBuf3

            ANDI tmp, 0b11110000 ; сброс тех же битов

            STS wrBuf3, tmp

            ; Сохранение содержимого регистра tmp в буфере 2

; ===== ЗАГРУЗКА В ПАМЯТЬ ПЕРВОГО МАССИВА

.EQU ArSize =10; Размеры массивов

.EQU aArBgn1 =$Е060

            : Используем константу аАrВgn как адрес начальной ячейки для хранения массива 1

            LDI ZL,low(aArBgn1)

            LDI ZH,high(aArBgn1 )

            ; Загрузка в регистр Z адреса начала массива 1

            LDI cnt,ArSize ; Загрузка в cnt размера массива

            LDI tmp,$FF; Загрузка константы в tmp

ARR1:        ST Z+, tmp;

                 ; Запись содержимого регистра tmp в ячейку памяти,

                 ; адрес которой — в регистре Z,

                 ; с последующим увеличением на 1 адреса в регистре Z

                 ; В первом цикле содержимое tmp запишется

                 ; по адресу aArBgn1 ($Е060), во втором цикле —

                 ; по адресу aArBgn1+1 ($Е061) и т. д.

           INC tmp; Увеличить содержимое tmp на единицу

           DEC cnt ; Уменьшить содержимое счетчика циклов на единицу

           BRNE ARR1

; Если бит (флаг) Z в регистре состояния процессора SREG

; не установлен — перейти на команду с меткой ARR1:

; ======ЗАГРУЗКА В ПАМЯТЬ ВТОРОГО МАССИВА

.EQU   аАгВgn2 =$FFFC

           ; Используем константу аАгВgn2 как адрес последней ячейки

           ; для хранения массива 2

           LDI ZL,lоw(аАгВgn2+1)

           LDI ZH,high(aArBgn2+1 )

                 ; Загрузка в двухбайтный регистр Z адреса конечного

                 ; элемента массива 2, увеличенного на единицу

           LDI cot,ArSize ; Загрузка в cnt размера массива

           LDI tmp,$03; Загрузка константы в tmp

ARR2:        ST -Z, tmp;

                 ; Сначала уменьшается на единицу адрес,

                 ; хранящийся в регистре Z (поэтому в Z загружался

                 ; адрес, увеличенный на единицу), затем по новому

                 ; адресу запишется содержимое регистра tmp

                 ; В первом цикле содержимое tmp запишется; по адресу (аАrВgn2+1)-1, то есть, по адресу аАrВgn2

                 ; во втором цикле — по адресу аАrВgn2-1, и т. д.

           DEC tmp ; Уменьшить содержимое tmp на единицу

           DEC cnt ; Уменьшить содержимое счетчика циклов на единицу

           BRNE ARR2

; Если бит (флаг) Z в регистре состояния процессора SREG

; не установлен — перейти на команду с меткой ARR2:

STOP: RJMP STOP ; Зацикливание программы

                          ; (перейти на команду с меткой STOP:)

Ассемблируем программу (клавиша F7). При обнаружении ошибок проверьте правильность набора программы в строках, содержащих ошибки — номера строк с ошибками и комментарии к этим ошибкам выводятся в окне Project output, появляющемся после ассемблирования.

Директивой. INCLUDE в программу вставлен файл 8515def.inc, использовавшийся в предыдущем примере и находящийся с проектом в разных директориях. Поэтому ассемблирование пройдет нормально, если файл c: \avr\try\8515def.inc еще существует.

Начнем отладку программы, нажав клавишу F11. В появившемся окне Simulator options выберем микроконтроллер: Device | AT90S8515 with External SRAM.

Для контроля отладки вызовем окна переменных, процессора и памяти: View | Watches, View | Processor и View | New Memoiy view.

В окно Watches введем переменные tmp и cnt.

Первые три команды были отлажены в проекте Memory.apr, поэтому переместим курсор на первую команду ЗАГРУЗКИ ДАННЫХ В БУФЕР: LDItmp,$AA и нажмем Ctrl+F10.

Нажмем F11 — в переменную tmp (смотрите в окне Watch) загрузилась константа $АА.

В окне просмотра памяти Memoiy перейдем на адрес S7FFD (адрес wrBuf1), для этого в окне ввода, расположенном в первой строке окна справа, заменим 0x0060 на 0x7FFD. Жмем F11 — в ячейке с адресом 7FFD появились данные АА.

Таким же способом переходим к ячейке SBFFE (wrBu£2), дважды жмем F11, проверяем состояние ячейки, оно равно 55.

Повторяем действия для ячейки SDFFF — ее состояние станет равным 71.

Теперь указатель выполнения команд находится на первой команде ЧТЕНИЯ/ИЗМЕНЕНИЯ ДАННЫХ В БУФЕРАХ:

LDS tmp, rdBuf1

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

Возможный выход — до выполнения команды LDS вручную загрузить в ячейки с адресами rdBuf1, rdBuf2 и rdBuf3 данные, которые должны были в них загрузиться при записи в буферы 1…3 ($АА, $55 и $71 соответственно).

Итак, в окне Memory перейдем к ячейке с адресом $FFFD (rdBuf1). В этом случае удобнее воспользоваться скроллером, переместив его в самый низ, так как интересующие нас ячейки с адресами $FFFD, $FFFE, $FFFF (rdBuf1, rdBuf2 и rdBuf3 соответственно) являются тремя последними ячейками, которые могут отображаться в окне Memory.

Щелкнем на третьей от конца ячейке — ее адрес в виде 0xFFFD отобразится в окошке адреса (справа в первой строке окна Memory). Вместо содержимого этой ячейки (скорее всего 00) введем АА, курсор автоматически перейдет на следующую ячейку с адресом 0xFFFE, введем в нее значение 55, в ячейку OxFFFF — значение 71.

Второй вариант (здесь не реализован, проверьте его отладку самостоятельно): после команд записи типа

STS wrBuf1,tmp

добавлять команды

STS rdBuf1.tmp

то же для буферов 2 и 3. Что при этом произойдет?

В реальной схеме:

• при выполнении первой из команд запись произойдет как в буфер, так и в микросхему памяти (подробности смотрите в описании схемы);

• вторая команда еще раз запишет те же данные в ту же ячейку микросхемы памяти.

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

При этом программа несколько удлиняется. Однако после отладки дополнительные команды можно удалить.

Продолжим отладку. Следя за ячейками памяти wrBuf1 wrBuf1 и wrBu3, а также за переменной tmp, выполним 9 команд (9 нажатий F11).

Теперь указатель выполнения команд находится на первой команде ЗАГРУЗКИ В ПАМЯТЬ ПЕРВОГО МАССИВА.

Интерес в записи первого массива в память представляет первый цикл.

В окне Memoiy перейдем к ячейке с адресом 0хЕ060 (aArBgn1). Переведем курсор на команду NCtmp и нажмем Ctrl+F10. В ячейке с адресом 0хЕ060 появился байт данных FF, соответствующий значению переменной tmp.

Нажмем F11 — выполнение команды INC привело к тому, что переменная tmp стала равна нулю, в результате чего установился бит Z (окно Processor). Поэтому если бы команда INC стояла перед командой BRNE, выполнился бы лишь один цикл записи элементов массива в память.

Замечание: SFF+1 = $100, результат — двухбайтная величина, но поскольку регистр может хранить только один байт, в нем остается младшая часть, то есть, ноль. Аналогичным образом $00-1 = $FF, при этом вычитание происходит как бы из двухбайтного числа $100.

Жмем F11 — выполнение команды DEC уменьшило до 9 значение переменной cnt, то есть, результат не нулевой, поэтому флаг Z сбросился (окно Processor).

Отладка аналогичного цикла уже комментировалась, поэтому можно установить курсор на команде с меткой ARR2: и нажать Ctrl+F10. Заметьте, в регистр Z (окно Processor) записался адрес OxFFFD, соответствующий константе aArBgn2+1.

В окне Memoiy перейдем к ячейке 0xFFFD, жмем F11: запись содержимого регистра tmp произошла в ячейку с адресом OxFFFC (aArBgn2), как и было необходимо.

Отладка цикла не требует комментариев.

Установим курсор на последней команде программы и нажмем Ctrl+F10. Проконтролируйте содержимое ячеек памяти, в которые записался массив 2.

Что будет, если убрать последнюю команду? Вообще-то для проверки удобнее заменить ее командой NOP. Ассемблируем программу, нажмем FI 1, установим курсор на последней команде NOP. В окне Processor | Cycle counter заметим число циклов процессора (153). Нажмем F11: выполнение программы опять началось с первой команды, но посмотрите на число циклов процессора: их стало 4206! После того, как все команды нашей программы были извлечены из памяти программ и выполнены, продолжался поиск команд в свободной части памяти программ, при переходе к новой свободной ячейке счетчик циклов процессора увеличивал свое состояние, а вместе с ним и счетчик команд (Processor | Program counter). Максимальное значение, которое может храниться в этом счетчике для микроконтроллера AT90S8515, равно 4095, следующее значение — снова ноль, поэтому и произошел переход в начало программы, а к содержимому счетчика циклов процессора добавились циклы загрузки отсутствующих команд.

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

Stop: ijmpStop

Автор: Баранов Вадим Николаевич (E-mail: ).

Подключение внешней памяти 512 Кбайт к микроконтроллеру AT90S8535

На момент написания книги лишь два микроконтроллера серии АТ90 были снабжены интерфейсом подключения внешней памяти: AT90S4414 и AT90S8515. К тому же первый из них уже снят с производства.

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

Здесь представлен возможный вариант подключения внешней памяти большого объема к микросхеме AT90S8535. Микроконтроллер снабжен встроенным 10-разрядным АЦП с восемью входами, коммутируемыми программно. В качестве входов АЦП в микроконтроллере используются контакты порта А, поэтому не будем задействовать этот порт для организации связи с внешней памятью.

На рис. 6.27 представлен фрагмент схемы, реализующий подключение микросхемы статической памяти K6T4008CIB-GB55 (DD5) к микроконтроллеру AT90S8535 (DD6).

Рис. 6.27. Подключение микросхемы статической памяти К6Т4008CIB-GB55

Микросхема памяти K6T4008CIB-GB55 производства фирмы Samsung Electronics по своим функциям не отличается от аналогичной микросхемы НМ62256, использованной в схеме подключения внешней памяти к микроконтроллеру AT90S8515 (смотрите предыдущий пример). Процедура записи и считывания данных для обеих микросхем одинакова.

Одинаково функционируют их двунаправленные 8-разрядные шины данных (контакты IO0…IO7), а также линии

• записи (контакт WR);

• управления шиной данных (контакт ОЕ);

• выбора микросхемы (контакт CS).

Отличие состоит в объеме памяти: 19-разрядная шина адреса (контакты А0…А18) микросхемы K6T4008CIB-GB55 обеспечивает обращение к 219 ячейкам памяти, это значит, что в микросхеме можно хранить до 512 Кбайт данных (512К х 8).

Описание схемы

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

В исходном состоянии микроконтроллер устанавливает высокие уровни на линиях WR и RD, низкие уровни на линиях BUF1 и BUF2.

Программно память как бы разбита на 8 страниц по 64 Кбайт в каждой. Три бита адреса из 19-ти микроконтроллер формирует на линиях AR0…AR2, выбирая одну из страниц памяти.

Адрес внутри страницы формируется двумя 8-разрядными регистрами 74НСТ573 (DD3 и DD4).

С этой целью на шину данных (линии В0…В7) через порт В микроконтроллера (контакты РВ0…РВ7) выводится младший байт адреса, после чего микроконтроллер устанавливает высокий уровень на линии BUF2 для записи младшего байта адреса в регистр DD3. Затем на линии BUF2 устанавливается низкий уровень.

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

Теперь на линиях А0…А18, следовательно, и на контактах А0…А18 микросхемы памяти адрес ячейки памяти установлен, можно производить запись или считывание.

Некоторое смущение может вызвать то, что к контактам микросхемы памяти А0…А19 подсоединены линии адреса с другими именами (к контакту А17, например, линия А14). Это сделано для удобства трассировки печатной платы, на которой размещены детали схемы. Для микросхемы памяти безразлично, в какой последовательности выбираются ячейки, последовательность же выбора при записи и при считывании будет одинаковой.

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

Запись в ячейку

После формирования адреса микроконтроллер выводит через порт В на шину данных В0…В7 байт информации. Затем микроконтроллер устанавливает низкий уровень на линии WR, что приводит к записи байта информации в ячейку памяти. После записи на линии WR устанавливается высокий уровень.

Считывание из ячейки

После формирования адреса микроконтроллер переводит порт В в режим ввода данных в микроконтроллер. Затем на линии RD микроконтроллер устанавливает низкий уровень, что приводит к выводу данных из адресуемой ячейки памяти на шину данных В0…В7. Микроконтроллер через порт В считывает байт данных, установленный на линиях В0…В7, после чего устанавливает высокий уровень на линии RD. Обратите внимание на резисторы R43 и R44.

При программировании линии портов микроконтроллера находятся в высокоимиедансном состоянии, а поскольку входы WE и ОЕ микросхемы памяти также имеют высокий импеданс, наводки на подключенных к ним линиях могут привести как к записи в произвольную ячейку памяти, так и к выводу данных из произвольной ячейки на линии В0…В7. Последняя ситуация опасна, так как запись программы в микроконтроллер производится с использованием линий В5…В7 (разъем для подключения программатора ХР1 изображен справа на схеме). Поэтому возможны сбои при программировании (наблюдались в отсутствие резистора R43), выход из строя программатора (маловероятно из-за достаточной стойкости используемой в нем микросхемы) или микросхемы памяти (вполне возможно).

Установка резистора R43, подключенного к источнику питания +5 В, создает в описанной ситуации на линии RD высокий уровень и переводит выходы Ю0…Ю7 микросхемы памяти в высокоимпедансное состояние, исключая подобную ситуацию, резистор R44 устраняет возможность хаотичной записи при программировании. В обычном режиме работы эти резисторы не мешают микроконтроллеру управлять микросхемой памяти.

Программа записи данных в ОЗУ 512 Кбайт

В рабочей программе, взятой за основу для нашего примера, АЦП микроконтроллера производил группу преобразований, результаты которых записывались во внутреннюю оперативную память микроконтроллера. Результаты группы преобразований обрабатывались и также записывались во внутреннюю оперативную память в виде массива размером в 45 байтов, начиная с ячейки с адресом аРаск. Этот массив переносился во внешнюю память для хранения. АЦП выполнял новую группу преобразований, которые тем же способом обрабатывались, а очередной массив размером в 45 байтов добавлялся во внешнюю память. Процесс продолжался до заполнения страницы внешней памяти (64 Кбайт), после чего вся информация со страницы внешней памяти через микроконтроллер передавалась в компьютер.

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

Создадим новую директорию c: \Avr\Ram512, поместим в нее файл 8535def.inc, полученный при распаковке файла avr000.exe (ищите его там же, где распаковался файл 8515def.inc). В директории c: \Avr\Ram512 создадим проект Ram512 с новым файлом программы Ram512.asm, в который следует перенести следующую программу.

; Подключение 03У512К к AT90S8535

;================================

.include "с: \atmel\8535def.inc"

.equ RamH =$16а ; Адрес ячейки внутренней SRAM, хранящей

; старший байт адреса страницы внешней памяти

.equ RamL =$16b ; Адрес ячейки внутренней SRAM, хранящей

; младший байт адреса внешней памяти

; Три старших бита адреса внешней памяти

; (номер страницы внешней памяти):

.equ Ar0 =PC0

.equ Ar1 =PC1

.equ Ar2 =PC2

.equ Wr =PC4 ; Управление записью во внешнюю память

.equ Rd =PC3 ; Управление считыванием из внешней памяти

.equ Buf1 =PD3 ; Управление записью в буфер 1

.equ Buf2 =PD4 ; Управление записью в буфер 2

.equ aADC =$60 ; Адрес для переноса массива из внешней памяти во внутреннюю SRAM

.equ aPack =$Ь0 ; Адрес 45-байтового массива во; внутренней SRAM микроконтроллера

.def tm =r16

.def cnt =r19

;====

RESET:

ldi    tm, (1<<Wr)+(1<<Rd) ; Устанавливаем высокие уровни

        out PORTC,tm ; на линиях WR и RD

        ldi tm,$ff ; контакты порта С -

        out DDRC,tm ; в режиме выходов

        clr tm ; На линиях порта D низкие уровни

        out PORTD,tm ; (в том числе BUF1, BUF2=0)

        ldi tm,$ff ; контакты порта D -

        out DDRD,tm ; в режиме выходов

        ser tm ; Установка регистра tm (tm=$ff)

        out DDRB,tm ; вывод в порт DDRB содержимого tm

                           ; контакты порта В — в режиме выходов

        ldi tm,low(RAMEND)

; Стек — начиная с конца внутренней SRAM

        out SPL,tm

        ldi tm,high(RAMEND)

        out SPH,tm

; Загрузка во внутреннее ОЗУ микроконтроллера,

; начиная с ячейки с адресом аРаск

; 45-ги байтов массива: $20, $21, $22….

        ldi XL,low(aPack)

        ldi XH,high(aPack)

        ldi tm,$20

        ldi cnt,45

StRAM:

st     X+,tm

        inc tm

        dec cnt

        brne StRAM

; Перепишем этот массив во внешнюю память на страницу № 2

; (старшие биты адреса внешней памяти AR2 AR1 AR0 = 101)

        in tm,PORTC

        andi tm, $ff-((1<<AR2)+(1<<AR1)+(1<<AR0))

                ; очистка битов AR2, AR1, AR0

                ; 1111 1000 = $ff-@K0D = ((K<AR2)+(1«AR1)+(1«AR0))

        ori tm, (1<<AR1)

                ; Установка бита AR1(aдpec страницы 010

                ; (страница № 2))

        out PORTC,tm ; Вывод в порт С содержимого tm

        clr tm ; Очистка содержимого tm (tm=0)

        sts RamH,tm ; Сохранение в ячейке RamH содержимого tm

        sts RamL,tm

        rcall St45bt ; Вызов подпрограммы St45bt

; Скопируем массив из внешней памяти на странице № 2

; (старшие биты адреса внешней памяти AR2 AR1 AR0 = 101)

; во внутреннее ОЗУ микроконтроллера

         clr tm ; Очистка содержимого tm (tm=0)

         sts RamH,tm ; Сохранение в ячейке RamH содержимого tm

         sts RamL,tm

         rcall DOutPrp

cycle: rjmp cycle

; Подпрограммы:

; ==============

Копирование массива из внутренней во внешнюю память

St45bt:

         ldi ZL,low(aPack)

         ldi ZH,high(aPack)

         ldi cnt,45

mSt45:

         rcall SetAddr

         rcall DataSt

         dec cnt

         brne mSt45

         ret

; ======

; Подпрограмма копирования 45 байтов из внешней памяти во внутреннее ОЗУ

DOutPrp:

ldi     ZL,low(aADC)

         ldi ZH,high(aADC)

         ldi cnt,45

DOut1:

         rcall SetAddr

         rcall DataLd

         dec cnt

         brne DOut1

         ret

; ========

; Подпрограмма установки

SetAddr:

Ids     XL,RamL ; Скопировать содержимое ячейки RamL в XL

         Ids XH,RamH

         out PORTB,XL ; Вывести в порт В содержимое XL

         nop ; Задержка 156 нc

         nop ; Задержка 156 нc

         sbi PORTD,Buf1 ; Установить бит Buf1 порта D

         nop

         nop

         cbi PORTD,Buf1 ; Сбросить бит Buf1 порта D

         nop

         nop

         out PORTB,XH ; Вывести в порт В содержимое ХН

         nop

         nop

         sbi PORTD,Buf2 ; Установить бит Buf2 порта D

         nop

         nop

         cbi PORTD,Buf2 ; Сбросить бит Buf2 порта D

         adiw XL,1 ; Увеличить содержимое пары XH:

         sts RamH,XH ; Сохранить содержимое ХН в ячейке RamH

         sts RamL,XL

         ret ; Возврат из подпрограммы

; =========

Подпрограмма копирования байта из внутреннего ОЗУ во внешнюю память

DataSt:

         Id tm,Z+

         out PORTB,tm

         nop

         nop

         cbi PORTC,Wr

         nор

         nор

         sbi PORTC,Wr

         ret

; ==========

; Подпрограмма копирования байта из внешней памяти во внутреннее ОЗУ

DataLd:

         clr tm ; Очистка tm

         out DDRB,tm ; Контакты порта В в режиме входов

         cbi PORTC,Rd ; Сбросить бит Rd порта С

         nор

         nор

         in tm,PINB ; Считать данные на контактах порта В в tm

         sbi PORTC,Rd; Установить бит Rd порта С

         St Z+, tm; Сохранить содержимое tm в ячейке ОЗУ

         ser tm ; Установить tm

         out DDRB,tm ; Все контакты порта В в режиме выходов

         ret

Отладка программы

После ассемблирования (клавиша F7) при отсутствии ошибок приступим к отладке (клавиша F11), в появившемся окне Simulator options | Device выберем микроконтроллер AT90S8535.

Рассмотрим определение констант в программе. Ячейки внутренней памяти микроконтроллера с адресами RamH и RamL хранят старший и младший байты адреса внешней памяти. То есть, в этих ячейках будет отражаться информация об адресе внешней памяти, которая выводится в буферы адреса (регистры DD3 и DD4 по схеме электрической).

Константа AR0 равна константе РС0; РС0 в свою очередь определена в файле, вставляемом в программу директивой. include.

Остальные константы, не определенные в тексте программы, также определяются в файле 8535def.inc.

Нажмем F11. Выполним View | New 10 view, в открывшемся окне выполним PortB | +, здесь же выполним PortC | + и PortD | + для контроля информации, выводимой на контакты портов В, С и D, а следовательно и на шину данных нашей схемы (порт В), и на линии управления внешней памятью (соответствующие контакты портов С и D).

Замечание. Имена констант в программе соответствуют именам линий управления внешней памятью на электрической схеме.

Переносим курсор на команду ldiXL,low(aPack) блока загрузки внутренней SRAM массивом в 45 байтов и жмем Ctrl+F10.

Произошла инициализация портов В, С и D, а также установлен начальный адрес стека.

Проверяем состояние линий портов в окне 10. Порт В инициализирован для вывода данных из микроконтроллера. Линии РС4 и РСЗ порта С установлены (в электрической схеме на соответствующих линиях WR и RD будут высокие уровни). На линиях PD3 и PD4 порта D (соответствуют линиям BUF1, BUF2 в схеме) — низкие уровни. В этом же окне 10 выполняем CPU | + и проверяем состояние регистров указателя стека SPH и SPL.

Для имитации массива, получаемого в результате одной группы преобразований АЦП, в область памяти, начинающуюся адресом аРаск, записывается простой массив размером в 45 байтов.

Вызовем окно Memory, выполнив View | New memory view, перенесем курсор на команду, следующую за циклом записи массива (команда intm,PINC) и нажмем Ctrl+F10. Во внутренней памяти микроконтроллера создан массив, первый байт которого ($22) находится в ячейке с адресом аРаск (0х00В0).

Жмем FI 1 пять раз, проверяем состояние порта С в окне 10. Линии РС2 PCI РС0 приобрели состояние 010, таким же будет состояние трех старших линий адреса (AR2 AR1 AR0), определяющих номер рабочей страницы внешней памяти.

Трижды жмем F11, очищаем старший и младший адреса внешней памяти в ячейках RamH, RamL.

Указатель выполнения команд остановился на команде вызова подпрограммы переноса данных во внешнюю память. В окне Memory перейдем в самый конец внутренней памяти, переместив скроллер окна вниз до конца, выполним View | Processor, в открывшемся окне процессора заметим состояние счетчика команд (0x00001D — это номер команды rcall, которая будет выполняться) и указателя стека (=0x000025F).

Нажмем F11. Указатель выполнения команд перескочил на команду с меткой St45bt: (окно программы), в счетчике команд — номер команды с этой меткой. Во внутренней памяти по адресу 25F находится двухбайтный номер команды, на которую надо вернуться после выполнения подпрограммы (001Е — именно эта команда cледует за командой с номером 001D, и именно адрес 001Е загрузится в счетчик команд после выполнения подпрограммы). Указатель стека сместился на две ячейки влево (25D), если внутри нашей подпрограммы будет подпрограмма следующего уровня вложения, в ячейки 25D:25C запишется двухбайтный адрес возврата из этой подпрограммы.

Устанавливаем курсор на команду с меткой mSt45:, жмем Ctrl+F10. Регистр Z содержит адрес начала массива во внутренней оперативной памяти микроконтроллера. Указатель выполнения команд находится в начале цикла побайтного копирования данных из внутренней оперативной памяти микроконтроллера во внешнюю память. Отличие цикла от уже рассматривавшихся в этой главе циклов — в наличии двух вызовов подпрограмм. Первый из них: rcall SetAddr записывает младший и старший байты адреса внешней памяти в два буфера (по электрической схеме — в регистры DD3 и DD4). Второй вызов подпрограммы rcallDataSt извлекает из ячейки внутренней оперативной памяти микроконтроллера (внутреннего ОЗУ) байт данных и записывает его в ячейку внешней памяти.

Наблюдая содержимое счетчика команд и указателя стека в окне Processor, а также изменения в окне Memory (последняя строка, начинающаяся адресом 0x025F), нажимаем F11, указатель выполнения команд перемещается на первую команду подпрограммы установки адреса внешней памяти.

Подпрограмма установки адреса SetAddr:

• из ячеек RamH, RamL внутреннего ОЗУ в регистры ХН, XL загружаются младший и старший байты адреса внешней памяти (две команды Ids);

• младший байт адреса выводится на шину данных В0…В7 (смотрите схему) через порт В (команда out PORTB,XL);

• после задержки, необходимой для завершения переходных процессов (две команды пор), на линии BUF1 устанавливается высокий уровень (команда sbi PORTD,BUFl), по которому в регистр DD4 записывается информация, установленная на шине данных;

• после задержки (две команды пор) на линии BUF1 устанавливается низкий уровень (команда cbi PORTD,BUFl), запись в регистр завершена;

• старший байт адреса выводится на шину данных В0…В7 через порт В (команда out PORTB,ХН);

• после задержки на линии BUF2 устанавливаегся высокий уровень (команда sbi PORTD,BUF2), по которому в регистр DD3 записывается информация, установленная на шине данных;

• после задержки (две команды пор) на линии BUF2 устанавливается низкий уровень (команда cbi PORTD,BUF2), завершая запись в регистр DD3;

• команда adiw увеличивает содержимое пары регистров XH: XL, теперь в них хранится адрес следующей ячейки внешней памяти, запись в нее будет произведена в следующем цикле установки адреса.

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

В регистре ввода-вывода PORTC хранится то, что программа вывела в порт, в регистре ввода — вывода PINC — то, что присутствует на контактах порта (так в PORTC можно вывести байт 11111111, контакты микроконтроллера РСО и РС2 соединить с общим проводом, тогда состояние PORTC останется без изменений, состояние PINC станет равным 11111010). Для команд типа out PORTC,tm в окне Ю информация появляется сначала в строке Port С Data (PORTC), а после выполнения следующей команды — в строке Input pins (PINC).

Перед входом в цикл, начинающийся меткой mSt45:, в регистр Z был занесен адрес начала массива во внутреннем ОЗУ микроконтроллера.

Подпрограмма копирования байга из внутреннего ОЗУ DataSt.

• первая команда подпрограммы (Id tm,Z+) копирует первый байт массива из ячейки, адрес которой хранится в регистре Z, в регистр tm, затем адрес, хранящийся в регистре Z, увеличивается на единицу, теперь Z указывает на следующий элемент массива;

• элемент массива выводится на шину данных через порт В (команда out PORTB,tm);

• после задержки (команды пор) на линии WR устанавливается низкий уровень (команда cbi PORTC,Wr), информация, установленная на шине данных, записывается в ячейку внешней памяти, адрес которой был определен при выполнении подпрограммы SetAddr;

• после задержки (команды пор) на линии WR устанавливается высокий уровень (команда sbi PORTC,Wr), копирование байта данных во внешнюю память завершено;

• выполняется возврат из подпрограммы.

Выполните один раз пошаговую отладку подпрограммы DataSt. Отладка цикла, в котором находится эта подпрограмма, не представляет интереса.

Отследить состояние внешней памяти с занесенным в нее массивом мы не сможем, так как наш вариант подключения внешней памяти к микроконтроллеру не предусмотрен симулятором AVR Studio.

По этой же причине мы не сможем полностью провести отладку копирования массива из внешней памяти во внутреннее ОЗУ микроконтроллера, однако можно воспользоваться приемами, которые предлагались в предшествующем примере:

• отразить массив в области внутреннего ОЗУ специально для отладки;

• или после команды ввода в порт информации с шины данных вводить эти данные в порт вручную.

Воспользуемся вторым приемом при отладке подпрограммы копирования данных из внешней памяти во внутреннее ОЗУ микроконтроллера DataLD.

Переместите курсор на команду rcall DoutPrp и нажмите Ctrl+F10.

Первая пара команд подпрограммы загружает в регистр Z адрес новой области внутреннего ОЗУ микроконтроллера для массива, копируемого из внешней памяти.

Далее выполняется команда загрузки счетчика циклов, затем цикл, начинающийся меткой Dout1:.

Цикл не отличается от ранее рассматривавшихся циклов. Подпрограмма SetAddr рассматривалась ранее. Поэтому, доходя до команды вызова этой подпрограммы, нажимайте клавишу F10, чтобы не производить повторную отладку.

В результате выполнения подпрограммы SetAddr на линиях адреса внешней памяти сформирован адрес.

Подпрограмма копирования данных из внешней памяти во внутреннее ОЗУ DataLd.

В порт DDRB выводится нулевое значение, переводящее порт В на прием информации (все контакты порта В работают как входы).

На линии RD устанавливается низкий уровень (команда cbi PORTC,Rd), поэтому из ячейки внешней памяти, адрес которой сформирован при выполнении подпрограммы SetAddr, на шину данных выводится информационный байт.

После задержки (команды пор) через порт В с шины данных в регистр микроконтроллера tm считывается байт информации (команда in tm, PINB).

На линии RD устанавливается высокий уровень (команда sbi PORTC,Rd), контакты микросхемы памяти IO0…IO7 переводятся в высокоимпедансное состояние.

Данные из регистра tm переносятся в ячейку внутреннего ОЗУ микроконтроллера, адрес которой хранится в регистре Z, после чего содержимое Z увеличивается на единицу, теперь регистр Z указывает на следующую ячейку ОЗУ, в которую будет произведена запись в следующем цикле (команда stZ+,tm).

Порт В переводится в режим вывода данных (все контакты порта — выходы).

Возврат из подпрограммы.

Переводить порт В из режима передачи данных (выход) в режим приема (вход) только на время выполнения подпрограммы DataLd удобно, так как в других подпрограммах порт В должен всегда находиться в режиме передачи.

При отладке подпрограммы DataLd перед командой in tm,PINB для имитации ввода данных установите какие-нибудь флажки в строке Input pins для окна Ю | Port В, тогда соответствующие данные будут занесены в ячейку внутреннего ОЗУ микроконтроллера.

В заключение следует заметить, что приведенные программы адаптированы для лучшего понимания работы устройства, программы и отладчика. Однако считать их завершенными, а файлы hex, полученные при их ассемблировании, загружать в микроконтроллер не следует. В реальной рабочей программе сторожевой таймер (Watch dog timer) должен периодически программно сбрасываться командами wdr, размещенными по всей программе, иначе программа будет регулярно сбрасываться этим таймером, возвращаясь на метку RESET. В приведенных примерах программ не размещены векторы прерываний, являющиеся неотъемлемой частью практически любой программы. Перечисленные темы в этой главе не рассматриваются.

Автор: Баранов Вадим Николаевич (E-mail: ).