Обсуждаем контроллеры компании Atmel.
Ответить

Преобразование байта в три десятичные цифры

Ср май 29, 2019 15:40:24

Всем привет ! прошу сильно меня не бить. Если создаю не нужную тему.
Искал я как то решение по отображению результатов работы ацп - на экране ТМ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

Если я ничего не напутал, то вам нужно преобразовать двоично-десятичный код в десятичный.

пример первый попавшийся

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

спасибо за интерес. Здесь не преобразования bcd. Аппнот 204 - прочитал - не сильно внимательно. Но мою задачу он не решает быстро и просто. Я только учу ассемблер - ваш пример для меня
в диковинку - я таких команд не знаю. Пример у нас есть число 211 - нужно его вывести на экран ТМ1637 - там типа интерфейса I2C. Передаём весовые коды - обычные байты с кодом символа.
На нужно отдельно последовательно передать цифры 2---1---1. (начал изучать ацп attiny13 - интересно посмотреть на его показания в реальном времени)

Re: Преобразование байта в три десятичные цифры

Ср май 29, 2019 18:20:10

Ответа не нашёл.

Вот один из самых эффективных алгоритмов для этой цели.

Re: Преобразование байта в три десятичные цифры

Чт май 30, 2019 00:34:16

Вот ещё способ: Допустим, нужно разложить однобайтовое число 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

это если мега... а тиня умножать не умеет

Re: Преобразование байта в три десятичные цифры

Чт май 30, 2019 04:10:26

Тоже неплохой алгоритм, давным давно взятый с электроникса. Легко адаптируется для разложения больших чисел.
Код:
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

Не знаю как для других вариантов преобразования, но для варианта "байт->3 десятичные цифры", оптимальным является именно вариант из первого поста, т.е. последовательное вычитание 100,10, с учетом последнего замечания akl (остаток единиц, получается автоматом). Его преимущество и в размере (меньше на полтора десятка байт) и в скорости выполнения.

Достоинство метода "сдвиг-сложение с 3" (double-dabble), в том, что этот алгоритм выполняется всегда за почти одинаковое время, например для 16МГц это примерно 8мкс, не зависимо от числа на входе. Дело конечно в заранее известном количестве циклов, но при этом занята +1 переменная под счетчик цикла, и в конце еще надо распихивать нибблы, в отдельные байты, так как там в каждом байте результата получается по 2 разряда десятичного числа.

Для метода последовательных вычитаний время выполнения от 750нс (на входе 0), до 4мкс (на входе 199, максимальное количество вычитаний), и каждый байт в итоге уже содержит один разряд десятичного числа.

Re: Преобразование байта в три десятичные цифры

Чт май 30, 2019 09:35:26

Метод последовательного вычитания быстр и начинает проигрывать при преобразовании больших чисел, когда вес оных превышает возможности ассемблера (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

test

Добавлено after 13 minutes 21 second:
Спасибо всем за предложенные варианты. Особое спасибо за подсказку after 6. Буду дальше изучать тиньку - что из нее можно выжать. Для моих экспериментов её более чем достаточно.

Re: Преобразование байта в три десятичные цифры

Чт май 30, 2019 19:04:03

Например, преобразование 40 разрядного числа...
Знакомые строки :)
Пользуясь случаем, еще раз спасибо akl за конкурсный LC-метр, в особенности за выложенные исходники. До сих пор приятно в них покопаться и переиначить на очередное устройство. Два здесь публиковались, одно на конкурсе даже до 5 места (или 6, не припомню) добралось. Создается и третье, вполне себе оригинальное. Кроме того, те исходники - отличный образец многоразрядной арифметики. :beer:

Re: Преобразование байта в три десятичные цифры

Сб июн 01, 2019 21:09:25

Спойлер
Код:
;----------
#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

Не знаю как для других вариантов преобразования, но для варианта "байт->3 десятичные цифры", оптимальным является именно вариант из первого поста, т.е. последовательное вычитание 100,10, с учетом последнего замечания akl (остаток единиц, получается автоматом). Его преимущество и в размере (меньше на полтора десятка байт) и в скорости выполнения.

Долго мучил голову в попытках опровергнуть это утверждение. Этого, конечно, сделать не удалось, но в результате всех мучений родилось такое вот извращение:

Код:
;==============
.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

Если честно "стандартное преобразование" данных 0-99 (типовые часики, считалки)
наиболее просто решаются табличным способом.
А уже куда побольше - за сотню - там математика.
Однако это уже потребует заготовок не только на один байт.
Ибо 999 это 0х3E7 - а 3 знакоместа подразумевают 0-999.
8)

Re: Преобразование байта в три десятичные цифры

Вт июн 11, 2019 20:32:09

вот еще один немудреный алгоритм.
исходное число в 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

Нужно почистить ещё и регистр десятков 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

в данном случае (с одним байтом) да, нужно очистить R11.
это я взял свой кусок текста, где вычитались тысячи и сотни, и исходное число было в R10 и R11.
поспешил скопировать и вставить, а про очистку R11 забыл...
и после вычитания сотен R11 сам очистится, предварительно его чистить не надо.

Re: Преобразование байта в три десятичные цифры

Пн июн 24, 2019 10:25:06

Метод последовательного вычитания быстр и начинает проигрывать при преобразовании больших чисел, когда вес оных превышает возможности
Код:
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

Кто подскажет, как работает этот метод - добавляем к ниблу число 3, и если нибл становится больше 7, то отнимаем 3, а если не больше 7, то не отнимаем ???

Re: Преобразование байта в три десятичные цифры

Пн июн 24, 2019 11:13:42

Чем мой пример не угодил?

Re: Преобразование байта в три десятичные цифры

Пн июн 24, 2019 12:02:11

Кто подскажет, как работает этот метод - добавляем к ниблу число 3, и если нибл становится больше 7, то отнимаем 3, а если не больше 7, то не отнимаем ???
Не забывайте, что преобразование идет побитно в цикле. Для лучшего понимания можно в симуляторе погонять преобразование байта в двоично-десятичный код.
Код:
;Преобразование 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
Ответить