Рекомендация по применению AVR242
;**** Рекомендация по применению AVR242 ************************
;*
;* Наимен.: Устройство дин. управления светодиодами и опроса клавиатуры 4х4
;* Версия: 1.0
;* последнее обновление: 98.07.24
;* Микроконтроллер: Любой AVR-микроконтроллер
;*
;* Поддержка через E-mail:avr@atmel.com
;*
;* Описание:
;* Данное приложение выполняет функции промышленного таймера или часов реального
;* времени с 24 часовым форматом счета времени с двойным назначением линий
;* ввода-вывода. Ввод организован через матричную клавиатуру 4х4, вывод данных
;* осуществляется на 4 разрядный цифровой светодиодный индикатор и два
;* дискретных светодиода, имитирующих вкл./выкл. нагрузки. Вместо данных
;* светодиодов можно применить согласовывающие элементы для управления внешней
;* нагрузкой. Нажатие на клавиши подтверждается звуковым сигналом, генерируемого
;* пьезокерамическим излучателем. В состав входит основная программа,
;* которая позволяет с помощью клавиатуры установить реальное время и времена
;* вкл./выкл. нагрузок, выполняет функции счета реального времени, сканирования
;* клавиатуры. Пример написан для микроконтроллера AT90S1200 для демонстрации
;* возможности преодоления ограничений ввода-вывода. Но можно применить любой
;* AVR-микроконтроллер, для чего необходимо откорректировать вектора,
;* ЭСППЗУ и указатель стека.
;* В схеме тактирования предполагается использование кварца 4.096 МГц
;* (4 МГц кварц создаст погрешность -0.16%, если в таймер перезагружать код 178,
;* а не 176,как в этом примере, следовательно, в программу необходимо внести
;* небольшие изменения. Таблица преобразования, записанная в ЭСППЗУ, хранит
;* семисегментные коды для индикатора, в т.ч. со специальными символами
;* для установки текущего времени и времен включения/выключения, а также коды
;* клавиш. Если ЭСППЗУ запланировано использовать в других целях, то таблицы
;* можно перенести в память программ, но с учетом использования более мощного
;* микроконтроллера.
;***************************************************************************
;***** Регистры используемые в пределах всей программы
;******Глобальные переменные, используемые процедурами
.def loset =r1 ;содержит минуты после установки
.def hiset =r2 ;содержит часы после установки
.def ld1minon =r3 ;время включения нагрузки 1 (минуты)
.def ld1hron =r4 ;время включения нагрузки 1 (часы)
.def ld1minoff =r5 ;время выключения нагрузки 1 (минуты)
.def ld1hroff =r6 ;время выключения нагрузки 1 (часы)
.def ld2minon =r7 ;время включения нагрузки 2 (минуты)
.def ld2hron =r8 ;время включения нагрузки 2 (часы)
.def ld2minoff =r9 ;время выключения нагрузки 2 (минуты)
.def ld2hroff =r10 ;время выключения нагрузки 2 (часы)
.def temp =r16 ;универсальный временный регистр
.def second =r17 ;счетчик секунд часов реального времени (ЧРВ)
.def minute =r18 ;счетчик минут ЧРВ
.def hour =r19 ;счетчик часов ЧРВ
.def mask =r20 ;маска мигания разрядов
.def blink =r21 ;Счетчик скорости мигания двоеточия
.def bounce =r22 ;Счетчик антидребезга клавитатуры
.def flash =r23 ;счетчик задержки зажигания
.def lobyte =r24 ;хранение разрядов минут для функции display
.def hibyte =r25 ; хранение разрядов часов для функции display
.def key =r26 ;Кол нажатой клавиши после опроса клавиатуры
;***'key' значения возвращаемые 'keyscan'***************************
;Код 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
;Кнопка 1 2 3 4 F 5 6 E 7 8 9 D A 0 B C Нет нажатия
;Функция 1 2 3 LD1ON 4 5 6 LD1OFF 7 8 9 LD2ON SET 0 CLEAR LD2OFF
.def tock =r27 ;5 мс импульс
.def flags =r28 ; флаги команд клавиатуры
;7 6 5 4 3 2 1 0
;5мс keyok ld2off ld2on ld1off ld1on ld2 ld1
; tick 0 = вкл., 1 = выкл.
.equ ms5 =7 ; 5мс интервалы для задания времени индикации одного разряда
.equ keyok =6 ; устанавливается после подавления дребезга кнопки
.equ ld2off =5 ;устанавливаются при нажатии кнопок задания времен
.equ ld2on =4 ; включения или выключения нагрузок 1 и 2
.equ ld1off =3 ;
.equ ld1on =2
.equ ld2 =1 ; разрешение управления нагрузками 2 и 1. Устанавливаются
.equ ld1 =0 ; автоматически после задания соответствующих времен включения
;***T-флаг регистра статуса используется как флаг установки времени
.equ clear =0 ;флаг изменения состояния ЧРВ (например, секунд)
;Линии порта B
.equ col1 =0 ;Сегмент a индикатора/столбец 1 клавиатуры
.equ col2 =1 ;Сегмент b индикатора/столбец 2 клавиатуры
.equ col3 =2 ;Сегмент c индикатора/столбец 3 клавиатуры
.equ col4 =3 ;Сегмент d индикатора/столбец 4 клавиатуры
.equ row1 =4 ;Сегмент e индикатора/строка 1 клавиатуры
.equ row2 =5 ;Сегмент f индикатора/строка 2 клавиатуры
.equ row3 =6 ;Сегмент g индикатора/строка 3 клавиатуры
.equ row4 =7 ;Сегмент : индикатора/строка 4 клавиатуры
;Линии порта D
.equ A1 =0 ; Выходы управления общими анодами индикатора (активный низкий)
.equ A2 =1 ;
.equ A3 =2 ;
.equ A4 =3 ;
.equ LOAD1 =4 ;Выход нагрузки 1 (активный низкий)
.equ LOAD2 =5 ;Выход нагрузки 2 (активный низкий)
.equ PZ =6 ;Выход пьезокерамического излучателя (активный низкий)
.include "1200def.inc"
;***** Регистры, используемые в процедуре обработки прерывания
.def timer =r31 ;временный регистр для перезагрузки таймера
.def status =r0 ; регистр для запоминания регистра статуса
;*****Таблица преобразования в семисегментный код **********************
.eseg ;Сегмент данных в ЭСППЗУ
.org 0
table1:
.db 0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90
;Отобр.0 1 2 3 4 5 6 7 8 9
.db 0x86,0x8E,0xA3,0xAB,0XFF,0XFF
;Отобр. E f o n пробел спец. символы
;****Таблица преобразования нажатой клавиши в удобный для пользователя код ***
;key1 2 3 F 4 5 6 E 7 8 9 D A 0 B C
table2:
.db 1, 2, 3,15, 4, 5, 6,14, 7, 8, 9, 13, 10, 0, 11, 12
;Значение 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
;****Код программы ***************************************************
.cseg ; Сегмент кода программы
.org 0
rjmp reset ;обработка сброса
nop ;не исп. прерывание
rjmp tick ;переполнение таймера (5 мс)
nop ;неиспользуемое прерывание аналогового компаратора
;*** Сброс **************************************************
;*** инициализация порта, таймера и прерывания
reset:
ser temp ;
out DDRB,temp ;настройка порта В на вывод
out DDRD,temp ;настройка порта D на вывод
out PORTB,temp ;все столбцы =1 / выключение сегментных светодиодов
out PORTD,temp ;выключение светодиодов и нагрузок
ldi temp,0x04 ;установка коэффициента предделения таймера 256
out TCCR0,temp
ldi timer,176 ;настройка периода переполнения на 5мс
out TCNT0,timer ;(256 - n)*256*0.2441 мкс
ldi temp,0x02 ;разрешение прерывания таймера
out TIMSK,temp
clr flags ;очистка флагов управления
clr tock ;очистка 5мс счетчика
clr bounce ;очистка счетчика дребезга клавиши
clr flash
clr blink
sei ;разрешение глобальных прерываний
;** Предупреждающее мигание символами EEEE на индикаторах**
;****Ожидает нажатия кнопки SET на клавиатуре
timesetting:
ldi hibyte,0xaa ; запись символов "EEEE"
ldi lobyte,0xaa ;для отображения
ser mask ;и установка мигания индикатора
notyet:
rcall display ;индикация и опрос клавиатуры
brtc notyet ;ожидание нажатия клавиши SET
rcall setrtc ; установка времени
mov hour,hiset ; и обновление часов
mov minute,loset ;и минут
clt ;очистка Т-флага
;***** Основной цикл сервисных функций ***************************
do:
clr mask ;начало выполнения функций
cpi blink,100 ;если прошло 0.5с,
brne nohalf
clr blink
com flash ;то инверсия мигания
nohalf:
cpi second,60 ; прошла 1 минута?
brne nochange ;нет
clr second ;да, очистка секунд и
inc minute ;увеличение минут
mov temp,minute
andi temp,0x0f ;маскирование ст. разр. минут
cpi temp,10 ; прошло 10 минут?
brne nochange ;нет
andi minute,0xf0 ; очистка мл. разряда минут
ldi temp,0x10
add minute,temp ;инкремент ст. разряда минут
cpi minute,0x60 ; прошло 60 минут?
brne nochange ; нет
clr minute ; да, очистка минут и
inc hour ;увеличение часов
mov temp,hour
andi temp,0x0f ;маскирование ст. разряда часов
cpi temp,10 ;прошло 10 часов?
brne nochange ;нет
andi hour,0xf0 ;да, инкремент
ldi temp,0x10
add hour,temp ;ст. разряда часов
nochange:
cpi hour,0x24 ;прошло 24 часа?
brne sameday ;нет,
clr hour ;да, очистка переменных времени
clr minute ;для начала отсчета времени нового дня
clr second
sameday: ;Обновление времени
mov lobyte,minute
mov hibyte,hour
rcall display ;отображение времени за 20 мс
brtc case1 ;переход, если нет нажатия кнопки SET
rcall setrtc ;переустановка времени
mov hour,hiset ;обновление часов
mov minute,loset ; и минут
clt ;очистка флага Т
case1:sbrc flags,ld1 ;нагрузка 1 включена?
rjmp chkload1 ;да, проверка на необходимость выключения нагрузки 1
case2:sbrc flags,ld2 ;нагрузка 2 включена?
rjmp chkload2 ; да, проверка на необходимость выключения нагрузки 2
case3:
sbrc flags,ld1on ;время включения нагрузки 1 установлено?
rjmp setld1on ;нет, установка времени включения
case4:
sbrc flags,ld1off ;время выключения нагрузки 1 установлено?
rjmp setld1off ;нет, установка времени выключения
case5:
sbrc flags,ld2on ;время включения нагрузки 2 установлено?
rjmp setld2on ;нет, установка времени включения
case6:
sbrc flags,ld2off ;время выключения нагрузки 2 установлено?
rjmp setld2off ;нет, установка времени выключения
case7:
rjmp do ;переход на начало основного цикла
;* Обработка событий по достиж.времен вкл./выкл. нагрузок и нажатиях клавиатуры*
chkload1:
cp hour,ld1hroff; время выключения достигнуто?
brne onload1
cp minute,ld1minoff
brne onload1
sbi PORTD,LOAD1; да, выключить нагрузку 1
onload1:
cp hour,ld1hron; время включения нагрузки 1 достигнуто?
brne case2
cp minute,ld1minon
brne case2
cbi PORTD,LOAD1; да, включить нагрузку 1
rjmp case2;переход на действия при включенной нагрузке
chkload2:
cp hour,ld2hroff;время выключения нагр.2 достигнуто?
brne onload2
cp minute,ld2minoff
brne onload2
sbi PORTD,LOAD2;да, выключить нагрузку 2
onload2:
cp hour,ld2hron;время включения нагрузки 2 достигнуто?
brne case3
cp minute,ld2minon
brne case3
cbi PORTD,LOAD2;да, включить нагрузку 2
rjmp case3;переход на действия с включенной нагрузкой
setld1on:
sbr flags,0x01;установка флага активизации функции управл. нагрузкой 1
rcall setrtc; установка нового времени включения
mov ld1hron,hiset ;и запоминание
mov ld1minon,loset
cbr flags,0x04; очистка флага ld1on
rjmp case4
setld1off:
rcall setrtc ;установка времени выключения
mov ld1hroff,hiset ;и запоминание
mov ld1minoff,loset
cbr flags,0x08 ;очистка флага ld1off
rjmp case5
setld2on:
sbr flags,0x02;установка флага активизации функции управл. нагрузкой 2
rcall setrtc; установить новое время
mov ld2hron,hiset; и сохранение
mov ld2minon,loset
cbr flags,0x10; очистка флага ld2on
rjmp case6
setld2off:
rcall setrtc; установка нового времени включения
mov ld2hroff,hiset; и сохранение
mov ld2minoff,loset
cbr flags,0x20; очистка флага ld2off
rjmp case7
;****Процедура динамического управления индикацией и сканирования клавиатуры****
display:
ser temp ;выключение индикатора
out PORTB,temp
;**** сканирование клавиатуры и установка флагов нажатия клавиш***************
keyscan:
cbr flags,0x40 ;сброс флага нажатия клавиши
ldi key,0x10 ;установка значения нет нажатой клавиши
ser temp ; установка высоких уровней на порте клавиатуры
out PORTB,temp ;
in temp,PORTD ;выключение индикатора без влияния на состояние нагрузки
ori temp,0x0f ;на время
out PORTD,temp ;сканирования клавитатуры
ldi temp,0x0f ;установка столбцов на выход и
out DDRB,temp ;строк на вход с подтягивающими резисторами
ldi temp,0xf0 ;и возбуждение столбцов
out PORTB,temp ;мл. тетрада готова для опроса
ldi temp,20 ;короткое время установления
tagain1:
dec temp
brne tagain1
sbis PINB,ROW1 ;поиск нажатия в строке
ldi key,0 ;и установка указателя строки
sbis PINB,ROW2
ldi key,4
sbis PINB,ROW3
ldi key,8
sbis PINB,ROW4
ldi key,12
ldi temp,0xF0 ;изменение настроек порта В
out DDRB,temp ;для поиска нажатия в столбце
ldi temp,0x0F ;разрешение подтягивающих резисторов и
out PORTB,temp ;запись нулей в строки
ldi temp,20 ;короткое время установления
tagain2:
dec temp
brne tagain2 ;задержка для установки новых настроек порта
clr temp
sbis PINB,COL1 ;найти нажатие в столбце
ldi temp,0 ;и установить указатель столбца
sbis PINB,COL2
ldi temp,1
sbis PINB,COL3
ldi temp,2
sbis PINB,COL4
ldi temp,3
add key,temp ;объединить указатели строк и столбцов
cpi key,0x10 ;если нет нажатия
breq nokey ;выход из процедуры, иначе
ldi temp,0x10
add key,temp ;изменение к табл. 2
out EEAR,key ;загрузка адреса ЭСППЗУ (0 - 15)
sbi EECR,EERE ;разрешение чтения ЭСППЗУ
in key,EEDR ;чтение дешифрированного значения действительной клавиши
convert:
cpi key,10 ;это клавиша SET?
brne notset ;нет, проверка следующей кнопки
set ;да, установка флага Т в регистре статуса
notset:
cpi key,11 ;это кнопка CLEAR?
brne notclear ;нет, проверка следующей кнопки
sbi PORTD,LOAD1 ;да, выключение всех нагрузок
sbi PORTD,LOAD2
cbr flags,0x03 ;сброс флагов включения нагрузок
notclear:
cpi key,15 ;это кнопка LD1ON?
brne notld1on ;нет, проверка следующей кнопки
sbr flags,0x04 ;да, установка флага LD1ON
notld1on:
cpi key,14 ;это клавиша LD1OFF?
brne notld1off ;нет, проверка следующей клавиши
sbr flags,0x08 ;да, установка флага LD1OFF
notld1off:
cpi key,13 ;это кнопка LD2ON?
brne notld2on ;нет, проверка след. кнопки
sbr flags,0x10 ;да, установка флага LD2ON
notld2on:
cpi key,12 ;это кнопка LD2OFF?
brne notld2off ;нет, проверка следующей кнопки
sbr flags,0x20 ;да, установка флага LD2OFF
notld2off:
;***Звуковая сигнализация нажатия клавиш ***********************
;***генерирует 4кГц тон для пьезо-излучателя в течение 5мс *****
tactile:
cbr flags,0x80
cbi PORTD,PZ ;включить излучатель на короткое время
ldi temp,125 ; на короткое время
t1again:
dec temp
brne t1again
sbi PORTD,PZ ;включить излучатель
ldi temp,125 ; на короткое время
t2again:
dec temp
brne t2again
sbrs flags,ms5 ;повтор в течение 5мс
rjmp tactile
notok:
cpi bounce,40
brlo nokey
sbr flags,0x40 ;установка флага окончания дребезга
nokey:
ser temp
out DDRB,temp ;переинициализация порта В на вывод
out PORTB,temp ;и выключение сегментов
;*** Процедура динамического управления индикацией ****************
cbi PORTD,A1 ;включить разряд 1
mov temp,lobyte ;загрузить мл. разряд минут
digit1:
cbr flags,0x80 ;очистка 5мс флага хода часов
andi temp,0x0f ;маскирование ст. тетрады
out EEAR,temp ;установка адреса ЭСППЗУ (0 - 15)
sbi EECR,EERE ;разрешение чтения из ЭСППЗУ
in temp,EEDR ;считывание семисегментного кода
sbrs flash,clear ;мигание каждые 0.5с
or temp,mask ;наложить маску мигания на разряд
out PORTB,temp ;запись в сегментные разряды и удержание 5 мс
led1:
sbrs flags,ms5 ;5 мс прошло?
rjmp led1 ;нет, ждем пока пройдет
sbi PORTD,A1 ;выключить разряд 1
ser temp ;очистка дисплея
out PORTB,temp
cbi PORTD,A2;
mov temp,lobyte ;загрузка старшего разряда минут
swap temp
digit2:
cbr flags,0x80 ;очистка 5 мс флага
andi temp,0x0f ;маскирование старшей тетрады
out EEAR,temp ;загрузка адреса ЭСППЗУ (0 - 15)
sbi EECR,EERE ;разрешение чтения ЭСППЗУ
in temp,EEDR ;считывание семисегментного кода
sbrs flash,clear ;мигание каждые 0.5с
or temp,mask ;наложить маску мигания на разряд
out PORTB,temp ; запись в сегментные разряды и удержание 5 мс
led2:
sbrs flags,ms5 ; 5 мс прошло?
rjmp led2 ;нет, ждем пока пройдет
sbi PORTD,A2 ;
ser temp ;очистка индикатора
out PORTB,temp
cbi PORTD,A3 ;
mov temp,hibyte ; загрузка мл. разряда часов
digit3:
cbr flags,0x80 ;очистка 5 мс флага
andi temp,0x0f ;маскирование ст. тетрады разряда
out EEAR,temp ;загрузка адреса ЭСППЗУ (0 - 15)
sbi EECR,EERE ;разрешение чтения ЭСППЗУ
in temp,EEDR ;считывание семисегментного кода
sbrs second,clear ;
andi temp,0x7f ; добавить индикацию секунд :
sbrs flash,clear ;мигание каждые 0.5с
or temp,mask ;наложение маски мигания
out PORTB,temp ; запись в сегментные разряды и удержание 5 мс
led3:
sbrs flags,ms5 ;5 мс прошло?
rjmp led3 ;не, ожидаем пока пройдет
sbi PORTD,A3
ser temp ;очистка индикатора
out PORTB,temp
cbi PORTD,A4;
mov temp,hibyte ; загрузка старшего разряда часов
swap temp
andi temp,0x0f ;ст. разряд часов равен 0?
brne digit4
ldi temp,0xff ;да, выключить разряд
digit4:
cbr flags,0x80 ;очистка 5 мс флага
andi temp,0x0f ;маскирование старшей тетрады
out EEAR,temp ;загрузка адреса в ЭСППЗУ (0 - 15)
sbi EECR,EERE ;разрешение чтения из ЭСППЗУ
in temp,EEDR ;считывание семисегментного кода
sbrs flash,clear ;мигание каждые 0.5с
or temp,mask ;наложение маски мигания
out PORTB,temp ; запись в сегментные разряды и удержание 5 мс
led4:
sbrs flags,ms5 ;5 мс прошло?
rjmp led4 ;нет, ждем опять
sbi PORTD,A4
ser temp ;очистка индикатора
out PORTB,temp
tst mask ;мигание завершено?
breq outled ;да, выход
cpi blink,50 ;время мигания прошло?
brlo outled ;нет, выход
clr blink ;да, очистка счетчика скорости мигания
com flash ;и инверсия байта зажигания
outled:
ret
;****Функция установки часов реального времени/времен включения-выключения в ;часах и минутах под управлением клавиатуры
;****возвращает минуты в 'loset' часы в 'hiset'
setrtc:
ser mask ;установка мигания индикатора
ldi hibyte,0xdf ;запись кода 'n' в старший разряд часов
ser lobyte ;и выключение мл. разрядов минут и часов
hihrus:
clr bounce
bounce1:
rcall display ;Индикация и опрос клавиатуры
sbrs flags,keyok
rjmp bounce1
cbr flags,0x40 ;очистка флага keyok
cpi key,0x03 ; Старший разряд часов > 2 ?
brsh hihrus ; если да, что считываем кнопки снова
hihrok: ; если нет, то корректный вход
swap key ;перемещение значения старшего разряда часов в старшую тетраду
mov hiset,key ; и запись в регистр установки часов
ldi hibyte,0x0d ; запись 'n' в мл. разряд часов
add hibyte,hiset ;добавляем к hihour код 'n'
lohrus:
clr bounce
bounce2:
rcall display ; Индикация и опрос клавиатуры
sbrs flags,keyok ;клавиша нажата?
rjmp bounce2 ; нет – повтор снова
cbr flags,0x40 ;да – очистка флага keyok
mov temp,hibyte ;Проверка общего значения часов
andi temp,0xf0 ;на непревышение 24
add temp,key
cpi temp,0x24 ;часы >24?
brsh lohrus ;да – чтение кнопки снова
add hiset,key ;нет – объединить ст. и мл. разряды часов
lohrok:
mov hibyte,hiset ;отображение установленных часов
ldi lobyte,0xdf ;записать код 'n' в старший разряд минут
himinus:
clr bounce
bounce3:
rcall display ; индикация и опрос клавиатуры
sbrs flags,keyok
rjmp bounce3
cbr flags,0x40 ;очистка флага keyok
cpi key,6 ; ст. разряд минут >5 ?
brsh himinus ;нет – чтение клавиатуры снова
lominok:
swap key ;перемещение ст. разр. мин. в старшую тетраду
mov loset,key ; и сохранение минут
ldi lobyte,0x0d ; записать 'n' в мл. разряд минут
add lobyte,loset ;и объединить этот код с ст.разрядом минут
lominus:
clr bounce
bounce4:
rcall display ; индикация и опрос клавиатуры
sbrs flags,keyok
rjmp bounce4
cbr flags,0x40 ;очистка флага keyok
cpi key,10 ;клавиша >9 ?
brsh lominus ; нет- снова чтение кнопок
add loset,key ;да – сложить ст. и мл. разряды минут
clr mask ;сброс мигания разрядов
ret ;и выход с установками времени
;****Процедура обработки прерывания по переполнению таймера**************
;*Вызывается каждые 5мс для обновления флага хода часов, счетчиков мигания и антидребезга
tick:
in status,SREG ;сохранение регистра статуса при входе
inc tock ;+ 1 счетчика 5мс периодов 'tock'
inc blink ; + 1 счетчика частоты мигания
inc bounce ; + 1 счетчика задержки на дребезг контактов
sbr flags,0x80 ;установка флага хода часов (5 мс)
cpi tock,200 ;прошла 1 секунда?
breq onesec ;да, прибавляем 1 секунду
nop ;балансировка времени прерывания
rjmp nosecond ;нет, секунда еще не прошла
onesec:
inc second ;прибавление 1 к секундам
clr tock ;очистка 5мс счетчика
nosecond:
ldi timer,176 ;перезагрузка таймера
out TCNT0,timer
out SREG,status ;восстановление регистра статуса
reti ;возврат к основному процессу
мебель для спальни на заказ