BIN 2 BCD for AVR

Loading thread data ...

Wed, 03 Nov 2004 20:25:34 +0300 Unrau Alexander wrote to All:

UA> Блин! И ведь пашет. UA> А название у этого алгоритма есть? UA> И существует ли что-то подобное для преобразования дробной части числа UA> (битов после запятой)?

Может кому-то пригодится. Как-то попались две мессаги на эту тему, я их затарил. А позже они очень пригодились, когда пришлось реализовывать подобное на MSP430 - помогли быстро разобраться с алгоритмом. Мессаги следующие:

Первая.

=========Beginning of the citation============== From: "Alexander Volkov" snipped-for-privacy@p55.f.n5051.z2.fidonet.org>

Subject: Re: BinBcd Date: 18 февраля 2002 г. 13:10

Привет, Mihail! MP> Подскажите алгоритм сабжа для 16 разрядного числ. MP> Hужно реализовать на 8051. Алгоритм простой: сначала определяется цифра десятков тысяч вычитанием 10000 из исходного числа, затем цифра тысяч последовательным вычитанием 1000, затем цифры сотен, десятков. Остаток дает цифру единиц. Алгоритм менее простой:десятичный эквивалент числа Х можно получить, сдвигая двоичное число влево и подавая выдвигаемые двоичные цифры в младший разряд десятичного регистра. Одновременно со сдвигом двоичного регистра необходимо удваивать содержимое десятичного регистра. Метод основан на представлении числа в виде полинома Горнера: Х=Xn-1*2^(n-1)+Xn-2*2^(n-2)+...+X1*2+X0 - это дв.представление X=(...(Xn-1*2+Xn-2)*2+...+X1)*2+X0 - это по Горнеру

PS: А исходник у меня есть в кодах К580ИК80(это не одно и тоже с 8051 ?)

С наилучшими пожеланиями. Alexander. =========The end of the citation================

Вторая.

=========Beginning of the citation============== From: "Alexander Volkov" snipped-for-privacy@p55.f.n5051.z2.fidonet.org>

Subject: Re: BinBcd Date: 20 февраля 2002 г. 1:16

Привет, Roman! >> Алгоритм менее простой:десятичный эквивалент числа Х можно >> получить, RS> сдвигая >> двоичное число влево и подавая выдвигаемые двоичные цифры в младший >> разряд десятичного регистра. Одновременно со сдвигом двоичного >> регистра RS> необходимо >> удваивать содержимое десятичного регистра. Метод основан на >> представлении RS> числа RS> непонятно. точнее то, что тут написано никак не является сабжем. Абисняю на примере. Имеем дв.код 1111 , т.е. 15 1) выдвигаем влево 1-ю ед. из дв.рг 2) удваиваем сод. дес.рг (0*2=0000_0000) не забываем, что удвоение - десятичное, т.е. с коррекцией 3) вдвигаем выдв.1 в дес.рг(0000_0001) 4) шаг 1 5) шаг 2 (1*2=0000_0010) 6) шаг 3 (0000_0011) 7) шаг 1 8) шаг 2 (3*2=0000_0110) 9) шаг 3 (0000_0111) 10) шаг 1 11) шаг 2 Внимание! Самое интересное: в данном случае удвоение происходит так: т.к. исх.число >= 5 , то вводим коррекцию +3 получаем 0000_1010, удваиваем ЭТО число (0001_0100) 12)шаг 3 (0001_0101) Это что? Вроде бы дв/дес. 15. Hе так ли ?

С наилучшими пожеланиями. Alexander. =========The end of the citation================

На основе этого код для MSP430 (для IAR'овского ассемблера) выглядит так:

****************************************************** module bin2BCD

public bin2BCD16

; r11 - bit counter

; r12 - input binary number ; r13 - result (low: 4 digits) ; r14 - addr ; r15 - result (high: 1 digit)

rseg CODE(1)

;---------------------------------------------------- bin2BCD16: push r11 push r10

mov.w #16,r11 clr r13 clr r15

;----------------------------------- ; ; собственно преобраование ; convert: clr r10 rla r12 rlc r10

dadd.w r13,r13 dadd.w r15,r15 dadd.w r10,r13 dadd.w #0,r15

dec r11 jne convert ;-----------------------------------

; ; дальше просто распаковка десячисных цифр каждую в отдельный байт, ; к самому алгоритму отношения не имеет. ; ;

unpack: call #extract_byte swpb r13 call #extract_byte mov.w r15,r12 call #store_byte

pop r10 pop r11 ret ;----------------------------------- extract_byte: mov.w r13,r12 call #store_byte mov.w r13,r12 call #get_high_nibble call #store_byte ret ;----------------------------------- store_byte: bic.w #0xfff0,r12 mov.b r12,0(r14) inc r14 ret ;----------------------------------- get_high_nibble: rlc.b r12 rlc.b r12 rlc.b r12 rlc.b r12 rlc.b r12 ret ;------------------------------------------------------------

end

******************************************************

Сам алгоритм очень короткий и эффективный благодаря наличию арифметических команд с десятичной коррекцией (то самое прибавление по условию).

Потом, правда, нашел похожую реализацию в одной из аппликух, но не жалею о проделанной работе, было интересно разобраться. :)

Для AVR есть у Atmel подобная функция, написанная на асме. Адаптированная под IAR она выглядит так:

Сишный прототип: void bin2BCD16(word num, byte* Buf);

****************************************************** module Num_2_BCD_Module

public bin2BCD16

rseg CODE:CODE:NOROOT(1)

;--------------------------------------------------------------------------- ;* ;* "bin2BCD16" - 16-bit Binary to BCD conversion ;* ;* This subroutine converts a 16-bit number (fbinH:fbinL) to a 5-digit ;* packed BCD number represented by 3 bytes (tBCD2:tBCD1:tBCD0). ;* MSD of the 5-digit number is placed in the lowermost nibble of tBCD2. ;* ;* Number of words :25 ;* Number of cycles :751/768 (Min/Max) ;* Low registers used :3 (tBCD0,tBCD1,tBCD2) ;* High registers used :4(fbinL,fbinH,cnt16a,tmp16a) ;* Pointers used :Z ;* ;---------------------------------------------------------------------------

;***** Subroutine Register Variables #define zl r30 #define zh r31

#define AtBCD0 0 // address of tBCD0 #define AtBCD2 1 // address of tBCD2

#define tBCD0 r0 // BCD value digits 1 and 0 #define tBCD1 r1 // BCD value digits 3 and 2 #define tBCD2 r2 // BCD value digit 4 #define fbinL r16 // binary value Low byte #define fbinH r17 // binary value High byte #define cnt16a r20 // loop counter #define tmp16a r21 // temporary value

#define rtmp tmp16a #define BCD_AddrLow r18 #define BCD_AddrHigh r19

bin2BCD16: ldi cnt16a,16 ;Init loop counter clr tBCD2 ;clear result (3 bytes) clr tBCD1 clr tBCD0 clr zh ;clear ZH (not needed for AT90Sxx0x)

bBCDx_1: lsl fbinL ;shift input value rol fbinH ;through all bytes rol tBCD0 ; rol tBCD1 rol tBCD2 dec cnt16a ;decrement loop counter brne bBCDx_2 ;if counter not zero

;fill array and ... mov zl,BCD_AddrLow mov zh,BCD_AddrHigh mov rtmp,tBCD0 andi rtmp,0x0f st z+,rtmp ;BCD[0] = value0 (LSD) swap tBCD0 mov rtmp,tBCD0 andi rtmp,0x0f st z+,rtmp ;BCD[1] = value1 mov rtmp,tBCD1 andi rtmp,0x0f st z+,rtmp swap tBCD1 ;BCD[2] = value2 mov rtmp,tBCD1 andi rtmp,0x0f st z+,rtmp ;BCD[3] = value3 st z,tBCD2 ;BCD[4] = value4 (MSD) ret ; ... return

bBCDx_2: ldi r30,AtBCD2+1 ;Z points to result MSB + 1

bBCDx_3: ld tmp16a,-z ;get (Z) with pre-decrement subi tmp16a,-$03 ;add 0x03 sbrc tmp16a,3 ;if bit 3 not clear st z,tmp16a ; store back ld tmp16a,z ;get (Z) subi tmp16a,-$30 ;add 0x30 sbrc tmp16a,7 ;if bit 7 not clear st z,tmp16a ; store back cpi zl,AtBCD0 ;done all three? brne bBCDx_3 ;loop again if not rjmp bBCDx_1

end

Это, кстати, обсосано в соответствующей аппликухе. Только там принцип не объяснен (afair), а только последовательность действий.

Reply to
Harry Zhurov

День добрый!

..............

А не писал ли кто подобный код на С (8051) и не поделится ли? - сэкономит время на упражнения самому.

С уважением, Юрий Копылов inlog(@)mtu-net.ru

Reply to
Yuri Kopylov
6-Nov-04 09:18 Yuri Kopylov wrote to Harry Zhurov:

YK> А не писал ли кто подобный код на С (8051) и не поделится ли? - сэкономит YK> время на упражнения самому.

Вдогонку. Этот алгоритм - это как бы "по определению", мы просто берём запись числа в виде

sum( A[n] * E^n )

где A[n] - n-я цифра числа, E - основание системы счисления, '^' - "в степени"

представив A[n] и E исходной системы счисления в виде чисел целевой системы и производим все вычисления в целевой системе. Естественно, как тут уже говорилось, для упрощения вычислений сумму расписываем по Горнеру. При переводе из двоичной системы A[n] - однобитовые знаки а E==2 и "умножить на E" в цепочке вместе с прибавлением очередного A[n] превращается в сдвиг и последующую десятичную коррекцию.

wbr,

Reply to
Oleksandr Redchuk
6-Nov-04 09:18 Yuri Kopylov wrote to Harry Zhurov:

YK> ..............

YK> А не писал ли кто подобный код на С (8051) и не поделится ли? - сэкономит YK> время на упражнения самому.

Если на C, то причём тут 8051?

Чесслово, очень фигово писать не на С, а на "С для 8051", "С для XXX", "С для YYY". Практически полностью теряется смысл писания на C, потом для другого процессора хоть переписывай, хоть заново пиши. Это говорит печальный опыт 10-летней давности, когда в погоне за призрачной "эффективностью" я писал не просто "на C для 8051", а вообще "на AVOCET C51", используя всякие фокусы, основанные на знании того, как ЭТОТ компилятор компилирует те или иные конструкции. В частности, в программе нигде не было if(a != b) было if(a^b)

В итоге при полной переделке проекта под новое железо, но тоже на 8051, и переходе на Keil - я те места, где было действительно критично - переписал на ассемблере (число трок асма по сравнению спервым вариантом вырасло раза в два, с 10 до 20% строк проекта), а остальное писал на "гораздо более натуральном С".

Итого "а если для 8051, то почему не на асме?".

Я когда-то кидал сюда куски на асме с соглашениями о регистрах для вызова из Keil C

bin -> packed BCD packed -> unpacked unpacked -> ASCII

единое компактное тело для работы с 8,16,24,32 - битными целыми. Дело в том, что у 51-го есть команда десятичной коррекции после сложения, которая очень хорошо в этих алгоритмах исползуется, причём и для преобразования тетрады в ascii 0-9,A-F тоже. Это "ещё более когда-то" кидал сюда Василевский, но для какой-то фиксированной разрядности и фиксированной длины ASCII-выхода. А году в 98 я переписал под переменную длину (как во входных битах, так и в выходных цифрах, например, заданием нужных параметров при вызове word2ascii для выдачи чисел 0..9999 в 4-байтовый выходной массив).

Лень сейчас рыться на компакте с архивом проектов, заново выковыривать, переводить комментарии... Должно быть в архивах эхи.

wbr,

Reply to
Oleksandr Redchuk
Reply to
Vitaliy Romaschenko

Добрый день!

Oleksandr Redchuk snipped-for-privacy@real.kiev.ua> пишет

Но ведь понятно же, что я имел ввиду - у меня и так сейчас есть процедуры перевода форматов чисел, которые уже давно тягаю из проекта в проект. На Keil-С для 8051. А компилятор тут при том, что у многих м.б. разные библиотеки на циклический сдвиг, например, наличие или отсутствие к-д дес. коррекции и пр. Либо м.б. asm-вставки. И вопрос-то не в том, чтобы написать код на С - текст-то действительно может быть универсальным и переносимым. А вопрос в скорости, оптимальности и занимаемой памяти. И даже после того, как взял чужой С-код, обычно есть возможность его сократить при адаптации хотя бы за счет разных вариантов использования памяти - data или idata, использования common-переменных и пр.

Например, на моем процессоре AT89S53 по-разному написанная процедура

void long_to_ascii(long d) // char знак + 7 цифр = 8 символов

с использованием операций вида с = (d % 10000)/1000 | 0x30; // Тысячи выполняется около 3.5 мсек, а с использованием d -= ((long)c)*100000; c = (char)(d/10000);

не более 2 мсек, хотя и на 160 байт длиннее.

Вот об этом я и говорил - может, можно еще быстрее и короче. Кому что когда нужно.

Лично для меня сейчас на С - намного быстрее написать и изменить. И в конкретном случае (на С) общий код получается короче и быстрее универсального, поскольку моя процедура перевода не возвращает некоторую строку как параметр, а внутри себя пишет результат в разные места, в том числе в буфер вывода RS-232. Еще и учитывается проблема с местом под стек. Да и нужен мне пока перевод фиксированной длины.

Попробую посмотреть. Спасибо.

WBR Юрий Копылов

Reply to
Yuri Kopylov

Привет!

Vitaliy Romaschenko snipped-for-privacy@p6.f.n5060.z2.fidonet.org> пишет

См. мой ответ OR - вопрос в скорости выполнения и занимаемой памяти. По моим измерениям, быстрее оказалось использовать деление/умножение вместо % и линейная программа на 8 десятичных знаков вместо цикла. Попробую померять в симуляторе твою конструкцию -результат напишу. Сейчас у меня используется следующий вариант (аналогично делаю перевод float_to_ascii - писал неск. лет назад, когда начал писать на С вместо asm):

void long_to_ascii(long d) // знак + 7 цифр = 8 знаков, тестовое время выполнения 1.943...1.961 msec при 24 мГц { char c;

if (d < 0) {que[qlen++] = '-'; d = -d;} else que[qlen++] = '+';

c = (char)(d/1000000); que[qlen++] = c | 0x30; // Миллион d -= ((long)c)*1000000; c = (char)(d/100000); que[qlen++] = c | 0x30; // Сотни тысяч d -= ((long)c)*100000; c = (char)(d/10000); que[qlen++] = c | 0x30; // Десятки тысяч d -= ((long)c)*10000; c = (char)(d/1000); que[qlen++] = c | 0x30; // Тысячи d -= ((long)c)*1000; c = (char)(d/100); que[qlen++] = c | 0x30; // Сотни d -= ((long)c)*100; c = (char)(d/10); que[qlen++] = c | 0x30; // Десятки c = (char)(d-((long)c)*10); que[qlen++] = c | 0x30; // Единицы }

С уважением, Юрий Копылов

Reply to
Yuri Kopylov

Привет!

Vitaliy Romaschenko snipped-for-privacy@p6.f.n5060.z2.fidonet.org> пишет

8051, 22.1184 МГц. Перевожу в символы число 1234567 dec.
  1. Программа на базе твоей строчки (выше) короче моего линейного варианта на ~ 479 байтов Время выполнения тестового примера - 4.63867 мсек
  2. Мой текущий вариант (линейная программа и деления на 10 и вычитания): Время выполнения тестового примера - 2.36111 мсек

В 2 раза быстрее. Вот потому и хотел попробовать что-либо поинтеллектуальнее, чем простое деление на 10 и вычитание. Тем более, если микро имеет к-ды BCD и если есть возможность при этом не лезть в asm.

WBR Юрий Копылов

Reply to
Yuri Kopylov
Reply to
Vladimir Vassilevsky
Reply to
Vitaliy Romaschenko
7-Nov-04 14:45 Yuri Kopylov wrote to Vitaliy Romaschenko:

YK> 8051, 22.1184 МГц. Перевожу в символы число 1234567 dec.

YK> 1. Программа на базе твоей строчки (выше) короче моего линейного варианта YK> на YK> ~ 479 байтов YK> Время выполнения тестового примера - 4.63867 мсек

YK> 2. Мой текущий вариант (линейная программа и деления на 10 и вычитания): YK> Время выполнения тестового примера - 2.36111 мсек YK> В 2 раза быстрее. Понятно почему - одно деление заменено на умножение, которое гораздо быстрее.

YK> поинтеллектуальнее, чем простое деление на 10 и вычитание. Тем более, YK> если YK> микро имеет к-ды BCD и если есть возможность при этом не лезть в asm. Я даже не вникал - какие у Keil есть inline функции "ближе к железу", кроме просто необходимых cli() sei().

Продолжаю утверждать, что если какой-то кусок типа этого конвертора нужно вылизать на объём и/или скорость, то гораздо правильнее его выписать на ассемблере - это проще, чем извращаться на С и сопровождаться/изменяться оно будет не сложнее, чем "якобы С-шный код". В данном конкретном случае его в строках будет ненамного больше, чем "С"-шного.

Поэтому сейчас продолжаю обсуждать "стандартно-С-шные" методы.

Непонятно почему, но Keil не даёт в стандартной библиотеке функций div и ldiv, ведь тело для них в библиотеке есть гарантированно, надо только засунуть его в нужную обёртку.

#include <stdlib.h>

div_t div(int num, int denom); ldiv_t ldiv(long num, long denom);

Дело в том, что внутренняя библиотечная функция деления в процессе рабоы всегда получает и частное, и остаток. При / вызывается эта функция и берётся частное, при % - ещё раз вызывается она же и берётся остаток. Функции div, ldiv просто возвращают оба значения сразу - и частное, и остаток. Итого

int8_t dig[4]; div_t dt;

dt.quot = N; for(i =3; i>=1; --i) { dt = div( dt.quot, 10); dig[i] = dt.rem; } dig[0] = dt.quot;

Для AVR (mega64) этот код несколько длиннее, чем с / % (в основном за счёт увеличенного количества обращений к памяти), но в два раза быстрее (что вполне естественно). И немного медленнее, чем линейная программа с делением и вычитанием умноженного (не стал разбираться почему), зато более чем в 2 раза короче.

wbr,

Reply to
Oleksandr Redchuk
7-Nov-04 20:25 Vladimir Vassilevsky wrote to Oleksandr Redchuk:

OR>> Чесслово, очень фигово писать не на С, а на "С для 8051", "С для XXX", "С OR>> для YYY". Практически полностью теряется смысл писания на C, потом для OR>> другого процессора хоть переписывай, хоть заново пиши. Это говорит

VV> Я стараюсь писать по-возможности портабельно и прозрачно, однако все VV> равно VV> случалось наступать на хорошие грабли. Однажды пришлось портировать код VV> на TMS320C55xx, у которого в С нет 8-битного типа. В коде много где Дык я и не говорю, что С асолютно портабельный. Но самому себе усложнять будущее, применяя в "общеупотребительном" коде небольшого размера фичи, завязанные на пару кристалл+производитель_компилятора - считаю "несколько необдуманным".

wbr,

Reply to
Oleksandr Redchuk
Reply to
Anatoly Mashanov

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.