Кодовый замок на pic

 

 

Содержание

Введение………………………………………………………...…………….. 2

  1. Разработка структурной схемы…………………………….……..……… 3
    1. Принцип действия замка………………………………….….………. 4
  2. Разработка принципиальной схемы………………...….……..…………. 6
    1. Выбор элементов схемы…………………………………..………….. 7
    2. Обоснование выбора микроконтроллера…………..……....………... 9
  3. Разработка алгоритма управляющей программы.…………………….. 12
  4. Разработка управляющей программы………………………………….. 13

Заключение………………………………………………………………….. 28

Список использованной литературы…………………………………….… 29

Приложение 1……...………………………………………………………… 30

Приложение 2…………………………………………………………...…… 31

Приложение 3………………………………………………………………… 32

 

 

 

 

 

 

 

 

 

Введение

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

В данном курсовом проекте осуществляется разработка электронного кодового замка с функцией дверного звонка и изменением пароля на  микроконтроллере ATtiny2313.

 

 

 

 

 

 

 

  1. Разработка структурной схемы

Рисунок 1.1 – Структурная схема устройства

Введение кода доступа происходит посимвольно на клавиатурной матрице размером 4х3 и завершается после окончания контрольного промежутка времени (1с), то есть через 1 секунду после прекращения манипуляций с клавиатурой, при этом фильтруются случайные переключения контактов при нажатии клавиш и одновременном нажатии нескольких клавиш. Длина кода возможна любая, она ограничена только размером ОЗУ и EEPROM микроконтроллера. Матричная организация уменьшает количество занятых выводов МК.

Проверка кода доступа происходит сравнением введённого значения с тем, которое было установлено при конфигурировании кода доступа (которое было записано в EEPROM контроллера).

При нажатии кнопки звонка генерируются мелодии, записанные в памяти микроконтроллера. Они сохраняется в ПЗУ в виде отсчётов сигнала. Генерация звукового сигнала совершается с помощью пьезоэлектрического динамика подачей на него периодического сигнала звуковой частоты.

Изменение пароля происходит следующим образом: ввод старого пароля, проверка введенного пароля, если правильно введён, проверка режима записи, если замок находится в режиме записи, то ввод нового пароля, контрольный промежуток времени (1с.), открытие двери.

    1. Принцип действия замка

Замок имеет два режима работы. Режим "Записи кода" и режим "Работы". Для выбора режима предназначен переключатель S14 (см. Рисунок 2.1). Если контакты этого переключателя разомкнуты - замок находится в режиме "Работа", если замкнуты - в режиме "Запись кода". В режиме "Записи кода" хозяин квартиры нажимает кнопки набора кода в любом порядке и в любых комбинациях. Микроконтроллер отслеживает все изменения на клавиатуре и записывает их в ОЗУ. Причем не берется во внимание время удержания той либо иной комбинации. В память записывается лишь факт смены комбинаций. Длина кодовой последовательности ограничена только размерами ОЗУ. Сигналом к окончанию ввода кода служит прекращение манипуляций с клавиатурой. Процессор считает, что ввод кода закончился, если состояние клавиатуры не изменилось в течение одной секунды. В процессе набора код записывается в ОЗУ. При обнаружении паузы в 1 секунду процессор переписывает код в свою внутреннюю долговременную память (EEPROM). Этот код представляет собой последовательность байтов, отражающих все состояния клавиатуры во время набора. После того, как коды будут записаны, замок можно перевести в режим «Работа» (разомкнув контакты S14).

В режиме «Работа» замок постоянно находится в ожидании нажатия кнопок (S1...S12). То есть, ждет ввода кода. Для того, что бы дверь открылась, необходимо повторить те же самые манипуляции с кнопками, которые были сделаны в режиме записи кода . Микроконтроллер так же, как и в предыдущем случае отслеживает эти манипуляции, и записывает полученный таким образом код в ОЗУ. По окончании ввода кода (не нажимая

 кнопок в течении 1 секунды) программа переходит в режим сверки кода. Она сравнивает код находящийся в ОЗУ и код, записанный в EEPROM. Сначала сравнивается длина обоих кодов. Затем коды сверяются побайтно. Если сравнение прошло успешно, микроконтроллер подает сигнал на ключ механизма открывания замка (VT2). Транзистор открывается и подает питание на реле K1, которое и открывает замок. Тут можно сделать по разному. Например, контакты реле K1 могут включать соленоид замка.

Кроме собственно замка это устройство выполняет так же роль дверного звонка. Для этого в схеме есть кнопка звонка (S13) и звуковой излучатель (VF1). В качестве усилителя звука используется ключ на транзисторе VT1. Программа сделана таким образом, что при прекращении звука на выходе PB3 контроллера всегда устанавливается логический ноль. Ключ закрывается и ток через транзистор и динамик VF1 не идет. Таким образом удалось обойтись без разделительной емкости. Дверной звонок работает независимо от кодового замка. Правда только в режиме ожидания кода. Когда код уже начали набирать, звонок отключается. При нажатии на кнопку звонка звучит одна из восьми записанных в память мелодий. При каждом нажатии новая мелодия. Мелодия звучит, пока нажата кнопка.

 

 

 

 

 

 

 

 

 

 

  1. Разработка принципиальной схемы

Рисунок 2.1 – Принципиальная схема кодового замка

    1. Выбор элементов схемы

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

Для более точного подсчёта времени и корректного воспроизведения звука возьмём кварцевый резонатор (Q1) рассчитанный на частоту 4МГц. Для его корректной работы также необходимо выбрать С1 и С2, формирующие цепи согласования кварцевого резонатора. Емкости конденсаторов C1 и C2, подключаемых между выводами резонатора и общим проводом, зависят от частоты и типа резонатора. Для кварцевого резонатора на частоту 4 МГц, например, емкость каждого конденсатора составляет порядка 22 пФ.

Для подключения звукового излучателя (динамика) применяется ключевой каскад на транзисторе VT1. Это самый простой способ получить звук достаточной громкости, учитывая, что наш сигнал это прямоугольные импульсы с амплитудой, почти равной напряжению питания. Транзисторный каскад нужен лишь для повышения нагрузочной способности.

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

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

Теперь определимся с тем, как наш сигнал будет попадать на внешний вывод микроконтроллера. Микроконтроллер предусматривает прямой вывод сигнала на один из своих выходов. Причем предусмотрены отдельные выходы для каждого из каналов совпадения. Для канала А подобный выход называется ОС1А. Он совмещен с третьим разрядом порта РВ и является альтернативной функцией данного контакта.

Подключение и отключение сигнала совпадения к внешнему выводу ОС1А производится программным путем. Это позволяет программе в нужный момент включать или выключать звук. Так как для вывода звука мы будем использовать один из разрядов порта РВ, то для подключения датчиков воспользуемся другим портом. А именно портом PD.

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

Переключатель S14 предназначен для выбора режима работы. Если контакт переключателя S14 (на вход PB7 «0») замкнут, замок переходит в режим «Запись». Разомкнутые контакты (на PB7 «1») соответствуют режиму «Работа».

Переключатель S13 предназначен для звонка. Если контакт переключателя S13 (на вход PB6 «0») замкнут, звонок звенит. Разомкнутые контакты (на PB6 «1») – звонок прекращается.

Схема управления механизмом замка состоит из транзисторного ключа VT1 и электромагнитного реле К1. Резистор R2 ограничивает ток базы ключа. Диод VD1 служит для защиты от напряжения самоиндукции, возникающей на катушке реле. Питание реле осуществляется от отдельного источника + 12 В (питание микроконтроллера +5 В). Если в качестве VT1 применять транзистор КТ315, то электромагнитное реле может иметь рабочее напряжение +12 В и рабочий ток не более 250 мА. Контакты реле должны быть рассчитаны на управление исполнительным механизмом (соленоидом).

Звуковая часть и сам микроконтроллер питаются от напряжения +5 В. А электромагнит замка питается от отдельного источника + 12 В. Напряжение питания, подаваемое на микроконтроллер, обязательно должно

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

Обычно напряжение на входе стабилизатора равно +7...+11 В. Поэтому можно электромагнит и звуковую схему запитывать от одного нестабилизированного источника +12 В. Те же 12 В можно подавать на вход стабилизатора, с выхода которого снимать напряжение +5 В для микроконтроллера.

Для экономии и уменьшения количества занятых выводов применяем матричную организацию клавиатуры 3х4. Выводы подключаются к порту PORTD.

Каждый вывод строчек клавиатуры подтянут к «плюсу» резистором 2 кОм. Это нужно для того, чтобы в случае, если ни одна кнопка не нажата, на входы контроллера приходила «1». Если этого не сделать – на входе будет так называемое 3-е состояние – не пойми чего. Это не пойми чего, контроллер может понять совершенно произвольно. Может посчитать «нулем», может – «единицей», что вызовет ложные «нажатия» клавиш.

 

    1. Обоснование выбора микроконтроллера

Рисунок 2.2 – Расположение выводов ATtiny2313

Микросхема ATtiny2313 представляет собой восьмиразрядный микроконтроллер с внутренней программируемой Flash – памятью размером 2 Кбайт. Выполняя команды за один цикл, ATtiny2313 достигает производительности 1 MIPS при частоте задающего генератора 1 МГц, что позволяет разработчику оптимизировать отношение потребления к производительности.

AVR ядро объединяет богатую  систему команд и 32 рабочих регистра  общего назначения. Все 32 регистра  непосредственно связаны с арифметико-логическим  устройством (АЛУ), что позволяет  получить доступ к двум независимым  регистрам при выполнении одной  команды. В результате эта архитектура  позволяет обеспечить в десятки  раз большую производительность, чем стандартная CISC архитектура.

ATtiny2313 имеет следующие  характеристики:

  • 2 Кбайт системной программируемой Flash – памяти программ
  • 128 байт EEPROM
  • 128 байт SRAM (ОЗУ)
  • 18 линий ввода – вывода (I/O)
  • 32 рабочих регистра
  • однопроводной интерфейс для внутрисхемной отладки
  • два многофункциональных таймера/счетчика с функцией совпадения
  • поддержка внешних и внутренних прерывании; последовательный программируемый USART – порт
  • универсальный последовательный интерфейс с детектором начала передачи
  • программируемый сторожевой таймер с внутренним генератором
  • три программно изменяемых режима энергосбережения

 

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

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

Микросхема изготовлена с использованием уникальной высокоточной технологии фирмы Atmel. Внутренняя Flash – память программ может быть перепрограммирована при помощи ISP – интерфейса без извлечения микроконтроллера из платы. Объединение 8 – разрядного RISC – процессора внутрисистемной перепрограммируемой Flash – памятью на одном кристалле делают микросхему ATtiny2313 мощным средством, которое обеспечивает очень гибкие и недорогие решения многих прикладных задач управления.

 

 

 

 

 

 

  1. Разработка алгоритма управляющей программы

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

 

  1. Разработка управляющей программы

;------------------------- Псевдокоманды  управления

 

.include "tn2313def.inc" ; Присоединение файла описаний

.list     ; Включение листинга

.def drebL = R1   ; Буфер антидребезга младший байт

.def drebH = R2   ; Буфер антидребезга старший байт

.def temp1 = R3

.def temp = R16   ; Вспомогательный регистр

.def data = R17   ; Регистр передачи данных

.def flz = R18   ; Фаза работы замка

.def count = R19  ; Регистр передачи данных

.def addre = R20  ; Текущий адрес в EEPROM

.def prewcode = R21  ; Временный буфер кода

.def Key =  R22   ; Код клавиши

.def loop = R23   ; Регистр счетчика

.def fnota = R24  ; Частота текущей ноты

.def dnota = R25  ; Длительность текущей ноты

.DEF LastKey=r9

.DEF DelayVar=r7

;------------------------- Определение  констант

 

.equ bsize = 30   ; Размер буффера для хранения кода

.equ zad = 3000  ; Порог чувствительности

.equ kandr = 20   ; Константа антидребезга

 

;------------------------- Резервирование  ячеек памяти (SRAM)

.dseg   ; Выбираем сегмент ОЗУ

.org 0x60  ; Устанавливаем текущий адрес сегмента

bufr:  .byte bsize  ; Буфер для приема кода

melod: .byte 1  ; Номер текущей мелодии

 

;------------------------- Резервирование  ячеек памяти  (EEPROM)

 

.eseg   ; Выбираем сегмент ОЗУ

.org 0x08  ; Устанавливаем текущий адрес сегмента

klen:  .byte 1 ; Резервирование ячейки для хранения длины кода

bufe:  .byte bsize  ; Буфер для хранения кода

 

;------------------------- Начало программного  кода

 

.cseg   ; Выбор сегмента программного кода

.org 0  ; Устанеовка текущего адреса на ноль

start: rjmp init  ; Переход на начало программы

reti   ; Внешнее прерывание 0

reti   ; Внешнее прерывание 1

reti   ; Прерывание по захвату таймера T1

rjmp propr  ; Прерывание по совпадению T1

rjmp propr  ; Прерывание по переполнению T1

reti   ; Прерывание по переполнению T0

reti   ; Прерывание UART прием завершен

reti   ; Прерывание UART регистр данных пуст

reti   ; Прерывание UART передача завершена

reti   ; Прерывание по компаратору

reti   ; Прерывание по изменению на любом контакте

reti   ; Таймер/счетчик 1. Совпадение B

reti   ; Таймер/счетчик 0. Совпадение B

reti   ; Таймер/счетчик 0. Совпадение A

reti   ; USI Стартовая готовность

reti   ; USI Переполнение

reti   ; EEPROM Готовность

reti   ; Переполнение охранного таймера

 

 

;******************************************************

;*                Модуль инициализации                *

;******************************************************

init:

;-------------------------- Инициализация  стека

 

ldi  temp, RAMEND ; Выбор адреса вершины стека

out  SPL, temp  ; Запись его в регистр стека

 

;-------------------------- Инициализация  портов В/В

 

ldi  temp,0x18  ; Инициализация порта PB

out  DDRB,temp

ldi  temp,0xE7

out  PORTB,temp

ldi  temp, 0x0F  ; Инициализация порта PD

out  DDRD, temp

ldi  temp, 0x7F  

out  PORTD, temp

 

;------------------------ Инициализация (выключение) компаратора

ldi  temp, 0x80

out  ACSR, temp

 

;--------------------------- Номер мелодии

ldi  temp,0

sts  melod,temp

 

;******************************************************

;*              Начало основной программы             *

;******************************************************

 

main:

;--------------------------- Инициализация  таймера

 

ldi  temp,high(zad) ; Записываем коэффициент задержки

out  OCR1AH,temp

ldi  temp,low(zad)

out  OCR1AL,temp

ldi  temp,0x03  ; Режим работы таймера

out  TCCR1B,temp

m0:  rcall incod    ; Ввод и проверка кода клавиш

brne m0

m1:  in  temp,PINB  ; Проверка кнопки звонка

sbrs temp,6

rjmp kk1  ; Если нажата, переходим к звуковой части

m2:  ldi  ZH,high(bufr) ; Установка указателя

ldi  ZL,low(bufr) ; на начало буфера

clr  count   ; Сброс счетчика байт

 

;---------------------- Цикл ввода  кода

 

m3:  cli     ; Запретить все прерывания

ldi  data,1  ; Вызываем задержку первого типа

rcall wait    ; К подпрограмме задержки

m5:  rcall incod    ; Ввод и проверка кода кнопок

st  Z+,Key  ; Записываем его в буфер

inc  count   ; Увеличение счетчика байтов

cpi  count,bsize ; Проверяем не конец ли буфера

brsh m7    ; Если конец, завершаем ввод кода

mov  prewcode,Key ; Записываем код как старый

ldi  data,2  ; Вызываем задержку второго типа

(подготовка к задержке)

rcall wait

m6:  rcall incod    ; Ввод и проверка кода кнопок

cp  Key,prewcode

brne m3   ; Если изменилось, записываем в буфер

cpi  flz,1  ; Проверка окончания фазы ввода кода

brne  m6

m7:  sbic PINB,7  ; Проверка состояния тумблера

rjmp m9   ; Если тумблер в режиме"работы", то на проверку кода

ldi  addre,0x08 ; Адрес начала хранения кода

rcall eerd   ; Чтение значения ячейки из EEPROM

cpi  data,0xFF ; Сравнение со значением FF(пусто)

breq eewrite  ; Если равны, то пропускаем проверку и переходим к записи

 

; ----------------------------- Процедура  проверки кода

m9:

ldi  addre,klen  ; Адрес хранения длины кода

rcall eerd    ; Чтение длины кода из EEPROM

cp  count,data  ; Сравнение с новым значением

brne m13    ; Если не равны, к началу

ldi  addre,bufe  ; В YL начало буфера в EEPROM

ldi  ZH,high(bufr) ; В регистровую пару Z записываем

ldi  ZL,low(bufr) ; адрес начала буфера в ОЗУ

m10:  rcall eerd   ; Читаем байт из EEPROM и

ld  temp,Z+ ; Читаем байт из ОЗУ и

cp  data,temp ; Сравниваем байты разных кодов

brne  m13   ; Если не равны, переходим к началу

dec  count  ; Уменьшаем содержимое счетчика байтов

brne m10

rjmp    m11

; ----------------------------- Режим записи  кода

eewrite:

mov  data,count   ; Помещаем длину кода в data

ldi  addre,klen  ; Адрес хранения длины кода

rcall eewr    ; Записываем в длину кода EEPROM

ldi  addre,bufe  ; В регистр адреса начало буфера

ldi  ZH,high(bufr) ; В регистровую пару Z записываем

ldi  ZL,low(bufr) ; адрес начала буфера в ОЗУ

m8:  ld  data, Z+ ; Читаем очередной байт из ОЗУ

rcall eewr   ; Запись байта в EEPROM

dec  count  ; Декримент счетчика байтов

brne m8

; ----------------------------- Открывание  замка

m11:  

sbi  PORTB,4  ; Команда "Открыть замок"

ldi  data,3  ; Вызываем задержку третьего типа

rcall wait

cbi  PORTB,4  ; Команда "Закрыть замок"

m13:  rjmp main

 

;******************************************************

;*              Вспомогательные процедуры             *

;******************************************************

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

incod:

clr r31

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

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

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

brne KeyPressed

ldi r31,$3D

rcall GetKeyAnswer

brne KeyPressed

ldi r31,$6B

rcall GetKeyAnswer

brne KeyPressed

ldi r31,$97

rcall GetKeyAnswer

brne KeyPressed

ret

KeyPressed:

set

swap r31

andi r31,$0F

swap Key

andi Key,$07

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

lsr Key

brcc CCFind

breq NoMul

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

NoMul: mov Key,r31

ret

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

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

GetKeyAnswer:

mov Key,r31

andi Key,$0F

out PORTD,Key

clr DelayVar

Delay1: dec DelayVar

brne Delay1

in Key,PIND

com Key

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

Ret

 

; ------------------------------ Подпрограмма  задержки

wait:

cpi  data,1  ; Проверяем код задержки

brne w1

ldi  temp,0x40  ; Разрешаем прерывание по совпадению

rjmp w2

w1:  ldi  temp,0x80  ; Разрешаем прерывания по переполнению

w2:  out  TIMSK,temp  ; Записываем маску

clr  temp    ; Обнуляем таймер

out  TCNT1H,temp

out  TCNT1L,temp

ldi  flz,0   ; Сбрасываем флаг задержки

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

cpi  data,2  ; Если это задержка 2-го типа

breq w4    ; Завершаем подпрограмму

w3:  cpi  flz,1   ; Ожидание окончания задержки

brne  w3

w4:  ret

 

; ------------------------------ Запись  байта в ячейку EEPROM

eewr:

sbic  EECR,EEWE   ; Проверяем готовность EEPROM

rjmp eewr    ; Если не готов ждем

out  EEAR,addre  ; Записываем адрес в регистр адреса

out  EEDR,data  ; Записываем данные в регистр данных

sbi  EECR,EEMWE ; Устанавливаем бит разрешения записи

 

sbi  EECR,EEWE  ; Устанавливаем бит записи

inc  addre   ; Увеличиваем адрес в EEPROM

ret

 

; ------------------------------ Чтение  байта из ячейки EEPROM

eerd: 

sbic EECR,EEWE   ; Проверяем готовность EEPROM

rjmp eerd    ; Если не готов ждем

out  EEAR,addre  ; Записываем адрес в регистр адреса

sbi  EECR,EERE  ; Устанавливаем бит инициализации чтения

in  data, EEDR  ; Помещаем прочитанный байт в temp

inc  addre   ; Увеличиваем адрес в EEPROM

ret

 

;******************************************************

;*           Процедура обработки прерываний           *

;******************************************************

 

propr: ldi  flz,1   ; Установка флага задержки

reti     ; Завершаем обработку прерывания

 

;******************************************************

;*                  Мелодичный звонок                 *

;******************************************************

kk1:

 

;--------------------------- Инициализация  таймера T1

 

ldi  temp, 0x09   ; Включаем режим CTC

out  TCCR1B, temp

km1:  ldi  temp, 0x00   ; Выключаем звук

out  TCCR1A, temp

 

 

;-------------------------- Вычисление  номера нажатой кнопки

 

lds  count,melod ; Читаем код текущей мелодии

inc  count   ; Увеличение номера мелодии на 1

cpi  count,4  ; Проверка не последняя ли мелодия

brne km2    ; Если не последняя, переход

clr  count   ; Обнуление счетчика

km2:  sts  melod,count ; Помещаем номер в ячейку памяти

 

;-------------------------- Выбор мелодии

km3:

mov  YL, count  ; Вычисляем адрес, где

ldi  ZL, low(tabm*2) ; хранится начало мелодии

ldi  ZH, high(tabm*2)

rcall addw  ; К подпрограмме 16-разрядного сложения

lpm  XL, Z+  ; Извлекаем адреса из таблицы

lpm  XH, Z   ; и помещаем в X

 

;--------------------------- Воспроизведение  мелодии

 

km4:  mov  ZH, XH  ; Записываем в Z начало мелодии

mov  ZL, XL

km5:  in  temp, PINB  ; Читаем содержимое порт B  

sbrc temp, 6   ; Проверяем нажата ли еще кнопка звонка

rjmp km6    ; Если равно (кнопки отпущены) в начало

lpm  temp, Z  ; Извлекаем код ноты

cpi  temp, 0xFF  ; Проверяем не конец ли мелодии

breq km4    ; Если конец, начинаем мелодию сначала

andi temp, 0x1F   ; Отделяем от кода частоту

mov  fnota, temp ; Записываем в регистр частоту ноты

lpm  temp, Z+  ; Еще раз берем код ноты

rol  temp   ; Сдвигаем так, что бы три старших

rol  temp   ; разряда стали младшими

rol  temp

rol  temp

andi temp, 0x07  ; Извлекаем код длительности задержки

mov  dnota, temp; Помещаеем ее в ячейку длительности

rcall nota   ; К подпрограмме воспроизведения ноты

rjmp km5   ; В начало цикла (следующая нота)

km6:  ldi  temp, 0x00  ; Выключаем звук

out  TCCR1A, temp

rjmp main

 

;******************************************************

;*          Вспомогательные подпрограммы              *

;******************************************************

; ---------------------- Подпрограмма 16-ти разрядного сложения

 

addw:  push YH  

lsl  YL  ; Умножение первого слагаемого на 2

ldi  YH, 0  ; Второй байт первого слагаемого = 0

add  ZL, YL ; Складываем два слагаемых

adc  ZH, YH

pop  YH

ret

;----------------------- Подпрограмма  исполнения одной ноты

nota:  push ZH

push ZL

push YL

push temp

cpi  fnota, 0x00 ; Проверка не пауза ли

breq nt1  ; Если пауза, переходим сразу к задержке

mov  YL, fnota  ; Вычисляем адрес, где хранится

ldi  ZL, low(tabkd*2) ; коэффициент деления для текущей ноты

ldi  ZH, high(tabkd*2)

rcall addw  ; К подпрограмме 16-разрядного сложения

lpm  temp, Z+ ; Извлекаем мл. разр. КД для текущей ноты

lpm  temp1, Z ; Извлекаем ст. разр. КД для текущей ноты

out  OCR1AH, temp1 ; Записать в старш. часть регистра совпадения

out  OCR1AL, temp ; Записать в младш. часть регистра совпадения

ldi  temp, 0x40  ; Включить звук

out  TCCR1A, temp

nt1:  rcall wait1    ; Задержка

ldi  temp, 0x00  ; Выключить звук

out  TCCR1A, temp

ldi  dnota,0  ; Сбрасываем задержку

rcall wait1    ; Пауза между нотами

pop  temp

pop  YL

pop  ZL

pop  ZH

ret

;----------------------- Подпрограмма задержки

wait1: push ZH

push ZL

push YH

push YL

mov  YL, dnota  ; Вычисляем адрес, где хранитсяldi  ZL, low(tabz*2) ; нужный коэффициент задержкиl

di  ZH, high(tabz*2)

rcall addw     ; К подпрограмме 16-разрядного сложения

lpm  YL, Z+ ; Читаем первый байт коэффициента задержки

lpm  YH, Z   ; Читаем второй байт коэффициента задержки

clr  ZL   ; Обнуляем регистровую пару Z

clr  ZH   ; Цикл задержки

ww1:  ldi  loop,255  ; Пустой внутренний цикл

ww2:  dec  loop

brne  ww2

adiw R30, 1  ; Увеличиваем регистр Z на единицу

cp  YL, ZL ; Проверка младшего разряда

brne ww1

cp  YH, ZH ; Проверка старшего разряда

brne ww1

pop  YL  ; Завершение подпрограммы

pop  YH

pop  ZL

pop  ZH

ret

 

;******************************************************

;*                Таблица задержек                    *

;******************************************************

 

tabz:    .dw      128,256,512,1024,2048,4096,8192

 

;******************************************************

;*          Таблица коэффициентов деления             *

;******************************************************

tabkd: .dw  0

.dw  4748,4480,4228,3992,3768,3556,3356,3168,2990,2822,2664,2514

Кодовый замок на pic