Вы находитесь на странице: 1из 8

5/11.

Вариант подпрограммы преобразования двоичных чисел в двоично-


десятичные (или “покушение” на “глобальную” процедуру) и применение ее на
практике.

Как это очень часто бывает в жизни, толчком к действию является стремление человека
упростить (сделать понятным) то, с чем связан повышенный уровень “мути”, причем, в
приложении к конкретному “телу”.
При этом, достаточно часто, изначально, он даже полномасштабно и не осознает, с
каким “матерым зверюгой” он “схлестнулся”.
Понимание этого приходит потом, а человек просто не успевает испугаться и
ужаснуться, и в этом его главное преимущество.
При наличии трудолюбия, упертости и мозгов, достаточно велика вероятность того, что,
в конечном итоге, человек без званий и ученых степеней, как это не прискорбно для
“академиков”, реально, и имея на то основания, начинает их учить уму разуму (против
практического результата “не попрет” даже “академик”, хотя и среди них всякие “бяки”
попадаются).
Ниже имеет место быть практическое подтверждение этих слов.
Информация от Сергея Рослика выделена темно-синим цветом.
А теперь по-порядку.
После окончания работы над подразделом 5/10, я получил от Сергея письмо, в котором
он описал принцип преобразования формы числа и предоставил соответствующую
программу.

Предлагаю Вашему вниманию ПП преобразования двоичного кода в двоично-десятичный.


Смысл этой программы простой, она напоминает ПП быстрого деления.
Например: в двоичном коде записано число .57
Отнимаем от него десятки, подсчитываем, сколько раз отняли, и количество вычитаний
записываем в регистр десятков LED1, а остаток записываем в регистр единиц LED0.
57-10=47   1
47-10=37   2
37-10=27   3
27-10=17   4
17-10=7     5
Количество операций (5) записывается в LED1, а остаток (7) - в LED0.
Я эту ПП вставил в программу 01_grad1.asm и проверил в железе, работает быстро и чётко. Есть
возможность наращивания разрядов.

;********************************************************************************
; ПП преобразования двоичного кода в двоично-десятичный
; Используется м/контроллер PIC16F873. Частота кварца 4 Мгц.
;************************************************************************
LIST p=16f873
__CONFIG 3731H
;
================================================================================
; Определение положения регистров специального назначения.
;
================================================================================
PC equ 02h ; Счетчик команд.
Status equ 03h ; Регистр Status.
;
================================================================================
; Присваивание названий битам.

1
;
================================================================================
C equ 0 ; Бит регистра статус.
;
================================================================================
; Определение места размещения результатов операций.
;
================================================================================
F equ 1 ; Результат направить в регистр.
;********************************************************************************
; Определение названия и положения регистров общего назначения.
;********************************************************************************
Temp_LSB equ 20h ; Регистр перекодируемого числа.
LED0 equ 21h ; Разряд единиц.
LED1 equ 22h ; Разряд десятков.
;********************************************************************************
org 0 ; Начать исполнение программы с PC=0.
;************************************************************************

;========================================================================
; Начало выполнения подпрограммы.
;========================================================================
START movlw .36 ; Запись двоичного числа
movwf Temp_LSB ; в регистр Temp_LSB.
;
========================================================================;
ПП преобразования однобайтного двоичного числа в двухзначное десятичное число.
;========================================================================
BIN2_10 clrf LED0 ; Подготавливаем регистры для загрузки в
clrf LED1 ; них результатов двоично-десятичного
; преобразования.
B10 movlw .246 ; Отнимаем из разряда десятков .10
addwf Temp_LSB,F ; Temp_LSB-10->Temp_LSB
incf LED1,F ; Подсчитываем, сколько раз отняли без
; остатка.
btfsc Status,C ; Проверяем, остаток был или нет.
goto B10 ; Если остатка небыло, то продолжаем
; отнимать .10
decf LED1,F ; Отнимаем избыточную операцию инкремента.
;--------Выделение разряда единиц------------------------------------------
movlw .10 ; Выделяем из разряда единиц остаток.
addwf Temp_LSB,F ;
movf Temp_LSB,w ; Загружаем остаток в регистр результата
movwf LED0 ; преобразования единиц.
goto START ; Переход на новый цикл программы.
;************************************************************************
end ; Конец программы.

По умолчанию, в программу “заложена” константа .36, но можно “влепить” и другую.


Программа составлена под PIC16F873, но можно перейти и на PIC16F84A (и т.д.).

Работа подпрограммы Сергея.

До входа в ПП BIN2_10, в регистр Temp_LSB должно быть записано какое-то число.


В данном случае, это число .36.
В начале ПП Bin2_10, в регистры результата преобразования формы числа, с названиями
LED0 и LED1, записывается 0 (подготовка к работе).
Далее следует команда записи в регистр W константы .246.
С какого “потолка упало” это числовое значение?

2
Дело в том, что 246 = 256 (количество чисел, которое может отобразить байт) минус
число .10 (“важняк” десятичной системы исчисления).
Если, после этого, просуммировать содержимое регистра Temp_LSB и содержимое
регистра W, то в случае, если в регистре Temp_LSB “лежит” число большее .10,
произойдет выход за границы диапазона чисел, которые способен отобразить байт.
При этом, в регистре Temp_LSB “оседает” число, равное разнице между арифметическим
результатом суммирования и числом 256, а флаг С поднимется (перенос. С=1).
Последнее можно использовать в качестве критерия обнаружения остатка от деления на
число .10, который всегда меньше .10, что и имеет место быть.
Предположим, что происходит первое суммирование.
В результате исполнения команды addwf Temp_LSB,F, в регистре Temp_LSB “осядет”
число .246 + .36 = .282 - .256 = .26 (то есть, .36 - .10 = .26).
Обратите внимание: применяется команда суммирования (addwf), но по факту происходит
вычитание (эстеты это оценят должным образом).
Так как произошел выход за числовые границы одного байта, флаг С поднимется (С=1).
После этого, происходит инкремент содержимого регистра десятков LED1 (incf LED1,F).
А как же иначе? Ведь нужно “задокументировать” факт переноса.
Обращаю Ваше внимание на то, что команда incf не воздействует на флаг С (см.
распечатку команд), и по этой причине, проверку состояния флага С можно производить
без “оглядки” на нее, что и имеет место быть (btfsc Status,C).
В рассматриваемом случае, после исполнения команды addwf Temp_LCB,F, флаг С
поднялся, следовательно, происходит безусловный переход goto B10.
Это означает то, что сказанное выше повторится, только с числом .246 суммируется не
число .36, а число .26: .246 + .26 = .272 - .256 = .16.
Флаг С будет поднят.
После инкремента, число, “лежащее” в регистре LED1 увеличится на 1 и станет равным
1 + 1 = 2.
После анализа состояния бита С, произойдет переход на 3-й “виток”, в результате чего: .
246 + .16 = .262 - .256 = .6.
Флаг С будет поднят.
После инкремента, число, “лежащее” в регистре LED1 увеличится на 1 и станет равным
2 + 1 = 3.
После анализа состояния бита С, произойдет переход на 4-й “виток”, в результате чего: .
246 + .6 = .252.
А вот в этом случае, переноса не будет (С=0).
После инкремента, число, “лежащее” в регистре LED1 увеличится на 1 и станет равным
3 + 1 = 4, то есть, произойдет “перебор” в том смысле, что количество десятков в числе .
36 равно 3-м, а получилось 4.
Поэтому, после анализа состояния бита С, первой командой, после выхода из
внутреннего цикла (а это и происходит), должна быть команда декремента содержимого
регистра LED1 (decf LED1,F).
А вот теперь все в норме: в LED1 нужно было получить тройку, и она в нем “лежит”.
Итак, разряд десятков “укомплектован” и можно переходить к обработке остатка.
Он такой: .252.
Естественно, что это не .6 (то, что требуется), и простой инверсией всех битов тут не
обойтись (получится не то, что нужно).
А выход очень простой - суммирование (addwf Temp_LSB,F) числа .252 с числом .10:
.252 + .10 = .262 - .256 = .6
После этого нужно только “переправить” число .6 (через регистр W) из регистра
Temp_LSB,F в регистр остатка LED0.

3
Все. Задача решена: в регистре LED1 “осядет” число .3, а в регистре LED0 - число .6,
что и есть двоично-десятичное разложение двоичного числа .36.
При этом, в мозгах “слышатся райские песни” и имеет место быть “полнейшая ляпота”.
Подсчитываю “девиденды”.
Подпрограмма Сергея Рослика имеет “массу” в 13 команд, причем, количество переходов
минимально.
Классическая ПП BIN2_10, примененная в программах раздела 5 “Практикума…” имеет
“массу” в 30 команд (без учета команды clrwdt), и алгоритм ее работы гораздо сложнее
того, который обозначен выше.
Плюс, большое количество переходов.
Я произвел сравнительную оценку этих двух подпрограмм (в приложении к
однобайтному, двоичному числу .36) и получил следующий “умопомрачительный”
результат:
- выигрыш по количеству команд - примерно в 2,3 раза,
- выигрыш по скорости - примерно в 5,7 раза.
А вот против этих фактов, как говорится, “не попрешь”. Они убеждают лучше чем что-
либо.
“Для полного счастья” (и объективности), “гоню” подпрограмму Сергея через “железо”
(вношу коррективы в программы 0_1grad3.asm и 0_1grad4.asm).
Это выглядит так:

……………..
;--------------------------------------------------------------------------------
; Установка (или нет) признака добавления (к показаниям) символа "1"
; (признак устанавливается при температуре от 100 градусов и выше).
;--------------------------------------------------------------------------------
movlw .100 ;
subwf Temp_LSB,W ; Temp_LSB - .100 = ? Результат - в W.
btfss Status,C ; Результат положительный или отрицательный?
goto BIN2_10 ; Если отрицательный, то признак добавления
; символа "1"
; не устанавливается (бит №7 регистра Flag=0).
bsf Flag,7 ; Если положительный, то устанавливается
; (бит №7 регистра Flag=1).
;********************************************************************************
; Подпрограмма двоично-десятичного преобразования (для двух десятичных чисел).
;********************************************************************************
BIN2_10 clrf LED0 ; Подготовка регистров для загрузки в них
clrf LED1 ; результатов двоично-десятичного
; преобразования.
;-------------------------------------
; Работа с разрядом десятков.
;-------------------------------------
B10 movlw .246 ;
addwf Temp_LSB,F ; Temp_LSB-.10=... ->Temp_LSB
incf LED1,F ; Подсчитываем, сколько раз отняли
; без остатка.
btfsc Status,C ; Переполнение было или нет?.
goto B10 ; Если было, то продолжаем отнимать .10.
decf LED1,F ; Если не было, то "ликвидация" избыточного
; инкремента.
;-------------------------------------
; Работа с разрядом единиц.

4
;-------------------------------------
movlw .10 ; Запись в W корректирующей константы.
addwf Temp_LSB,F ; Cуммирование Temp_LSB с корректирующей
; константой (в результате – остаток).
movf Temp_LSB,w ; Запись остатка в регистр
movwf LED0 ; разряда единиц (LED0).
;********************************************************************************
; Процедура преобразования кода.
;================================================================================
movlw 30h ; Запись в регистр W числа 30h.
iorwf LED0,F ; Логическое "ИЛИ" содержимого регистра W и
; регистра LED0 с сохранением результата
; в LED0.
iorwf LED1,F ; То же самое для LED1.
;********************************************************************************

……………..
Предмет разговора (13 – 1 = 12 команд) выделен черным цветом.
Подпрограмма прекрасно работает, но только до +99 градусов.
После перехода к +100 градусам и выше, в разряде десятков на индикацию выводится не
символ “0”, а другой символ.
Например, в диапазоне от +100 до +109 градусов, на индикацию выводится символ “;”
(точка с запятой).
Этот символ имеет в таблице знакогенератора HD44780 адрес 3Ah.
Вычитаю из него константу процедуры преобразования кода (30h) и получаю число 0Ah,
то есть, .10.
Таким образом, при исполнении данной процедуры преобразования формы числа, в
диапазоне чисел от .100 и более, в регистре LED0 “оседает” “паразитное” число .10.
Как быть?
“Убить” и все дела.
Кроме того, я “убил” и команду clrf LED0.
Объяснение простое: с регистром LED производится одна – разъединственная операция
(movwf LED0), и совершенно не важно, какое именно число в нем находилось “на
подлете” к этой операции, так как происходит запись “по верху”, и это “подлетное”
число будет просто-напросто проигнорировано.
А вот с регистром LED1 “этот номер не пройдет”, так как “на подлете” к 1-му “витку”
внутреннего цикла подпрограммы, в нем обязательно должен “лежать” ноль.
В противном случае, будет ошибочный результат преобразования.
Вот что получилось:

……………..
;--------------------------------------------------------------------------------
; Установка (или нет) признака добавления (к показаниям) символа "1"
; (признак устанавливается при температуре от 100 градусов и выше).
;--------------------------------------------------------------------------------
movlw .100 ;
subwf Temp_LSB,W ; Temp_LSB - .100 = ? Результат - в W.
btfss Status,C ; Результат положительный или отрицательный?
goto BIN2_10 ; Если отрицательный, то признак добавления
; символа "1"
; не устанавливается (бит №7 регистра Flag=0).
bsf Flag,7 ; Если положительный, то устанавливается

5
; (бит №7 регистра Flag=1).
;********************************************************************************
; Подпрограмма двоично-десятичного преобразования (для двух десятичных чисел).
;********************************************************************************
BIN2_10 clrf LED1 ; Подготовка к работе регистра LED1.
;-------------------------------------
; Работа с разрядом десятков.
;-------------------------------------
B10 movlw .246 ;
addwf Temp_LSB,F ; Temp_LSB-.10=... ->Temp_LSB
incf LED1,F ; Подсчитываем, сколько раз отняли
; без остатка.
btfsc Status,C ; Переполнение было или нет?.
goto B10 ; Если было, то продолжаем отнимать .10.
decf LED1,F ; Если не было, то "ликвидация"
; избыточного инкремента.
;-------------------------------------
; Работа с разрядом единиц.
;-------------------------------------
movlw .10 ; Запись в W корректирующей константы
; (она же - поправка).
btfsc Flag,7 ; Двоичное число больше или меньше .99 ?
subwf LED1,F ; Если больше, то внесение поправки
; (LED1-.10=...), после чего программа
; исполняется далее.
addwf Temp_LSB,F ; Если меньше, то поправка в LED1 не
; вносится, а происходит суммирование
; Temp_LSB с корректирующей константой.
movf Temp_LSB,w ; Запись остатка в регистр
movwf LED0 ; разряда единиц (LED0).
;********************************************************************************
; Процедура преобразования кода.
;================================================================================
movlw 30h ; Запись в регистр W числа 30h.
iorwf LED0,F ; Логическое "ИЛИ" содержимого регистра W и регистра LED0
; с сохранением результата в LED0.
iorwf LED1,F ; То же самое для LED1.
;********************************************************************************

……………..
В конечном итоге, это “удовольствие потянуло” все на те же 13 команд, и программа
работает точно так же, как и при использовании “тяжеловесной и мозгозаворотной
классики”, а по своим качественным показателям, она значительно превосходит
“старушку”.
Но не нужно “сбрасывать эту старушку со счета” (об этом позднее).
Таким образом, имеет место быть вполне профессиональная работа.
Именно в этих случаях говорят “мал золотник, да дорог”.
В такого рода делах важна идея (принцип), а то, что я ее слегка “подмарафетил”
(“подтянул гайку”), в “глобальном” мало что меняет.
Если речь идет о работе в диапазоне чисел от 00 до 99, то этого “марафета” и не
нужно.
При разложении одного байта на 2 десятичных числа, на мой взгляд, подпрограмма
Сергея вне конкуренции (полная и сокрушительная победа).
Плюс эстетическое удовольствие и полная ясность в мозгах.

6
Далее, я попросил Сергея “просканировать” возможность наращивания байтности.
Об этом речь пойдет немного позже.

Примечание: к файлу этого подраздела прилагаются файлы программ:


0_1grad5.asm (“мать” - 0_1grad3.asm) и
0_1grad6.asm (“мать” - 0_1grad4.asm),
в которых классическая ПП BIN2_10 заменена на ПП Сергея.
Обратите внимание на то, что объемы этих программ уменьшились.
Обе этих программы я проверил в “железе”.
Работают (а куда они, родимые, денутся?).

Дополнение
Иван Шевченко предлагает перенести метку B10 с команды movlw .246 на команду
addwf Temp_LSB,F.
Мотивация: начиная с команды addwf Temp_LSB,F и вплоть до команды goto B10, в
регистре W как “лежало” число .246, записанное в него командой movlw .246, так оно и
будет “лежать” (больше записи в W не производится), и поэтому вовсе незачем
исполнять команду movlw .246 на каждом “витке” внутреннего цикла вычитания.
Достаточно исполнить ее только один раз, то есть, “на подлете” (непосредственно перед
входом во внутренний цикл вычитания).
Чтобы этого добиться, нужно переставить метку B10 с команды movlw .246 на команду
addwf Temp_LSB,F.
После этого, за счет исключения одной команды из внутреннего цикла вычитания,
скоростные качества данной ПП улучшаются.
“Железобетонная” мотивация. Согласен.
Можно (и нужно) смело переставлять.

"Практикум по конструированию устройств на PIC контроллерах"      http://ikarab.narod.ru       E-mail: karabea@lipetsk.ru

7
"Практикум по конструированию устройств на PIC контроллерах"      http://ikarab.narod.ru       E-mail: karabea@lipetsk.ru