Программа, рассмотренная в этой статье, разработана для контроллера 1-wire-шлюза (шлюз, как вы помните, реализован на ATTiny2313). Эта программа позволяет из терминальной программы персонального компьютера общаться через шлюз с различными 1-wire устройствами в качестве Master-а.
Для реализации обмена данными по 1-wire между контроллером и подключаемым устройством были использованы стандартные, написанные нами ранее процедуры (только задержки в них изменены, с учётом того, что здесь у нас кварц на 20 МГц, а не на 8).
Наша программа умеет делать следующие вещи: 1) по команде с компьютера формировать на шине 1-wire сигнал Reset, определять был ли получен ответ (Presence) от слэйва на этот сигнал и сообщать об этом компьютеру; 2) отправлять принятую с компа информацию по шине 1-wire; 3) считывать информацию с шины 1-wire и отправлять её компу. Причём, информацию по шине 1-wire наш шлюз умеет отправлять и принимать как побайтно, так и побитно.
Протокол обмена с компьютером состоит из сообщений, размером в 1 байт. Обмен всегда начинается с какой-либо команды от компьютера. Формат обмена для каждой команды строго определён и описан ниже.
Итак, команды используются такие:
- 01h — Reset. После получения такой команды — шлюз формирует на шине 1-wire сигнал сброса, потом проверяет — был ли на шине в ответ на сброс сформирован сигнал Presence и в зависимости от этого отправляет компьютеру код 02h (если Presence не был сформирован) или 03h (если Presence был сформирован).
- 02h — Send. Эта команда означает, что следующий принятый байт будет отправлен шлюзом по шине 1-wire. После принятия команды Send шлюз посылает назад компьютеру код 01h (готовность), после чего переходит к ожиданию байта, который нужно будет отправить по 1-wire. Далее, получив от компьютера этот байт, шлюз отправляет его по 1-wire и снова посылает копьютеру код 01h, который означает, что команда выполнена успешно и можно посылать шлюзу следующую команду.
- 03h — Read. Получив эту команду, шлюз считывает 1 байт с шины 1-wire и отправляет этот байт компьютеру.
- 04h — Send_One_Bit. Эта команда аналогична команде Send, за исключением того, что по шине 1-wire отправляется не весь принятый от компьютера байт, а только его младший бит.
- 05h — Read_One_Bit. После получения этой команды — шлюз считывает с шины 1-wire 1 бит и, в зависимости от значения этого бита, отправляет компьютеру код 0Fh (если бит равен нулю) или код 8Fh (если бит равен единице).
В случае, если шлюз принимает неизвестную ему команду, — он отсылает назад компьютеру код FFh.
Вот пожалуй и всё описание (если возникнут вопросы — не стесняйтесь, задавайте на форуме), а теперь перейдём к алгоритму и программе.
Алгоритм:
Итак, в аппаратной части мы имеем:
- PB2 — линия 1-wire
- PD0 — линия Rx
- PD1 — линия Tx
.device ATtiny2313 .include "tn2313def.inc" .list ;-- определяем свои переменные .def w=r16 ; это будет наш аккумулятор .def Flags=r17 ; флаги 1-wire .def Transmit_R=r18 ; байт для передачи .def Recieve_R=r20 ; принятый байт .def Bit_counter=r21 ; счётчик переданных/принятых бит ;-- определяем константы (и даём им имена) .equ line_1_wire=2 ; PortB2/PinB2 - на этой ноге сделан порт 1-wire .equ Port_reg=PORTB .equ Pin_reg=PINB .equ DDR_reg=DDRB ;---------------------------- .equ SEND_BIT=0 .equ READ_BIT=1 .equ SEND_RESET=2 .equ FREE_BUS=3 .equ SLAVE_IS=4 ;-- начало программного кода .cseg .org 0 rjmp Init ; переход на начало программы (вектор сброса) ;-- дальше идут вектора прерываний ;-- если не используем - пишем reti, иначе - переход на начало обработчика reti ; внешнее прерывание INT0 reti ; внешнее прерывание INT1 reti ; Input capture interrupt 1 reti ; Timer/Counter1 Compare Match A reti ; Overflow1 Interrupt reti ; Overflow0 Interrupt rjmp RX_INT ; USART0 RX Complete Interrupt reti ; USART0 Data Register Empty Interrupt reti ; USART0 TX Complete Interrupt reti ; Analog Comparator Interrupt reti ; Pin Change Interrupt reti ; Timer/Counter1 Compare Match B reti ; Timer/Counter0 Compare Match A reti ; Timer/Counter0 Compare Match B reti ; USI start interrupt reti ; USI overflow interrupt reti ; EEPROM write complete reti ; Watchdog Timer Interrupt ;-- начало программы Init: ldi w,RAMEND ; устанавливаем указатель вершины out SPL,w ; стека на старший байт RAM sbi ACSR,ACD ; выключаем компаратор ;-- инициализируем порты ser w ; w=0xFF out DDRA,w ; настраиваем порт A. все линии на выход out DDRD,w ; настраиваем порт D. все - выходы clr w ; w=0x00 out PORTA,w ; на всех линиях ноль out PORTD,w ldi w,0b11111011 ; настраиваем порт B. PB2 - вход, остальные - выходы out DDRB,w ; clr w ; начальное состояние выходов и подтяжки на входах out PORTB,w ; (выходы - нули, подтяжек нет) ;-- инициализируем UART out UBRRH,w ; UBRR (для кварца 20 МГц и скорости 115200) ldi w,10 ; равен 10, т.е. UBRRH=0, UBRRL=10 out UBRRL,w ldi w,0b00001110 ; поднимаем биты USBS, UCSZ1:0 out UCSRC,w ; формат: 1 старт, 8 данные, 2 стоп sbi UCSRB, TXEN ; включить передатчик nop sbi UCSRB, RXEN ; включить приёмник ;-- разрешаем прерывания от приёмника sbi UCSRB,RXCIE ;-- инициализируем таймер clr w out TCCR0A,w ; таймер отключен от выводов OC0A(B) out TCNT0,w ; сбрасываем таймер out TIMSK,w ; запрещаем все прерывания от него ser w out TIFR,w ; сбрасываем флаги таймера ;-- сообщаем компу, что загрузились и ждём команду ldi w,0x01 out UDR,w ;-- разрешаем глобальные прерывания sei ;-- ждём у моря погоды --- Wait_data: rjmp Wait_data ;************************************************ ;------------------------------------------------ ;--- Прерывание от UART ------------------------- RX_INT: in w,UDR ; читаем байт из приёмника в w cpi w,0x01 ; принятый байт равен 1? breq Reset cpi w,0x02 ; принятый байт равен 2? breq Send cpi w,0x03 ; принятый байт равен 3? breq Read cpi w,0x04 ; принятый байт равен 4? breq Send_One_Bit cpi w,0x05 ; принятый байт равен 5? breq Read_One_Bit ;-- Сообщаем что приняли неизвестную команду и выходим Error_Command: ldi w,0xFF out UDR,w reti ;--- ОБРАБОТЧИКИ КОМАНД ------------------------- Reset: rcall Send_Reset_proc ldi w,0x02 ; никого нет sbrc Flags,SLAVE_IS ldi w,0x03 ; есть слэйв out UDR,w ; шлём компу результат reti ;------------------------------ Send: ldi w,0x01 ; сообщаем компу, что ждём байт для передачи out UDR,w ldi Bit_counter,8 ; готовимся передать 8 бит Wait_byte: sbis UCSRA,RXC rjmp Wait_byte in Transmit_R,UDR ; пишем байт для передачи в Transmit_R Send_next_bit: rcall Send_Bit_proc lsr Transmit_R dec Bit_counter brne Send_next_bit ; если передали не все биты - прыгаем ldi w,0x01 ; сообщаем компу, что передали все биты out UDR,w reti ; и выходим ;------------------------------ Read: ldi Bit_counter,8 ; готовимся прочитать 8 бит Read_next_bit: rcall Read_Bit_proc dec Bit_counter brne Read_next_bit ; если прочитали не все биты - читаем следующий out UDR, Recieve_R ; посылаем компу то, что прочитали reti ; и выходим ;------------------------------ Send_One_Bit: ldi w,0x01 ; сообщаем компу, что ждём байт, out UDR,w ; содержащий бит для передачи Wait_byte2: ; передавать будем только младший бит этого байта sbis UCSRA,RXC rjmp Wait_byte2 in Transmit_R,UDR ; пишем байт для передачи в Transmit_R rcall Send_Bit_proc ldi w,0x01 ; сообщаем компу, что передали бит out UDR,w reti ; и выходим ;------------------------------ Read_One_Bit: ldi Recieve_R,0b00011110 rcall Read_Bit_proc out UDR, Recieve_R ; посылаем компу то, что прочитали (1 - 8F, 0 - 0F) reti ; и выходим ;************************************************ ;------------------------------------------------ ;--- Процедуры 1-Wire --------------------------- _1_wire_null: sbi DDR_reg,line_1_wire ret _1_wire_Z: cbi DDR_reg,line_1_wire ret ;------------------------------------------------ Send_Reset_proc: sbr Flags, 1<<SEND_RESET cbr Flags, 1<<SLAVE_IS ;--- настраиваем таймер --- ldi w,177 ; начальное значение таймера (255-78) out TCNT0,w ldi w,216 ; +39 тиков out OCR0A,w ldi w, 221 ; +5 тиков out OCR0B,w ;--- конец настройки таймера --- ldi w,0b00000100 out TCCR0B,w ; запускаем таймер с предделителем 1:256 rcall _1_wire_null ; формируем низкий уровень на шине ;--- ждём отсчёта первого интервала --- M1: in w,TIFR sbrs w,OCF0A ; если флаг установлен - пропустить команду rjmp M1 rcall Occured_OCF0A ; первое действие ;--- ждём отсчёта второго интервала --- M2: in w,TIFR sbrs w,OCF0B ; если флаг установлен - пропустить команду rjmp M2 rcall Occured_OCF0B ; второе действие ;--- ждём отсчёта третьего интервала --- M3: in w,TIFR sbrs w,TOV0 ; если флаг установлен - пропустить команду rjmp M3 rcall Occured_TOV0 ; третье действие cbr Flags, 1<<SEND_RESET ; флаг передачи сигнала "Reset" ret ; и выходим Send_Bit_proc: sbr Flags, 1<<SEND_BIT ;--- настраиваем таймер --- ldi w,85 ; начальное значение таймера (255-170) out TCNT0,w ldi w,89 ; +4 тика out OCR0A,w ldi w, 241 ; +156 тиков out OCR0B,w ;--- конец настройки таймера --- ldi w,0b00000010 out TCCR0B,w ; запускаем таймер с предделителем 1:8 rcall _1_wire_null ; формируем низкий уровень на шине ;--- ждём отсчёта первого интервала --- M4: in w,TIFR sbrs w,OCF0A ; если флаг установлен - пропустить команду rjmp M4 rcall Occured_OCF0A ; первое действие ;--- ждём отсчёта второго интервала --- M5: in w,TIFR sbrs w,OCF0B ; если флаг установлен - пропустить команду rjmp M5 rcall Occured_OCF0B ; второе действие ;--- ждём отсчёта третьего интервала --- M6: in w,TIFR sbrs w,TOV0 ; если флаг установлен - пропустить команду rjmp M6 rcall Occured_TOV0 ; третье действие cbr Flags, 1<<SEND_BIT ; сбрасываем флаг передачи бита ret ; и выходим Read_Bit_proc: sbr Flags, 1<<READ_BIT ;--- настраиваем таймер --- ldi w,85 ; начальное значение таймера (255-170) out TCNT0,w ldi w,89 ; +4 тика out OCR0A,w ldi w, 119 ; +34 тика = 14,55 мкс (+35 тиков = 14,95 мкс) out OCR0B,w ;--- конец настройки таймера --- ldi w,0b00000010 out TCCR0B,w ; запускаем таймер с предделителем 1:8 rcall _1_wire_null ; формируем низкий уровень на шине ;--- ждём отсчёта первого интервала --- M7: in w,TIFR sbrs w,OCF0A ; если флаг установлен - пропустить команду rjmp M7 rcall Occured_OCF0A ; первое действие ;--- ждём отсчёта второго интервала --- M8: in w,TIFR sbrs w,OCF0B ; если флаг установлен - пропустить команду rjmp M8 rcall Occured_OCF0B ; второе действие ;--- ждём отсчёта третьего интервала --- M9: in w,TIFR sbrs w,TOV0 ; если флаг установлен - пропустить команду rjmp M9 rcall Occured_TOV0 ; третье действие cbr Flags, 1<<READ_BIT ; сбрасываем флаг чтения бита ret ; и выходим ;-------------------------------------------- Occured_OCF0A: sbrc Flags, SEND_RESET rjmp Send_Reset_OCR0A sbrc Flags, SEND_BIT rjmp Send_Bit_OCR0A sbrc Flags, READ_BIT rjmp Read_Bit_OCR0A ret Send_Reset_OCR0A: ; первое действие для Send_Reset - отпустить шину rcall _1_wire_Z ret Send_Bit_OCR0A: ; первое действие для Send_Bit - установить бит sbrc Transmit_R,0 ; если передаём ноль - ничего делать не нужно rcall _1_wire_Z ; если передаём единицу - отпускаем шину ret Read_Bit_OCR0A: ; первое действие для Read_Bit - отпустить шину rcall _1_wire_Z ret Occured_OCF0B: sbrc Flags, SEND_RESET rjmp Send_Reset_OCR0B sbrc Flags, SEND_BIT rjmp Send_Bit_OCR0B sbrc Flags, READ_BIT rjmp Read_Bit_OCR0B ret Send_Reset_OCR0B: ; второе действие для Send_Reset - прочитать "Presence" sbis Pin_reg,line_1_wire ; если 1 - пропускаем команду sbr Flags, 1<<SLAVE_IS ret Send_Bit_OCR0B: ; второе действие для Send_Bit - отпустить шину rcall _1_wire_Z ret Read_Bit_OCR0B: ; второе действие для Read_Bit - прочитать бит с шины lsr Recieve_R ; сдвигаем регистр вправо, а в старший пишем ноль sbic Pin_reg, line_1_wire ; если 0 - пропускаем команду sbr Recieve_R,1<<7 ret Occured_TOV0: sbrc Flags, SEND_RESET rjmp Send_Reset_TOV0 sbrc Flags, SEND_BIT rjmp Send_Bit_TOV0 sbrc Flags, READ_BIT rjmp Read_Bit_TOV0 ret Send_Reset_TOV0: ; третье действие для Send_Reset - конец процедуры Send_Bit_TOV0: ; третье действие для Send_Bit - конец тайм-слота Read_Bit_TOV0: ; третье действие для Read_Bit - конец тайм-слота clr w out TCCR0B,w ; останавливаем таймер ser w out TIFR,w ; сбрасываем флаги таймера, ret ;--------------------------------------------------------- |
Для правильной работы шлюза в контроллере должны быть «запрограммированы» следующие фьюзы: SPIEN, SUT0
Скачать готовую прошивку и asm-файл
Приведу небольшой пример работы со шлюзом.
Пусть у нас есть обычный ключ-таблетка от подъездной двери (сразу скажу, что в примере ключ не от моего подъезда). Присоединяем шлюз к компьютеру, выводы 1-wire и GND нашего шлюза присоединяем к этой таблетке, заходим в терминалку, выбираем порт и скорость обмена (скорость у нас 115200), подключаемся и делаем следующее:
— отправляем шлюзу 01h | // (послать сигнал Reset) |
— получаем от шлюза 03h | // шлюз сообщает, что увидел Presence от ключа |
— отправляем шлюзу 02h | // сообщаем шлюзу, что следующий байт нужно отправить по шине 1-wire |
— получаем от шлюза 01h | // шлюз говорит, что ждёт байт для передачи |
— отправляем шлюзу 33h | // посылаем ключу команду Read ROM |
— получаем от шлюза 01h | // шлюз сообщает, что команда отправлена |
— отправляем шлюзу 03h | // просим шлюз считать байт с шины 1-wire |
— получаем от шлюза 01h | // получаем первый прочитанный байт |
— отправляем шлюзу 03h | // просим шлюз считать байт с шины 1-wire |
— получаем от шлюза 00h | // получаем второй прочитанный байт |
— отправляем шлюзу 03h | // просим шлюз считать байт с шины 1-wire |
— получаем от шлюза 15h | // получаем третий прочитанный байт |
— отправляем шлюзу 03h | // просим шлюз считать байт с шины 1-wire |
— получаем от шлюза 47h | // получаем четвёртый прочитанный байт |
— отправляем шлюзу 03h | // просим шлюз считать байт с шины 1-wire |
— получаем от шлюза D9h | // получаем пятый прочитанный байт |
— отправляем шлюзу 03h | // просим шлюз считать байт с шины 1-wire |
— получаем от шлюза 00h | // получаем шестой прочитанный байт |
— отправляем шлюзу 03h | // просим шлюз считать байт с шины 1-wire |
— получаем от шлюза 00h | // получаем седьмой прочитанный байт |
— отправляем шлюзу 03h | // просим шлюз считать байт с шины 1-wire |
— получаем от шлюза 62h | // получаем восьмой прочитанный байт |
В результате этих действий мы получаем все 8 байт, зашитых в ROM ключа-таблетки: 01 00 15 47 D9 00 00 62.
Здравствуйте. Не могли бы Вы привести значения старшего и младшего байта фьюзов? Собрал шлюз, но видимо где-то напортачил с фьюзами. Нормально не хочет работать.
Установить только SUT0 и SPIEN. Учтите, что эта прошивка не работает с программами для коммерческой версии шлюза. Там другие команды (их больше, поскольку там ещё I2C и SPI, и номера команд не совпадают). Соответственно, для этой прошивки нет ПО верхнего уровня, только хексом из терминалки. Но! ПО для коммерческой версии выложено с исходниками, так что вы можете его сами переделать как вам нужно.
спасибо.