Страница 1 из 3
Преобразование байта в три десятичные цифры
Добавлено: Ср май 29, 2019 15:40:24
neitrino777
Всем привет ! прошу сильно меня не бить. Если создаю не нужную тему.
Искал я как то решение по отображению результатов работы ацп - на экране ТМ1637. И нужно было Преобразование байта в три десятичные цифры. Ответа не нашёл.
И просто сделал так. см. код. Не знаю насколько это оптимально - но мою задачу решает. Комментарии подробные писать не стал. На усмотрение модератора - можно удалить это.
; Редакция 27-05-19 - . Преобразуем 8 битное число в 3 десятичные цифры
;*******************************************
.include "tn13def.inc"
;****** РЕГИСТРЫ
.def ADCREZULT =r16 ; наши данные из ацп
.def dig0 =r17 ; результат для вывода на экран
.def dig1 =r18 ; результат для вывода на экран
.def dig2 =r19 ; результат для вывода на экран
rjmp RESET ;Reset Handle
reti ;External InterruptO Vector Address
reti ;External Interruptl Vector Address
reti ;Прерывание по переполнению таймера/счетчика 0
reti ;EE_READY_vect EEPROM готова
reti ;ANALOG_COMP_vect Аналоговый компаратор переключился
reti ;TIMER0_COMPA_vect Прерывание по сравнению, канал A таймера/счетчика 0
reti ;TIMER0_COMPB_vect Прерывание по сравнению, канал B таймера/счетчика 0
reti ;WDT_vect Сторожевой таймер (если используется в качестве источника прерывания)
reti ;ADC_vect Преобразование АЦП завершено
.cseg ; область команд
RESET:
clr dig0
clr dig1
clr dig2
clr ADCREZULT
ldi ADCREZULT,0xD3
povtor: subi ADCREZULT,100
brcs dalshe ;прекратить при установки флага переноса
inc dig2
rjmp povtor
dalshe: subi ADCREZULT,-100 ; компенсируем вычитание
povtor1: subi ADCREZULT,10
brcs dalshe2
inc dig1
rjmp povtor1
dalshe2: subi ADCREZULT,-10
povtor2: subi ADCREZULT,1
brcs dalshe3
inc dig0
rjmp povtor2
dalshe3: nop
nop
nop
Re: Преобразование байта в три десятичные цифры
Добавлено: Ср май 29, 2019 15:54:57
SSkot
Если я ничего не напутал, то вам нужно преобразовать двоично-десятичный код в десятичный.
пример первый попавшийся
uint8_t hex = 0x11;
assert(((hex & 0xF0) >> 4) < 10); // More significant nybble is valid
assert((hex & 0x0F) < 10); // Less significant nybble is valid
int dec = ((hex & 0xF0) >> 4) * 10 + (hex & 0x0F);
Re: Преобразование байта в три десятичные цифры
Добавлено: Ср май 29, 2019 16:43:00
neitrino777
спасибо за интерес. Здесь не преобразования bcd. Аппнот 204 - прочитал - не сильно внимательно. Но мою задачу он не решает быстро и просто. Я только учу ассемблер - ваш пример для меня
в диковинку - я таких команд не знаю. Пример у нас есть число 211 - нужно его вывести на экран ТМ1637 - там типа интерфейса I2C. Передаём весовые коды - обычные байты с кодом символа.
На нужно отдельно последовательно передать цифры 2---1---1. (начал изучать ацп attiny13 - интересно посмотреть на его показания в реальном времени)
Re: Преобразование байта в три десятичные цифры
Добавлено: Ср май 29, 2019 18:20:10
ПростоНуб
[uquote="neitrino777",url="/forum/viewtopic.php?p=3641598#p3641598"]Ответа не нашёл.[/uquote]
Вот один из самых эффективных алгоритмов для этой цели.
Re: Преобразование байта в три десятичные цифры
Добавлено: Чт май 30, 2019 00:34:16
Ser60
Вот ещё способ: Допустим, нужно разложить однобайтовое число n на цифры. Обозначим их n1,n2,n3. Используем 8х8->16 перемножитель в AVR8.
1. Вычислить d = (n*205) >> 11. Умножение даст (в общем случае) 16-битное число, и на ассемблере сдвиг на 11 можно реализовать взяв старший байт полученного числа и сдвинув его вправо на 3 позиции.
Полученное число d равно целой части n / 10.
2. Вычислить n3 = n - d*10 - это будет младшая цифра.
3. Вычислить n1 = (d*26) >> 8, заменив сдвиг просто взятием старшего байта произведения.
4. Вычислить n2 = d - n1*10. Получим среднюю цифру.
Пример для n = 219:
1. d = (219*205) = 0xAF5F (в шестнадцатиричном обозначении). Старший байт = 0xAF = 175. Сдвинув его на 3 разряда вправо получим 21 (= целая часть 219/10)
2. n3 = 219 - 21*10 = 9 (последняя цифра)
3. n1 = (21*26) = 0x0222. Старший байт = 2 (первая цифра)
4. n2 = 21 = 2*10 = 1 (средняя цифра)
Re: Преобразование байта в три десятичные цифры
Добавлено: Чт май 30, 2019 03:25:46
Ivanoff-iv
это если мега... а тиня умножать не умеет
Re: Преобразование байта в три десятичные цифры
Добавлено: Чт май 30, 2019 04:10:26
akl
Тоже неплохой алгоритм, давным давно взятый с электроникса. Легко адаптируется для разложения больших чисел.
Код: Выделить всё
BINBCD:
LDS R17,$70 ; HEX IN
CLR R30 ; BCD OUT 1'000,100
CLR R31 ; BCD OUT 10,1
LDI R28,8
bin8_bcd3:
subi r31,-0x33 ;add 0x33
sbrs r31, 3 ;if carry to bit 3
subi r31, 3 ;subtract 3
sbrs r31, 7 ;if carry to bit 7
subi r31, 0x30 ;subtract 0x30
subi r30,-0x33 ; \n" /*add 0x33*/
sbrs r30, 3 ; \n" /*if carry to bit 3,*/
subi r30, 3 ; \n" /*subtract 3*/
sbrs r30, 7 ; \n" /*if carry to bit 7,*/
subi r30, 0x30 ; \n" /*subtract 0x30*/
LSL R17 ;shift input*/
rol r31
rol r30 ; \n" /*shift out buffer*/
dec R28 ;\n"
brne bin8_bcd3 ;repeat for all bits*/
RET
Добавлено after 6 minutes 55 seconds:
neitrino777 Замечу, что в коде ниже можно просто остаток единиц занести в dig0
Код: Выделить всё
povtor2: MOV dig0,ADCREZULT
; subi ADCREZULT,1
;brcs dalshe3
;inc dig0
;rjmp povtor2
dalshe3: nop
Re: Преобразование байта в три десятичные цифры
Добавлено: Чт май 30, 2019 09:14:17
Engineer_Keen
Не знаю как для других вариантов преобразования, но для варианта "байт->3 десятичные цифры", оптимальным является именно вариант из первого поста, т.е. последовательное вычитание 100,10, с учетом последнего замечания akl (остаток единиц, получается автоматом). Его преимущество и в размере (меньше на полтора десятка байт) и в скорости выполнения.
Достоинство метода "сдвиг-сложение с 3" (double-dabble), в том, что этот алгоритм выполняется всегда за почти одинаковое время, например для 16МГц это примерно 8мкс, не зависимо от числа на входе. Дело конечно в заранее известном количестве циклов, но при этом занята +1 переменная под счетчик цикла, и в конце еще надо распихивать нибблы, в отдельные байты, так как там в каждом байте результата получается по 2 разряда десятичного числа.
Для метода последовательных вычитаний время выполнения от 750нс (на входе 0), до 4мкс (на входе 199, максимальное количество вычитаний), и каждый байт в итоге уже содержит один разряд десятичного числа.
Re: Преобразование байта в три десятичные цифры
Добавлено: Чт май 30, 2019 09:35:26
akl
Метод последовательного вычитания быстр и начинает проигрывать при преобразовании больших чисел, когда вес оных превышает возможности ассемблера (4 байта). Пользуюсь и тем и другим.
Например, преобразование 40 разрядного числа только на старшей части регистрового файла AVR.
Спойлер
Код: Выделить всё
LDS R16,$70
LDS R17,$71
LDS R18,$72
LDS R19,$73 ; HEX IN
LDS R20,$74
CLR R25 ; BCD OUT 10'000'000'000'000,1'000'000'000'000
CLR R26 ; BCD OUT 100'000'000'000,10'000'000'000
CLR R27 ; BCD OUT 1'000'000'000,100'000'000
CLR R28 ; BCD OUT 10'000'000,1'000'000
CLR R29 ; BCD OUT 100'000,10'000
CLR R30 ; BCD OUT 1'000,100
CLR R31 ; BCD OUT 10,1
LDI R24,40
bin40bcd13:
subi r31,-0x33 ;add 0x33
sbrs r31, 3 ;if carry to bit 3
subi r31, 3 ;subtract 3
sbrs r31, 7 ;if carry to bit 7
subi r31, 0x30 ;subtract 0x30
subi r30,-0x33 ; \n" /*add 0x33*/
sbrs r30, 3 ; \n" /*if carry to bit 3,*/
subi r30, 3 ; \n" /*subtract 3*/
sbrs r30, 7 ; \n" /*if carry to bit 7,*/
subi r30, 0x30 ; \n" /*subtract 0x30*/
subi r29,-0x33 ;\n" /*add 0x33*/
sbrs r29, 3 ;\n" /*if carry to bit 3,*/
subi r29, 3 ;\n" /*subtract 3*/
sbrs r29, 7 ;\n" /*if carry to bit 7,*/
subi r29, 0x30 ;\n" /*subtract 0x30*/
subi r28,-0x33 ;\n" /*add 0x33*/
sbrs r28, 3 ;\n" /*if carry to bit 3,*/
subi r28, 3 ;\n" /*subtract 3*/
sbrs r28, 7 ;\n" /*if carry to bit 7,*/
subi r28, 0x30 ;\n" /*subtract 0x30*/
subi r27,-0x33 ;\n" /*add 0x33*/
sbrs r27, 3 ;\n" /*if carry to bit 3,*/
subi r27, 3 ;\n" /*subtract 3*/
sbrs r27, 7 ;\n" /*if carry to bit 7,*/
subi r27, 0x30 ;\n" /*subtract 0x30*/
subi r26,-0x33 ;\n" /*add 0x33*/
sbrs r26, 3 ;\n" /*if carry to bit 3,*/
subi r26, 3 ;\n" /*subtract 3*/
sbrs r26, 7 ;\n" /*if carry to bit 7,*/
subi r26, 0x30 ;\n" /*subtract 0x30*/
subi r25,-0x33 ;\n" /*add 0x33*/
sbrs r25, 3 ;\n" /*if carry to bit 3,*/
subi r25, 3 ;\n" /*subtract 3*/
sbrs r25, 7 ;\n" /*if carry to bit 7,*/
subi r25, 0x30 ;\n" /*subtract 0x30*/
lsl R20
rol R19
rol R18
rol R17 ;shift input*/
rol R16
rol r31
rol r30
rol r29
rol r28 ; \n" /*shift out buffer*/
rol r27
rol r26
rol r25
dec R24 ;\n"
brne bin40bcd13 ;repeat for all bits*/
RET
Re: Преобразование байта в три десятичные цифры
Добавлено: Чт май 30, 2019 17:24:44
neitrino777
test
Добавлено after 13 minutes 21 second:
Спасибо всем за предложенные варианты. Особое спасибо за подсказку after 6. Буду дальше изучать тиньку - что из нее можно выжать. Для моих экспериментов её более чем достаточно.
Re: Преобразование байта в три десятичные цифры
Добавлено: Чт май 30, 2019 19:04:03
AlexVi
[uquote="akl",url="/forum/viewtopic.php?p=3641961#p3641961"]Например, преобразование 40 разрядного числа...[/uquote] Знакомые строки
Пользуясь случаем, еще раз спасибо
akl за конкурсный LC-метр, в особенности за выложенные исходники. До сих пор приятно в них покопаться и переиначить на очередное устройство. Два здесь публиковались, одно на конкурсе даже до 5 места (или 6, не припомню) добралось. Создается и третье, вполне себе оригинальное. Кроме того, те исходники - отличный образец многоразрядной арифметики.

Re: Преобразование байта в три десятичные цифры
Добавлено: Сб июн 01, 2019 21:09:25
Demiurg
Спойлер
Код: Выделить всё
;-------------------------------------------------------------------------
#define BCD YES
;===================
.macro ldx
ldi XH,High(@0)
ldi XL,Low(@0)
.endmacro
.macro ldy
ldi YH,High(@0)
ldi YL,Low(@0)
.endmacro
.macro ldz
ldi ZH,High(@0)
ldi ZL,Low(@0)
.endmacro
;-------------------------------------------------------------------------
#define tab_h(x) HIGH(x), LOW(x)
#define tab_l(x) LOW(x), HIGH(x)
;-------------------------------------------------------------------------
;----------------------------- Отладка -----------------------------------
rcall Clr_Hex_Dex_Buffer
ldi r16, 199
rcall Hex_Dec_8_Bit
rcall Clr_Hex_Dex_Buffer
ldi r16, LOW (64999)
ldi r17, HIGH (64999)
rcall Hex_Dec_16_Bit
;-------------------------------------------------------------------------
/************************************************************************/
#if (BCD == YES)
.dseg
HEX_DEC_BUFFER:
.equ HEX_DEC_BUFFER_LENGHT = 5
.byte HEX_DEC_BUFFER_LENGHT
.cseg
Clr_Hex_Dex_Buffer:
ldx HEX_DEC_BUFFER
clr r16
ldi r17, HEX_DEC_BUFFER_LENGHT
Clr_Hex_Dex_Buffer_Cycle:
st X+, r16
dec r17
brne Clr_Hex_Dex_Buffer_Cycle
ret
//==================
// r16 - hex data
// r17 - вычитаемое число из таблицы
// r18 - cnt_1
// r19 - cnt_2
Hex_Dec_8_Bit:
ldx HEX_DEC_BUFFER
ldz TAB_HEX_DEC_8_BIT*2
ldi r18, 2
Hex_Dec_8_Bit_Cycle_1:
lpm r17, Z+
ldi r19, -1
Hex_Dec_8_Bit_Cycle_2:
inc r19
sub r16, r17
brsh Hex_Dec_8_Bit_Cycle_2
add r16, r17
ori r19, 0x30
st X+, r19
dec r18
brne Hex_Dec_8_Bit_Cycle_1
ori r16,0x30
st X+, r16
Hex_Dec_8_Bit_End:
ret
//------------------------------------------------------------------------
//------------------------------------------------------------------------
TAB_HEX_DEC_8_BIT:
.db 100, 10
//==================
//==================
// r16, r17 - hex data
// r18, r19 - вычитаемое число из таблицы
// r20 - cnt_1
// r21 - cnt_2
Hex_Dec_16_Bit:
ldx HEX_DEC_BUFFER
ldz TAB_HEX_DEC_16_BIT*2
ldi r20, 4
Hex_Dec_16_Bit_Cycle_1:
lpm r18, Z+
lpm r19, Z+
ldi r21, -1
Hex_Dec_16_Bit_Cycle_2:
inc r21
sub r16, r18
sbc r17, r19
brsh Hex_Dec_16_Bit_Cycle_2
add r16, r18
adc r17, r19
ori r21, 0x30
st X+, r21
dec r20
brne Hex_Dec_16_Bit_Cycle_1
ori r16,0x30
st X+, r16
Hex_Dec_16_Bit_End:
ret
//------------------------------------------------------------------------
//------------------------------------------------------------------------
TAB_HEX_DEC_16_BIT:
.db tab_l (10000)
.db tab_l (1000)
.db tab_l (100)
.db tab_l (10)
//==================
#endif
/************************************************************************/
Re: Преобразование байта в три десятичные цифры
Добавлено: Вт июн 11, 2019 12:10:30
B@R5uk
[uquote="Engineer_Keen",url="/forum/viewtopic.php?p=3641953#p3641953"]Не знаю как для других вариантов преобразования, но для варианта "байт->3 десятичные цифры",
оптимальным является именно вариант из первого поста, т.е. последовательное вычитание 100,10, с учетом последнего замечания
akl (остаток единиц, получается автоматом). Его преимущество и в размере (меньше на полтора десятка байт) и
в скорости выполнения.[/uquote]
Долго мучил голову в попытках опровергнуть это утверждение. Этого, конечно, сделать не удалось, но в результате всех мучений родилось такое вот извращение:
Код: Выделить всё
;==============
.def cntr = r16
.def dig1 = r17
.def dig2 = r18
.def dig3 = r19
.def sbtr = r20
;==============
; Время выполнения 65 тактов
ldi dig3, 0xFF ; Преобразуемое в десятичный вид число
ldi cntr, 0x02 ; Выделяется 2 бита первой цифры
ldi sbtr, 0xC8 ; 100 * 2 ^ (2 - 1)
clr dig1 ; Подготовка искомой цифры
loop_1: lsl dig1 ; Удвоение искомой цифры
sub dig3, sbtr ; Выделение очередного бита
brcs loop_1a
inc dig1 ; Бит 1: увеличить искомую цифру
loop_1a: brcc loop_1b
add dig3, sbtr ; Бит 0: восстановить аргумент
loop_1b: lsr sbtr ; Значение следующего бита искомой цифры
dec cntr
brne loop_1 ; Повтор для всех битов
ldi cntr, 0x04 ; Выделяется 4 бита второй цифры
ldi sbtr, 0x50 ; 10 * 2 ^ (4 - 1)
clr dig2 ; Подготовка искомой цифры
loop_2: lsl dig2 ; Удвоение искомой цифры
sub dig3, sbtr ; Выделение очередного бита
brcs loop_2a
inc dig2 ; Бит 1: увеличить искомую цифру
loop_2a: brcc loop_2b
add dig3, sbtr ; Бит 0: восстановить аргумент
loop_2b: lsr sbtr ; Значение следующего бита искомой цифры
dec cntr
brne loop_2 ; Повтор для всех битов
; Третья цифра получается автоматически
;==============
Основная идея была в том, что код топикстартера имеет линейную сложность и её можно улучшить до логарифмической небольшим усложнением кода. Деление с остатком методом вычитания заменяется побитовым делением с остатком. Однако, усложнение приводит к б
ольшей константе перед логарифмом (по сравнению с константой перед линейным членом в "примитивном" алгоритме) и, поскольку под логарифмом стоит число от 1 до 10 (то есть слишком маленькое, чтобы была принципиальная разница между аргументом логарифма и его значением), в результате ускорения добиться не получается.
Думаю, все остальные алгоритмы (какими бы они не были) обладают той же проблемой даже при б
ольших разрядностях исходного числа. Вот, если бы мы пользовались бы не 10-ой системой, а какой-нибудь 24-ой или ещё лучше 60-ой, то было бы совсем другое дело!!!
На всякий случай модифицированный код топикстартера. Время выполнения варьируется от 11 тактов (для байтов, меньших 10) до 61 такта (для байтов, начинающихся на 19).
Код: Выделить всё
;==============
.def dig1 = r17
.def dig2 = r18
.def dig3 = r19
;==============
; Время выполнения 11 + 5 * {сумма первой и второй цифр} тактов
ldi dig3, 0xFF ; Преобразуемое в десятичный вид число
clr dig1 ; Подготовка первой цифры
loop_1: subi dig3, 0x64 ; Цикл вычитания 100
brcs loop_1a ; Окончание цикла, когда остаток меньше 100
inc dig1 ; Инкремент первой цифры
rjmp loop_1
loop_1a: subi dig3, 0x9C ; Коррекция остатка
clr dig2 ; Подготовка второй цифры
loop_2: subi dig3, 0x0A ; Цикл вычитания 10
brcs loop_2a ; Окончание цикла, когда остаток меньше 10
inc dig2 ; Инкремент второй цифры
rjmp loop_2
loop_2a: subi dig3, 0xF6 ; Коррекция остатка, являющ. третьей цифрой
;==============
Re: Преобразование байта в три десятичные цифры
Добавлено: Вт июн 11, 2019 15:18:49
BOB51
Если честно "стандартное преобразование" данных 0-99 (типовые часики, считалки)
наиболее просто решаются табличным способом.
А уже куда побольше - за сотню - там математика.
Однако это уже потребует заготовок не только на один байт.
Ибо 999 это 0х3E7 - а 3 знакоместа подразумевают 0-999.

Re: Преобразование байта в три десятичные цифры
Добавлено: Вт июн 11, 2019 20:32:09
Starichok51
вот еще один немудреный алгоритм.
исходное число в R10.
Код: Выделить всё
clr R12
ldi R26, 100
test_100:
cp R10, R26
brcs test_10_1
sub R10, R26
inc R12
rjmp test_100
test_10_1:
ldi R26, 10
test_10:
cp R10, R26
brcs end_test
sub R10, R26
inc R11
rjmp test_10
end_test:
Re: Преобразование байта в три десятичные цифры
Добавлено: Ср июн 19, 2019 03:48:44
akl
Нужно почистить ещё и регистр десятков R11.
Код: Выделить всё
CLR R11
clr R12
ldi R26, 100
test_100:
cp R10, R26
brcs test_10_1
sub R10, R26
inc R12
rjmp test_100
test_10_1:
ldi R26, 10
test_10:
cp R10, R26
brcs end_test
sub R10, R26
inc R11 ; при входе этот регистр должен очищаться
rjmp test_10
end_test:
RJMP PC
Re: Преобразование байта в три десятичные цифры
Добавлено: Ср июн 19, 2019 07:38:20
Starichok51
в данном случае (с одним байтом) да, нужно очистить R11.
это я взял свой кусок текста, где вычитались тысячи и сотни, и исходное число было в R10 и R11.
поспешил скопировать и вставить, а про очистку R11 забыл...
и после вычитания сотен R11 сам очистится, предварительно его чистить не надо.
Re: Преобразование байта в три десятичные цифры
Добавлено: Пн июн 24, 2019 10:25:06
Frogfot
[uquote="akl",url="/forum/viewtopic.php?p=3641961#p3641961"]Метод последовательного вычитания быстр и начинает проигрывать при преобразовании больших чисел, когда вес оных превышает возможности
Код: Выделить всё
bin40bcd13:
subi r31,-0x33 ;add 0x33
sbrs r31, 3 ;if carry to bit 3
subi r31, 3 ;subtract 3
sbrs r31, 7 ;if carry to bit 7
subi r31, 0x30 ;subtract 0x30
[/uquote]
Кто подскажет, как работает этот метод - добавляем к ниблу число 3, и если нибл становится больше 7, то отнимаем 3, а если не больше 7, то не отнимаем ???
Re: Преобразование байта в три десятичные цифры
Добавлено: Пн июн 24, 2019 11:13:42
Demiurg
Чем
мой пример не угодил?
Re: Преобразование байта в три десятичные цифры
Добавлено: Пн июн 24, 2019 12:02:11
akl
[uquote="Frogfot",url="/forum/viewtopic.php?p=3655792#p3655792"]Кто подскажет, как работает этот метод - добавляем к ниблу число 3, и если нибл становится больше 7, то отнимаем 3, а если не больше 7, то не отнимаем ???[/uquote]Не забывайте, что преобразование идет побитно в цикле. Для лучшего понимания можно в симуляторе погонять преобразование байта в двоично-десятичный код.
Код: Выделить всё
;Преобразование 8bin-3dec
BINBCD:
LDS R17,$70 ; HEX IN
CLR R30 ; BCD OUT 1'000,100
CLR R31 ; BCD OUT 10,1
LDI R28,8
bin8_bcd3:
subi r31,-0x33 ;add 0x33
sbrs r31, 3 ;if carry to bit 3
subi r31, 3 ;subtract 3
sbrs r31, 7 ;if carry to bit 7
subi r31, 0x30 ;subtract 0x30
subi r30,-0x33 ; \n" /*add 0x33*/
sbrs r30, 3 ; \n" /*if carry to bit 3,*/
subi r30, 3 ; \n" /*subtract 3*/
sbrs r30, 7 ; \n" /*if carry to bit 7,*/
subi r30, 0x30 ; \n" /*subtract 0x30*/
LSL R17 ;shift input*/
rol r31
rol r30 ; \n" /*shift out buffer*/
dec R28 ;\n"
brne bin8_bcd3 ;repeat for all bits*/
RJMP BINBCD
.EXIT