Вы находитесь на странице: 1из 39

===================================== Справочник по программированию «Bascom-AVR» ==

Создание микропроцессорной системы

Теория
На первом этапе создания микропроцессорной системы до начала программирования необходимо решить
несколько важных задач:
а) разработать план использования ресурсов процессора, определив: какие задачи решаются программными
средствами, а какие с помощью внутренних аппаратных средств микроконтроллера. Определив необходимый и
достаточный состав внутренних устройств микроконтроллера, производится выбор внешних устройств, с которы-
ми будет работать микроконтроллер. Все это, этап разработки электрической схемы микропроцессорного устрой-
ства. Именно на этом этапе происходит изучение документации на все применяемые компоненты, определение
достаточности их нормируемых и функциональных возможностей для решения поставленной задачи. В круг изу-
чаемых устройств входит и процессор, даже при условии, что используемый компилятор обеспечивает «слепую»
работу с его периферией;
б) выбрать модель процессора, обеспечивающий внутренними ресурсами, разработанный план действий.
Кажется, что первая и вторая задачи едины, но тут надо вспомнить о дополнительных требованиях к разрабаты-
ваемому устройству, которые могут стать приоритетными – экономичность, напряжение питания, способ про-
граммирования (записи программы), возможность перепрограммирования, поддержка компилятором и т.п. Здесь
необходимо заложить некоторую избыточность производительности и, особенно, размеров памяти, обусловлен-
ную не стопроцентной эффективностью компилированного кода;
в) выбрать средство аппаратной отладки, отвечающее задаче, решаемой отлаживаемой программой и ее па-
раметрам. Если устройство отладки забирает у системы какие-либо аппаратные или программные ресурсы, то
нужно попытаться обойтись без них, или, по крайней мере, использовать их наименьшей степени и в последнюю
очередь. Например, порты микроконтроллера, используемые для последовательного программирования, не нужно
без крайней необходимости использовать в схеме разрабатываемого устройства;
г) провести предварительное моделирование и проверку самых важных алгоритмов обработки данных или
управления физическими объектами с использование специализированных программ: “Micro-Cap”, “Mathcad”,
“Quick-Basic” и т.п.;
д) определить значения требуемой производительности систем ввода-вывода, вычислений, преобразований,
реагирования на внешние события. Оценить возможность решения этих задач с необходимой скоростью, напри-
мер, путем написания и запуска (даже в отладчике) тестовых программ с интересующими фрагментами;
е) программы, управляющие реальными устройствами, особенно вновь применяемыми написать отдельно в
виде эскизов. Эскизы включить в состав тестовых программ, проверить и отладить на реальных объектах
Время исполнения некоторых типовых операций микроконтроллера AVR на частоте генератора 8 МГц
указано в таблице. Приведенные числовые значения приблизительны, так как время исполнения операций во мно-
гом зависит от значения операнда.

Действие Время исполнение, мкс


Сложение и вычитание чисел в формате с плавающей точкой (Single/Double) 20/50
Умножение чисел в формате с плавающей точкой (Single/Double) 50/150
Деление чисел в формате с плавающей точкой (Single/Double) 200/800
Вычисление математической функции в формате (Single/Double) 1000/5000
Считывание (загрузка) 12-16 разр. АЦП (ЦАП) с программ. послед. интерфейсом 20
Считывание (загрузка) 12-16 разр. АЦП (ЦАП) с аппаратным послед. интерф. 10
Считывание (загрузка) 12-16 разр. АЦП (ЦАП) с параллельным интерфейсом. 1-2
Опрос клавиатуры в 16 кнопок 50-100
Запись байта (знака) в символьный модуль (4-битном/8-битной режиме) 50/50
Запись символа (изображения или фигуры) в графический модуль 5(200 – 1000)
Вывод сообщения длиной 10 символов со скоростью 9.6 Кбод 13 мс (1.1 мс на символ)
Формирование и преобразование текстовых строк 10-20 мкс на символ
Преобразование числа в формате с плавающей точкой (Single/Double) в строку 200/1000
Тоже в форматированную строку 500/10000
Запись (считывание) четырехбайтового числа в EEPROM с интерфейсом I2C 10000(500)
Запись (считывание) четырехбайтового числа в внутреннее EEPROM 20000(10)
Измерение напряжения с помощью внутреннего АЦП 100 - 200
Измерение частоты с разрешающей способностью не менее 10-4 10-500 мс

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

============================================================================= 1
===================================== Справочник по программированию «Bascom-AVR» ==

Порядок подключения внешних устройства (приемников и источников управляющих сигналов и данных) к


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

Задача микро- Рекомендуемые порты Используемые ресурсы


контроллера
Формирование Только порты с функцией вы- Таймеры 0, 1 или 2 (имеющийся в используемой модели
частоты (вари- хода по совпадению: микроконтроллера) в режиме самозагрузки. Достоинства:
ант 1) OC0A, OC0B (Timer0), широкий диапазон частот и чистый сигнал. Процессор неза-
OC1A, OC1B (Timer1), гружен.
OC2A, OC2B (Timer2)
Формирование Любой Любой таймер, запрограммированный вызывать прерывание
частоты (вари- каждые полпериода, генерируемой частоты. В прерывании
ант 2) производится инверсия порта. Невозможно формировать час-
тоту выше десятков килогерц. Имеется шум периода. Загруз-
ка процессора незначительная.
Формирование Любой С помощью программ задержки формировать длительности
частоты (вари- импульса и паузы выходного сигнала. Полная загрузка про-
ант 3) цессора. Этот способ используется в операторе “Sound”
Bascom. Недостаток – прерывания портят форму выходного
сигнала.
Измерение Только порты с функцией входа Два таймера: один формирует базу времени (интервал счета
частоты (вари- таймера: T0, T1, T2, T3 периодов измеряемой частоты); второй – счетчик числа пе-
ант 1) риодов. В прерывании первого запускается и останавливается
второй таймер. В прерывании второго таймера считается
число переполнений в программном счетчике.
Измерение Только порты с функцией пре- Любой таймер запускается или останавливается по спаду
периода рывания по фронту или спаду: измеряемого сигнала. Частота заполнения таймера от Fкв.
(вариант 1) INT0, INT1, ICP Производится подсчет длительности любого числа полупе-
риодов.
Измерение Только порты с функцией входа Два таймера: один предварительный делитель частоты. Вто-
периода таймера: T0, T1, T2, T3 рой счетчик длительности, который запускается или останав-
(вариант 2) ливается по переполнению таймера 2. Частота заполнения
второго таймера от Fкв.
Клавиатура из Любой. 8 или 10 линий для 16 Организовать в виде матрицы 4 на 4, 8 на 2 или 8 на 4. Для
замыкающих кнопок, 12 линий для 32 кнопок экономии портов возможно использование шины данных
кнопок индикатора для опроса клавиатуры.
Аналоговая Только порты с функцией входа Организовать в виде схемы резистивной матрицы, в которой
клавиатура из АЦП: ADC0…ADC7 замыкание каждой кнопка вызывает появление на входе АЦП
замыкающих уникального уровня напряжения. Пригодно для малокнопоч-
кнопок ных клавиатур (не более 16 на каждый вход)
АЦП с после- Любые. При наличии сигнала Программный интерфейс SPI или Micro-Wire. Наличие у
довательным готовности порт с функцией АЦП сигнала готовности позволяет организовать считывание
интерфейсом прерывания по уровню INT по прерыванию. Могут потребоваться еще какие-то линии.
АЦП с SPI Только порты с функцией Аппаратный интерфейс SPI. Порт SS при этом желательно не
интерфейсом MOSI, MISO, SCK использовать (установить «1»)
ЦАП с после- Любые или порты с функцией Соответственно для программного или аппаратного интер-
довательным MOSI, MISO, SCK фейса. В некоторых случаях удается использовать USART,
интерфейсом если он не используется по основному назначению
Светодиодный Любые – 8(16) линий данные, В прерывании с частотой 300 – 1000 Гц производится про-
7(14) - сег- остальные для кодирования граммой вывод данных в мультиплексном режиме из внут-
ментный ин- знакоместа (соответственно реннего буфера, в котором формируется код всех сегментов
дикатор двоичного или позиционного) индикатора.
Символьный Любой – 6 линий. Рекомендует- Индикатор используется в режиме управления по 4-разр.
индикатор ся использовать 4-разрядный Шине с побитным управлением портами, подключенными к
режим и линии Portc индикатору.
Графический Два порта: рекомендуется Porta Данные пересылаются побайтно, а управление осуществляет-
индикатор – шина данных, Portc – порт ся побитно.
управления
============================================================================= 2
===================================== Справочник по программированию «Bascom-AVR» ==

Практика
Проведем разработку микропроцессорной системы поэтапно с подробным комментированием. Кроме во-
просов программирования с помощью “Bascom-AVR” рассматриваются технические и организационные вопросы
создания программного проекта. В качестве объекта разработки, например, выберем многофункциональные элек-
тронные часы со светодиодным индикатором.
Этап 1. «Техническое задание». Задача этапа – определение и выработка исходных данных, необходимых
для создания программы.
Определим главные функциональные характеристики проекта:
- основной режим с отображением времени в формате ЧЧММ с мигающей секундной точкой;
- установка времени с клавиатуры в режиме редактирования;
- сопровождение нажатий клавиатуры звуковым сигналом.
Дополнительные функциональные режимы;
- режим будильника с остановкой времени срабатывания и звуковым сигналом (желательно мелодичным);
- режим таймера (секундомера) с отображением времени в формате ММСС;
- цифровая коррекция погрешности хода (цифровая калибровка задающего генератора);
- потенциальная возможность измерения и отображения параметров окружающей среды (температуры,
влажности, давления), автоматизации обслуживания аккумулятора, регулирование яркости индикатора в зависи-
мости от освещенности автоматически. Имеется в виду, что базовые технические и программные решения основ-
ных функций не должны препятствовать расширению системы добавлением аппаратных и программных модулей.
Основные требования к аппаратному исполнению изделия, определяемые, в том числе, функциональными
характеристиками:
- минимальное число компонентов;
- четырехразрядный семисегментный индикатор. Для упрощения конструкции целесообразно использовать
стандартный модуль с мультиплексным управлением. Желательно наличие единичного индикатора секунд;
- клавиатура, содержащая не менее четырех замыкающих кнопок;
- звуковой индикатор;
- система сетевого и автономного питания.
Настоящий этап заканчивается, когда определены, данные, необходимые для создания программы. Отсут-
ствующие данные при этом вырабатываются и согласовываются. Результатом данного этапа также является, хотя
бы, структурная схема подключения внешних устройств к микроконтроллеру и план распределения ресурсов, ко-
торый желательно сразу оформлять в виде полей комментариев к программе.
На рисунке 1 изображена принципиальная схема разрабатываемой системы, а в заголовке последующего
примера программы расписан план использования ресурсов системы. Схема разработана именно с учетом макси-
мального использования внутренней периферии микроконтроллера и возможности расширения. Выбор модели
микроконтроллера (ATmega8), в первую очередь, обусловлен двумя факторами:
- необходимостью в трех таймерах для обслуживания задач подсчета часового времени с тактированием от
специального часового кварцевого резонатора (асинхронный режим), формирования временных интервалов ска-
нирования клавиатуры и мультиплексного индикатора и формирования звуковых тонов.
- наличием аппаратно и программно совместимых старших моделей (ATmega168, ATmega16, ATmega32),
позволяющих без труда расширять систему.
Этап 2. «Построение скелета программы». Задача этапа – создание драйверов внутренних и внешних
устройств и на их основе общей структуры программы микропроцессорной системы.
Необходимость начать разработку программы с ее скелета обусловлено последовательным характером ра-
боты над программой, то есть, когда последовательно решаются одна задача за другой. Последовательный харак-
тер работы подразумевает и последовательную отладку программы. Ведь очевидно, что ошибки будут устраняться
быстрее, если каждый вновь созданный программный модуль тут же проверять. Технология написания программ
от скелета и является начальным элементов последовательного программирования. Скелет программы, должен
обеспечивать функционирование всех устройств и систем, по крайней мере, той части, которые необходимы на
начальном этапе разработки программы. Отлаженный скелет программы обеспечивает ввод данных, например, с
клавиатуры и вывод данных, например, на индикатор. Он позволяет без применения внешних средств проверять
работу вновь добавленных модулей.
Следующий далее текст демонстрационной программы, выделен шрифтом Courier. Кроме собственных
комментариев в текст добавлены замечания обозначающие назначение, правильный (или рекомендуемый) поря-
док расположения частей программы.

============================================================================= 3
===================================== Справочник по программированию «Bascom-AVR» ==

Рисунок 1 – Схема электрическая принципиальная электронных часов

ПРОГРАММА 1

Заголовок Комментарии, содержащие идентифицирующие сведения о программе: принадлежность изделию,


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

'------------------------------------------
' Программа электронных часов "AVR-WATCH"
'------------------------------------------
' Микроконтроллер типа ATMEGA8
'------------------------------------------
' Программируемые Fuses:
' CKSEL3...CKSEL0 = 1001 - низкочастотный кварцевый резонатор
' CKOPT = 0 - внутренние конденсаторы подключены к кварцевому резонатору
' RSTDISBL = 1 - порт PC6 используется как сброс
' WDTON = 1 - сторожевой таймер разрешен
' SPIEN = 0 - последовательное программирование разрешено
' BOOTSZ1,BOOTSZ0 = 00, BOOTRST=1 - параметры загрузчика
' EESAVE = 1 - НЕ ОЧИЩАТЬ EEPROM при программировании
' BODLEVEL = 1, BODEN = 1 - ПАРАМЕТРЫ СБРОСА
' SUT1 = 1,SUT1 = 0 - ВРЕМЯ СТАРТА
'------------------------------------------
' Используемый ресурсы микроконтроллера:
' Генератор RC - внутренний 1 МГц, а к TOSC1 and TOSC2 подключен кристалл 32768 Гц
' Timer0 – генератор прерываний с частотой 250 Гц для обслуживания индикатора, клавиатуры
' и других служб реального времени

============================================================================= 4
===================================== Справочник по программированию «Bascom-AVR» ==
' Timer1 – генератор звукового сигнала программируемой частоты
' На выходе PB1 (OC1A) формируется звуковой сигнал в форме меандра
' Timer2 – генератор прерываний с частотой 1 Гц, на основании которого вычисляется пройденное время
' Дополнительно на выходе PB3 (OC2) формируются импульсы длительностью 1 с (0.5 Гц),
' используемые для включения единичного светодиода
' Индикатор:
' Portd выход сегментов, Portc0...Portc3 - код знакоместа (позиционный). Он же порт
' сканирования клавиатуры. Индикатор с общим анодом. Активным уровнем - является "0"
' Четыре кнопки: Port0...Portc3 - код сканирования клавиатуры, Portc4 - линия возврата клавиатуры
' На линию Portc5 от источника питания "+5 В" приходит сигнал "Включение сети"
'------------------------------------------

Определить модель Записать имя файла, определяющего набор (имена) регистров с помощью директивы
микроконтроллера $Regfile. Если это не сделать, но компилятор использует установки меню Options
$regfile = "m8def.dat" 'определить тип микроконтроллера
'------------------------------------------

Определение Блок определения переменных оформляется в порядке возрастания их длины: Byte, Word,
переменных Integer, Long, Single, Double, String, Array. Переменные типа Bit не имеют смысла, тоже зани-
мают один байт и принимают только два значения нуль и не нуль
Dim Ndat As Byte 'признак "обновить данные"
Dim Ddat As Byte 'признак "обновить индикатор"
'---------------------
Dim R_btim As Byte 'счетчик времени звонка
'---------------------
Dim Rsec As Byte 'счетчик секунд
Dim Rmin As Byte 'счетчик минут
Dim Rhour As Byte 'счетчик часов
'---------------------
Dim Buf_kl As Byte 'код нажатой кнопки
Dim Buf_pkl As Byte 'предварительный код нажатой кнопки
Dim R_ckl As Byte 'счетчик удержания клавиатуры
'---------------------
Dim Buf_dis(4) As Byte 'буфер индикации (копия сегментов)
Dim Cnt_dis As Byte 'счетчик индикации
'---------------------
Dim Bufr As String * 20 'текстовая строка
'------------------------------------------

Настройка Объявление используемых прерываний и имен программ обработки прерываний (векторов).


прерываний Задание типа прерываний и приоритетов. Применение опции Nosave обеспечивает отключение
режима сохранение всех регистров общего назначения (R0…R31 и Sreg)
' назначение векторов прерывания.
On Timer0 Timer0_int Nosave 'вектор прерывания от таймера 0
On Timer2 Timer2_int Nosave 'вектор прерывания от таймера 2
'------------------------------------------

Настройка Объявление частоты тактового генератора, скорости и последовательного интерфейса, режима


периферии счетчиков, портов. Если, что-то из необходимых параметров конфигурации не будет объявлено,
то компилятор использует значения, установленные в окнах меню Options
$crystal = 1000000 'генератор 1 МГц
'---------------------
Mcucr = &B10000000 'режим IDLE – разрешить
' ТАЙМЕРЫ
Tccr0 = &B00000011 'режим таймера 0: Fкв/64 - часы реального времени
'---------------------
Tccr1a = &B01000000 'режим таймера 1: генератор звука 2 кГц с выходом на PB1
(OC1A)
Tccr1b = &B00001000 'делитель Fкв / 1 и с выходам Oc1a
Ocr1ah = 0 : Ocr1al = 250 'и коэффициетном Fкв/250*2
'---------------------
'режим таймера 2: генератор секундных импульсов
Assr.3 = 1 : Wait 1 'включить НЧ-генератор AS2 и подождать стабилизации амплитуды
Tcnt2 = 0 : Waitms 10 'очистить счетчик и подождать
'пока значение перепишется из временного буфера
Ocr2 = 128 : Waitms 10 'установить значение сравнения
'установить коэффициент деления прескалера К=128
Tccr2 = &B00010101 'и выход секундных импульсов на PB3 (OC2)
'---------------------
' ПОРТЫ
Ddrb = &B00001010 'направление линий порта B: 1 - на вывод
Portb = &B11111111 'исходное состояние линий B: hhhh1h1h

============================================================================= 5
===================================== Справочник по программированию «Bascom-AVR» ==
'---------------------
Ddrc = &B00001111 'направление линий порта C: 1 - на вывод
Portc = &B11111111 'исходное состояние линий C: hhhh1111
'---------------------
Ddrd = &B00000000 'направление линий порта D: 1 - на вывод
Portd = &B11111111 'исходное состояние линий D: hhhhhhhh
'* h - высокий уровень, подтянутый через резистор
'---------------------

Разрешение Разрешение отдельных типов прерываний и общее разрешение. Выполняется после инициали-
прерываний зации периферии
' Прерывания
Timsk = &B010000001 'разрешить прерывание от переполнения таймера 0 и таймера 2
‘Enable Timer0 'допускаемая, но не рекомендуемая форма
‘Enable Timer2
Enable Interrupts 'разрешить прерывания.
'---------------------

Основная Главная программа и ее ветви. Описывается построение главного цикла программы, в котором
программа происходит ожидание событий, и ветви отработки поступающих Чтобы придать главной про-
грамме компактный вид, повторяющиеся и функционально законченные модули оформляются в
виде подпрограмм.
'ГЛАВНЫЙ ЦИКЛ
Mc:

Idle
Goto Mc
'----------------------------------------------------------------------------------

Подпрограм- Обычно небольшие по объему модули для быстрой реакции на события, и поэтому критичные
мы обработки к времени исполнения и количеству используемых ресурсов. Программы обработки прерыва-
прерываний ний, в первую очередь, пишутся на ассемблере. Данные из программ прерывания передаются
главной программе через общие регистры.
'обработка прерывания таймера 0
'используется как часы реального времени с частотой 244 Гц (4 мс)
'для этого нужно организовать схему деления 1 МГц/64/64 = 0,000244140625 МГц
Timer0_int:
$asm
Push R31 'сохраним регистры
In R31 , Sreg
Push R31
Push R30
'-----
Ldi R31 , 192 'переустановим счетчик
Out Tcnt0 , R31
'---------------------
Rcall Led_out 'вывод в индикатор
Rcall In_kl 'опрос клавиатуры
'---------------------
'обработка счетчика времени звонка
Intt2_1:
Lds R31 , {r_btim} 'считаем счетчик времени звонка
And R31 , R31
Breq Intt2_2 'если он равен 0 - переход
Dec R31 'уменьшить
Sts {r_btim} , R31
Ldi R31 , &B00001001 'и разрешить пищать
Rjmp Intt2_3
Intt2_2:
Ldi R31 , &B00001000 'запретить пищать
Intt2_3:
Out Tccr2 , R31 'в регистр управления таймером 2
'-----
Pop R30 'восстановим регистры
Pop R31
Out Sreg , R31
Pop R31
Reti 'дополнительная команда завершения прерывания
'в конце процедуры его обработки. Рекомендуется применять перед каждой командой Return, которая
'должна скомпилироваться как Reti, однако по какой-то причине может быть пропущена и
'скомпилирована как Ret. Подобное может происходить, когда в программе обработки прерывания
'применено несколько команд завершения, а компилятор автоматически заменяет Ret на Reti только
'один раз – только первую команду Return после метки, обозначающей начало программу прерывания.

============================================================================= 6
===================================== Справочник по программированию «Bascom-AVR» ==
$end Asm
Return
'----------------------------------------------
'обработка прерывания таймера 2. Происходит 1 раз в секунду
Timer2_int:
$asm
Push R31 'сохраним регистры
'-----
Ldi R31 , 255
Sts {ndat} , R31
'-----
Pop R31 'восстановим регистры
$end Asm
Return
'----------------------------------------------------------------------------------

Драйверы устройств (под- Экономичные, небольшие и быстро работающий модули. Также критичные к вре-
программы обслуживания мени исполнения и используемым ресурсам. Используются при обработке преры-
периферии ввода вывода) ваний. Также пишутся на ассемблере.
' ВВОД ДАННЫХ С КЛАВИАТУРЫ
' клавиатура опрашивается при выводе очередного знакоместа на индикатор
' код кнопки совпадает с номером знакоместа. Осуществляется подавление дребезга
' срабатывание фиксируется при удержании более 6 циклов опроса
In_kl:
$asm
Lds R31 ,{Buf_kl} 'пока буфер клавиатуры не опустеет
And R31 , R31
Breq Inkl 'сканирование не проводится
Ret
'---------
' НАЧИНАЕМ
Inkl:
Lds R31 , {r_ckl} 'подготовим счетчика нажатий к обработке
Sbic Pinc , 4 'считать состояние линии опроса кнопки
Rjmp N_st 'нет нажатия - переход
'---------
' НАЖАТИЯ ЕСТЬ
Y_st:
Cpi R31 , 6 'если равен 6 - нажатие уже было зафиксировано
Breq Y_st1 'на выход без запоминания.
Inc R31 'иначе нужен инкремент
Sts {r_ckl} , R31 'и сохранить
Cpi R31 , 6
Brne y_st1 'меньше 6 - тоже выход без действий
'---------
' НАЖАТИЕ ЗАФИКСИРОВАТЬ
Ldi R31 , 3 'звонить 60 мс
Sts {r_btim} , R31
Lds R31 , {Cnt_dis} 'преобразуем код знакоместа (от 0 до 3)
Inc R31 'в код кнопки (от 1 до 4)
Sts {Buf_kl} , R31
Y_st1:
Ret
'---------
' НАЖАТИЯ НЕТ
N_st:
And R31 , R31 'проверим счетчик "удержания"
Breq N_st1 'не равен нулю
Dec R31 'декремент
Sts {r_ckl} , R31 'и сохранить
N_st1:
Ret
$end Asm
'----------------------------------------------------------------------------------
Const N_scn = &B11110000 ' МАСКА СКАНИРОВАНИЯ ДЛЯ ОА
Const N_sc1 = &B11111110 ' ЗНАЧЕНИЕ ДЛЯ ПОДСВЕТКИ 1-ГО ЗНАКОМЕСТА
Const N_sc2 = &B01111101 ' ЗНАЧЕНИЕ ДЛЯ ПОДСВЕТКИ 2-ГО ЗНАКОМЕСТА
Const N_sc3 = &B11111011 ' ЗНАЧЕНИЕ ДЛЯ ПОДСВЕТКИ 3-ГО ЗНАКОМЕСТА
Const N_sc4 = &B11110111 ' ЗНАЧЕНИЕ ДЛЯ ПОДСВЕТКИ 4-ГО ЗНАКОМЕСТА
'-----------------------------
'обслуживание светодиодного 7-сегментного индикатора
Led_out:
$asm
Lds R31 , {Cnt_dis} 'считать значение счетчика сканирования
Inc R31
Andi R31 , 3
Sts {Cnt_dis} , R31 'и сохранить

============================================================================= 7
===================================== Справочник по программированию «Bascom-AVR» ==
'---------
Cpi R31 , 1
Breq Led_1
'---------
Cpi R31 , 2
Breq Led_2
'---------
Cpi R31 , 3
Breq Led_3
Led_0:
'---------
Lds R30 , {buf_dis}
Ldi R31 , N_sc1 ' ЗНАЧЕНИЕ ДЛЯ ПОДСВЕТКИ 1-ГО ЗНАКОМЕСТА
Rjmp Led_e
'---------
Led_1:
Lds R30 , {buf_dis+1}
Ldi R31 , N_sc2 ' ЗНАЧЕНИЕ ДЛЯ ПОДСВЕТКИ 2-ГО ЗНАКОМЕСТА
Rjmp Led_e
'---------
Led_2:
Lds R30 , {buf_dis+2}
Ldi R31 , N_sc3 ' ЗНАЧЕНИЕ ДЛЯ ПОДСВЕТКИ 3-ГО ЗНАКОМЕСТА
Rjmp Led_e
'---------
Led_3:
Lds R30 , {buf_dis+3}
Ldi R31 , N_sc4 ' ЗНАЧЕНИЕ ДЛЯ ПОДСВЕТКИ 4-ГО ЗНАКОМЕСТА
Led_e:
Out Portd , R30
In R30 , Portc
Ori R30 , N_scn ' МАСКА СКАНИРОВАНИЯ
And R30 , R31
Out Portc , R30
Ret
$end Asm
'----------------------------------------------------------------------------------
Скелет готов. Еще раз о результатах проделанной работы. Приведенная выше программа является полно-
стью работоспособной, но при этом не совершает никаких внешних действий. С ее помощью оживлена и функ-
ционирует вся периферия, однако со стороны основной программы это будет незаметно, так как все устройства
будут обслуживаться в фоновом режиме. Во-вторых, почти все написано на ассемблере и это сделано для полного
контроля используемых ресурсов и обеспечения максимальной производительности, что является немаловажным
при низкой тактовой частоте. В-третьих, практически аппаратная реализация счетчика прошедшего времени, зву-
кового тонального генератора и мультиплексного индикатора обеспечивает хорошие потребительские свойства
изделия за счет исключения «паразитных» эффектов, свойственных программной реализации этих функций, таких
как погрешность хода, «грязный» (модулированный) звук, низкочастотное мерцание отдельных знакомест (за счет
неравномерности и неодинаковости времени свечения). В-четвертых, система готова работать и для выполнения
многих других задач и обеспечивает необходимую возможность расширения
Этап 3. «Построение главного цикла программы». Задача этапа – создание базовой структуры програм-
мы и определение номенклатуры базовых программных модулей.
Структура программы с ГЛАВНЫМ ЦИКЛОМ (ГЦ) является базовой для продолжительно работающих
программ с многократно повторяющимся алгоритмом обработки данных. С другой стороны, ГЦ является средст-
вом выравнивания скорости работы более быстрого процессора и медленного процесса генерации исходных дан-
ных. Как правило, в ГЦ осуществляется последовательная проверка необходимости выполнения некоторого набо-
ра задач. Малое время исполнения опроса обеспечивает быстрое реагирования на события. В ГЦ включается опе-
рация перевода ядра микроконтроллера в спящий режим, способствующий снижения потребления энергии во
время ожидания заданий. Выход из спящего режима происходит только по прерыванию и это не вызывает затруд-
нений, так как и появление новых задач обычно обусловлено прошедшим прерыванием. По крайней мере, про-
грамма строится так, чтобы наиболее важные события сами вызывали прерывания или возникали вследствие пре-
рываний.
Для нашего примера в ГЦ будет осуществляться проверка всего трех задач:
- окончания очередной секунды, по появлению ненулевого значения переменной “Ndat” ;
- нажатие кнопки, по появлению ненулевого значения переменной “Buf_kl” ;
- обновления индикатора, по появлению ненулевого значения переменной “ Ddat ” .

============================================================================= 8
===================================== Справочник по программированию «Bascom-AVR» ==

ПРОГРАММА 2

Главный Состоит из операций проверки появления одной из задач, вызова программы обработки задачи и
цикл очистки признака наличия задачи. Причем для клавиатуры очистка производится после обработки,
так как сам признак является носителем информации о номере кнопки. Проверяемые задачи рас-
полагаются в порядке приоритета, а после обработки одно задачи производится переход на начало
ГЦ.
'----------------------------------------------------------------------------------
'ГЛАВНЫЙ ЦИКЛ
Mc:
If Ndat <> 0 Then
Ndat = 0 'очистить "обновить данные"
Gosub New_data 'обработать данные
Goto Mc
End If
If Ddat <> 0 Then
Ddat = 0 'очистить "обновить индикатор"
Gosub New_led 'обновить индикатор
Goto Mc
End If
If Buf_kl <> 0 Then
Gosub W_klb 'обработать нажатие, только потом
Buf_kl = 0 'очистить уже обработанное нажатие
Goto Mc
End If
Idle
Goto Mc
'----------------------------------------------------------------------------------

Заготовки для программ Создаются одновременно с оформлением структуры ГЦ, чтобы не потерять все
обработки событий ГЦ имеющиеся метки (имена подпрограмм)
'ОБРАБОТКА ПРОГРАММ ГЛАВНОГО ЦИКЛА
'---------------------
'обработать данные времени по истечении секунды
New_data:

Return
'----------------------------------------------------------------------------------
'обновить индикатор по завершению одной из операций в соответствии с текущим состоянием
New_led:

Return
'----------------------------------------------------------------------------------
'обработать нажатие кнопки в соответствии с текущим режимом
W_klb:

Return
'----------------------------------------------------------------------------------

После добавления конструкции ГЦ программа работоспособна и пригодна к дальнейшему расширению пу-


тем добавления новых к списку задач.
Этап 4. «Создание программ главного цикла». Задача этапа – создание исполнительных программных мо-
дулей.
Данный этап позволяет создать первую версию рабочей программы, в соответствие с задачами, решаемыми
программами ГЦ. При этом происходит описание действий, производимой программой.

ПРОГРАММА 3

Рабочие программы Содержать три программных модуля по числу обрабатываемых задач


'----------------------------------------------------------------------------------
'ОБРАБОТКА ПРОГРАММ ГЛАВНОГО ЦИКЛА
'---------------------
'обработать данные времени по истечении секунды
New_data:
Incr Rsec 'плюс секунда
If Rsec > 59 Then 'при необходимости
Rsec = 0 : Goto Ndata1 'скорректировать счетчик секунд
End If
Goto Ndata3
Ndata1:
Incr Rmin 'плюс минута

============================================================================= 9
===================================== Справочник по программированию «Bascom-AVR» ==
If Rmin > 59 Then 'при необходимости
Rmin = 0 : Goto Ndata2 'скорректировать счетчик минут
End If
Goto Ndata3
Ndata2:
Incr Rhour 'плюс час
If Rhour > 23 Then 'при необходимости
Rhour = 0 'скорректировать счетчик часов
End If
Ndata3:
Ddat = 255 'поставить признак обновления индикации
Return
'----------------------------------------------------------------------------------

Программа Самая сложная программа. Содержит последовательность преобразований (символьное изо-


формирования бражение – двоичное число – сегментный код числа). Повторяющиеся модули оформлены в
изображения виде подпрограмм.
'обновить индикатор по завершению одной из операций в соответствии с текущим состоянием
New_led:
'сформировать символьное изображение времени
Bufr = "" 'очистить строку
If Rhour < 10 Then 'если значение часов выражается одной цифрой
Bufr = Bufr + "0" 'добавить символ лидирующего нуля
End If
Bufr = Bufr + Str(rhour) 'добавить значение часов
If Rmin < 10 Then 'если значение часов выражается одной цифрой
Bufr = Bufr + "0" 'добавить символ лидирующего нуля
End If
Bufr = Bufr + Str(rmin) 'добавить значение минут
'---------
Gosub Asc_seg : Buf_dis(4) = Tmpb 'получить код первого знакоместа
'---------
Gosub Asc_seg2 : Buf_dis(3) = Tmpb 'получить код второго знакоместа
'---------
Gosub Asc_seg2 : Buf_dis(2) = Tmpb 'получить код третьего знакоместа
'---------
Gosub Asc_seg2 : Buf_dis(1) = Tmpb 'получить код четчертого знакоместа
'---------
Return
'-----------------------------------------------------------
'П/П ВЫБОРА ИЗ ТАБЛИЦЫ КОДА ЧИСЛА В 7-СЕГМЕНТНОМ КОДЕ ИЗ ASCII (ВТОРОГО СИМВОЛА)
Asc_seg2:
Bufr = Mid(bufr , 2) 'сдвинуть строку влево на одну позицию
'---------
'П/П ВЫБОРА ИЗ ТАБЛИЦЫ КОДА ЧИСЛА В 7-СЕГМЕНТНОМ КОДЕ ИЗ ASCII
Asc_seg:
Tmpb = Asc(bufr) 'считать первый символ строки
Tmpb = Tmpb - &H30 'преобразовать ASCII в число
Tmpb = Lookup(tmpb , Tabseg0) 'считать из таблицы, используя число как смещение
Return
'---------
'ТАБЛИЦА РАСПОЛОЖЕНИЯ СЕГМЕНТОВ ИНДИКАТОРА - ПРОТОТИПА
'---------
' D7 D6 D5 D4 D3 D2 D1 D0
' -----------------------------------------------
'BUF_DIS(1) ¦ h1 g1 f1 e1 d1 c1 b1 a1
'BUF_DIS(2) ¦ h2 g2 f2 e2 d2 c2 b2 a2
'BUF_DIS(3) ¦ h3 g3 f3 e3 d3 c3 b3 a3
'BUF_DIS(4) ¦ h4 g4 f4 e4 d4 c4 b4 a4
'-------------------------------------------
' РАСПОЛОЖЕНИЕ СЕГМЕНТОВ НА ПОЛЕ ИНДИКАТОРА
' ===============================================
' ¦ ¦
' ¦ -a4- -a3- -a2- -a1- ¦
' ¦ I I I I I I I I ¦
' ¦ f4 b4 f3 b3 f2 b2 f1 b1 ¦
' ¦ I I I I I I I I ¦
' ¦ -g4- -g3- -g2- -g1- ¦
' ¦ I I I I I I I I ¦
' ¦ e4 c4 e3 c3 e2 c2 e1 c1 ¦
' ¦ I I I I I I I I ¦
' ¦ -d4- -d3- -d2- -d1- ¦
' ¦ [h4] [h3] [h2] [h1]¦
' ¦ ¦
' ===============================================
'-----------------------------------------------------------
' ТАБЛИЦА КОДОВ СЕГМЕНТОВ ОТ 0(00h) ДО 15(0Fh) , СВЕЧЕНИЕ "0"

============================================================================= 10
===================================== Справочник по программированию «Bascom-AVR» ==
'-----------------------------------------------------------
'ШЕСТНАДЦАТИРИЧНЫЕ ЦИФРЫ
Tabseg0:
Data &HC0 , &HF9 ' 0 , 1 -d0 -
Data &HA4 , &HB0 ' 2 , 3 I I
Data &H99 , &H92 ' 4 , 5 D5 D1
Data &H82 , &HF8 ' 6 , 7 I I
Data &H80 , &H90 ' 8 , 9 -d6 -
Data &H88 , &H83 ' A , B I I
Data &HC6 , &HA1 ' C , D D4 D2
Data &H86 , &H8E ' E , F I I
' -D3- [D7]
'----------------------------------------------------------------------------------
' программы клавиатуры
'---------------------------------------------------------
' ---------------------------------------
' | AVR-WATCH |
' | --------------------------------- |
' | | --- --- --- --- | |
' | | | | | | | | | | | |
' | | --- --- --- --- | |
' | | | | | | | | | | | |
' | | --- [] --- [] --- [] --- [] | |
' | --------------------------------- |
' | 1 2 3 4 |
' | _ _ _ _ |
' | |_| |_| |_| |_| |
' | |
' | Reset + + - |
' | Min Hour Min Min |
' ---------------------------------------
' выполнение программ обработки кнопок - основной список
' 0: пустой переход из-за того, что кнопки нумеруются с 1-й, а не с 0-й
' 1: Сбросить минуты 2: Добавить час 3: Добавить минуту 4: Убавить минуту
'---------------------------------------------------------

Программа об- Назначение кнопок выбрано произвольно. Для перехода к программам обработки кнопок
работки кнопок используется специальная функция On – Goto с индексированием списка меток. Дает быст-
рый и экономичный код. Применение функции Select Case не рекомендуется, так как не по-
зволяет вкладывать объемные конструкции.
W_klb:
On Buf_kl Goto Kn , Km1 , Km2 , Km3 , Km4
'обработать нажатие кнопки
'---------
Km1:
Rmin = 0 'сбросить минуты
If Rmin > 30 Then 'при необходимости округления
Goto Km2 'скорректировать счетчик минут
End If
Km1_1:
Ddat = 255 'поставить признак обновления индикации
Kn:
Return
'---------
Km2:
Incr Rhour 'плюс час
If Rhour > 23 Then 'при необходимости
Rhour = 0 'скорректировать счетчик часов
End If
Goto Km1_1
'---------
Km3:
Incr Rmin 'плюс минута
If Rmin > 59 Then 'при необходимости
Rmin = 0 'скорректировать счетчик минут
End If
Goto Km1_1
'---------
Km4:
Decr Rmin 'минус минута
If Rmin = 255 Then 'при необходимости
Rmin = 59 'скорректировать счетчик минут
End If
Goto Km1_1
'----------------------------------------------------------------------------------

============================================================================= 11
===================================== Справочник по программированию «Bascom-AVR» ==

Первая версия программы готова, она полностью работоспособна, но функционально бедна. Структура
программы во всех компонентах открыта для расширения функциональных возможностей. Определив набор до-
бавляемых функций, следует выбрать путь реализации новых задач. Существует несколько подходов:
- простое добавление новых функций по имеющимся правилам работы программы. Этот путь пригоден для
небольших добавлений, похожих на уже имеющиеся;
- добавление новых функций по другим правилам построения программы. Необходимость в этом возникает
при создании многофункциональных систем и обычно решается создание других разделов с собственной структу-
рой ГЦ;
- добавление сложных функций в виде отдельных модулей, содержащих все необходимые компоненты вво-
да и отображения данных. Это приемлемый вариант реализации небольшого количества функций при некритич-
ном времени реагирования на внешние воздействия. Примером таких функций могут быть программы тестирова-
ния, сохранения данных, обработка протокола установления связи;
- создание новых функций посредством специальных инструментов – утилит. Утилиты – специальные про-
граммы, облегчающие реализацию часто повторяющихся операций. Использование утилит для расширения сис-
темы наиболее эффективно при реализации большого количества несложных и похожих функций. Рассмотрим
примеры построения и применения утилит.
Этап 5. «Создание утилит для расширения функциональных возможностей». Задача этапа – создание на-
бора стандартных программных модулей для выполнения элементарных операций.
Требуемый для расширения набор утилит определяется составом добавляемых функций. Выберем, напри-
мер, следующий набор расширяемых режимов:
- выбор с помощью меню;
- многофункциональная клавиатура;
- отображение текстовых сообщений.
Для этого нам понадобятся следующие утилиты:
- вывод указанного текстового сообщения на индикатор, например, с помощью индекса;
- останов на фиксированное время с ожиданием нажатия кнопки.
При добавлении этих утилит прошлось расширить список используемых переменных и функций прерыва-
ния реального времени. Все изменения в программе можно наблюдать в полном измененном тексте, который бу-
дет приведен дальше.

Этап 6. «Расширение функциональных возможностей с помощью утилит».

В программу версии 2 добавлено:


- несколько новых переменных для обеспечения работы новых систем;
- расширена функция программы прерывания таймера 0. Добавлена ветка формирования временных интер-
валов 64 мс и проверка запрета звукового сигнала;
- расширена функция программы формирования изображения;
- добавлен и выделен отдельно раздел утилит (вспомогательных универсальных программ формирования
изображения и управления). Там же расположены расширенные таблицы кодов сегментов и сообщений;
- добавлено начальное сообщение;
- изменены и добавлены функции клавиатуры. Функции, конечно, надуманные, но это демонстрационная
программа. Показан один из основных механизмов создания многофункциональной клавиатуры;
- добавлены программы коррекции монтажа индикатора и клавиатуры (при отклонении фактической раз-
водки от теоретической модели.
И так, программа работает и обеспечивает все требуемые режимы работы. Однако работа не закончена. Не-
обходимо привести ее в порядок. Имеется в виду введение последних непринципиальных улучшений и подготов-
ка исходного текста к дальнейшей модернизации, возможно даже, через продолжительное время. Все это можно
назвать этапом оптимизации.

Этап 7. «Оптимизация программы».

Прагматичный (лишь бы работало) стиль программирования часто оказывается, безответственным. Прак-


тика показывает, что встроенное программное обеспечение часто подвергается доработке в процессе производства
и даже эксплуатации. Законченный программный проект в той или иной степени должен удовлетворять возмож-
ности дальнейшей доработки или, в крайней случае, должен быть пригоден для заимствования его частей для дру-
гих проектов. Что нужно для этого сделать? Следующее:
- просмотреть текст программы и одинаковые участки, даже если это всего одна строка, попытаться офор-
мить в качестве отдельной подпрограммы. В приведенной программе такие примеры имеются;
- попробовать свести к одной подпрограмме похожие участки программы, сделав, например, несколько
входов с различными параметрами и промежуточное преобразование;
- устранить слишком длинные участки программ, оформив их как функционально законченную подпро-
грамму. В первую очередь это относится к вычислительным программам;

============================================================================= 12
===================================== Справочник по программированию «Bascom-AVR» ==

- провести подробное комментирование программы. Особенно тщательно описывая, протоколы, форматы


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

ПРОГРАММА 4

Вторая версия программы Содержит все добавления.

'------------------------------------------
' Программа "AVR-WATCH" (версия 2)
'------------------------------------------
' Микроконтроллер типа ATMEGA8
'------------------------------------------
' Программируемые Fuses:
' CKSEL3...CKSEL0 = 1001 - низкочастотный кварцевый резонатор
' CKOPT=0 - внутренние конденсаторы подключены к кварцевому резонатору
' RSTDISBL=1 - порт PC6 используется как сброс
' WDTON=1 - сторожевой таймер разрешен
' SPIEN=0 - последовательное программирование разрешено
' BOOTSZ1,BOOTSZ0 = 00, BOOTRST=1 - параметры загрузчика
' EESAVE = 1 - НЕ ОЧИЩАТЬ EEPROM при программировании
' BODLEVEL=1, BODEN=1 - ПАРАМЕТРЫ СБРОСА
' SUT1=1,SUT1=0 - ВРЕМЯ СТАРТА
'------------------------------------------
' Ресурсы:
' Генератор RC - внутренний 1 МГц, а к TOSC1 and TOSC2 подключен кристалл 32768 Гц
' На выходе PB3 (OC2) импульсы длительностью 1 с (0.5 Гц)
' На выходе PB1 (OC1A) звуковой сигнал
' Порт Portd выход сегментов
' Portc0...Portc3 - код знакоместа (позиционный) он же порт сканирования клавиатуры
' Четыре кнопки: Port0...Portc3 - код сканирования клавиатуры, Portc4 - линия возврата клавиатуры
'------------------------------------------
$regfile = "m8def.dat" 'определить тип микроконтроллера
'------------------------------------------
Dim Ndat As Byte 'признак "обновить данные"
Dim Ddat As Byte 'признак "обновить индикатор"
'---------------------
Dim Mdisp As Byte 'режим отображения: 0 - ЧЧММ, 1 - ММСС, 2 - погашен
Dim Msnd As Byte 'режим звука: 0 - включен, <>0 - выключен
'---------------------
Dim S_tim As Byte 'программный счетчик времени
Dim W_tim As Byte 'счетчик времени задержек
Dim R_btim As Byte 'счетчик времени звонка
'---------------------
Dim Rsec As Byte 'счетчик секунд
Dim Rmin As Byte 'счетчик минут
Dim Rhour As Byte 'счетчик часов
'---------------------
Dim Buf_kl As Byte 'код нажатой кнопки
Dim Buf_pkl As Byte 'предварительный код нажатой кнопки
Dim R_ckl As Byte 'счетчик удержания клавиатуры
'---------------------
Dim Buf_dis(4) As Byte 'буфер индикации (копия сегментов)
Dim Cnt_dis As Byte 'счетчик индикации
Dim Tmpb As Byte 'временное значение
Dim Index As Byte 'временное значение
Dim Value As Byte 'временное значение
'---------------------
Dim Bufr As String * 10 'текстовая строка
'------------------------------------------
$crystal = 1000000 'генератор 1 МГц, необходим компилятору для правильного
' вычисления задержек и других временных параметров
'------------------------------------------
' назначение векторов прерывания.
On Timer0 Timer0_int Nosave 'вектор прерывания от таймера 0
On Timer2 Timer2_int Nosave 'вектор прерывания от таймера 2
'------------------------------------------
Mcucr = &B10000000 'режим IDLE - разрешить

============================================================================= 13
===================================== Справочник по программированию «Bascom-AVR» ==
' ТАЙМЕРЫ
Tccr0 = &B00000011 'режим таймера 0: Fкв/64 - часы реального времени
'---------------------
'режим таймера 1:
Tccr1a = &B01000000 'генератор звука 2 кГц с выходом на PB1 (OC1A)
Tccr1b = &B00001000 'делитель Fкв / 1 и с выходам Oc1a
Ocr1ah = 0 : Ocr1al = 250 'и коэффициетном Fкв/250*2
'---------------------
'режим таймера 2: генератор секундых импульсов
Assr.3 = 1 : Wait 1 'включить НЧ-генератор AS2 и подождать стабилизации амплитуды
Tcnt2 = 0 : Waitms 10 'очистить счетчик и подождать пока значение
'перепишется из временного буфера
Ocr2 = 128 : Waitms 10 'установить значение сравнения
'установить коэффициент деления прескалера К=128
Tccr2 = &B00010101 'и выход секундных импульсов на PB3 (OC2)
'---------------------
' ПОРТЫ
Ddrb = &B00001010 'направление линий порта B: 1 - на вывод
Portb = &B11111111 'исходное состояние линий B: hhhh1h1h
'---------------------
Ddrc = &B00001111 'направление линий порта C: 1 - на вывод
Portc = &B11111111 'исходное состояние линий C: hhhh1111
'---------------------
Ddrd = &B00000000 'направление линий порта D: 1 - на вывод
Portd = &B11111111 'исходное состояние линий D: hhhhhhhh
'* h - высокий уровень, подтянутый через резистор
'---------------------
' прерывания.
Timsk = &B010000001
Enable Timer0 'разрешить прерывание от переполнения таймера 0
Enable Timer2 'разрешить прерывание от переполнения таймера 2
Enable Interrupts 'разрешить прерывания.
'---------------------
Index = 1 : Gosub Dis_tr 'ВЫВОД СООБЩЕНИЯ СООБЩЕНИЯ (НОМЕР ВЕРСИИ)
Wait 1 'подождать
'ГЛАВНЫЙ ЦИКЛ
Mc:
If Ndat <> 0 Then
Ndat = 0 'очистить "обновить данные"
Gosub New_data 'обработать данные
Goto Mc
End If
If Ddat <> 0 Then
Ddat = 0 'очистить "обновить индикатор"
Gosub New_led 'обновить индикатор
Goto Mc
End If
If Buf_kl <> 0 Then
'следующая строка добавляется при необходимости
' Gosub Tr_klb 'скорректировать номер кнопок
Gosub W_klb 'обработать нажатие
Buf_kl = 0 'очистить оработанное нажатие
Goto Mc
End If
Idle
Goto Mc
'----------------------------------------------------------------------------------
'ОБРАБОТКА ПРОГРАММ ГЛАВНОГО ЦИКЛА
'---------------------
'обработать данные времени по истечении секунды
New_data:
Incr Rsec 'плюс секунда
If Rsec > 59 Then 'при необходимости
Rsec = 0 : Goto Ndata1 'скорректировать счетчик секунд
End If
Goto Ndata3
Ndata1:
Incr Rmin 'плюс минута
If Rmin > 59 Then 'при необходимости
Rmin = 0 : Goto Ndata2 'скорректировать счетчик минут
End If
Goto Ndata3
Ndata2:
Incr Rhour 'плюс час
If Rhour > 23 Then 'при необходимости
Rhour = 0 'скорректировать счетчик часов
End If
Ndata3:
Ddat = 255 'поставить признак обновления индикации

============================================================================= 14
===================================== Справочник по программированию «Bascom-AVR» ==
Return
'----------------------------------------------------------------------------------

Здесь проведена оптимизация Подпрограммы Add_vtmpb и Add_sz появились в результате оптимизации


исходно текста

'обновить индикатор по завершению одной из операций в соответствии с текущим состоянием


New_led:
Bufr = "" 'очистить строку
If Mdisp = 1 Then 'выбрать вариант обработки индикатора
Goto Nled2 'на формирования отображения ММСС
Elseif Mdisp = 2 Then
Goto Nled3 'на гашение
End If
'сформировать символьное изображение времени в виде ЧЧММ
Tmpb = Rhour : Gosub Add_vtmpb 'добавить значение часов
Tmpb = Rmin : Gosub Add_vtmpb 'добавить значение минут
'---------
Nled1:
Gosub Asc_seg : Buf_dis(1) = Tmpb 'получить код первого знакоместа
'---------
Gosub Asc_seg2 : Buf_dis(2) = Tmpb 'получить код второго знакоместа
'---------
Gosub Asc_seg2 : Buf_dis(3) = Tmpb 'получить код третьего знакоместа
'---------
Gosub Asc_seg2 : Buf_dis(4) = Tmpb 'получить код четвертого знакоместа
Return
'---------
'сформировать символьное изображение времени в виде ММСС
Nled2:
Tmpb = Rmin : Gosub Add_vtmpb 'добавить значение минут
Tmpb = Rsec : Gosub Add_vtmpb 'добавить значение секунд
Goto Nled1
'---------
'погасить индикатор
Nled3:
Buf_dis(1) = 255 'погасить все сегменты
Buf_dis(2) = &HFF
Buf_dis(3) = &B11111111
Buf_dis(4) = 255
Return
'---------
'п/п результат оптимизации
Add_vtmpb:
If Tmpb < 10 Then 'если значение выражается одной цифрой
Gosub Add_sz 'добавить символ лидирующего нуля
End If
Bufr = Bufr + Str(tmpb) 'добавить значение
Return
'---------
Add_sz:
Bufr = Bufr + "0" 'добавить символ лидирующего нуля
Return
'----------------------------------------------------------------------------------
' программы клавиатуры
'---------------------------------------------------------
' ---------------------------------------
' | AVR-WATCH |
' | --------------------------------- |
' | | --- --- --- --- | |
' | | | | | | | | | | | |
' | | --- --- --- --- | |
' | | | | | | | | | | | |
' | | --- [] --- [] --- [] --- [] | |
' | --------------------------------- |
' | 1 2 3 4 |
' | _ _ _ _ |
' | |_| |_| |_| |_| |
' | |
' | Reset + + Shift |
' | Min Hour Min |
' ---------------------------------------
' выполнение программ обработки кнопок - основной список
' 0: пустой переход из-за того, что кнопки нумеруются с 1-й, а не с 0-й
' 1: Сбросить минуты 2: Добавить час 3: Добавить минуту 4: Второе назначение
'
' | _ _ _ _ |
' | |_| |_| |_| |_| |

============================================================================= 15
===================================== Справочник по программированию «Bascom-AVR» ==
' | |
' | ЧЧММ ММСС ON/OFF ON/OFF |
' | Формат врем. Sound Disp |
' ---------------------------------------
' выполнение программ обработки кнопок - дополнительный список
' 0: пустой переход из-за того, что кнопки нумеруются с 1-й, а не с 0-й
' 1: Отображать ЧЧММ 2: Отображать ММСС 3: Вкл.Выкл. Звук 4: Вкл.Выкл. Индикацию
'---------------------------------------------------------
W_klb:
On Buf_kl Goto Kn , Km1 , Km2 , Km3 , Km4
'----------------------------------------------
W_skl:
On Buf_kl Goto Km1_1 , Ks1 , Ks2 , Ks3 , Ks4
'---------
Km1:
Rsec = 0 : Rmin = 0 'сбросить секунды и минуты
If Rmin > 30 Then 'при необходимости округления
Goto Km2 'скорректировать счетчик минут
End If
Km1_1:
Ddat = 255 'поставить признак обновления индикации
Kn:
Return
'---------
Km2:
Incr Rhour 'плюс час
If Rhour > 23 Then 'при необходимости
Rhour = 0 'скорректировать счетчик часов
End If
Goto Km1_1
'---------
Km3:
Incr Rmin 'плюс минута
If Rmin > 59 Then 'при необходимости
Rmin = 0 'скорректировать счетчик минут
End If
Goto Km1_1
'---------
' переключить режим второго назначения кнопок
Km4:
Index = 2 : Gosub Dis_tr 'выдать приглашение " SH "
Gosub Waitklc 'ожидание нажатий 2.5 с
If Buf_kl <> 0 Then 'есть нажания кнопки?
Goto W_skl 'да - переходим к обработке списка
End If
Goto Km1_1 'на обновление индикации
'---------
Ks1:
Mdisp = 0 'установить режим отображения ЧЧММ
Goto Km1_1 'на обновление индикации
'---------
Ks2:
Mdisp = 1 'установить режим отображения ММСС
Goto Km1_1 'на обновление индикации
'---------

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

Ks3:
If Msnd <> 0 Then 'изменить режим звука на противоположенный
Msnd = 0
Else
Msnd = 255
End If
Goto Km1_1 'на обновление индикации
'---------
Ks4:
If Mdisp <> 2 Then 'изменить режим гашения на противоположенный
Mdisp = 2 'установить режим гашения
Else
Mdisp = 0 'снять режим гашения
End If
Goto Km1_1 'на обновление индикации
'----------------------------------------------------------------------------------
'обработка прерывания таймера 0

============================================================================= 16
===================================== Справочник по программированию «Bascom-AVR» ==
'используется как часы реального времени с частотой 244 Гц (4 мс)
'для этого нужно организовать схему деления 1 МГц/64/64 = 0,000244140625 МГц
Timer0_int:
$asm
Push R31 'сохраним регистры
In R31 , Sreg
Push R31
Push R30
'-----
Ldi R31 , 192 'переустановим счетчик
Out Tcnt0 , R31
'---------------------
Rcall Led_out 'вывод в индикатор
Rcall In_kl 'опрос клавиатуры
'---------------------
'обработка счетчика времени
Lds R31 , {s_tim} 'считать счетчик
Dec R31 'уменьшить
Sts {s_tim} , R31 'сохранить
Andi R31 , &h0f 'маска
Brne Intt2_4 'каждое 16 прерывание (64 мс) обрабатываем еще два счетчика
'---------------------
'обработка счетчика переменных временных интервалов
Lds R31 , {w_tim} 'считать счетчик
Dec R31 'может уменьшаться только от 127 до нуля.
Brmi Intt2_1
Sts {w_tim} , R31
'---------------------
'обработка счетчика времени звонка
Intt2_1:
Lds R31 , {Msnd} 'проверим, а звонок разрешен?
And R31 , R31
Brne Intt2_2 'если не равен 0 - переход на отключение
'разрешен
Lds R31 , {r_btim} 'считаем счетчик времени звонка
And R31 , R31
Breq Intt2_2 'если он равен 0 - переход
Dec R31 'уменьшить
Sts {r_btim} , R31
Ldi R31 , &B00001001 'и разрешить пищать
Rjmp Intt2_3
Intt2_2:
Ldi R31 , &B00001000 'запретить пищать
Intt2_3:
Out Tccr2 , R31 'в регистр управления таймером 2
'-----
Intt2_4:
Pop R30 'восстановим регистры
Pop R31
Out Sreg , R31
Pop R31
$end Asm
Return
'----------------------------------------------
'обработка прерывания таймера 2. Происходит 1 раз в секунду
Timer2_int:
$asm
push R31 'сохраним регистры
' In R31 , Sreg
' Push R31
'-----
ldi R31 , 255
Sts {ndat} , R31
'-----
' Pop R31 'восстановим регистры
' Out Sreg , R31
Pop R31
Reti
$end Asm
Return
'----------------------------------------------------------------------------------
' ВВОД ДАННЫХ С КЛАВИАТУРЫ
' клавиатура опрашивается при выводе очередного знакоместа на индикатор
' код кнопки совпадает с номером знакоместа. Осуществляется подавление дребезга
' срабатывание фиксируется при удержании более 6 циклов опроса
In_kl:
$asm
Lds R31 ,{Buf_kl} 'пока буфер клавиатуры не опустеет
And R31 , R31

============================================================================= 17
===================================== Справочник по программированию «Bascom-AVR» ==
Breq Inkl 'сканирование не проводится
Ret
'---------
' НАЧИНАЕМ
Inkl:
Lds R31 , {r_ckl} 'подготовим счетчика нажатий к обработке
Sbic Pinc , 4 'считать состояние линии опроса кнопки
Rjmp N_st 'нет нажатия - переход
'---------
' НАЖАТИЯ ЕСТЬ
Y_st:
Cpi R31 , 6 'если равен 6 - нажатие уже было зафиксировано
Breq Y_st1 'на выход без запоминания.
Inc R31 'иначе нужен инкремент
Sts {r_ckl} , R31 'и сохранить
Cpi R31 , 6
Brne y_st1 'меньше 6 - тоже выход без действий
'---------
' НАЖАТИЕ ЗАФИКСИРОВАТЬ
Ldi R31 , 3 'звонить 60 мс
Sts {r_btim} , R31
Lds R31 , {Cnt_dis} 'преобразуем код знакоместа (от 0 до 3)
Inc R31 'в код кнопки (от 1 до 4)
Sts {Buf_kl} , R31
Y_st1:
Ret
'---------
' НАЖАТИЯ НЕТ
N_st:
And R31 , R31 'проверим счетчик "удержания"
Breq N_st1 'не равен нулю
Dec R31 'декремент
Sts {r_ckl} , R31 'и сохранить
N_st1:
Ret
$end Asm
'----------------------------------------------------------------------------------
'данные константы позволяют учесть и скорректированит "нерегулярность" монтажа индикатора
Const N_scn = &B11110000 ' МАСКА СКАНИРОВАНИЯ ДЛЯ ИНДИКАТОРА С ОА
Const N_sc1 = &B11111110 ' МАСКА ДЛЯ ПОДСВЕТКИ 1-ГО ЗНАКОМЕСТА
Const N_sc2 = &B01111101 ' МАСКА ДЛЯ ПОДСВЕТКИ 2-ГО ЗНАКОМЕСТА
Const N_sc3 = &B11111011 ' МАСКА ДЛЯ ПОДСВЕТКИ 3-ГО ЗНАКОМЕСТА
Const N_sc4 = &B11110111 ' МАСКА ДЛЯ ПОДСВЕТКИ 4-ГО ЗНАКОМЕСТА
'-----------------------------
'обслуживание светодиодного 7-сегментного индикатора
Led_out:
$asm
Lds R31 , {Cnt_dis} 'считать значение счетчика сканирования
Inc R31
Andi R31 , 3
Sts {Cnt_dis} , R31 'и сохранить
'---------
Cpi R31 , 1
Breq Led_1
'---------
Cpi R31 , 2
Breq Led_2
'---------
Cpi R31 , 3
Breq Led_3
Led_0:
'---------
Lds R30 , {buf_dis}
Ldi R31 , N_sc1 ' ЗНАЧЕНИЕ ДЛЯ ПОДСВЕТКИ 1-ГО ЗНАКОМЕСТА
Rjmp Led_e
'---------
Led_1:
Lds R30 , {buf_dis+1}
Ldi R31 , N_sc2 ' ЗНАЧЕНИЕ ДЛЯ ПОДСВЕТКИ 2-ГО ЗНАКОМЕСТА
Rjmp Led_e
'---------
Led_2:
Lds R30 , {buf_dis+2}
Ldi R31 , N_sc3 ' ЗНАЧЕНИЕ ДЛЯ ПОДСВЕТКИ 3-ГО ЗНАКОМЕСТА
Rjmp Led_e
'---------
Led_3:
Lds R30 , {buf_dis+3}
Ldi R31 , N_sc4 ' ЗНАЧЕНИЕ ДЛЯ ПОДСВЕТКИ 4-ГО ЗНАКОМЕСТА

============================================================================= 18
===================================== Справочник по программированию «Bascom-AVR» ==
Led_e:
Out Portd , R30
In R30 , Portc
Ori R30 , N_scn ' МАСКА СКАНИРОВАНИЯ
And R30 , R31
Out Portc , R30
Ret
$end Asm
'----------------------------------------------------------------------------------
' УТИЛИТЫ
'----------------------------------------------------------------------------------
'ВЫВОД СООБЩЕНИЯ СООБЩЕНИЯ, НОМЕР КОТОРОГО УКАЗАН Index
Dis_tr:
For Value = 1 To 4 'определим число считываемых символов
Tmpb = Index * 4 'вычислить индекс, указывающих на начало сообщения
Tmpb = Tmpb + Value 'вычислить индекс, указывающих на текущий символ
Decr Tmpb 'коррекция индекса
Tmpb = Lookup(tmpb , Tr_led) 'считать из таблицы код символа
Gosub Pos_seg 'преобразовать в код сегментов
Buf_dis(index) = Tmpb 'записать в буфер индикации
Next Index
Return
'----------------------------------------------------------------------------------
'П/П ВЫБОРА ИЗ ТАБЛИЦЫ КОДА ЧИСЛА В 7-СЕГМЕНТНОМ КОДЕ ИЗ ASCII (ВТОРОГО СИМВОЛА)
Asc_seg2:
Bufr = Mid(bufr , 2) 'сдвинуть строку влево на одну позицию
'---------
'П/П ВЫБОРА ИЗ ТАБЛИЦЫ КОДА ЧИСЛА В 7-СЕГМЕНТНОМ КОДЕ ИЗ ASCII
Asc_seg:
Tmpb = Asc(bufr) 'считать первый символ строки
Tmpb = Tmpb - &H30 'преобразовать ASCII в число
'---------
'П/П ПРЕОБРАЗОВАНИЯ ПОЗИЦИОННОГО КОДА В 7-СЕГМЕНТНЫЙ КОД
Pos_seg:
Tmpb = Lookup(tmpb , Tabseg0) 'считать из таблицы, используя число как смещение
'при необходимости подключить следующую строчку
' Gosub Tr_seg 'исправить монтаж сегментов индикатора
Return
'---------
'П/П ПЕРЕСЫПКИ БИТОВ 7-СЕГМЕНТНОГО КОДА ДЛЯ КОРРЕКЦИИ "НЕПРАВИЛЬНОГО" МОНТАЖА
Tr_seg:
Value.0 = Tmpb.7 : Value.1 = Tmpb.6 : Value.2 = Tmpb.5 'например "отзеркалить"
Value.3 = Tmpb.4 : Value.4 = Tmpb.3 : Value.5 = Tmpb.2 : Value.6 = Tmpb.1 : Value.7 = Tmpb.0
Tmpb = Value : Return
'----------------------------------------------------------------------------------
'ТАБЛИЦА РАСПОЛОЖЕНИЯ СЕГМЕНТОВ ИНДИКАТОРА - ПРОТОТИПА
'---------
' D7 D6 D5 D4 D3 D2 D1 D0
' -----------------------------------------------
'BUF_DIS(4) ¦ h1 g1 f1 e1 d1 c1 b1 a1
'BUF_DIS(3) ¦ h2 g2 f2 e2 d2 c2 b2 a2
'BUF_DIS(2) ¦ h3 g3 f3 e3 d3 c3 b3 a3
'BUF_DIS(1) ¦ h4 g4 f4 e4 d4 c4 b4 a4
'-------------------------------------------
' РАСПОЛОЖЕНИЕ СЕГМЕНТОВ НА ПОЛЕ ИНДИКАТОРА
' ===============================================
' ¦ ¦
' ¦ -a4- -a3- -a2- -a1- ¦
' ¦ I I I I I I I I ¦
' ¦ f4 b4 f3 b3 f2 b2 f1 b1 ¦
' ¦ I I I I I I I I ¦
' ¦ -g4- -g3- -g2- -g1- ¦
' ¦ I I I I I I I I ¦
' ¦ e4 c4 e3 c3 e2 c2 e1 c1 ¦
' ¦ I I I I I I I I ¦
' ¦ -d4- -d3- -d2- -d1- ¦
' ¦ [h4] [h3] [h2] [h1]¦
' ¦ ¦
' ===============================================
'-----------------------------------------------------------
' ТАБЛИЦА КОДОВ СЕГМЕНТОВ , СВЕЧЕНИЕ "0"
'-----------------------------------------------------------
'ШЕСТНАДЦАТИРИЧНЫЕ ЦИФРЫ ОТ 0(00h) ДО 15(0Fh)
Tabseg0:
Data &HC0 , &HF9 ' 0 , 1 -d0 -
Data &HA4 , &HB0 ' 2 , 3 I I
Data &H99 , &H92 ' 4 , 5 D5 D1
Data &H82 , &HF8 ' 6 , 7 I I
Data &H80 , &H90 ' 8 , 9 -d6 -

============================================================================= 19
===================================== Справочник по программированию «Bascom-AVR» ==
Data &H88 , &H83 ' A , B I I
Data &HC6 , &HA1 ' C , D D4 D2
Data &H86 , &H8E ' E , F I I
' -D3- [D7]
'-----------------------------------------------------------
'ДРУГИЕ СИМВОЛЫ (УСЛОВНАЯ НУМЕРАЦИЯ)
Data &HAF ' R *(16)
Data &HE3 ' U *(17)
Data &HC7 ' L(18)
Data &HFF ' ПРОБЕЛ(19)
Data &H0B ' H *(20)
Data &HB7 ' =(21)
Data &HF7 ' _(22)
Data &HC3 ' ЛЕВАЯ ЧАСТЬ БУКВЫ "W"(23)
Data &HE1 ' ПРАВАЯ ЧАСТЬ БУКВЫ "W"(24)
Data &H0C ' P(25)
Data &HFB ' I *(26)
Data &H07 ' T *(27)
Data &HAB ' П *(28)
Data &HA3 ' O *(29)
Data &HE7 ' L *(30)
Data &H09 ' H(31)
Data &H9C ' ГРАДУС(32)
Data &HAF ' ФРОНТ -(33)
Data &HBB ' СПАД ¬(34)
Data &HBF ' МИНУС -(35)
'ПРИМЕЧАНИЕ. * - МАЛЫЕ СИМВОЛЫ
'-----------------------------------------------------------
'сообщения индикатора, записанные в номерах символов таблицы Tabseg0
Tr_led:
Data 17 , 22 , 1 , 2 ' "v_12" (0)
Data 19 , 5 , 31 , 19 ' " SH " (1)
Data 14 , 16 , 12 , 5 ' "ErCS" (2)
Data 19 , 0 , 15 , 15 ' " OFF" (3)
Data 19 , 0 , 18 , 19 ' " OL " (4)
Data 19 , 18 , 0 , 19 ' " LO " (5)
Data 19 , 23 , 24 , 19 ' " W " (6)
'-----------------------------------------------------------
'ожидание нажатий 2.5 с
Waitklc:
W_tim = 40 'задать время ожидания 2500=64 мс * 40
Buf_kl = 0 'очистим регистр клавиатуры от кода предыдущего нажати
'цикл ожидания нажатий
Do
If Buf_kl <> 0 Then 'есть нажания кнопки?
Goto Wtklend 'да - на выход
End If
Idle
Loop Until W_tim = 0 'или истечения разрешенного времени 2.5 с
Wtklend:
'следующая строка добавляется при необходимости
' Gosub Tr_klb 'скорректировать номер кнопокReturn
'---------
'П/П ПЕРЕСЫПКИ НОМЕРОВ КНОПОК - ПЕРЕХОД ОТ ФИЗИЧЕСКИХ НОМЕРОВ К ЛОГИЧЕСКИМ
Tr_klb:
Buf_kl = Lookup(Buf_kl , tklb) : Return
Tklb:
Data 0 , 4 , 3 , 2 , 1 'таблица перекодировки, например, наоборот
'-----------------------------------------------------------

============================================================================= 20
===================================== Справочник по программированию «Bascom-AVR» ==

Обработка прерываний

Обстоятельства и правила возникновения прерывания


Прерывания в микроконтроллере AVR возникают при возникновении событий, обусловленных изменением
состояния аппаратного компонента периферии, находящейся на кристалле. Предусмотрено прерывания по сле-
дующим событиям:
- изменение уровня на входной линии порта. Возможные варианты срабатывания на низкий, высокий или
любое изменение уровня;
- переполнение счетчика таймера;
- совпадение значения счетчика таймера с заданным фиксированным значением;
- прием байта приемником одного из интерфейсов (UART, SPI, I2C);
- опустошением передатчика одного из интерфейсов
- окончанием задержки отработанной сторожевым таймером;
- окончание преобразования АЦП;
- окончание записи в EEPROM;
- изменения соотношения аналоговых уровней, сравниваемых компаратором.
Наличие событий прерывания и сочетание с другими функциями определяется моделью микроконтроллера.
Для каждого типа прерываний предусмотрен собственный вектор – адрес, с которого начинается исполне-
ния программы в случае регистрации события. Все векторы прерывания размешены в программной памяти, начи-
ная с нулевого адреса. Размер области вектора составляет:
- два байта для облегченных микроконтроллеров (семейства Mega и всех семейства Tiny) и предусматрива-
ет размещение в векторе только одной короткой команды перехода RJMP;
- четыре байта для микроконтроллеров семейства Mega и позволяет разместить в векторе и короткую RJMP
и длинную команду перехода JMP.
В векторе прерывания может быть еще размещена команда возврата RETI (возврат без обработки). Никаких
других разумных вариантов применения иных команд не предполагается. По команде, размещенной в векторе,
производится переход к самой программе обработки события прерывания.
Чтобы в Bascom-AVR обозначить адрес такого перехода записываются следующие директивы:

'---------------------
' назначение векторов прерывания
On Int0 Int0_int Nosave 'вектор внешнего прерывания 0
On Timer0 Timer0_int Nosave 'вектор прерывания от таймера 0
On Urxc Rxd_int Nosave 'вектор прерывания от приемника UART
'---------------------

которые содержат:
- прерывания (или тип), по которому компилятор данный вектор соответствующему адресу (определяемо-
му моделю микроконтроллера);
- имя метки, с которой начинается программы обработки прерывания;
- необязательная опция Nosave , запрещающая компилятору автоматическое сохранение всех регистров
общего назначения (R0-R31) и регистра статуса SREG. Применение данной опции рекомендуется, но требует от
программиста самостоятельного определения списка сохраняемых регистров. Этот список определяется выпол-
няемыми операциями при обработке прерываний. Перемещение данных требует сохранения только используемых
регистров, а использование арифметических и логических команд еще сохранения статуса SREG.

Происхождение прерываний
Прерывание происходит в случае выполнения, как минимум, трех условий:
а) наличие общего разрешения прерываний, которое производится установкой (в «1») бита SREG.7 (бита I:
Global Interrupt Enable). Управление этим битом может производиться многими способами. Напрямую установка
его значения производится ассемблерными командами SEI (разрешение) и CLI (запрещение) или соответствую-
щими командами Bascom-AVR:

'---------------------
Enable Interrupts 'разрешить прерываниЯ

Или

'---------------------
Disable Interrupts 'запретить прерываниЯ

б) разрешения выбранного типа прерывания. Как правило, разрешение прерывания производится установ-
кой (в «1») соответствующего бита регистров управления. Запись данной операции операторами Bascom-AVR
выполняется следующим образом:

============================================================================= 21
===================================== Справочник по программированию «Bascom-AVR» ==
'---------------------
Enable Timer0 'разрешить прерывание таймера 0
Enable Int0 'разрешить внешнее прерывание 0
Enable Urxc 'разрешить прерывание по заполнению приемника
'---------------------
Однако лучше применять следующую форму записи, как более очевидную и экономичную с точки зрения
размера кода:

Timsk = &B00000001 'разрешить прерывание таймера 0

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

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

Программный механизм прерываний


При возникновении любого прерывания происходит переход на исполнение команды, записанной по адресу
соответствующего вектора. При этом в стеке запоминается адрес (два байта) команды, которая должна исполнять-
ся после завершения обработки прерывания (которая бы выполнялась при отсутствии прерывания). Указатель сте-
ка при наступлении прерывания уменьшается на два. Таким образом, прерывания эквивалентно выполнению двух
команд - CALL и CLI. Для возврата к исполнению прерванной программы после завершения обработки прерыва-
ния применяется команда RETI, эквивалентная двум командам - RET (возврат по адресу, запомненному в стеке) и
SEI (разрешение прерываний). После завершения обработки прерывания указатель стека возвращается к исходно-
му значению (увеличивается на два). Источником прерывания всегда является соответствующий флаговый бит,
устанавливаемый аппаратно по совпадению назначенных условий. После выполнения обработки прерывания по
команде RETI сбрасывается и бит, вызвавший прерывание. Однако имеются случаи, когда этого не происходит
(смотреть описание микроконтроллера) и требуется его программный сброс.

Сохранение данных прерванной программы


Для обеспечения правильной работы прерванной программы требуется сохранение данных, которые могут
быть испорчены при обработке прерывания. Предлагаемый (по умолчанию) компилятором вариант сохранения
всех регистров общего назначения для оптимального программирования не годится. Во-первых, очень редко в
программе обработки прерывания используется не более четырех регистров. Во-вторых, даже эта мера не позво-
ляет использовать в программе прерывания все функции Bascom-AVR. В число запрещенных входят функции,
использующие служебные области памяти - внешние переменные, служебный строковый буфер, программный
стек (обработка строк, вычисления (обработка) чисел в формате с плавающей точкой, обработка протоколов связи
и т.п.). О наличие служебных областей можно узнать, посмотрев файл отчета (“Cntr-W”) после компиляции (до
этого нужно еще нужно назначить его создание и включение в список внутренних переменных). В число разре-
шенных функций входят:
- операторы назначения и перезаписи данных;
- целочисленные вычисления;
- проверка условий с целыми числами;
- управление внутренними и внешними периферийными устройствами.
Тем не менее, рекомендуется программу обработки прерываний выполнять только на ассемблере, предель-
но сократив количество выполняемых действий (операций).

Другие вопросы совместного использования ресурсов


Кроме очевидных и не очень очевидных случаев использования общих регистров памяти в главной про-
грамме и программе прерывания, следует иметь в виду другие возможности совместного использования ресурсов.
Например, время исполнения участка программы разорванного прерыванием увеличивается. В одних слу-
чаях это становится просто заметным и появляется в искажение звука при программной генерации сигналов или
модуляция яркости индикаторов, время свечения которых определяется программно. В других случаях сбои, вы-
званные обработкой прерывания, становятся критичными для работы программы. Как это проявляется:
- замедляется реакция на внешние события или они пропускаются совсем;
- пропускаются отдельные элементы последовательно принимаемые данные;
============================================================================= 22
===================================== Справочник по программированию «Bascom-AVR» ==

- происходит нарушение формы (временных соотношений) программно формируемой временной диаграм-


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

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

Встроенные прерывания
В некоторых функциях “Bascom-AVR” предполагается использование прерываний. Главным образом, это
функции программных интерфейсов, работающие режимах синхронизации от внешних устройств (в режиме ве-
домого - Slave). Библиотеки “Bascom-AVR”, обычно, используют для этого самые популярные прерывания Int0 и
Timer0. Это нужно учитывать при проектировании системы и не занимать прерывания, используемые такими
функциями, другими задачами. При этом резервируются и линии портов соответственно используемым прерыва-
ниям. Прерывания используют следующие функции: AT-клавиатура, I2C – двухпроводный программный интер-
фейс (ведомый), SPI – последовательный программный интерфейс (ведомый), TCP/IP – сетевой протокол с микро-
схемой W3100A, RC5 –программный инфракрасный порт дистанционного управления. Типы используемых пре-
рываний: внешнее от изменения уровня - Int0 или Int2, от переполнения счетчиков - Timer0 или Timer2. Причем
компилятор во всех случаях предлагает выбрать одного из двух каждого типа.

Оформление программ прерывания


Важнейшей частью программы обработки прерывания является сохранение, а затем перед завершением
восстановление регистров общего назначения. По умолчанию автоматически сохраняются все регистры (R0…R31
и статусный регистр SREG). Если в операторе, формирующем вектор прерывания, применена опция NOSAVE, то
операции автоматического сохранения-восстановления регистров не будут включены в текст программы, а об
этом нужно позаботится самостоятельно. Компилятор “Bascom-AVR” не предоставляет ассемблерного листинга.
Написав программу обработки прерывания с использованием операторов бейсика, весьма затруднительно опреде-
лить список используемых регистров, чтобы произвести их выборочное сохранение. Поэтому рекомендуется про-
граммы обработчика прерывания выполнять на ассемблере. Это оптимально во всех отношениях. И хорошая
практика, чтобы не забыть ассемблер, и быстрое исполнение программы, и надежная предсказуемая работа про-
граммы, независимость от версии компилятора.
Чтобы обеспечить возврат из программы обработки с разрешением прерываний следует применить команду
микроконтроллера RETI. Компилятор делает это автоматически - первая команда RETURN, встретившаяся после
метки с именем вектора прерывания скомпилируется как команда микроконтроллера RETI. Чтобы такая автозаме-

============================================================================= 23
===================================== Справочник по программированию «Bascom-AVR» ==

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

' ......
Gosub Transfer ' перезаписать данные
' ......

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

' ......
Dim Ndat As Byte '"новые данные"
Dim R_bd As Long 'двоичный результат
' ......
$asm
Sts {r_bd} , R0 'запишем сумму
Sts {r_bd + 1} , R1
Sts {r_bd + 2} , R26
Sts {r_bd + 3} , R27
Ldi R31 , &HFF
Sts {ndat} , R31
Rjmp Adci_2
$end Asm

- запись в регистры индексов (адресов), определяемых при компиляции. Операции, указанные в после-
дующем примере могут без ограничения использоваться в программах прерывания, так как они модифицируют
только регистр приемника данных. Например,
'---------------------
Dim K_dc0 As Single
Dim Fldb As Single
'---------------------
' ......
'перезапись данных
Transfer:
Loadadr K_dc0 , X 'записать адрес переменной K_dc0 в индексный регистр X
Loadadr Fldb , Z 'записать адрес переменной Fldb в индексный регистр Z
$asm
Ld R16 , Z+
St X+ , R16
Ld R16 , Z+
St X+ , R16
Ld R16 , Z+
St X+ , R16
Ld R16 , Z+
St X+ , R16
$end Asm
Return
'---------------------
' ......
'установка индексного регистра Z для считывания байтовых данных
Ldi R30 , Low(Tab_phs * 2) 'записать адрес метки Tab_phs в индексный регистр Z
Ldi R31 , High(Tab_phs * 2) 'младший и старший байты
' ......
'установка специального индексного регистра компилятора
Restore Tab_phs 'запись указателя на метку в регистры R8 и R9
' ......
Tab_phs:
Data 55 , 55 , 55 , 56 'поля данных
' ......
M12:
Read Tmpb 'считывание данных

============================================================================= 24
===================================== Справочник по программированию «Bascom-AVR» ==

Индексы, получаемые в приведенном выше примере, предполагают побайтную адресацию (соответствуют


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

' ......
'установка индексного регистра Z для операции перехода
Ldi R30 , Low(M12 * 1) 'записать адрес метки M12 в индексный регистр Z
Ldi R31 , High(M12 * 1) 'младший и старший байты как адрес перехода
Ijmp Z 'переход по считанному адресу
' ......

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


'---------------------
Dim W As Word
'---------------------
' ......
W = Varptr(fldb) 'запись в переменную W указателя на переменную fldb
W = Loadlabel(Tab_phs) 'запись в переменную W указателя на метку
' ......

Типовые программы прерывания


Далее приведены примеры программ обработки прерываний различного типа.
Пример 1 - Прерывание таймера
'----------------------------------------------------------------------------------
Dim R_tim As Byte 'счетчик временных интервалов, задаваемых в главной программе
' ......
On Timer0 Timer0_int Nosave 'вектор прерывания от таймера 0
' ......
Tccr0 = &B00000101 'режим таймера 0: Fкв/1024 - часы реального времени
Enable Timer0 'разрешить прерывание таймера
Enable Interrupts 'разрешить прерывания
' ......
'----------------------------------------------------------------------------------
'обработка прерывания таймера 0 (8-битный таймер)
'используется как часы реального времени с частотой 10 мс
'для этого организована схема деления 8 МГц/1024/78
Timer0_int:
$asm
Push R31 'сохраним регистры
In R31 , Sreg
Push R31
'-----
Ldi R31 , 178 'переустановим счетчик
Out Tcnt0 , R31
'-----
Lds R31 , {r_tim} 'обработка счетчика переменных временных интервалов
And R31 , R31
Breq Intt0_e
Dec R31
Sts {r_tim} , R31
'-----
Intt0_e:
Pop R31
Out Sreg , R31 'восстановим регистры
Pop R31
$end Asm
Return
'----------------------------------------------------------------------------------

Пример 2 - Прерывание от изменения уровня на линии порта


'----------------------------------------------------------------------------------
Dim Edat As Byte 'признак "Есть новые данные энкодера"
Dim R_cdec As Byte 'СЧЕТЧИК ЩЕЛЧКОВ ВЛЕВО
Dim R_cinc As Byte 'СЧЕТЧИК ЩЕЛЧКОВ ВПРАВО
' ......
On Int0 Int0_int Nosave 'вектор внешнего прерывания 0
' ......
'---------------------
Mcucr = &B10000010 'IDLE - разрешить, INT0 - по спаду
'---------------------
' ......
Enable Int0 'разрешить внешнее прерывание 0

============================================================================= 25
===================================== Справочник по программированию «Bascom-AVR» ==
Enable Interrupts 'разрешить прерывания
' ......
'----------------------------------------------------------------------------------
'ОБРАБОТКА ВНЕШНЕГО ПРЕРЫВАНИЯ 0, ОБСЛУЖИВАЮЩЕГО ДВУХФАЗНЫЙ ЭНКОДЕР
'ПРЕРЫВАНИЕ ПРОИСХОДИТ ПО СПАДУ НА ВХОДЕ P_CHA. ЗАТЕМ ПРОГРАММА ИНТЕГРИРУЕТ
'(ФИЛЬТРУЕТ) ЭТОТ УРОВЕНЬ, ДОЖИДАЯСЬ НАДЕЖНОГО УСТАНОВЛЕНИЯ "0". В МОМЕНТ
'ФИКСАЦИИ "0" НА ЛИНИИ P_cha РЕГИСТРИРУЕТСЯ УРОВЕНЬ P_chb , ПО КОТОРОМУ И
'ОПРЕДЕЛЯЕТСЯ НАПРАВЛЕНИЕ ВРАЩЕНИЯ
'-------------------------------------------
' P_cha Alias PIND.2 'ОПРЕДЕЛЕНИЕ ПОРТОВ ВВОДА КОДА ЦИФРОВОЙ РУЧКИ
' P_chb Alias PIND.3
'-------------------------------------------
Const Con_irt = 30 'КОНСТАНТА ИНТЕГРИРОВАНИЯ УРОВНЯ НА ВХОДЕ
'---------
Int0_0:
Inc R31 'УРОВЕНЬ ВЫСОКИЙ - ОТКАТ СЧЕТЧИКА
Cpi R31 , 45 'это Con_irt*1.5
Brne Int0_1 'ЕСЛИ ЖДЕМ СЛИШКОМ ДОЛГО - ВЫХОД
Rjmp Int0_e
'---------
'ВНЕШНЕЕ ПРЕРЫВАНИЕ ОТ ЦИФРОВОЙ РУЧКИ
Int0_int:
$asm
Push R31 'сохраним регистры
In R31 , Sreg
Push R31
'-----
Ldi R31 , 30 'ПОСТОЯНАЯ ИНТЕГРИРОВАНИЯ
Int0_1:
Sbic Pind , 2
Rjmp Int0_15 'ЕСЛИ НА НОЖКЕ "1"
Dec R31 'ПОКА НЕ ОБНУЛИЛСЯ СЧЕТЧИК ЖДЕМ
Brne Int0_1 'УСТАНОВЛЕНИЯ НИЗКОГО УРОВНЯ
Rjmp Int0_2
'-----
Int0_15:
Inc R31 'УВЕЛИЧИВАЕМ СЧЕТЧИК
Cpi R31 , 60 'ЕСЛИ НА "1" УСТАНОВЛЕНА СЛИШКОМ ДОЛГО
Brne Int0_e 'НА ВЫХОД
Rjmp Int0_1
'---------------------------------------------
'ПРОГРАММА РАСЧИТАНА НА КОДОВЫЙ ПЕРЕКЛЮЧАТЕЛЬ
'С НОРМАЛЬНО РАЗОМКНУТЫМИ КОНТАКТАМИ, ЗАМЫКАЮЩИМИСЯ
'ТОЛЬКО В МОМЕНТ ЩЕЛЧКА. ЭТО ОЧЕНЬ УПРОЩАЕТ ЗАДАЧУ
'---------------------------------------------
' |<----- ВРАЩЕНИЕ ВЛЕВО (ИЛИ НАОБОРОТ, В ЗАВИСИМОСТИ ОТ МОНТАЖА)
'chA--- --------- ------ ОПРЕДЕЛЕНИЕ НАПРАВЛЕНИЯ
' ¦ ¦ chA ¦ ¦ ВРАЩЕНИЯ ЦИФРОВОГО КОДЕРА
' --- --- ОСНОВАНО НА АНАЛИЗЕ СОСТОЯНИЯ
'chB----- | --------- ---- КАНАЛА "B" В МОМЕНТ ЗАМЫКАНИЯ
' | ¦ | ¦ chB ¦ ¦ (ЩЕЛЧКА) В КАНАЛЕ "A"
' | --- ---
' | |<------ ВРАЩЕНИЕ ВПРАВО
'---------------------------------------------
Int0_2:
'ОБРАБОТКА ИЗМЕНЕНИЯ УРОВНЯ
'---------
Sbis Pind , 3 'ОПРЕДЕЛИМ НАПРАВЛЕНИЕ ВРАЩЕНИЯ
Rjmp Int0_3
'---------
'ВРАЩЕНИЯ ВЛЕВО (УМЕНЬШЕНИЕ)
Lds R31 , {r_cdec} 'УВЕЛИЧИМ СЧЕТЧИК ЩЕЛЧКОВ ВЛЕВО
Inc R31
Sts {r_cdec} , R31
Rjmp Int0_4
'---------
Int0_3:
'ВРАЩЕНИЯ ВПРАВО (УВЕЛИЧЕНИЕ)
Lds R31 , {r_cinc} 'УВЕЛИЧИМ СЧЕТЧИК ЩЕЛЧКОВ ВПРАВО
Inc R31
Sts {r_cinc} , R31
Int0_4:
Ldi R31 , &HFF
Sts {edat} , R31 'поставить признак "НОВЫЕ ДАННЫЕ ЕНКОДЕРА"
'---------
Int0_e:
Pop R31
Out Sreg , R31
Pop R31

============================================================================= 26
===================================== Справочник по программированию «Bascom-AVR» ==
$end Asm
Return
'----------------------------------------------------------------------------------

Пример 3 - Прерывание АЦП при завершении преобразования


'----------------------------------------------------------------------------------
Dim Tmpb As Byte 'временные байтовые данные
Dim B_adc As Byte 'указатель "есть новые данные внутреннего АЦП"
Dim Dadc As Word 'данные АЦП (внутреннего)
Dim Ua As Single 'временное значение
' ......
Mcucr = &B10000000 'IDLE – разрешить
' ......
'------------------------------------------
' измерение с помощью внутреннего АЦП результат Ua, выражен в Вольтах шкалы
Rd_iadc:
Admux = &B00000001 'внешняя опора АЦП со входа AREF, внутренняя выключена
'измерение по входу PA1 (напряжение)
Adcsr = &B10001110 'разрешить АЦП с частотой тактирования F / 64 в режиме
'с естественным положением битов, прерывание разрешено
'-------
For Tmpb = 1 To 16 'произвести 16 измерений
Set Adcsr.6 'запустить АЦП
Rdiadc1:
Idle 'останов
If B_adc = 0 Then 'есть данные внутреннего АЦП?
Goto Rdiadc1 'нет - повторить
End If
Next Tmpb
Adcsr = &B00000110 'запретить АЦП
Dadc = Dadc - 25 'коррекция смещения нуля
Ua = Dadc : Dadc = 0 'в формат с плавающей точкой, а исходный очистить
Ua = Ua * 0.0003052 : Return 'привести к шкале 0...5 В
'------------------------------------------
'обработка прерывания от внутреннего АЦП
Adc_int:
$asm
Push R31 'сохраним регистры
In R31 , Sreg
Push R31
Push R30
Push R29
'-----
'считать данные внутреннего АЦП
lds R29 , {Dadc} 'считать сумму
Lds R30 , {Dadc + 1}
In R31 , Adcl 'считать показани
Add R29 , R31 'добавить к сумме показания АПЦ
In R31 , Adch
Adc R30 , R31
Sts {Dadc} , R29
Sts {Dadc + 1} , R30
'-----
Ldi R31 , 255 'есть данные внутреннего АЦП
Sts {B_adc} , R31
'-----
Adcinte:
Pop R29 'восстановим регистры
Pop R30
Pop R31
Out Sreg , R31
Pop R31
Reti
$end Asm
Return
'----------------------------------------------------------------------------------

Пример 4 - Прерывание приемника и передатчика UART


'----------------------------------------------------------------------------------
Dim Rdat As Byte 'признак "Есть новые данные из RS"
Dim Cnt_rc As Byte 'счетчик принимаемых из RS символов
Dim R_cch As Word 'указатель буфера принимаемых символов
Dim Bufrr As String * 20 'принятой строки из RS
Dim Bufr As String * 20 'обрабатываемой строки из RS
Dim T_cch As Word 'указатель буфера передаваемых символов
Dim Buft As String * 20 'передаваемая строка

============================================================================= 27
===================================== Справочник по программированию «Bascom-AVR» ==
' ......
'---------------------
' UART:
$baud = 9600 'скорость 9.6 кБ
$crystal = 8000000 'при кварце 8 МГц
'---------------------
' назначение векторов прерывания
On Urxc Rxd_int Nosave 'вектор прерывания от приемника UART
On Utxc Txd_int Nosave 'вектор прерывания от приемника UART
'---------------------
Gosub Clr_bufrr 'очистить переменные приемного буфера
Enable Urxc 'разрешить прерывание приемника UART
Enable Utxc 'разрешить прерывание передатчика UART
Enable Interrupts 'разрешить прерывания
'---------------------
' ......
Buft = "TRANSFERED DATA" 'передаваемая строка
Gosub Print_tb 'передать буфер
' ......
'----------------------------------------------
' обработка прерывания приемника UART
Rxd_int:
$asm
Push R31 'сохраним регистры
In R31 , Sreg
Push R31
Push R30
Push R27
Push R26
'---------
'принимаем данные интерфейса
Lds R26, {R_cch} 'значение текущей позиции буфера
Lds R27, {R_cch + 1}
In R31 , Udr 'считать принятый символ
'преобразование строчного символа в прописной
Andi R31 , &h7f 'ограничение диапазона символов
Cpi R31 , &h61 'ниже кода символа 'a'? (нижняя граница)
Brcs Rxdc_0 'да - переход
Cpi R31 , &h7b 'выше кода символа 'z'? (верхняя граница)
Brcc Rxdc_0 'да - переход
Andi R31 , &h5f 'между ними - наложить маску
'-----
Rxdc_0:
Cpi R31 , &H0A 'CR?
Breq Rxdc_e 'игнорировать
Cpi R31 , &H0D 'LF?
Breq Rxdc_2 'это конец строки
St X+ , R31 'остальное записывать в буфер
Lds R30, {Cnt_rc}
Inc R30
Cpi R30 , 20 'число 20 – длина буфера
Breq Rxdc_e 'если не последняя позиция
Rxdc_1:
Sts {r_cch} , R26 'записать измененный указатель буфера
Sts {r_cch + 1} , R27
Sts {cnt_rc} , R30 'и новое значение счетчика символов
Rxdc_e:
Pop R26
Pop R27
Pop R30
Pop R31
Out Sreg , R31
Pop R31
Reti
'---------
'принят конец строки – нужно переписать принятые данные в промежуточный буфер
Rxdc_2:
Ldi R31 , &HFF
Sts {rdat} , R31 'поставить признак наличия принятой строки в буфере Bufr
'---------
'займем еще регистры (Y) для копирования из Bufrr в Bufr
Push R29 'сохраним их
Push R28
$end Asm
Loadadr Bufr , X 'указатель буфера Bufr в Y на начало
$asm
Mov R28 , R26 'применим такой извратный способ, чтобы компилятор
Mov R29 , R27 'не увидел явного использования программного стека
$end Asm

============================================================================= 28
===================================== Справочник по программированию «Bascom-AVR» ==
Loadadr Bufrr , X 'указатель буфера Bufrr в X на начало
$asm
Lds R30, {Cnt_rc} 'считать число принятых символов
Rxdc_c:
Ld R31 , X+ 'считать из Bufrr
St Y+ , R31 'записать в буфер Bufr
Dec R30
Brne Rxdc_c
St Y , R30 'записать в буфер Bufr конец строки
Pop R28 'восстановим указатель программного стека
Pop R29
'---------
$end Asm
Loadadr Bufrr , X 'указатель буфера Bufrr в X снова на начало
$asm
Rjmp Rxdc_1 'и сбросить значение счетчика символов
$end Asm
Return 'пустая команда, на место которой компилятор
'поставит обязательную команду RETI
'----------------------------------------------
' обработка прерывания передатчика UART
Txd_int:
Push R31 'сохраним регистры
In R31 , Sreg
Push R31
Push R27
Push R26
'-----
Lds R26 , {t_cch} 'значение текущей позиции буфера
Lds R27 , {t_cch + 1}
Ld R31 , Y+
And R31 , R31 'проверить на нуль
Breq Txdint1 'если он равен 0 - переход
!Out Udr , R31 'записать передаваемый символ
Sts {t_cch} , R26 'записать измененный указатель буфера
Sts {t_cch + 1} , R27
'-----
Txdint1:
Pop R27 'восстановим регистры
Pop R26
Pop R31
!Out Sreg , R31
Pop R31
Reti
$end Asm
Return
'----------------------------------------------
' ......
'----------------------------------------------
'передача буфера
Print_tb:
Buft = Buft + Chr(13) + Chr(10) 'добавить к содержимому буфера команды конца строки
T_cch = Varptr(buft) 'установить указатель на начало буфера
Udr = Peek(t_cch) 'инициализировать начало передачи, загрузив
'первый символ в передатчик это можно было бы не делать,
'если бы можно установить бит TXC (Ucsra , 6),
'а использовать бит UDRE (Ucsra , 5) нельзя
Incr T_cch 'установить указатель на следующий символ
Return
'----------------------------------------------
'очистить переменные приемного буфера
Clr_bufrr:
Bufrr = "" 'сам буфер (записать нуль в начало)
Rdat = 0 'признак наполнения
Cnt_rc = 0 'счетчик принятых символов
R_cch = Varptr(bufrr) 'указатель буфера на начало
Return
'----------------------------------------------------------------------------------

Разумеется, что при отсутствии необходимости экономить ресурсы микроконтроллера программа прерыва-
ния может быть выполнена без ассемблерных вставок. Ниже приведен пример программы, содержащей такой об-
работчик прерываний. Сохранение регистров общего назначения выполняется компилятором самостоятельно (оп-
ция NOSAVE не применена) и все операции обработки данных производятся с использованием операторов Bas-
com-AVR.

============================================================================= 29
===================================== Справочник по программированию «Bascom-AVR» ==

Пример 5 - Прерывание от изменения уровня на линиях порта


'----------------------------------------------------------------------------------
$regfile = "m644def.dat" 'для чипа ATMega644
' ......

Dim Num_port As Byte 'номер порта с изменившимся уровнем


Dim Dub_pa As Byte 'дублер порта A
Dim Dub_pb As Byte 'дублер порта B
Dim Dub_pc As Byte 'дублер порта C
Dim Dub_pd As Byte 'дублер порта D
Dim Tmpb As Byte 'временные байтовые данные

' ......

'определим действующие линии портов как константы


Const Pcamsk = &B11111111 'порта PA
Const Pcbmsk = &B11111111 'порта PB
Const Pccmsk = &B00000011 'порта PC
Const Pcdmsk = &B00111100 'порта PD

' ......

On Pcint0 Pc_int0 'векторы прерывания от изменения уровней на линиях портов


On Pcint1 Pc_int1
On Pcint2 Pc_int2
On Pcint3 Pc_int3

' ......
'настроить систему
Pcicr = &B00001111 'разрешим прерывания от изменения уровны во всех портах
Pcmsk0 = Pcamsk 'определим действующие линии портов: порта PA
Pcmsk1 = Pcbmsk 'порта PB
Pcmsk2 = Pccmsk 'порта PC
Pcmsk3 = Pcdmsk 'порта PD
Dub_pa = Pina And Pcamsk 'запомнить текущее состояние линий портов
Dub_pb = Pinb And Pcbmsk
Dub_pc = Pinc And Pccmsk
Dub_pd = Pind And Pcdmsk
Enable Interrupts
' ......
'главный цикл
Mc:
If Num_port <> 0 Then
Print Num_port : Num_port = 0
End If
Goto Mc

'--------------------------------------
'программы обработки прерываний от изменения уровня, сообщающие главной программе
'о наличии изменения ненулевым значением переменной Num_port
'--------------------------------------
'по линиям порта A
Pc_int0:
Tmpb = Pina Xor Pcamsk : Tmpb = Pina Xor Dub_pa : Num_port = 0
Gosub Search_pin 'добавить в Num_port номер позиции единицы
Return
'--------------------------------------
'по линиям порта B
Pc_int1:
Tmpb = Pinb Xor Pcbmsk : Tmpb = Pinb Xor Dub_pb : Num_port = 8
Gosub Search_pin 'добавить в Num_port номер позиции единицы
Return
'--------------------------------------
'по линиям порта C
Pc_int2:
Tmpb = Pinc Xor Pccmsk : Tmpb = Pinc Xor Dub_pc : Num_port = 16
Gosub Search_pin 'добавить в Num_port номер позиции единицы
Return
'--------------------------------------
'по линиям порта D
Pc_int3:
Tmpb = Pind Xor Pcdmsk : Tmpb = Pind Xor Dub_pd : Num_port = 24
Gosub Search_pin 'добавить в Num_port номер позиции единицы
Return
'--------------------------------------
'подпрограмма поиска "1" в измененного уровня, на самом деле, это преобразование
'позиционного кода в Tmpb в двоичный в Num_port. Методом сдвига вправо и искремента результата

============================================================================= 30
===================================== Справочник по программированию «Bascom-AVR» ==
Search_pin:
While Tmpb <> 0 'повторять пока регистр Tmpb не станет равным нулю
Shift Tmpb , Right : Incr Num_port
Wend
Return
'----------------------------------------------------------------------------------

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

Пример 6 - Ассемблерный текст обработчика прерывания (листинг) из программы примера 5,


получаемый при компиляции Bascom-AVR
'----------------------------------------------------------------------------------
Pc_int0:
;сохранение регистрового блока
push r0
push r1
push r2
push r3
push r4
push r5
push r7
push r10
push r11
push r16
push r17
push r18
push r19
push r20
push r21
push r22
push r23
push r24
push r25
push r26
push r27
push r28
push r29
push r30
push r31
in r24,$3F
push r24
;Tmpb = Pina Xor Pcamsk
in r16,$0
ldi r20,#255
eor r16,r20
ldi r26,#5
ldi r27,#1
st x,r16
;Tmpb = Pina Xor Dub_pa
in r16,$0
lds r20,$101
eor r16,r20
ldi r26,#5
ldi r27,#1
st x,r16
;Num_port = 0
ldi r24,#0
sts $100,r24
;Gosub Search_pin 'добавить в Num_port номер позиции единицы
call 0000036A
;восстановление регистрового блока
pop r24
out $3F,r24
pop r31
pop r30
pop r29
pop r28
pop r27
pop r26
pop r25
pop r24
pop r23
pop r22
pop r21

============================================================================= 31
===================================== Справочник по программированию «Bascom-AVR» ==
pop r20
pop r19
pop r18
pop r17
pop r16
pop r11
pop r10
pop r7
pop r5
pop r4
pop r3
pop r2
pop r1
pop r0
;Return
Reti
;------
;подпрограмма Search_pin:
0000036A:
While Tmpb <> 0 'повторять пока регистр Tmpb не станет равным нулю
lds r16,$105
cpi r16,#0
brne 00000376
jmp 0000038E
00000376:
;Shift Tmpb , Right
ldi r25,#1
ldi r26,#5
ldi r27,#1
call 000003C6 ;вызов библиотечной программы
;Incr Num_port
ldi r26,#0
ldi r27,#1
ld r24,x
subi r24,#255
st x,r24
;Wend
jmp 0000036A
0000038E:
ret
;------
;библиотечная подпрограмма сдвига вправо на число в R25
000003C6:
cpi r25,#0
breq 000003D4
ld r24,x
000003CC:
lsr r24
dec r25
brne 000003CC
st x,r24
000003D4:
ret
'----------------------------------------------------------------------------------

============================================================================= 32
===================================== Справочник по программированию «Bascom-AVR» ==

Вопросы и ответы (1-10)


---------------------------------------------------------------------------------------------------------------------------------------------------
Вопрос 1

Но вот не могу понять только одного, почему в такой короткой программе, как эта:

Dim A As Byte
For A=1 to 30
Print A
Next A
End

…при компиляции размер кода составляет более 300 байт?? Абсурд! Баском конечно кушает память, но
чтобы так!.. Может я в чём не прав?

Ответ 1

1 Насчет размера вашего примера. Такой же будет для любого компилятора. В этот размер входит:
- заголовок программы, включаемый в код автоматически (по умолчанию) и содержащий место под все
вектора прерывания по 4 байт на вектор, очистку памяти 20-30 байт и инициализацию оборудования 30 - 100 байт;
- библиотека используемой функции и плюс 2-3 стандартных - около 100 байт;
- и собственно код вашей программы. Т.е. все сходится. Зато еще один вызов данной функции приводит к
добавлению всего 20 - 30 байт.
---------------------------------------------------------------------------------------------------------------------------------------------------
Вопрос 2

Да и вообще, здесь много подводных камней, про которые даже в pdf-файле не найдешь…Я изучал когда-то
давно Бейсик, сейчас плохо помню, приходиться заново учить. К какому Бейсику ближе Баском?

Ответ 2

Синтаксис и мнемоника, в принципе, отвечают стандарту Basic для DOS с учетом особенностей, ограниче-
ний и дополнительных возможностей. Лучше на стандартный Basic не ориентироваться. Слишком много ограни-
чений, обусловленных различными возможностями процессоров. Главное отличие в том, что нужно определить
тип переменных заранее, не допускать скрытого преобразования типов и не применять больше одного действия в
одном операторе.) Лучше ориентироваться на оригинальное описание Bascom-AVR – оно отличное. На русском
имеется только описание Bascom-8051. И еще на примеры - их очень много.
--------------------------------------------------------------------------------------------------------------------------------------------------
Вопрос 3

Я сначала ничего не подозревал. Пытался спокойно делать себе программки в Баскоме, как вдруг понял, что
реализация протокола I2C(TWI) в любом микроконтроллере происходит программно, независимо от того, есть ли
в нём аппаратная поддержка данного протокола или нет. Я бы ничего этого не заметил, если бы не наткнулся на
проблему нехватки памяти микроконтроллера. В конечном итоге выяснилось, что реализация аппаратного прото-
кола возможна только в лицензионной версии Баскома, так как в демо-версии нет соответствующей библиотеки.

Ответ 3

По моему наблюдению, использование аппаратного контроллера с точки зрения размера кода совершенно
неэффективно. При программной реализации размер кода получается равноценным. Потому, что работа с аппа-
ратным портом конечно проще, но при этом требуется организовать обработку соответствующего прерывания.
Преимущество аппаратного I2C только в возможности осуществлять "прозрачную" работу одновременно с други-
ми процессами и при наличии очень медленных устройств. Когда, например, в фоновом режиме нужно постоянно
считывать или дописывать данные. При этом в коде "весит" именно программа организации МАССИВА записы-
ваемых или считываемых данных, а не сам драйвер. Вот пример работающей программы на BAS и на ASM. В ос-
новной программе применяются готовые функции. В прерывании используется ассемблерная программа (а ее
размер минимален). Если устройство более медленное - нужно вместо NOP-ов добавить в программу подпро-
граммы более продолжительной задержки. Не забывать, что когда работаешь с I2C, нужно бы запретить прерыва-
ния. Если длительность импульса SCL превысит 10 мкс устройство может само отключится! В последующем
примере переключение уровней делается изменение направления порта. Это важно. Так можно манипулировать с
им намного быстрее.

'--------------------------------------------------------------

============================================================================= 33
===================================== Справочник по программированию «Bascom-AVR» ==
' Программы обслуживания АЦП типа ADS1110 (Burr-Broun)
'--------------------------------------------------------------
' результаты
' 1 смещение входа имеется и весьма заметно 1-2 мВ. Зависит от включенного коэффициента усиления
' 2 При усилении К=1 шум меньше 1 дискрета, что делает усреднение бесполезным
' 3 При максимальном усилении К=8 шум составляет около 20-30 мкВ пик-пик
'--------------------------------------------------------------
Config Sda = Portc.1 'линия данных
Config Scl = Portc.0 'линия синхронизации
Config I2cdelay = 1 'наивысшая скорость
'-----------------------------
'АВТООПРЕДЕЛЕНИЕ АДРЕСА АЦП
Find_adr:
Adradc = 142 'АДРЕС НУЛЕВОЙ ПРОШИВКИ(90h -2)
Disable Interrupts 'запретить прерывания
Findadr1:
Adradc = Adradc + 2 'АДРЕС ДЛЯ ЧТЕНИЯ + 2
If Adradc <> 160 Then
Goto Findadr2 'ПРОВЕРИМ НА МАКСИМУМ(90h + 16)
End If
Adradc = 144 'АДРЕС НУЛЕВОЙ ПРОШИВКИ (90h)
Goto En_intr 'НА ВЫХОД ЕСЛИ ВСЕ АДРЕСА ПРОЙДЕНЫ
Findadr2:
$asm
Lds R31 , {Adradc} 'АДРЕС ДЛЯ ЧТЕНИЯ
Inc R31
Rcall Start_bit 'СТАРТ, ДАННЫЕ И ЖДЕМ ПОДТВЕРЖДЕНИЯ
'------------
Rcall R_byte 'ПРИМЕМ СТАРШИЙ БАЙТ
Rcall Mstr_ack 'ВЫДАЧА СИГНАЛА ПОДТВЕРЖДЕНИЯ
Rcall R_byte 'ПРИМЕМ МЛАДШИЙ БАЙТ
Rcall Mstr_ack 'ВЫДАЧА СИГНАЛА ПОДТВЕРЖДЕНИЯ
Rcall R_byte 'ПРИМЕМ СТАТУС
Rcall No_ack 'ДА - БЕЗ ПОДТВЕРЖДЕНИЯ И СФОРМИРУЕМ СТОП-БИТ
Andi R31 , &B01100000 'АНАЛИЗИРУЕМ D5 И D6. ВСЕГДА Д.Б. = 0
Brne Findadr1 'ЕСЛИ НЕ НУЛЬ - ПОВТОРИТЬ
$end Asm
Goto En_intr 'НА ВЫХОД ЕСЛИ АДРЕС НАЙДЕН
'------------------------------------------
'п/п записи статуса АЦП ADS1110 (нормальный медленный режим) 15 изм/с. Шкала 1.024 В
Slow_adc:
' ------------ Запуск преобразования: 1 только в одиночном
' | --------- Запуск: 0 - автоматический, 1 - одиночное
' | |
' | | ------- Скорость: 00 - 240 SPS, 01 - 60 SPS
' | | | 10 - 30 SPS, 11 - 15 SPS
' | | |
' | | | ----- Усиление: 00 - 1 , 01 - 2
' | | | | 10 - 4 , 11 - 8
' | | | |
' | |||||
Value = &B00001101
Wr_adc:
Disable Interrupts 'запретить прерывания
I2cstart 'старт
I2cwbyte Adradc 'байт адреса
I2cwbyte Value 'байт режима
I2cstop 'стоп
$asm
'подготовить порты к дальнейшему использованию ассемблерной программой
Cbi Ddrc , 0 'установим на Scl плавающую "1"
Cbi Portc , 0 'подготовим "0" для вывода
Cbi Ddrc , 1 'установим на Sda плавающую "1"
Cbi Portc , 1 'подготовим "0" для вывода
$end Asm
En_intr:
Enable Interrupts 'разрешить прерывания
Return
'------------------------------------------
'п/п записи статуса АЦП ADS1110 (быстрый режим) 240 изм/с. Шкала 1.024 В
Speed_adc:
' ------------ Запуск преобразования: 1 только в одиночном
' | --------- Запуск: 0 - автоматический, 1 - одиночное
' | |
' | | ------- Скорость: 00 - 240 SPS, 01 - 60 SPS
' | | | 10 - 30 SPS, 11 - 15 SPS
' | | |
' | | | ----- Усиление: 00 - 1 , 01 - 2
' | | | | 10 - 4 , 11 - 8

============================================================================= 34
===================================== Справочник по программированию «Bascom-AVR» ==
' | | | |
' | |||||
Value = &B00000001
Goto Wr_adc
'------------------------------------------
'п/п записи статуса АЦП ADS1110 (нормальный режим) 15 изм/с. Шкала 2.048 В
Wide_adc:
' ------------ Запуск преобразования: 1 только в одиночном
' | --------- Запуск: 0 - автоматический, 1 - одиночное
' | |
' | | ------- Скорость: 00 - 240 SPS, 01 - 60 SPS
' | | | 10 - 30 SPS, 11 - 15 SPS
' | | |
' | | | ----- Усиление: 00 - 1 , 01 - 2
' | | | | 10 - 4 , 11 - 8
' | | | |
' | |||||
Value = &B00001100
Goto Wr_adc
'------------------------------------------
'ЧТЕНИЕ ДАННЫХ ИЗ АЦП В R27(ст), R26(мл), статус в R31
Rd_adc:
$asm
' Cbi Ddrc , 0 'установим на Scl плавающую "1"
' Cbi Portc , 0 'подготовим "0" для вывода
' Cbi Ddrc , 1 'установим на Sda плавающую "1"
' Cbi Portc , 1 'подготовим "0" для вывода
Lds R31 , {Adradc} 'АДРЕС ДЛЯ ЧТЕНИЯ
Inc R31
Rcall Start_bit 'СТАРТ, ДАННЫЕ И ЖДЕМ ПОДТВЕРЖДЕНИЯ
'------------
Rcall R_byte 'ПРИМЕМ СТАРШИЙ БАЙТ
Mov R29 , R31 'ЗАПИШЕМ ДАННЫЕ В ОЗУ
Rcall Mstr_ack 'ВЫДАЧА СИГНАЛА ПОДТВЕРЖДЕНИЯ
Rcall R_byte 'ПРИМЕМ МЛАДШИЙ БАЙТ
Mov R28 , R31 'ЗАПИШЕМ ДАННЫЕ В ОЗУ
Rcall Mstr_ack 'ВЫДАЧА СИГНАЛА ПОДТВЕРЖДЕНИЯ
Rcall R_byte 'ПРИМЕМ СТАТУС
Rjmp No_ack 'ДА - БЕЗ ПОДТВЕРЖДЕНИЯ И СФОРМИРУЕМ СТОП-БИТ
'------------------------------------------
'порты PC.0, PC1 инициализированы на ввод (исходно DDRC.0 = 0, DDRC.1 = 0),
'а в порты PORTC.0 PORTC.1 записывается "0". Таким образом на линиях устанавливается "1".
'воспроизведение "0" производися переключением направления
'------------------------------------------
' Portc.0 - Scl, Portc.1 - Sda
'------------------------------------------
'ФОРМИРОВАНИЕ СТАРТ-БИТА И ЕЩЕ ДВЕ ОПЕРАЦИИ
Start_bit:
Cbi Ddrc , 0 'УСТАНОВКА И ОЖИДАНИЯ ОТПУСКАНИЯ ЛИНИИ Scl_l
Nop '------------------
Nop ' ¦ ¦
Cbi Ddrc , 1 '--- -
Nop 'Scl-----------------
Nop '
Sbi Ddrc , 1 '-------------
Nop ' ¦ ¦
Nop '------- ------
Sbi Ddrc , 0 'Sda-----------------
'--------------------------
'ВЫДАЧА НА ЛИНИЮ БАЙТА ДАННЫХ ЗАПИСАННЫХ В R31
T_byte:
Ldi R30 , 8 'ЧИСЛО БИТ
T_by0:
Sbi Ddrc , 0 'ЦИКЛ ПЕРЕДАЧИ
Rol R31 'СДВИГ
Brcc T_by1
Cbi Ddrc , 1 'порт в "1"
Rjmp T_by2
T_by1:
Sbi Ddrc , 1 'порт в "0"
T_by2:
'УСТАНОВКА И ОЖИДАНИЯ ОТПУСКАНИЯ ЛИНИИ Scl_l
Cbi Ddrc , 0 '- ----- - - -- -----
Dec R30 ' ¦ ¦ ¦ ¦ ¦ ¦
Brne T_by0 '------ ---- ----
'Scl--------------------------------
'
'---- ---------- --- - - --- -------
' | | |

============================================================================= 35
===================================== Справочник по программированию «Bascom-AVR» ==
'---- ---------- --- - - --- -------
'Sda--------------------------------
'--------------------------
'СОСТОЯНИЕ ОЖИДАНИЯ БИТА ОТВЕТА (ПОДТВЕРЖДЕНИЕ)
Slave_ack:
Sbi Ddrc , 0 'УСТАНОВКА И ОЖИДАНИЯ ОТПУСКАНИЯ ЛИНИИ Scl_l
Nop ' ----
Cbi Ddrc , 1 '¦ ¦ ¦
Nop ' ------ ---
Cbi Ddrc , 0 'Scl-------------
Nop '
Nop '----------------
Sbi Ddrc , 0 ' ¦ | |
Ret '----- ------
'Sda-------------
'--------------------------
'БЕЗ ВЫДАЧИ СИГНАЛА ПОДТВЕРЖДЕНИЯ И ЕЩЕ ОДНА ОПЕРАЦИЯ
No_ack:
Cbi Ddrc , 1 'УСТАНОВКА И ОЖИДАНИЯ ОТПУСКАНИЯ ЛИНИИ Scl_l
' ------
Cbi Ddrc , 0 ' ¦ ¦
Nop '---- ----
Nop 'Scl-------------
Sbi Ddrc , 0 '----------------

'-
'Sda-------------
'--------------------------
'ФОРМИРОВАНИЕ СТОП-БИТА
Stop_bit:
Sbi Ddrc , 1 ' ---------
' ¦
'-----
Cbi Ddrc , 0 'Scl------------
Nop ' ----
Cbi Ddrc , 1 '¦ ¦
Ret ' ---------
'Sda------------
'------------------------------------------
'ЧТЕНИЕ С ЛИНИИ БАЙТА ДАННЫХ В R31
R_byte:
Cbi Ddrc , 1 'НА ВСЯКИЙ СЛУЧАЙ
Ldi R30 , 8 'ЧИСЛО СЧИТЫВАЕМЫХ БИТ
R_by0:
Cbi Ddrc , 0 'ЦИКЛ СЧИТЫВАНИЯ
Sec 'УСТАНОВКА И ОЖИДАНИЯ ОТПУСКАНИЯ ЛИНИИ SCL
Nop '------- --- -- ----
' ¦ ¦ ¦ ¦ ¦ ¦
Sbis Pinc , 1 '-- ---- ---- -----
Clc 'SCL--------------------------------
Sbi Ddrc , 0 '---------------- - ----------------
Rol R31 '¦ | R | R | R
Dec R30 '¦ | D | D | D
Brne R_by0 ' --------- - - ------- --------
Ret 'SDA--------------------------------
'--------------------------
'ВЫДАЧА СИГНАЛА ПОДТВЕРЖДЕНИЯ
Mstr_ack:
Sbi Ddrc , 0
Nop 'УСТАНОВКА И ОЖИДАНИЯ ОТПУСКАНИЯ ЛИНИИ SCL
Sbi Ddrc , 1 ' ------
Nop '¦ ¦ ¦
Cbi Ddrc , 0 ' -------- -----
Nop 'SCL-------------------
Sbi Ddrc , 0 '---- --
Nop ' ¦ ¦
Cbi Ddrc , 1 ' --------------
Ret 'SDA-------------------
'------------------------------------------
$end Asm

--------------------------------------------------------------------------------------------------------------------------------------------------
Вопрос 4

Я читал, что Bascom дает громоздкий и неоптимальный код. Это правда? Тогда какие выгоды дает примене-
ние этого компилятора?

============================================================================= 36
===================================== Справочник по программированию «Bascom-AVR» ==

Ответ 4

Не нужно паниковать о том, что Bascom дает некомпактный код. Размер кода полностью соответствует
классу компиляторов с языков высокого уровня. Уступает только CodeVision на малых проектах, а с другими
компиляторами Си успешно конкурирует. Bascom особенно хорош в больших проектах, когда мелочная оптими-
зация уже не приносит заметного выигрыша, а важнее структурированность исходного текста.
Размер кода в громадной степени зависит от стиля написания. Нужно применять тоже приемы, что и при
программировании на ассемблере:
- похожие программы оформлять в подпрограммы. Различия формировать ветвлением и дроблением участ-
ков;
- применять простые конструкции, минимум вложенности (от вложенности код становится еще больше).
Лучше больше ветвится по тексту программы, чем применять вложенные проверки условий. Чем длиннее текст и
применяются более мелкие операторы, тем короче код и он быстрее работает;
- программы прерывания писать на ассемблере, осуществляя минимум действий. Запрещать автоматически
сохранять регистры;
- применять больше переменных (на каждый чих свою, обычно ресурсов хватает), чтобы было меньше "за-
морочек", чтобы не вспоминать лишний раз о возможности одновременного использования;
- применять конструкцию On … Goto с таблицей переходов даже избыточной, а не любимую многими
Select Case;
- применять табличные методы установки и выбора параметров Lookup(offset,Table);
- программа должна построена таким образом, чтобы обращение к каждому элементу периферии встреча-
лось в программе только один раз;
- также как и задание числовых данных нужно делать один раз.
Не рекомендуется применять экзотические методы передачи параметров (через стек).
Просто советы:
- не вставлять Return внутрь закрытых конструкций (типа if.. do.. for..) , а применять переход Goto на вы-
ходную метку;
- не располагать переменные (Single, массивы и строковые) на границе страниц памяти. Поступать так -
скомпилить и посмотреть. При необходимости определить распределение памяти для Single и String самостоя-
тельно вручную. Например:
Dim Kzr0 As Single At 1024
Dim Kzr1 As Single At 1028
или набить промежутки на границе страниц пустыми неиспользуемыми переменными;
- не забывать один оператор - одно действие (это не Си).
--------------------------------------------------------------------------------------------------------------------------------------------------
Вопрос 5

При преобразовании демоверсии 1.11.8.1 в полную, добавление dll-файлов библиотека не расширяется. Как
быть?

Ответ 5

Неполность библиотек не имеет большого значения. Большинство из них просто не представлено в виде
исходного текста, который можно править и добавлять. За 10 лет использования Bascom ни разу в исходном она
мне понадобилась. На самом деле, отсутствуют только утилиты уникального оборудования, которое, все равно,
приходится описывать "вручную", чтобы не потерять критичные свойства. Во-вторых, имеющиеся библиотеки
часто не обеспечивают совместную (параллельную) работу с другим оборудованием (параллельными задачами).
В-третьих, со сложной периферией МК приходится работать напрямую - просто пишешь и читаешь регистры,
анализируешь биты. Так вернее и полезней - иначе нельзя использовать все возможности и не будешь отчетливо
представлять ее работу. Если лень читать, можно применять утилиту CodeVizard компилятора CodeVision. Сдела-
но очень точно.

--------------------------------------------------------------------------------------------------------------------------------------------------
Вопрос 6

Не могу понять, как правильно использовать эту настройку. 1. По поводу настройки ТАЙМЕРА1.В оригинальном
описании написано:
CONFIG TIMER1 = COUNTER | TIMER | PWM , EDGE=RISING | FALLING , PRESCALE= 1|8|64|256|1024 ,
NOISE CANCEL=0 |1, CAPTURE EDGE = RISING | FALLING , CLEAR TIMER = 1|0,
COMPARE A = CLEAR | SET | TOGGLE I DISCONNECT ,
COMPARE B = CLEAR | SET | TOGGLE I DISCONNECT

============================================================================= 37
===================================== Справочник по программированию «Bascom-AVR» ==

Ответ 6

Не пользуйтесь для настройки конфигурации ключевыми словами Баскома. Забыть их! Ведь кристаллы
разные, расположение битов и регистров различное, очень велика вероятность, что вы или компилятор ошибется.
Например, в Bascom-8051 кристаллы совместимы по периферии и все поэтому работает надежно. В Bascom-AVR
такие настройки оставлены для совместимости. Читайте Datasheet на процессоры и записывайте необходимые
настройки сами. Причем лучше пользоваться оригинальными документами. В переводных много ошибок. Их цен-
ность только в возможности быстрее изучить интересующий вопрос.

--------------------------------------------------------------------------------------------------------------------------------------------------
Вопрос 7

Как записать значение в регистр совпадения OCR1A, причём так, чтобы это можно было сделать в любом
месте программы? Как записать в значение в сам таймер-счётчик (регистр TCNT1)?

Ответ 7

'Да. Например, число 12345. Преобразуем в калькуляторе в HEX 12345=3039h


OCR1AH=&H30 : OCR1AL=&H39 'при записи обязательно старший байт первым
'-----------------------------
TCNT1H=&h11:TCNT1H=&hFF 'по записи первым старший потом младший
Byte1=TCNT1H:Byte2=TCNT1H 'при чтении первым младший потом старший
'-----------------------------
TCNT1H=High(word):TCNT1H=low(word) 'так записывается двухбайтное число
TEMPh=TCNT1H : TEMPl=TCNT1H : Word=Makeint(Temph,Templ) 'так читается в двухбайтовое число
'-----------------------------
'и как это выглядит на ассемблере
'-----------------------------
'считать значение, полученное в счетчике таймера 1
Rd_tim1:
$asm
In R30 , Tcnt1l 'СЧИТАТЬ ДАННЫЕ
In R31 , Tcnt1h 'из таймера
Sts {r_bd} , R30
Sts {r_bd + 1} , R31
Lds R30 , {c_msb}
'---------
In R31 , Tifr 'проверим бит переполнения таймера 1
Sbrs R31 , 2 'этой операцией мы учтем еще не обработанное переполнение таймера
Rjmp Rdtim_1 'если бит переполнения таймера 1 установлен,
Inc R30 'добавим единицу в счетчик старших разрядов
'---------
Rdtim_1:
Sts {r_bd + 2} , R30
Clr R31
Sts {r_bd + 3} , R31
$end Asm
Return

'----------------------------------------------
'оформление прерывания на ассемблере
'----------------------------------------------
'обработка прерывания от таймера 1
Timer1_int:
$asm
Push R31 'сохраним регистры
In R31 , Sreg
Push R31
'---------------------
Lds R31 , {c_msb}
Inc R31
Sts {c_msb} , R31 'добавить единицу в счетчик старших разрядов
'---------------------
Pop R31 'восстановим регистры
Out Sreg , R31
Pop R31
Reti
$end Asm
Return
'----------------------------------------------

--------------------------------------------------------------------------------------------------------------------------------------------------
Вопрос 8

============================================================================= 38
===================================== Справочник по программированию «Bascom-AVR» ==

Как подключить таймер1 в режиме сравнения к выводу OC1A?(а так же OC1B)?

Ответ 8

'все назначения конфигурации записывать в основной программе так


TCCR1A=&b01010000 'переключение выходов по совпадению OCR1A и OCR1B)
TCCR1B=&b00001001 'считать с частотой Fкв, перезагрузка по OCR1A

'если конфигурация делается в теле прерывания оформлять так


'----------------------------------------------
'запретить прерывание таймера 1
Dis_timer1:
$asm
Ldi R31 , &b00000001
Out Timsk , R31 'сбросить бит разрешения прерывания таймера 1.
Ldi R31 , &b00000100
Out Tifr , R31 'после запрещения сбросить флаг прерывания таймера 1
$end Asm
Return
'-----------------------------
'запретить внешнее прерывание 1
Dis_int0:
$asm
Ldi R31 , &b00000000 'сбросить бит разрешения внешнего прерывания.
Out Gimsk , R31 'по datasheet этот регистр называется Gicr
Ldi R31 , &b01000000
Out Gifr , R31 'после запрещения сбросить флаг прерывания.
$end Asm
Return

--------------------------------------------------------------------------------------------------------------------------------------------------
Вопрос 9

Могу ли я одновременно использовать COMPARE A и COMPARE В?

Ответ 9

'Да, но режим CTC (сброс по совпадению) возможен только от OCR1A


'или от OCR1B если не используется OCR1A
'вообще это "скользкое" место, плохо прописанное в оригинальной документации
'при этом коэффициент деления определяется OCR1A,
'а переключение выхода совпадением и с OCR1A и с OCR1B

--------------------------------------------------------------------------------------------------------------------------------------------------
Вопрос 10
Что означает Compare = Off(On) для компаратора? Я так понял, что это функция захвата таймера1, подклю-
ченного непосредственно к выходу компаратора.

'смотри регистр ACSR. Эта опция - фактически бит ACSR.2


'когда ACSR.2 = 1 по любому переключению компаратора значение TCNT1 запоминается в ICR1,
'как и по перепаду на входе CP1

Как можно в самой программе включить и выключить компаратор?


'это бит ACSR.7. По сбросу компаратор включен!
ACSR=&B1xxxxxxxx 'выключить
ACSR=&B0xxxxxxxx 'включить, значение остальных битов смотри сам

============================================================================= 39