Переходник i-Button (DS1990) — UART

Как ни странно, возникла необходимость считать ID из DS1990  Я сделал маленький макет ридера, который читает «таблетку» и передаёт по UART-у ASCII кодом (4800 бод 8N1). Вот маленькая схемка макета.
Схема переходника i-Button - UART на tiny13


Ниже привожу текст прошивки переходника.

;* ************************************************************************ *
;* ib2uart.avr, iButton-reader                                              *
;* ************************************************************************ *
.include "tn13def.inc"

; опции программирования
; BODLEVEL1=BODLEVEL0=SUT0=CKSEL0=0

.set    VERSION = 0x0100

; * описание портов контроллера *
; выходной интерфейс управления реле/ключом
.equ    CTBout  = PORTB         ; выходной порт
.equ    CTBcfg  = DDRB          ; порт конфигурации выходов
.equ    CTBin   = PINB          ; порт конфигурации выходов
.equ    ledpin  = 0             ; LED
.equ    ibpin   = 1             ; ib-вход/выход
.equ    ouart   = 4             ; UART-выход

.equ    CTB_CFG = (1<<ouart)|(1<<ledpin)|(0<<ibpin) ; константа cfg порта
.equ    CTB_OUT = (1<<ouart)|(1<<ledpin)|(0<<ibpin) ; константа out порта

; тактовая частота МГц
#define XCLK        9.6
.equ    CN2MKS      = INT((2*XCLK-7)/3)             ; константа 2мкс
.equ    CN5MKS      = INT((5*XCLK-9)/3)             ; константа 5мкс
.equ    CN10MKS     = INT((10*XCLK-9)/3)            ; константа 10мкс
.equ    CN50MKS     = INT((50*XCLK-9)/3)            ; константа 50мкс

; * назначение переменных *
.def    cnt         = R19
.def    tmp1        = R20           ; переменная общего пользования
.def    tmp2        = R21           ; переменная общего пользования
.def    tmp3        = R22           ; переменная общего пользования
.def    tmp4        = R23           ; переменная общего пользования

.equ    store       = SRAM_START    ; указатель на память

; кодовый сегмент
        .cseg                   ; CODE segment
;______________________________________________________________
; таблица 
        .org    0
        rjmp    main_rstcold    ; вектор сброса
        .org    INT0addr
        reti                    ; External Interrupt 0
        .org    PCI0addr
        reti                    ; External Interrupt Request 0
        .org    OVF0addr
        reti                    ; Timer/Counter0 Overflow
        .org    ERDYaddr
        reti                    ; EEPROM Ready
        .org    ACIaddr
        reti                    ; Analog Comparator
        .org    OC0Aaddr
        reti                    ; Timer/Counter0 Compare Match 0A
        .org    OC0Baddr
        reti                    ; Timer/Counter Compare Match B
        .org    WDTaddr
        reti                    ; Watchdog Time-out
        .org    ADCCaddr
        reti                    ; ADC Conversion Complete


;______________________________________________________________
uart_del:
; полубитная задержка
        ldi     tmp3,251        ; загрузка счетчика задержки
uart_dlp:
        nop                     ; пустой такт
        dec     tmp3            ; декремент счётчика задержки
        brne    uart_dlp        ; закрытие цикла ожидания
        ret                     ; выход

;______________________________________________________________
pr_hex:
        push    tmp1            ;
        swap    tmp1            ;
        rcall   pr_dig          ;
        pop     tmp1            ;
pr_dig:
        andi    tmp1,0x0F       ;
        cpi     tmp1,0x0A       ;
        ldi     tmp2,0x30       ;
        brcs    prhx_coring     ;
        ldi     tmp2,0x37       ;
prhx_coring:
        add     tmp1,tmp2       ;
;______________________________________________________________
putchar:
; функция передачи байта TxByte
;  IN - tmp1
; OUT - нет
; CHG - нет
        push    tmp1            ;
        push    tmp2            ;
        push    tmp3            ;

        ldi     tmp2,1+8+1      ; 1+8+1
        com     tmp1            ;
        sec                     ; установка старт-бита
putc_pblm:
        brcc    putc_psbm       ; переход на установку лог.1, если C==0
        cbi     CTBout,ouart    ; установка лог.0
        rjmp    putc_pbdm       ; переход к битовой задержке
putc_psbm:
        sbi     CTBout,ouart    ; установка лог.1
        nop                     ; тактовое уравнивание
putc_pbdm:
        rcall   uart_del        ;
        rcall   uart_del        ;

        lsr     tmp1            ; получение в C след.бита
        dec     tmp2            ; декремент счетчика битов
        brne    putc_pblm       ; проверка закрытия цикла посылки битов

        pop     tmp3            ;
        pop     tmp2            ;
        pop     tmp1            ;

        ret                     ; выход

;______________________________________________________________
; вектор сброса, вход в основную петлю программы
main_rstcold:
; инициализация сторожевого таймера
        cli                     ; запрет всех прерываний
; установка стека 
        ldi     tmp1,low(RAMEND); загрузка указателя стека
        out     SPL,tmp1        ; запись указателя
; начальная установка конфигурации порта B
;  PB5   PB4   PB3   PB2   PB1   PB0
;                    LED   RSL   RSH
;  in    in    in    out   out   out
        ldi     tmp1,CTB_OUT    ; значение выходов порта
        out     CTBout,tmp1     ; запись состояния выходов в порт
        ldi     tmp1,CTB_CFG    ; значение выходов порта B
        out     CTBcfg,tmp1     ; запись I/O конфигурации
; разрешаем внешние прерывания по фронту/срезу
        ldi     tmp1,(1<<ISC00) ; значение для int0 по фронту/срезу
        out     MCUCR,tmp1      ; запись в регистр
        ldi     tmp1,(0<<INT0)  ; значение для разрешения int0
        out     GIMSK,tmp1      ; разрешение прерываний int0

        sei                     ; разрешение всех прерываний
main_loop:
        rcall   ib_reset        ; сброс i-button
        brts    main_present    ; переход, если i-button ответил
        rjmp    main_loop       ; зацикливание
main_present:
        ldi     tmp1,0x33       ; код команды чтения ID DS1990
        rcall   ib_write        ; передача команды в i-button

        ldi     ZL,low(store)   ; мл.байта указателя на массив, куда будет запоминаться ID
        ldi     ZH,high(store)  ; ст.байта указателя на массив, куда будет запоминаться ID
        ldi     tmp4,0x08       ; кол-во байт ID
main_rdlp:
        push    tmp4            ; сохраняем счётчик
        rcall   ib_read         ; читаем данные из i-button
        pop     tmp4            ; восстанавливаем счётчик байтов
        push    tmp4            ; опять сохраняем счётчик
        cpi     tmp4,8          ; проверяем, 1й ли байт считан
        brne    main_famskp     ; переход, если не 1й
        cpi     tmp1,0x00       ; значение 1го байта ==0?
        brne    main_famskp     ; если !=0, то продолжаем чтение (считан тип ИМС)
        rjmp    main_loop       ; если ==0, то это просто замкнули контакты 
main_famskp:
        st      Z+,tmp1         ; запоминаем байт в памяти
        pop     tmp4            ; восстанавливаем счётчик байтов
        dec     tmp4            ; декремент счётчика
        brne    main_rdlp       ; закрываем цикл чтения ID
; проверка CRC        
        ldi     ZL,low(store)   ; мл.байта указателя на массив ID
        ldi     ZH,high(store)  ; ст.байта указателя на массив ID
        clr     tmp2            ; сброс начального значения CRC
        ldi     tmp4,0x07       ; кол-во байт, по которым считается CRC
main_crclp:        
        ld      tmp1,Z+         ; загружаем очередной байт
        rcall   update_crc      ; вычисляем очередное значение CRC
        dec     tmp4            ; декрементируем счётчик байтов
        brne    main_crclp      ; закрытие цикла подсчёта CRC
        ld      tmp1,Z          ; загружаем CRC, которое считано из i-button
        eor     tmp1,tmp2       ; сравниваем с CRC, которое вычислили сами
        breq    main_crcok      ; переход, если CRC совпали
        rjmp    main_loop       ; если ошибка CRC - возвращаемся в основную петлю

main_crcok:
        ldi     ZL,low(store)   ; мл.байта указателя на массив ID
        ldi     ZH,high(store)  ; ст.байта указателя на массив ID
        ldi     tmp4,0x08       ; установка кол-ва байтов, которые будут переданы по UART
        cli                     ; запрет прерываний
main_txlp:
        push    tmp4            ; сохранение счётчика
        ld      tmp1,Z+         ; загрузка очередного байта ID из памяти
        rcall   pr_hex          ; вывод байта по UART
        ldi     tmp1,0x20       ; установка кода пробела
        rcall   putchar         ; вывод символа по UART
        pop     tmp4            ; восстановление счётчика
        dec     tmp4            ; декремент
        brne    main_txlp       ; закрытие петли передачи ID

        ldi     tmp1,0x0D       ; установка кода "\r"
        rcall   putchar         ; вывод символа по UART
        ldi     tmp1,0x0A       ; установка кода "\n"
        rcall   putchar         ; вывод символа по UART
        sei                     ; разрешение прерываний

        cbi     CTBout,ledpin   ; включение светодиода
        ldi     tmp3,0x02       ; установка ст.байта задержки
ledp_lph:
        clr     tmp2            ;~1, установка "среднего" байта задержки
ledp_lpm:
        clr     tmp1            ;~1, установка "младшего" байта задержки
ledp_lpl:
        nop                     ;~1, пустые такты, чтобы задержка тянулась медленнее :)
        nop                     ;
        dec     tmp1            ;~1, декремент мл.счётчика задержки
        brne    ledp_lpl        ;~2/1, закрытие мл.цикла
        dec     tmp2            ;~1, декремент ср.счётчика задержки
        brne    ledp_lpm        ;~2/1, закрытие ср.цикла
        dec     tmp3            ;~1, декремент ст.счётчика задержки
        brne    ledp_lph        ;~2/1, закрытие ст.цикла
        sbi     CTBout,ledpin   ; выключение светодиода

        rjmp    main_loop       ; возврат в основную петлю

;______________________________________________________________
; задержка 50мкс, 3(rcall)+2(rjmp) + 3*n+4(ret) 
del50mks:
        ldi     tmp4,CN50MKS    ;~1, установка константы 50мкс
        rjmp    del2_lp         ;~2, переход на петлю задержки
;______________________________________________________________
; задержка 10мкс, 3(rcall)+2(rjmp) + 3*n+4(ret) 
del10mks:
        ldi     tmp4,CN10MKS    ;~1, установка константы 10мкс
        rjmp    del2_lp         ;~2, переход на петлю задержки
;______________________________________________________________
; задержка 5мкс, 3(rcall)+2(rjmp) + 3*n+4(ret) 
del5mks:
        ldi     tmp4,CN5MKS     ;~1, установка константы 5мкс
        rjmp    del2_lp         ;~2, переход на петлю задержки
;______________________________________________________________
; задержка 2мкс, 3(rcall)+2(rjmp) + 3*n+4(ret) 
del2mks:
        ldi     tmp4,CN2MKS     ;~1, установка константы 5мкс
del2_lp:
        dec     tmp4            ;~1, декремент счётчика задержки
        brne    del2_lp         ;~2/1, закрытие цикла
        ret                     ;~4, выход
;______________________________________________________________
; задержка 90мкс 
del90mks:
        rcall       del10mks        ; 10mks
        rcall       del10mks        ; 10mks
        rcall       del10mks        ; 10mks
        rcall       del10mks        ; 10mks
        rjmp        del50mks        ; 50mks
;______________________________________________________________
; задержка 100мкс 
del100mks:
        rcall       del50mks        ; 50mks
        rjmp        del50mks        ; 50mks
;______________________________________________________________
; задержка 500мкс 
del500mks:
        rcall       del100mks       ; 100mks
        rcall       del100mks       ; 100mks
        rcall       del100mks       ; 100mks
        rcall       del100mks       ; 100mks
        rjmp        del100mks       ; 100mks

;______________________________________________________________
; чтение/запись байта в i-button; запись 0xFF - это чтение
; из i-button, таймслоты те же
ib_read:
        ldi     tmp1,0xFF       ; установка константы для чтения
ib_write:
        mov     tmp2,tmp1       ; установка параметра (здесь tmp2 рабочий регистр)
        ldi     tmp3,8          ; установка счётчика битов
rwloop:
        sbi     CTBcfg,ibpin    ; опускаем линию i-button в 0
        rcall   del2mks         ; маленькая задержка 2мкс
        ror     tmp2            ; бит, который будем передавать толкаем в C
        brcc    wrzero          ; если бит ==0, то оставляем линию i-button ==0
        cbi     CTBcfg,ibpin    ; если бит ==0, то переводим линию i-button ==1
wrzero:
        rcall   del10mks        ; маленькая задержка, чтобы вывод перешёл в 1
        clc                     ; в C будет значение бита, посланного i-button
        sbic    CTBin,ibpin     ; читаем значение вывода i-button
        sec                     ; устанавливаем C==1, если вывод i-button==1
        ror     tmp1            ; записываем считанное значение в tmp1
        rcall   del50mks        ; выдерживаем до конца время тайм-слота
        cbi     CTBcfg,ibpin    ; поднимаем (отпускаем) линию i-button: 
        rcall   del2mks         ; маленькая задержка 2мкс, чтобы линия поднялась
        dec     tmp3            ; декремент счётчика битов
        brne    rwloop          ; закрытие цикла чтения 8ми битов
        ret                     ; выход

;______________________________________________________________
; сброс i-button и чтение импульса "присутствия" i-button на линии,
; результат возвращается в T
ib_reset:
        sbi     CTBcfg,ibpin    ; линия i-button ==0
        rcall   del500mks       ; большая задержка: i-button обесточивается
        cbi     CTBcfg,ibpin    ; поднимаем линию i-button
        rcall   del90mks        ; ждём заряда внутреннего конденсатора i-button
        clt                     ; сброс T
        sbis    CTBin,ibpin     ; проверяем состояние линии: если i-button присутствует,
                                ; то он в этот момент должен удерживать линию в 0
        set                     ; если линия удерживается в 0, то T==1, i-button на линии
        rcall   del500mks       ; ждём пока i-button отпустит линию
        ret                     ; выход

;______________________________________________________________
; подсчёт байта CRC
; в tmp1 байт, который надо добавить в CRC, в tmp2 уже накопленный CRC8
; алгоритм списан из спецификации на DS1990
update_crc:
        push    tmp3            ;
        push    tmp4            ;
        ldi     tmp4,8          ;
upcr_lp:
        mov     tmp3,tmp2       ;
        eor     tmp3,tmp1       ;
        sbrs    tmp3,0          ;
        rjmp    upcr_zbit       ;
        ldi     tmp3,0x18       ;
        eor     tmp2,tmp3       ;
        lsr     tmp2            ;
        sbr     tmp2,(1<<7)     ;
        rjmp    upcr_shift      ;
upcr_zbit:
        lsr     tmp2            ;
upcr_shift:
        lsr     tmp1            ;
        dec     tmp4            ;
        brne    upcr_lp         ;
        pop     tmp4            ;
        pop     tmp3            ;
        ret                     ; выход