Идея написания этой программы появилась при первых попытках создания своего «умного дома».
Почитал на форумах описание сетей, с которыми экспериментировали другие, и очень мне понравилась сеть на основе протокола 1-wire. Не сложный протокол (хотя и достаточно медленный), большое количество устройств на шине, достаточно большая протяженность линии, много готовых описаний и программ ведущего, ну и конечно широкий выбор готовых интегральных решений. Подробней рекомендую прочитать здесь или здесь.
Поскольку 1-wire является открытым протоколом, надеюсь, меня не забанят за нарушение авторских прав.
Как оказалось, купить что-либо в моем городе и даже в Украине не так просто, и кроме того достаточно дорого.
С реальными сложностями столкнулся, когда понадобилось разработать восьмикнопочную панель, работающую по протоколу 1-wire, для управления сценарным освещением. Это устройство далее и буду описывать.
К устройству предъявлялись следующие требования:
· 8 кнопок управления, состояние которых (нажата – не нажата) может считывать Master;
· 16 светодиодов (8 желтых + 8 зеленых) индикации выбранного сценария. Предполагается, что система может работать в ручном или в автоматическом режиме, в зависимости от режима должны гореть либо желтые, либо зеленые светодиоды, включением одного из 16 светодиодов управляет Master;
· приемник ИК кода, работающий по протоколу RC5. Принятые данные (адрес системы и команду) может считывать Master;
· «пищалка» для звукового оповещения нажатия кнопок, получения ИК команд и т.д. Формой сигнала пищалки управляет Master;
Теперь давайте посчитаем, сколько нужно микросхем с протоколом 1-wire для данного устройства:
· опрос 8 кнопок – DS2408 1 шт. – 75грн.
· управление 16 светодиодами – DS2408 2 шт. – 150грн.
· управление звуковой сигнал – DS2406 1 шт. – 37грн.
· обработка ИК команд – 1 микроконтроллер ATtiny 2313 + DS2408 (адрес+команда) 2 шт. – 11грн. + 150грн.
Итого – 423грн + дополнительный обвес… Хотите такой выключатель за 500грн??? Вот и я решил пойти другим путем.
Схема электрическая принципиальная.
Устройство собрано на микроконтроллере ATMEGA48 (DD1), поскольку он удовлетворяет все необходимые требования – 4К Flash памяти (протокол 1-wire занимает около 1,7кБ), 512 RAM, 256 EEPROM, внешние прерывания INTn и PCI, 3 таймера счетчика. Тактируется МК от внешнего кварцевого резонатора ZQ1, частотой 8МГц. Конденсаторы С5, С6, С10-С12 являются фильтрами по питанию.
Подключение устройства к сети 1-wire производится через гнездо XS1. Это стандартный компьютерный разъем RJ-45 (предполагалось все устройства подключать таким способом). По одной паре проводов (зеленый – бело-зеленый) подается питание +12В. По второй паре (оранжевый – бело-оранжевый) подключается сетевой протокол 1-wire. Остальные пары являются резервными, они подключены к «земле».
C 3 ножки разъема снимается напряжение питания, и через диод VD1 подается на интегральный стабилизатор напряжения DA1. Конденсаторы С1-С3 являются фильтрующими. Со стабилизатора напряжение +5В поступает на питание схемы устройства.
Линия 1-wire подключена к порту микроконтроллера PD3 (используется прерывание INT1), через резистор R1 и защитные диоды VD2-VD4. Диод VD2 – суппрессор, он установлен для подавления скачков напряжения.
Интегральный ИК приемник (DA2) подключается к порту PD2 микроконтроллера (используется прерывание INT0). Конденсатор С4 является фильтром по питанию.
Цепь R2, C9, VD5 служит для начального сброса микроконтроллера при включении напряжения питания.
16 светодиодов подключены к микроконтроллеру через токоограничительные резисторы R4-R8 по схеме дешифратора. Благодаря такому включению «экономятся» ножки микроконтроллера.
Кнопки (S1-S8) совместно с резисторами R10, R11 и диодами VD6, VD7 образуют матрицу 4х2, и подключены к порту B микроконтроллера (используется прерывание PCI).
«Пищалка» со встроенным генератором управляется транзистором VT1, который через резистор R3 подключен к порту PD5 микроконтроллера.
Для внутрисхемного программирования микроконтроллера предусмотрен разъем XP2. Это стандартная панелька под программатор AVR910.
Кроме того для индикации ошибок работы по сети 1-wire предусмотрен светодиод LED17, подключенный к порту PC0 микроконтроллера через токоограничительный резистор R9.
Пишем программу:
Забегая наперед, скажу, что с этой частью программы (только портированной под ATtiny2313) делал одно- и двух -канальные димеры, управляемые по сети 1-wire, а также датчик движения, и все отлично работает.
Для большего удобства написания программ и легкого понимания, что где лежит, я разбиваю программы на несколько файлов. Это удобно тем, что, например, файл макросов создается один раз, и копируется в другие проекты без изменений.
Все файлы проекта подключаются в главном файле программы:
; подключаемые файлы:
.nolist
.include "m48def.inc" ; файл описания контроллера
.include "macro.inc" ; файл макросов
.include "vectors.inc" ; вектора прерываний
.include "define.inc" ; имена регистров
.include "subr.inc" ; подпрограммы
.include "interrup.inc" ; файл обработчика прерываний
.include "init.inc" ; инициализация периферии
.include "tables.inc" ; файл таблиц данных
.include "skan_key.inc" ; процедура сканирования клавиатуры
.include "rec_ram.inc" ; процедура обработки записи данных в RAM
.list
m48def.inc – это файл описания микроконтроллера, он находится в папке AVR Studio.
macro.inc – файл макросов, в нем определены макросы записи данных в регистры и порты (outi, out16, outr, outr16), очистки порта (clri, clrr), сброса и установки отдельных битов в порту (cbrp, sbrp, cbrr, sbrr), задержки (delay_), включения и выключения отдельных портов (ledon, ledoff, soundon, soundoff), макросы сети 1-wire (wire1, wire0) и так далее.
В файле vectors.inc определены ячейки, зарезервированные в RAM и EEPROM, а также таблица векторов прерываний.
В файле define.inc определены все переменные, имена портов и ножки контроллера, имена регистров общего назначения.
Файл subr.inc содержит подпрограммы подсчета CRC, чтения из EEPROM, сканирования клавиатуры, обработки звукового сигнала и так далее.
Все обработчики прерываний находятся в файле interrup.inc
В файле init.inc находится начальная инициализация микроконтроллера – очистка RAM и РОН, инициализация стека, портов и всей необходимой периферии.
Все таблицы находятся в файле tables.inc, (таблица сигнатур для быстрого расчета контрольной суммы CRC-8, таблица кодов клавиатуры, таблица переключения светодиодов).
Процедура сканирования клавиатуры выделена в отдельный файл – skan_key.inc.
В файле rec_ram.inc находится процедура переключения светодиодов и обработки звука после получения данных от ведущего сети 1-wire.
Распределяем пространство памяти.
Давайте определимся, что и где мы будем хранить. Распределение памяти находятся в файле vectors.inc.
В RAM организуем буфер приема-передачи данных по сети 1-wire. Для этого нужно выделить под буфер 8 байт данных. Но, поскольку каждый байт имеет строго определенную функцию, лучше именовать каждый байт отдельно.
; =======================================================================================
; === Сегмент RAM ======================================================================
; =======================================================================================
.dseg
; резервируем байты в RAM для нужд программы
Type_Panel: .byte 1 ; бит тапа панели
Button_data: .byte 1 ; бит состояния кнопок
Sus_ir_data: .byte 1 ; бит IR системы
Com_ir_data: .byte 1 ; бит IR команды
Led_data: .byte 1 ; бит управления светодиодами
Sound_data: .byte 1 ; бит управления звуком
Nul: .byte 1 ; пустой байт
CRC_data: .byte 1 ; бит контрольной суммы
Type_Panel – в этом байте храниться тип устройства (количество кнопок на панели, количество каналов диммера и т.д.). Дело в том, что при написании программы ведущего я старался сделать более-менее универсальный алгоритм, то есть, если на шине установлены несколько подобных устройств (например, 8-кнопочная панель и 4-кнопочная панель или 1-канальные димеры и 2-канальные димеры , то ведущий должен понимать какие данные можно передавать на эти устройства. Этот байт записывается при инициализации микроконтроллера, он может быть считан, но не может быть перезаписан ведущим по сети.
Button_data – в этом байте хранится номер нажатой кнопки. То есть, пока не нажата не одна кнопка, значение этого байта $00. При нажатии на одну из 8 кнопок в этом байте будем хранить номер нажатой кнопки. Поскольку ведущий опрашивает устройства на шине несколько раз в секунду, а для гашения дребезга контактов введена задержка, как при нажатии, так и при отпускании кнопки, то ведущий однозначно определит, что кнопка была нажата и определит номер кнопки. Этот байт записывается устройством, может быть считан, но не может быть перезаписан ведущим по сети.
При разработке системы была предусмотрена возможность управления с ПДУ любым элементом «умного дома» из любой комнаты. Для этого пульт имеет 12 кнопок выбора номера системы. Каждой комнате присвоен свой номер в системе, например, зал – $21, кухня – $22, спальня – $23 и т.д. кроме того определен номер системы который передается по умолчанию. Если, находясь в спальне нажать номер сценария, например №5, то освещение в спальне переключится на соответствующий сценарий. Если, находясь в спальне нажать кнопку выбора системы, например с кодом $21 (зал), а затем кнопку выбора сценария №5, то сценарий освещения в спальне не изменится, но при этом в зале включится сценарий освещения соответствующий сценарию №5. Сигнал от ИК пульта декодируется и сохраняется в двух ячейках буфера приема-передачи.
Sus_ir_data – в этом байте хранится номер системы полученный от приемника ИК кода.
Com_ir_data – в этом байте хранится команда полученная от приемника ИК кода.
Пока ИК код не получен, значение обоих ячеек равно $00, после получения и распознавания ИК кода данные переписываются в эти ячейки. По истечении времени ожидания, если повторная ИК посылка не пришла, оба байта очищаются. Таким образом распознается короткое и продолжительное нажатие на кнопку ИК пульта. Эти байты записываются устройством, могут быть считаны, но не могут быть перезаписаны ведущим по сети.
Led_data – в этом байте хранится номер светодиода, который индицирует нажатую кнопку (выбранный сценарий). Этот байт записывается ведущим по сети, может быть считан, но не могжет быть перезаписан устройством. После приема данных этот байт проверяется на допустимое значение, если его значение не превышает 16 – байт переписывается в буфер приема и обрабатывается, иначе значение байта в буфере не изменяется.
Sound_data – в этом байте храниться форма сигнала звука, обрабатываемая устройством. Этот байт записывается ведущим по сети, может быть считан, и перезаписан устройством. То есть, после приема данных этот байт обрабатывается, и по окончании обработки, этот байт становится равным $00.
Nul – пустой байт, всегда равен $00. Этот байт только читается ведущим и устройством и не может быть изменен.
CRC_data – в этом байте хранится контрольная сумма принятых или передаваемых данных. Для принятых данных CRC вычисляется ведущим и проверяется устройством, после приема всей посылки. Для передаваемых данных CRC вычисляется устройством и считывается по сети ведущим.
Кроме того, для обработки данных включения светодиодов и управления звуковым сигналом, а также для работы команды Saerch Rom нужны еще несколько ячеек в RAM:
Temp_Led: .byte 1 ; временный регистр данных светодиодов
Temp_Sound: .byte 1 ; временный регистр данных звука
Count_bit_SR: .byte 1 ; счетчик битов команды Saerch Rom
Temp_Data_SR: .byte 1 ; временный регистр данных команды Saerch Rom
Для того чтобы можно было легко изменить 64 битный адрес устройства, предлагается хранить его в EEPROM.
ВНИМАНИЕ! Для соблюдения авторских прав компании Dallas Semiconductor предлагаю задавать адреса, которые компанией не используются.
Поскольку я разрабатывал устройство для себя, мне вообще было все равно, чьи права я нарушаю, устройство не было предназначено на продажу. Для адреса 8 кнопочной панели я использовал адрес B8 00 00 00 00 00 01 2C. Для 4 кнопочных панелей B4 00 … Для двухканального диммера адреса D2 00 … для одноканального D1 00 … Для датчиков CE 00 … ну и так далее.
; =======================================================================================
;=== Сегмент EEPROM ===================================================================
; =======================================================================================
.eseg
; резервируем байты в EEPROM
Reserv: .byte 2 ; пустые байты
ID_addr: .byte 8 ; ID адрес устройства
Распределяем периферию микроконтроллера:
Все подключения к микроконтроллеру выполнены исходя из следующих соображений.
ШИНА 1-WIRE - Поскольку все сигналы на шине 1-wire начинаются переходом от “1” в “0” (спадающим фронтом импульса), определять их очень удобно использовать внешнее прерывание INTn. Поэтому линия шины 1-wire подключена к ножке 1 микроконтроллера (порт PD3/INT1). Внешнее прерывание нужно настроить на срабатывание по спаду (смена уровня с 1 на 0), а вывод микроконтроллера как вход без подтяжки (Z-состояние). Кроме того для отслеживания сигнала Reset и формирования сигнала Presenceудобно использовать таймер. Для работы с шиной 1-wire выделен таймер счетчик 0. В нем будут задействованы два прерывания, по совпадению канала А – для обнаружения сигнала Reset, по совпадению канала В – для формирования сигнала присутствия на шине Presence.
ПРИЕМНИК ИК КОМАНД - Момент прихода начала посылки ИК кода так же удобно определять по срабатыванию внешнего прерывания. Поэтому выход ИК приемника подключен к ножке 32 микроконтроллера (порт PD2/INT1). Внешнее прерывание нужно настроить на срабатывание по спаду (смена уровня с 1 на 0), а вывод микроконтроллера как вход с подтяжкой. Кроме того для отсчета времени выборок используется таймер счетчик 2. Для обработки ИК команд понадобится только одно прерывание – по переполнению.
СВЕТОДИОДЫ - Для подключения светодиодов используется схема со встречным включением. Таким образом, для подключения 16 светодиодов используется всего 5 выводов микроконтроллера. При начальной инициализации выводы микроконтроллера к которым подключены светодиоды настраиваются как входы без подтяжки (Z-состояние).
ЗВУКОВОЙ СИГНАЛ - Форма звукового сигнала задается ведущим сети 1-wire и хранится в ячейке Sound_data RAM в виде 1 байта данных. Определим, что «1» включает звуковой сигнал, «0» выключает его. Тогда для получения трех коротких сигналов ведущий должен передать байт – 0b00010101 или 0b11011011, для получения двух длинных сигналов Master должен передать байт – 0b00110011, или 0b11100111 и т.д.
При этом длинна обработки каждого отдельного бита должна быть одинакова. Для отсчета времени звучания одного байта использует таймер-счетчик 1. Поскольку это 16-ти разрядный счетчик, время звучания сигнала можно менять в широких пределах. У этого таймера используем прерывание по совпадению канала А.
КНОПКИ - Клавиатура подключена матрицей 4х2, для определения состояния кнопок используются прерывания PCI, которое запускает сканирование клавиатуры.
Кроме того у механических контактов есть одно принеприятнейшее свойство – «дребезг» контактов. При нажатии или отпускании кнопки контакты создают множественные замыкания и размыкания. Поскольку частота работы микропроцессора достаточно велика, он отлавливает все эти колебания.
С этим явлением можно бороться, введя небольшую задержку, а затем сканировать порт клавиатуры. Для гашения дребезга нужен еще один таймер-счетчик, но у нас его нет, что же делать?
А сделаем мы вот что, поскольку нажатие кнопки и команда от ИК пульта взаимозаменяемы, можно для обеих процедур задействовать один таймер-счетчик, кроме того нужно сделать нажатие кнопки приоритетными, относительно ИК команд. То есть при одновременном нажатии на кнопку и приходе ИК команды, обрабатываться будет нажатие кнопки.
Таким образом, для гашения дребезга контактов используем таймер-счетчик 2, и его прерывание по совпадению канала А.
Определяем переменные, имена портов, имена регистров общего назначения.
Для работы программы необходимо определить некоторые переменные. Это удобно при модификации программы, например, если нужно сделать не 8-кнопочную а 4-кнопочную панель, не придется листать всю программу, а достаточно изменить только количество кнопок и светодиодов. Переменные находятся в файле define.inc. Все описывать не буду, остановлюсь на наиболее важных:
.equ T_0 = 30 ; время удержания линии при передаче "0"
.equ T_R = 14 ; время чтения на шине при приеме данных
При передаче бита = 0 по шине 1-wire линия удерживается в низком состоянии более 15 мксек. Переменная T_0 определяет время, удержания линии, когда устройство передает в линию бит = 0. Чтение на шине 1-wire происходит через 13-15 мксек. после спада импульса. Это время определяется переменной T_R.
.equ Pr_T0 = 64 ; предделитель ТС0
.equ TW_reset = XTAL/Pr_T0*460/1000000 ; время сигнала "сброс" (мкс)
.equ TW_pres = XTAL/Pr_T0*60/1000000 ; время сигнала присутствия (мкс)
Время сигналов Reset и Presence определяется переменными TW_reset и TW_pres. Причем значения этих переменных вычисляются исходя из частоты тактирования и предделителя таймера, обрабатывающего эти сигналы.
Время ожидания гашения дребезга контактов кнопок определяется следующими переменными:
.equ Time_Wait_Key = 200 ; время ожидания гашения дребезга контактов
.equ Wait_Key = F_timer*Time_Wait_Key/255/1000+1 ; количество циклов ожидания гашения
; дребезга контактов
Кроме того для обработки данных при сканировании кнопок определены маски порта к которому подключены кнопки.
.equ MaskKeyB = 0b00111100 ; маска сканирования клавиатуры
.equ MaskSkanB = 0b11000011 ; маска сканируемого порта
.equ MaskSkanPort = 0b00111111 ; маска порта клавиатуры
Звук обрабатывается побитно (бит = 1 – звук есть, бит = 0 – звука нет), время обработки 1 бита определено следующими переменными:
.equ T_S = 80 ; время обработки одного бита сигнала звука мсек
.equ Pr_T1 = 64 ; предделитель Т/С1
.equ TSound = XTAL/Pr_T1*T_S/1000 ; время обработки одного бита звука
Так же следует определить команды сети 1-wire, которые обрабатывает устройство:
.equ Match_Rom = $55 ; адресация конкретной системы
.equ Saerch_Rom = $F0 ; определение серийного номера всех датчиков
.equ Write_Scratchpad = $4E ; записать данные в RAM
.equ Read_Scratchpad = $BE ; считать данные из RAM
Далее для нужд программы определяются имена регистров общего назначения. Сейчас на них подробно останавливаться не буду, отмечу только, что регистры r20 и r21 являются регистрами флагов и используются для различных блоков программы, а для адресных регистров r26 – r31 условно определены области адресации:
.def flag_reg = r20 ; флаговый регитср
.equ PreVal = 0 ; значение первой половины байта принятого RC5 кода
.equ Key = 1 ; флаг обработки клавиатуры
.equ Ir = 2 ; флаг обработки ИК кода
.equ RecWS = 3 ; флаг записи данных командой Write_Scratchpad
.def wire_flaf = r21 ; флаговый регистр 1-Wire
.equ Work = 0 ; флаг разрешения работы
.equ WrRes = 1 ; флаг Reset
.equ ReByte = 2 ; флаг передачи данных
.equ SR = 3 ; флаг команды Read_ROM
.equ MR = 4 ; флаг команды Match_Rom
.equ WS = 5 ; флаг косанды Write_Scratchpad
.equ RS = 6 ; флаг команды Read_Scratchpad
//.def XL = r26 ; регистр адреса в EEPROM
//.def XH = r27 ;
//.def YL = r28 ; регистр адреса в RAM
//.def YH = r29 ;
//.def ZL = r30 ; регистр адреса в FLASH
//.def ZH = r31 ;
Запуск микроконтроллера и инициализация периферии.
При запуске выполняется начальная инициализация микроконтроллера. Программа инициализации находится в файле init.inc.
В начале выполняется очистка RAM и регистров общего назначения, это необходимо для того, чтобы очистить значение ячеек RAM и регистров, которые используются программой от случайных значений. Затем происходит инициализация стека.
Далее происходит настройка портов ввода-вывода:
outi DDRB,0b00000011 ; B0,B1 -> выход (0) [порт сканирования клавиатуры]
outi PortB, 0b00111100 ; B2...B5 -> вход с подтяжкой [порт клавиатуры]
; B6,B7 -> не подключены
outi DDRC,0b00000001 ; C0 -> выход (1) [выход светодиода ошибки]
outi PortC, 0b00000001 ; C1...C5 -> входы (Z) [порт светодиодов]
; C6 -> Reset
outi DDRD,0b00100000 ; D0,D1 -> не подключены
outi PortD, 0b00000100 ; D2 -> вход (подтяжка) [вход ИК приемника]
; D3 -> вход (Z) [вход шины 1-Wire]
; D4 -> не подключен
; D5 -> выход (0) [выход звука]
; D6,D7 -> не подключен
И в конце настраиваются таймеры и внешние прерывания, после чего программа переходит в файл главного цикла по метке Main.
Перед выполнением главного цикла программы необходимо записать данные в регистр приема-передачи, настроить прием ИК команд по протоколу RC5, и разрешить внешние прерывания. Эти действия выполняются между метками Main и Start.
Главный файл программы представляет собой бесконечный замкнутый цикл, в котором проверяются два флага регистра flag_reg.
Работа по шине 1-wire.
Распознавание команды Reset, формирование сигнала Presence.
Для сброса всех ведомых устройств на шине, ведущий передает команду Reset, устанавливая на шине низкий уровень на время 480 мксек. После этого ведущий отпускает шину и ожидает от ведомых устройств сигнала присутствия на шине. После окончания сигнала Reset ведомый ожидает около 60 мксек. и устанавливает на шине низкий уровень на время 60 мксек., формируя сигнал Presence, и таким образом сообщает ведущему о том, что на шине присутствует хотя бы одно устройство.
Рассмотрим, как это работает в программе.
При появлении на шине низкого уровня устанавливается флаг внешнего прерывания INT1 и программа переходит по вектору INT1addr и далее на метку Edge_Wire. Это метка обработчика прерывания, который находится в файле interrup.inc.
В начале обработчика прерывания в стек сохраняются главный флаговый регистр SREG и рабочий регистр temp1.
Далее сбрасывается счетный регистр и запускается (перезапускается) таймер 0.
После этого проверяется флаг разрешения работы (Work) в флагового регистра wire_flag, и если он установлен, программа переходит по метке Exit_Edge_Wire, где происходит выход из прерывания. При этом, для того, чтобы не вызывалось повторная обработка прерывания, сбрасывается флаг внешнего прерывания INT1, из стека восстанавливаются значения регистров temp1 и SREG и происходит возврат из прерывания с глобальным разрешением прерываний.
Если при проверке флаг Work окажется установленным, произойдет обработка чтения или передачи бита, но, поскольку ведущий прижимает шину к «земле», никаких изменений на шине не произойдет. В данный момент для нас важно запустить таймер на отсчет времени сигнала Reset.
Примерно через 460 мксек. (это время определено переменной TW_reset в файле define.inc) будет вызвано прерывание по совпадению канала А от таймера 0, при этом программа перейдет по вектору OC0Aaddr и далее по метке Timer0A_Ok.
В начале обработки прерывания сохраняются в стек регистры SREG и temp1, так же выключается светодиод ошибки.
Далее программа ожидает окончания сигнала Reset и проверяет наличие «1» на шине. Если шина все еще прижата к «земле», регистр temp2 уменьшается на 1 до тех пор, пока на шине не появится высокий уровень или пока регистр temp2 не будет равен 0. Если регистр temp2 = 0, а высокий уровень на шине не появилась (например, при коротком замыкании на шине), происходит обработка ошибки (переход на метку Err_T0A). При этом включается светодиод ошибки, останавливается таймер 0 и очищается регистр wire_flag, запрещая тем самым работу устройства по сети до прихода команды Reset.
Если примерно через 480 мксек. на шине установится высокий уровень, программа перейдет по метке Presence_Ok. При этом сбрасывается счетный регистр таймера 0, устанавливается флаг получения сигнала Reset (флаг WrRes, регистра wire_flag), запрещается прерывание INT1 и разрешается прерывание по совпадению канала В от таймера 0, после чего происходит выход из обработчика прерывания по метке Exit_T0A.
Примерно через 60 мксек. (это время определено переменной TW_pres в файле define.inc) будет вызвано прерывание по совпадению канала В от таймера 0, при этом программа перейдет по вектору OC0Baddr и далее по метке Timer0B_Ok.
В начале обработки прерывания сохраняются в стек регистры SREG и temp1, затем сбрасывается счетный регистр таймера 0 и проверяется состояние флага WrRes регистра wire_flag.
Поскольку флаг был установлен ранее, происходит переход на метку Start_Pres. При этом сбрасывается флаг WrRes, устройство прижимает шину к «земле» (выводы микроконтроллера настраиваются как выход -> 0) и происходит выход из прерывания по метке Exit_T0B.
Примерно через 60 мксек. снова происходит прерывание по совпадению канала В от таймера 0. При этом сбрасывается счетный регистр таймера, и проверяется состояние флага WrRes регистра wire_flag. Поскольку флаг был сброшен в предыдущей обработке прерывания, происходит переход по метке Stop_Pres. При этом шина отпускается (вывод микроконтроллера настраивается как вход без подтяжки (Z-состояние), запрещается прерывание по совпадению канала В, останавливается таймер 0, сбрасывается флаг внешнего прерывания INT1 и разрешается это прерывание, сбрасывается регистр wire_flag и в нем устанавливается флаг Work – флаг разрешения работы по сети1-wire. Кроме того устанавливается счетчик принимаемых (передаваемых) битов – регистр bit_counter.
Принимаем данные, обрабатываем команду Read_Scratchpad, передаем данные.
Для подробного рассмотрения работы устройства по протоколу 1-wire удобнее всего рассмотреть обработку команды Read_Scratchpad. Ведущий передает по шине команду $BE, и считывает 8 байт данных из буфера обмена.
Принимаем один бит.
Каждый сигнал на шине 1-wire начинается со спадающего фронта на линии. При этом программа переходит на обработку прерывания INT1 по метке Edge_Eire.
В начале обработки прерывания перезапускается таймер 0 (это необходимо для обработки команды Reset), и проверяется флаг разрешения работы Work. Поскольку этот флаг устанавливается после отсылки сигнала Presence, программа определяет, будут считаны или переданы данные. Направление работы шины определяется флагом ReByte регистра wire_flag. Если этот флаг сброшен, программа переходит к чтению данных по метке Read_Data.
Устройство ожидает 14 мксек. (это время определено переменной T_R в файле define.inc), и затем проверяет состояние шины. В зависимости от состояния шины происходит переход на метку Write_Bit_1 (если на шине высокий уровень) или на метку Write_Bit_0 (если на шине низкий уровень).
При высоком уровне на шине (ведущий передает “1”) программа устанавливает бит С регистра состояния и сдвигает регистр данных шины wdata в право через флаг С. Затем уменьшает на 1 счетчик принятых битов (регистр bit_counter), и проверяется его значение. Если счетчик не равен 0, программа выходит из обработчика по метке Exit_Edge_Wire.
Принимаем один байт.
Счетчик битов устанавливается после обработки команды Reset и после приема каждого байта. После приема 8 битов данных регистр счетчика bit_counter = 0, и программа выполняет переход по метке Proc_Byte. В начале этой процедуры устанавливается счетчик битов (bit_counter = 8, подготавливая устройство к следующему обмену данными), и определяется тип полученного байта (команда или данные).
При обработке каждой команды в флаговом регистре wire_fkag устанавливается флаг соответствующей команды, после обработки команды флаг сбрасывается. Поэтому проверяя состояние флагов можно определить тип принятого байта. Если не один флаг обработки команды не установлен, ведущий передал код команды, если установлен какой либо флаг, то получены данные команды.
Поскольку пока не установлен не один флаг команды, программа перейдет на метку Proc_Comand и определит, какую команду передал ведущий. Проверка производится сравнением данных регистра wdata и переменных – имен команд, определенных в файле define.inc.
При совпадении данных программа переходит по соответствующей метке обработки команды. Если данные не совпадают, например, передана команда Skip_Rom ($CC), происходит переход по метке обработки ошибок – Err_Proc_Command. При этом останавливается таймер 0 (для предотвращения ложного определения команды Reset) и очищается регистр wire_flag, запрещая дальнейшую работу с шиной до прихода команды Reset.
Обрабатываем команду Read_Scratchpad.
Если ведущий передал код команды Read_Scratchpad ($BE), то при сравнении полученных данных с командами определенными в файле define.inc программа перейдет по метке Command_RS. Здесь программа устанавливает флаги обработки команды RS и передачи данных ReByte в регистре wire_flag, и устанавливает счетчик передаваемых байтов (регистр byte_counter = 8).
Далее программа читает первый байт из буфера приема/передачи 1-wire, и переписывает его значение в регистр wdata. Устройство готово передавать данные в шину.
Передаем один бит в шину.
При чтении данных ведущий на 1 мксек. придавливает шину к «земле» и затем отпускает ее. Через 13-15 мксек. ведущий считывает состояние шины.
В ведомом устройстве, при приходе спадающего фронта, поскольку установлен флаг ReByte, обработчик прерывания перейдет на метку Send_Data – передача данных в шину.
Программа сдвигает данные регистра wdata вправо через флаг С регистра состояния. Если флаг С=1, программа переходит на метку Check_End_RX, и никаких действий с шиной не производится. Ведущий через 1 мксек. отпустит шину и через 13-15 мксек. проверяя состояние шины, прочитает принятый бит как «1».
Если бит С=0, программа переходит по метке Delay0, и происходит передача «0». Для передачи данных программа прижимает шину к «земле», сбрасывает флаг внешнего прерывания INT1, ожидает 30 мксек. (это время определяется переменной T_0 в файле define.inc) и затем отпускает шину. Таким образом, через 13-15 мксек. проверяя состояние шины, ведущий прочитает принятый бит как «0».
Далее программа устройства отпускает шину и переходит к уменьшению счетчика переданных битов по метке Check_End_RX. Счетчик переданных битов (регистр bit_counter) уменьшается на 1 и программа проверяется его значение. Если счетчик не равен 0, программа выходит из обработчика по метке Exit_Edge_Wire.
Передаем 8 байт в шину.
После передачи 8 битов данных, счетчик битов – регистр bit_counter = 8, и программа перейдет по метке Check_Flag. Здесь программа переустанавливает счетчик битов bit_counter = 8, и проверяет из какой команды велась передача данных (передача данных в шину может производиться либо из команды Read_Scratchpad, либо из команды Saerch_Rom. Так как установлен флаг RS, программа переходит к обработке команды Read_Scratchpad по метке Proc_RS.
Если произошла какая либо ошибка и флаги обоих команд сброшены – произойдет переход к обработке ошибки по метке Err_Proc_Command.
В обработчике команды Read_Scratchpad программа считывает следующий байт из буфера приема/передачи. Для этого вычисляется указатель точки чтения:
- в адресную пару Y загружается адрес начала буфера приема-передачи;
- вычисляется смещение точки чтения относительно счетчика переданного байта byte_counter;
- из буфера читаются данные в регистр wdata.
Далее программа уменьшает счетчик байтов – регистр byte_counter и проверяет, значение этого регистра. Если переданы не все байты, счетчик не равен нулю, и программа выходит из обработчика по метке Exit_Edge_Wire. Если переданы все байты из буфера (счетчик byte_counter = 0) программа сбрасывает флаги обработки команды (RS) и передачи данных (ReByte), останавливает таймер 0, и выходит из обработчика по метке Exit_Edge_Wire.
Обрабатываем команду Write_Scratchpad.
Для записи данных в устройство ведущий должен передать команду Write_Scratchpad ($4E) и далее передать 8 байт данных.
При получении последнего байта этой команды программа устройства переходит на обработчик внешнего прерывания Edge_Wire и проверяя полученную команду (по метке Proc_Comand) переходит на обработчик команды по метке Command_WS.
Далее программа устанавливает флаг команды WS в регистре wire_flag, сбрасывает регистр вычисленной контрольной суммы CRC8, устанавливает счетчик принимаемых байтов (byte_counter = 8) и выходит из обработчика прерывания. Устройство готово к приему 8 байт данных.
Программа, при приеме байта данных, в блоке проверки типа данных (метка Proc_Byte), определит установленный флаг обработки программы WS и перейдет по метке Proc_WS.
В первую очередь программа вычисляет контрольную сумму CRC8.
Вычисление CRC8.
Для вычисления контрольной суммы используется табличный метод. Вычисляется контрольная сумма в подпрограмме Wire_CRC8. Эту часть программы я взял отсюда (в прикрепленных файлах есть пример). Таблица значения контрольной суммы CRC_table находится в файле table.inc.
Далее программа проверяет разрешение записи байта данных в буфер. Поскольку для приема и передачи используется общий буфер, запись данных разрешена только в байты Led_Data и Sound_Data. Запись в остальные регистры не производится, но при этом в конце приема данных проверяется контрольная сумма.
Разрешение записи полученного байта проверяется по счетчику принятого байта byte_counter. Если счетчик байтов равен 4 (получен байт управления светодиодами), программа переходит по метке WS_L_Ok. Если счетчик байтов равен 3 (получен байт управления звуком), программа переходит по метке WS_S_Ok. Поскольку контрольная сумма всей посылки еще не вычислена, байты управления светодиодами и звуком переписываются в ячейки RAM, выделенные для временного хранения данных (это ячейки Temp_Led и Temp_Sound, определенные в файле vecrots.inc).
После записи данных в ячейки временного хранения, или если запись данных не разрешена (счетчик byte_counter не равен 3 или 4) программа переходит по метке RX_WS. Здесь уменьшается счетчик принятых байтов и проверяется его значение. Если регистр byte_counter не равен нулю, происходит выход из обработчика прерывания по метке Exit_Edge_Wire.
Когда ведущий передаст последний байт данных, программа, работая по метке RX_WS, определит, что byte_counter = 0 и перейдет на метку Exit_RX_WS.
В начале этой процедуры проверяется контрольная сумма. Если все байты посылки приняты правильно, то регистр CRC8 = 0. Если данные были приняты с ошибкой, программа перейдет на обработку ошибки по метке Err_Proc_Command.
Если ошибок приема не было, программа копирует данные из ячеек временного хранения данных Temp_Led и Temp_Sound в буфер приема/передачи. Далее, поскольку данные в буфере были изменены, программа вычисляет контрольную сумму данных буфера приема/передачи. Вычисление контрольной суммы для буфера производится в подпрограмме Calc_CRC8_RAM.
Эта процедура последовательно считывает семь байтов из буфера приема/передачи, вычисляет контрольную сумму и записывает ее в восьмой байт буфера (ячейка CRC_data).
Далее программа обработки команды Write_Scratchpad сбрасывает флаг WS в регистре wire_flag, останавливает таймер 0, устанавливает флаг выполнения записи в буфер Rec_WS в регистре flag_reg и выходит из обработчика прерывания.
Дело в том, что обработка приема последнего байта за счет вычисления CRC и копирования данных занимает много времени, а тайм слот для шины 1-wire не должен быть меньше 60 мксек. Поэтому при приеме данных от ведущего, программа устанавливает флаг Rec_WS, который проверяется и обрабатывается в главном цикле программы. Как происходит обработка, опишу при объяснении работы со светодиодами и звуком.
На данный момент главное, что 8 байт данных приняты от ведущего и записаны в буфер приема/передачи.
Обрабатываем команду Match_Rom.
Если к шине 1-wire подключено несколько устройств, ведущий, после команды Reset и получения сигнала Presence, для обмена данными с конкретным устройством должен передать по сети его 64 битный адрес, используя команду Match_Rom ($55).
При получении последнего байта этой команды программа устройства переходит на обработчик прерывания Edge_Wire, и проверяя полученную команду (по метке Proc_Comand) переходит на обработчик команды по метке Command_MR.
Далее программа устанавливает флаг команды MR в регистре wire_flag, устанавливает счетчик принятых байтов byte_counter = 8 и выходит из обработчика прерывания. Устройство готов к приему 8 байтов адреса.
При приеме байта данных, программа, в блоке проверки типа данных (метка Proc_Byte) определит установленный флаг MR и перейдет по метке Proc_MR.
В начале этой процедуры, программа считывает байт собственного адреса, используя подпрограмму Read_Byte_ID.
В этой подпрограмме в адресную пару X загружается адрес начала 64 битного адреса, затем вычисляется смещение по адресу с учетом счетчика полученных байтов, и считывается байт адреса из EEPROM. После выполнения подпрограммы байт адреса находится в регистре data.
Далее программа сравнивает полученный от ведущего байт и байт собственного адреса. Если байты не совпадают, программа выходит по метке Err_Proc_Command, запрещая работу устройства.
Если принятый от ведущего байт и байт собственного адреса совпадают, программа уменьшает счетчик принятых байтов и проверяет его значение. Если счетчик не равен нулю, программа выходит из обработчика.
Если приняты все 8 байтов, и они совпали с адресом устройства, программа сбрасывает флаг MR, останавливает таймер 0, и выходит из прерывания по метке Exit_Edge_Wire.
Обрабатываем команду Search_Rom.
Для того, чтобы ведущий мог обмениваться данными с устройствами по шине 1-wire, необходимо, чтобы ведущий «знал» все 64 битные адреса устройств, подключенных к шине. Для считывания адресов ведущий должен передать команду Search_Rom ($F0). Алгоритм работы и программа ведущего хорошо описаны здесь.
При получении последнего байта команды программа переходит на обработчик прерывания, и проверяя полученную команду (по метке Proc_Comand), переходит на обработчик команды по метке Command_SR.
Далее программа устанавливает флаг команды (SR) и флаг передачи данных (ReByte), переустанавливает счетчик битов (bit_counter = 2), устанавливает счетчики байтов (byte_counter = 8) и счетчик байтов для обработки (ячейка RAM Count_bit_SR = 8). Читает в регистр data байт собственного адреса (с учетом счетчика байтов) с помощью подпрограммы Read_Byte_ID.
Для определения передаваемого бита регистр data сдвигается вправо через бит С регистра состояния, а оставшиеся биты из ячейки data копируются в ячейку RAM – Temp_Data_SR (эта ячейка определена в файле vectors.inc).
Далее с помощью подпрограммы TX_SR определяем последовательность битов для передачи ведущему. Эта подпрограмма в зависимости от состояния бита С заносит в регистр wdata либо число 0b00000010 (если С = 0), либо число 0b00001001 (если С = 1):
0000 X 0 YY
| |__ биты прямого и обратного значения бита адреса
|______ бит проверки подтвержденного значения байта
После этого устройство готово к передаче 2 битов по запросу ведущего.
Ведущий считывает 2 бита данных от ведомых устройств, при этом:
· если ведущий получит байты 1-0 (передаваемые биты адресов нескольких устройств совпадают), он сохраняет бит адреса устройства = 1;
· если ведущий получит байты 0-1 (передаваемые биты адресов нескольких устройств совпадают), он сохраняет бит адреса устройства = 0;
· если ведущий получит байты 0-0, (передаваемые биты адресов устройств не совпадают), он сохраняет бит адреса устройства = 0 и по счетчику битов «запоминает» место несовпадения битов адреса;
· если ведущий получит байты 1-1, он воспринимает это как ошибку.
Передача пары байт производится в обработчике прерывания Edge_Wire. В начале процесса обработки проверяется флаг передачи данных ReByte. Поскольку он установлен, а данные переданы, происходит сброс флага передачи и установка счетчика передаваемых битов (bit_counter = 1). После этого происходит выход из обработчика прерывания. Ведомый готов принять байт, распознанный и сохраненный ведущим сети 1-wire.
Теперь ведущий передает в шину бит адреса устройства, который он распознал. Устройство принимает этот бит и сравнивает его с раннее переданной последовательностью битов. Если переданная последовательность не совпадает с полученным байтом, программа устройства прекращает работу с шиной до прихода команды Reset. Если данные совпадают, устройство обрабатывает следующий байт адреса.
При получении бита подтверждения от ведущего, вызывается обработчик прерывания Edge_Wire. По метке Check_Bit проверяет совпадение переданного бита и принятого. Как это происходит:
· Допустим, что 1-й бит адреса устройства = 1, тогда в регистр передачи данных wdata было записано число 0b00001001. Передавая данные, устройство дважды сдвигает этот регистр вправо
0b00001001 -> 0b00000100 -> 0b00000010
При приеме бита подтверждения от ведущего, регистр сдвигается еще раз, и принятый бит помещается на место 8-го бита. Если ведущий передал бит = 1, в регистре wdata будет число:
0b00000010 -> С=1 -> 0b10000001 ($81).
Если ведущий передал бит = 0, в регистре wdata будет число:
0b00000010 -> C=0 -> 0b00000001 ($01).
· Допустим, что 1-й бит адреса устройства = 0, тогда в регистр передачи данных wdata было записано число 0b00000010. Передавая данные, устройство дважды сдвигает этот регистр вправо
0b00000010 -> 0b00000001 -> 0b00000000
При приеме бита подтверждения от ведущего, регистр сдвигается еще раз, и принятый бит помещается на место 8-го бита. Если ведущий передал бит = 1, в регистре wdata будет число:
0b00000000 -> С=1 -> 0b10000000 ($80).
Если ведущий передал бит = 0, в регистре wdata будет число:
0b00000000 -> C=0 -> 0b00000000 ($00).
Таким образом, по метке Check_Bit проверяются данные регистра wdata после приема бита подтверждения. Если в регистре находится число $00 или $81, происходит переход на метку RX_SR. Иначе, если в регистре wdata находится любое другое число, программа переходит на обработчик ошибки по метке Err_Proc_Command, где запрещает работу с шиной 1-wire.
По метке RX_SR, программа устанавливает счетчик битов (bit_counter = 2), устанавливает флаг передачи данных Re_Byte и уменьшает счетчик битов (ячейка Count_Bit_SR в RAM). Если счетчик битов не равен нулю, программа сохраняет значение счетчика в RAM, читает из RAM (ячейка временного хранения данных Temp_Data_SR) обрабатываемый байт, сдвигает его через С, сохраняет данные в RAM для дальнейшей обработки и, используя подпрограмму TX_SR, в зависимости от состояния бита С, заносим данные в регистр wdata, после чего выходит из обработчика прерывания.
Если обработаны все биты и счетчик битов в RAM = 0, происходит переход по метке TX_Byte_Ok. Здесь устанавливается счетчик битов в RAM (ячейка Count_Bit_SR = 8), уменьшается счетчик байтов (bute_counter – 1) и проверяется передача всех байтов адреса устройства. Если счетчик не равен нулю, в регистр dataсчитывается следующий байт адреса (подпрограмма Read_Byte_ID), данные регистра сдвигаются через С и сохраняются в ячейке RAM Temp_Data_SR. В зависимости от состояния бита С в регистр wdata заносится соответствующее значение. Далее происходит выход из обработчика по метке Exit_Edge_Wire.
При передаче всех данных (byte_counter = 0), происходит переход на метку Exit_Proc_SR. Эта часть программы завершает обработку команды Search_Rom, и подготавливает устройство к дальнейшей работе. Здесь устанавливается счетчик байтов (bit_counter = 8), сбрасывается флаг команды SR и флаг передачи данных Re_Byte, останавливается таймер 0 и происходит выход из обработчика по метке Exit_Edge_Wire.
В заключении описания работы программы с шиной 1-wire должен отметить, что для достаточного быстродействия и нормальной работы при времени таймслота = 60 мксек., частота работы контроллера должна быть не ниже 8МГц.
ПРИЕМНИК ИК КОМАНД
Устройство принимает команды от ИК пульта дистанционного управления, работающего по протоколу RC5. Принятые команды, программа не обрабатывает, а декодирует и передает по сети 1-wire ведущему. Код программы я взял отсюда с небольшими переделками.
За время первой половины приема бита, программа выполняет три выборки, проверяя уровень на входе PD2. По окончании третьей выборки программа определяет флаг валидности и настраивает срабатывание внешнего прерывания INT0 соответствующим образом. Если первая принятая половина бита = 0 (принимаем «1»), флаг PreVal в регистре flag_reg устанавливается в 1, а прерывание настраивается на срабатывание по фронту. Если первая принятая половина бита = 1 (принимаем «0»), флаг PreVal в регистре flag_reg сбрасывается в 0, а прерывание настраивается на срабатывание по спаду.
При смене уровня на входе PD2 в обработчике прерывания INT0 – Edge_RC5 перезагружается таймер 2, обеспечивая синхронизацию по времени.
За время приема второй половины бита программа выполняет еще три выборки, проверяя уровень на входе PD2. По окончанию последней выборки программа проверяет правильность принятого бита.
Если вторая половина принятого бита = 1 (принимаем «1»), и флаг валидности PreVal в регистре flag_reg установлен – бит принят правильно. Если вторая половина принятого бита = 0 (принимаем «0»), и флаг валидности PreVal в регистре flag_reg сброшен – бит принят правильно. При возникновении ошибок, программа не будет декодировать код.
Далее программа заполняет регистры принятых битов, уменьшая счетчик битов. При приеме всей посылки программа декодирует принятые биты, выделяя номер системы и саму команду, и переписывает данные в буфер приема/передачи, исправляя при этом байт CRC.
В конце посылки программа запускает таймер 2, ожидая прихода следующей посылки. Если посылка ИК кода не приходит до срабатывания таймера, байты номера системы и номера команды в буфере приема/передачи очищаются, после чего исправляется байт CRC.
ОБРАБОТКА НАЖАТИЯ КНОПОК.
Кнопки клавиатуры подключены матрицей 4х2 для уменьшения используемых выводов микроконтроллера. Для определения момента нажатия и отпускания кнопок используется прерывание по изменению уровня на входе – PCI.
Выводы PB0 и PB1 являются сканирующими и настроены как выход с низким уровнем. Выводы PB2 – PB5 являются сканируемыми и настроены как входы с подтяжкой. Прерывание PCI настроено на срабатывание по изменению уровня на ножках PB2 – PB5. Пока не нажата не одна кнопка, на входах микроконтроллера высокий уровень, за счет внутренних подтягивающих резисторов.
При нажатии на любую кнопку на соответствующем выводе микроконтроллера появляется низкий уровень, вызывая срабатывание прерывания PCI.
В обработчике прерывания (по метке Key_Ok) происходит запрещение работы прерывания PCI (для предотвращения множественных срабатываний из-за дребезга контактов), и перенастройка и запуск таймера 2. Таймер используется и для обработки ИК кода и для задержки на время гашения дребезга контактов кнопок. Поэтому в обработчике прерывания запрещается работа ИК приемника, сбрасывается значение байтов номера системы и команды, исправляется CRC, таймер 2 перенастраивается на работу с прерыванием по совпадению канала А и запускается. Кроме того, на сканирующих линиях PB0, PB1 устанавливается высокий уровень.
Поскольку таймер 2 при частоте 8МГц и предделителе 256 переполняется достаточно быстро (8000000 / 256 = 31250 Гц = 0,000032 сек), введен дополнительный счетчик циклов переполнения (регистр count_wait), значение которого вычисляется в файле define.inc. При переполнении таймера 2 (так как регистр OCR2A = $00), вызывается прерывание по совпадению канала А (TC2A_Ok), в котором уменьшается значение счетчика циклов ожидания. Пока счетчик не равен нулю, дальнейшие действия не производятся.
Примерно через 200 мксек. (это значение определено в файле define.inc) значении счетчика станет равно нулю. При этом таймер 2 останавливается и перенастраивается на работу с ИК приемником, устанавливается флаг разрешения сканирования клавиатуры Key в регистре flag_reg.
В главном цикле, программа, определив установленный флаг, переходит к подпрограмме сканирования клавиатуры Proc_Key.
Обработчик клавиатуры последовательно устанавливает на сканирующих линиях клавиатуры низкий уровень и вызывает подпрограмму сканирования Sken_Key.
В этой процедуре программа читает данные порта клавиатуры и сравнивает его с кодом полученным из таблицы Skan_Cod_Table. Если данные не совпадают, происходит возврат в процедуру Proc_Key, где выбирается вторая линия для сканирования или выход из процедуры сканирования. Если данные порта клавиатуры совпадают с данными из таблицы, в буфер приема/передачи загружается номер нажатой кнопки, исправляется CRC и сбрасывается флаг сканирования клавиатуры Key.
Если не один из кодов таблицы не подошел, программа считает, что произошло отпускание кнопки (это может происходить также при нажатии нескольких кнопок одновременно). В этом случае в байт номера нажатой кнопки записывается 0, исправляется CRC, сбрасывается флаг сканирования клавиатуры и разрешается работа ИК приемника по прерывания INT0. На сканирующих линиях PB0 и PB1 устанавливается низкий уровень и разрешается прерывание по изменению уровня PCI.
УПРАВЛЕНИЕ СВЕТОДИОДАМИ.
Программа устройства позволяет включить один из 16 светодиодов. Включением светодиодов управляет ведущий по сети 1-wire.
При получении команды Write Scratchpad ($4E) устройство принимает от ведущего и записывает в буфер приема/передачи 2 байта данных (в том числе байт управления светодиодами), устанавливая при этом флаг RecWS в регистре flag_reg. В главном цикле программа постоянно проверяет этот флаг. Если флаг установлен, выполняется подпрограмма обработки полученных данных Proc_RecRAM.
В данной процедуре сбрасывается флаг записи новых данных в буфер, из буфера считывается байт управления светодиодами (ячейка Led_Data), проверяется правильность полученных данных. Если получены недопустимые данные (номер включенного светодиода больше 16), ячейка Led_Data очищается и исправляется CRC. Если полученный номер светодиода меньше или равен 16, из таблицы Led_Tableсчитываются данные и выводятся в порт управления светодиодами.
УПРАВЛЕНИЕ ЗВУКОВЫМ СИГНАЛОМ.
В качестве звукового излучателя в устройстве используется «пищалка» со встроенным генератором. С его помощью можно получить звуковые сигналы различной формы.
При получении команды Write Scratchpad ($4E) устройство принимает от ведущего и записывает в буфер приема/передачи 2 байта данных (в том числе байт управления звуком), устанавливая при этом флаг RecWS в регистре flag_reg. В главном цикле программа постоянно проверяет этот флаг. Если флаг установлен, выполняется подпрограмма обработки полученных данных Proc_RecRAM.
В данной подпрограмме после сброса флага RecWS и обработки светодиодов устанавливается счетчик обрабатываемых битов звукового сигнала (регистр count_sound), запускается таймер 1, и происходит обработка первого бита звукового сигнала в подпрограмме Proc_Sound.
В этой процедуре считывается байт данных из ячейки временного хранения данных формы звука (ячейка Temp_Sound в RAM), данные сдвигаются вправо через бит С регистра состояния и сохраняются обратно в RAM. В зависимости от состояния бита С программа либо включает, либо выключает звуковой сигнал, после чего уменьшает счетчик обрабатываемых битов звукового сигнала count_sound.
Пока счетчик битов не равен нулю, эта подпрограмма будет вызываться при срабатывании прерывания по переполнению канала А таймера 1 (обработчик прерывания Timer1A_Ok).
При обработке всех 8 битов звукового сигнала подпрограмма останавливает таймер 1, однозначно выключает звуковой сигнал, очищает ячейку формы звука в буфере приема/передачи (Sound_Data) и исправляет байт контрольной суммы.
КОНСТРУКЦИЯ.
Конструктивно устройство выполнено на двухсторонней печатной плате, рассчитанной под установку smdкомпонентов. В качестве корпуса используются два двухклавишных выключателя, например фирмы «Viko» и двойная рамка для них.
От выключателей используется четыре клавиши и обрезанные стойки, на которые крепятся клавиши. Стойки обрезаются на высоту 0,8 – 0,9 мм (зависит от используемых кнопок), и с помощью винтов устанавливаются на печатную плату. Двойная рамка крепится к печатной плате на шести штоках, из отрезков медной проволоки припаянных к печатной плате.
ФАЙЛЫ ПРОЕКТА
- проект в AVR Studio