1-Wire: алгориттма поиска ROM, реверс

Одолела тут детская болезнь — «написать свой 1-Wire с преферансом и куртизанками», точнее с определением количества устройств на шине. Готовые проекты, которые нашёл, не являются библиотеками по принципу «подключил и пользуйся», только как примеры. (Здесь тоже этого нет — возможно, позже). Сам алгоритм описан в AN187. Неплохо у Погребняк Дмитрия сделано, но меня не устроило. Был ещё один субъективный момент.
А именно — доводилось мне пользоваться CodeVision, но от него ушёл. Так там интересно сделано: проект «компилируется» в ассемблерный файл, а уже потом скармливается ассемблеру для сборки в бинарник. И часть библиотек, в том числе 1-Wire запрятана в компилятор первого уровня. А вот работа с термометрами — в отдельном файле, вполне себе Си, просто расширение *.LIB. Втемяшилось мне в голову использовать их библиотеку термометров (уже пожалел, но дело сделано), и решил содрать, как они опрос устройств делают, да ещё непонятно, зачем им 9 байт на устройство?
Кому интересно — разобранный и под писаный текст:
;-------------------------
.EQU MSK=r25	; маска бита
.EQU PRVCNF=r0	; предыдущий конфликт
.EQU BRSLT=r24	; бит результирующего
.EQU OWBITIN=r23; вход для записи бита 1-wire
.EQU DEVCNT=r20	; счётчик найденых устройств
.EQU LSTCNF=r1	; последний конфликт
.EQU CNT_BIT=r21; счётчик бит
.EQU CNT_BYT=r21; счётчик байт (п/п копирования элемента)
;__w1_read_bit возвращает r30=( x....... )
;_w1_write:	; Y { r28 low, r29 high } на массив к записи, r23 - сколько байт
; X {r26 low, r27 high} - ссылка на массив сэлементами 9 байт

_w1_search:		; поиск устройств
	push r20
	push r21; место под CNT_BIT
	clr  LSTCNF		; r1=0
	clr  DEVCNT
	ld   r26,y	; загрузка кода ROM, Alarm
	clr  r27
__w1_new_pass:	; начало прохода
	mov  PRVCNF,LSTCNF	;r0 = zero
	clr  LSTCNF
	rcall _w1_init	; вызов инициализации
	tst  r30	; выходной параметр
	breq __w1_srch_exit; если никого на шине - выход
	ldd  r30,y+1
	st   -y,r30	;сохранить с прединкрементом
	rcall _w1_write	;ищем ROM
	ldi  CNT_BIT,1
__w1_nextbit:
	cp   CNT_BIT,LSTCNF ; CNT_BIT >= LSTCNF ; CNT_BIT < LSTCNF
	brsh __no_last_confl	; не место посл. конфликта
	rcall __w1_read_bit	;первый бит
	sbrc r30,7			;пропустить, если 0 (бит 1)
	rjmp _bit1_is1		; --x1
	rcall __w1_read_bit	; читаем следующий бит  --0x
	sbrc r30,7			;пропустить, если 0 (бит 2)
	rjmp _bit2_is1		; --11 ! нереально, только что проверились
	rcall __curr_bit		; -00, анализ комбинации
	and  BRSLT,MSK
	brne _bit2_is1
	mov  LSTCNF,CNT_BIT	; запоминаем место конфликта
	rjmp _bit2_is1
_bit1_is1:				; --x1
	rcall __w1_read_bit
_bit2_is1:				; --11
	rcall __curr_bit		; текущий бит
	and  BRSLT,MSK ; если в результате 0
	ldi  OWBITIN,0
	breq __select_way0
__select_way1:	; выбрали ветку "1"
	ldi  OWBITIN,1
__select_way0:	; выбрали ветку "0"
	rcall __w1_write_bit
	rjmp __w1_leaf_done
__no_last_confl:		; не последний конфликт
	rcall __w1_read_bit
	sbrs r30,7; пропуск, если прочитали 1
	rjmp __bit1ok_is0	; go to ---x0
	rcall __w1_read_bit	; ---x1
	sbrs r30,7; пропуск, если прочитали 1
	rjmp __bit2ok_is1	; go to ---11
__w1_srch_exit:
	mov  r30,DEVCNT	;выход с DEVCNT = r20
	pop  r21 ; CNT_BIT уже не нужен
	pop  r20
	adiw r28,2
	ret		;выход
__bit2ok_is1:	; ---11
	set;T = 1
	rcall __store_bit
	rjmp __select_way1
__bit1ok_is0:	; ---x0
	rcall __w1_read_bit
	sbrs r30,7; пропуск, если прочитали 1
	rjmp __bit2ok_is0	; go ---0(0)
	rjmp __w1_leaf_0	; go ---1(0)
__bit2ok_is0: ; ---0(0)
	cp   CNT_BIT,PRVCNF ; предыдущий конфликт?
	breq __w1_leaf_1
	mov  LSTCNF,CNT_BIT ; новый конфликт, запоминаем место
__w1_leaf_0:	; конфликт, пишем 0
	clt	; T = 0
	rcall __store_bit
	clr  OWBITIN
	rcall __w1_write_bit
	rjmp __w1_leaf_done
__w1_leaf_1:	; конфликт, пишем 1
	set	; T = 1
	rcall __store_bit
	ldi  OWBITIN,1
	rcall __w1_write_bit
__w1_leaf_done:
	inc  CNT_BIT	 ;счётсик бит
	cpi  CNT_BIT,65
	brlt __w1_nextbit; CNT_BIT < 65
	rcall __w1_read_bit	;get ninth status bit
	rol  r30 		; [C] <-rX <-[C]
	rol  r30 		; [C] <-rX <-[C]
	andi r30,1		; оставляем прочитанный бит
	adiw r26,8		; адресуемся к статусному
	st   x,r30		; сохраняем статусный
	sbiw r26,8		; возврашаем к исходному
	inc  DEVCNT ; ещё устройство нашли
	tst  LSTCNF
	breq __w1_srch_exit ; если конфликтов не было - выход
	ldi  CNT_BYT,9	; размер элемента
__w1_cpy_prev_loop:	; копируем N to N+1
	ld   r30,x
	adiw r26,9	; указатель n = n+9 (следующий байт)
	st   x,r30
	sbiw r26,8	; а потом [n+1] предыдущего эл-та
	dec  CNT_BYT
	brne __w1_cpy_prev_loop
	rjmp __w1_new_pass	; следующий проход

__curr_bit:		; текущий бит
;r22 = cnt1
	mov  r30,CNT_BIT
	dec  r30	;сколько пройдено
	mov  cnt1,r30
	lsr  r30	; rX -> [C]
	lsr  r30	; rX -> [C]
	lsr  r30	; rX -> [C]
	add  r30,r26 	;Z lo <- X lo
	clr  r31	;Z hi
	ld   BRSLT,z	; -- взять байт из приёмника
	ldi  MSK,1 ; загружаем 0x01
	andi cnt1,7 ; маска 0x07
__curr_bit0:
	breq __curr_bit_exit; если cnt1==0 - выход
	lsl  MSK ; [C]<- rX , N не трогаем
	dec  cnt1; уменьшаем C не трогаем
	rjmp __curr_bit0
__curr_bit_exit:
	ret

__store_bit:
	rcall __curr_bit	; получить номер бита
	brts __store_bit2 ; |=
	com  MSK		; ~MSK
	and  BRSLT,MSK	; занулить бит результата
	rjmp __store_bit3	; выход
__store_bit2:			; |=
	or   BRSLT,MSK	; установить бит результата
__store_bit3:
	st   z,BRSLT	; сохранить в приёмнике
	ret
;--------------------

Продолжение от 02.06.2014. По поводу Application note от Maxim: всё проще, чем кажется.
Суть алгоритма AN187 такова: после инициализации смотрим, ответил ли кто, если нет — выход. Так же на выход при комбинации «11» — стоит расценивать как обрыв связи. А дальше, после инициализации в цикле делаем следующее: если прилетают «01» или «10» — просто записываем биты в результат. Если получем «00» — тут самая сложная часть алгоритма. Если мы не дошли до запомненной последней ветки — то следуем по направлению предыдущего прохода. Тут есть одни неявные грабли. В апноте массив объявлен без инициализации, что не есть хорошо. Так как, если отключено обнуление памяти перед запуском main, то начнём искать с малопредсказуемого значения. Если дошли до предудещего «разногласия», то идём по «1», если прошли — то опять по «0». В процессе всего этого запоминаем для последующего вызова позицию последнего «0» и последнего расхождения.
Ещё у Texas Instrumets имеется документ SPMA057. Словесное описание подобного алгоритма состоит из 14 пунктов, страница 8.
Вообще, интересен подход — чтобы не «съедать» стек, заменили рекурсию на множественный вызов функции с учётом предыдущего состояния. Если задуматься — всё правильно, но не очевидно.
Дорисовал алгоритм. Уже не особо нужен, но завершить начатое сильно уж хотелось. На простоту восприятия не претендует, но явно проще, чем ассемблерный листинг.
CodeVision w1_search

Комментарии (4)

RSS свернуть / развернуть
похоже асма не когда не канет в лета, будут использовать как инструмент, измерения, генеталий…
простите если обидел своими словами…
0
Если учиться программировать микроконтроллеры с нуля, то лучше с асма начинать, а потом уже со временем переходить на си. Сразу на СИ это как то не очень. Никогда не поймешь до конца ядро МК. А это важно для написания грамотных программ. Поэтому АСМ никогда не умрет.
0
ASM не умрет потому, что это единственный язык уровня конкретного процессора.
А учиться программировать лучше с ЯВУ. Изучить ядро всегда можно (и, разумеется, крайне желательно), но начиная с асма можно попасть под известную цитату Дейкстры про программистов на бейсике (в этом плане асм и бейсик идентичны).
0
Неплохо у Погребняк Дмитрия
Не знал, что Дима еще и этим занимается. Молодец.
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.