Převod čísla na ASCII řetězec

Ahoj! Nedávno jsem tu zmiňoval jednu špatnou vlastnost AVR - neumějí dělení. Už jsem se dohrabal k práci s LCD řízenými HD44780, a tak nějak občas je docela dost potřeba vypisovat nějaká ta čísla, tak se nyní ptám, jak to realizovat bez nutnosti dělení, respektive aby převod na ASCII řetězec byl co nejrychlejší.
KDysi jsem napsal pěknou rutinu pro 8051 (převod 8b čísla na ASCII, bez/s výpisu nul před číslo) , která toto uměla, bohužel jsem v ní použil asi 2x dělení. Postup byl následující. číslo vydělit 100 (zjistit cifru stovek), k tomu co vyšlo jsem přičetl vhodnou konstantu (48 = ‘0’). Tudíž jsem získal ASCII kód první cifry. Poté jsem zbytek tohoto dělení dělil 10,… atd. Ještě jsem nějak řešil odstraňování nul před číslem.

Naprosto super, podařilo se mi ty staré rutiny vyhrabat, zde přidávám ukázku, jak jsem to kdysi řešil.

;********************* RUTINA Byte2dSTR verze 1.0 *****************************
;Rutina Byte2dstr prevede 8bit cislo na ASCII retezec
;cislo pro prevod je v A, adresa SRAM, kam se ulozi retezec je v R0.
;Muze zabrat az 3 byte. (255)
;vypisuje nuly pred cislo, napr. 001, 039, 000,...

Byte2dstr1:	mov B, #100
		div AB
		add A, #48
		mov @R0, A
		inc R0
		mov A, B
		mov B, #10
		div AB
		add A, #48
		mov @R0, A
		inc R0
		mov A, B
		add A, #48
		mov @R0, A
		ret

;********************* RUTINA Byte2dSTR verze 2.1 *****************************
;Rutina Byte2dstr prevede 8bit cislo na ASCII retezec
;cislo pro prevod je v A, adresa SRAM, kam se ulozi retezec je v R0.
;Muze zabrat az 3 byte. (255)
;nevypisuje nuly pred cislo, napr. 001, 039, 000,... ale vypisuje klasicky 1, 39, 0,...
;ovlivnuje: priznak F0 (z PSW registru), C, P, OV, A, B, R0
;verze 2.0: nefungovalo vynechavani nuly u desitek ->
;verze 2.1: misto carry (ktere nechtene ovlivnuje DIV) se pouzil priznak F0 z PSW registru

Byte2dstr2:	mov B, #100
		clr F0
		div AB
		jz bs1
		add A, #48
		mov @R0, A
		inc R0
		setb F0
bs1:		mov A, B
		mov B, #10
		div AB
		jb F0, bs2
		jz bs3
bs2:		add A, #48
		mov @R0, A
		inc R0
bs3:		mov A, B
		add A, #48
		mov @R0, A
		ret

Poradíte mi prosím, jak něco podobného vyřešit na AVR, kde není dělení? Nebo aspoň jaký jiný případně i lepší postup mám použít?
Dík moc, Honza

Tak ako pises. Presne sposobom delenia a vyseparovavania jednotlivych radov to riesim i v C.

V C (ja viem, ze o nom nemam pisat, len na ukazku :slight_smile:) sa da pouzit funkcia prinf() a sprintf(),ktora riesi velmi vela roznych uloh, ako napriklad zaporne cisla, desatinne cisla, orezanie cisel na desatiny, skratka kopec muklovacky, ale zapisanie takejto funkcie do programu ho naboptna zhruba o 2kB(ale nie po kazdom vlozeni, iba prvy krat potom uz tolko nie). Z toho usudzujem (preto som si dovolil toto odbocenie od ASM), ze aj prekladac to tiez robi cez sw delenie a separovanie, separovanie a separovanie… :slight_smile:

Ak ti ide len o cisla od 0 do 255, tak ako si pisal v predchadzajucich prispevkoch o deleni a potrebujes co najvyssiu ryhlost, kludne si sprav do Flashky 2D tabulku string[256][3] a ako prvy index pouzi cislo, ktore chces zobrazit a v ramci neho mas k dispozicii hned tri prisluchajuce znaky.
Zaberie to 768B vo Flash, co pri velkostiach Flash v ATmega (neviem s akym typom konkretne robis) vacsinou nie je ziadny problem. So 16b cislami by to uz problem asi bol :slight_smile:

Tobe tedy staci prevod do BCD, ascii uz si z toho vytvoris prictenim konstanty… Muzes to provest bud postupnym odecitanim zakladu mocnin 10^x nebo pres hornerovo schema… Hornerovo schema ted nemam v hlave, takze ti pisnu jen to odecitani zakladu:

Po každém kroku se zkoumání, je-li výsledek záporný: ne -> inkrementován čítač na dané pozici i. ano = daný řád byl eliminován; čítač obsahuje počet základů pro daný řád (tedy hledanou číslici v daném řádu) Příklad: tisíce = 0 1234 – 1000 = 234 < 0 – ne, tisíce = 1 234 – 1000 = –766 < 0 – ano, hledané tisíce = 1, 234 je základ pro eliminaci v řádu 100vek stovky = 0 234 – 100 = 134 < 0 – ne, stovky = 1 134 – 100 = 34 < 0 – ne, stovky = 2 34 – 100 = –76 < 0 – ano, hledané stovky = 2, 34 je základ pro eliminaci v řádu 10tek atd… Výhody: poměrně jednoduchá realizace i pro delší čísla (vícebytové odčítání je jednoduché). Nevýhody: nekonstantní doba převodu (závislá na převáděném čísle)
pozn. je to doslovna citace ze studijnich materialu, snad se jejich autor nebude zlobit :slight_smile:

Ty Cčkové fce znám. Jen detajl: kdbych dělal vCčku, tak se neptám, a už mi tam tyhle fce běhají :slight_smile: Kdybych to célé psal v C, tak mám pochybnosti o tom, zdali by ten kontrolér stíhal dělat taky něco jiného, než hromadu věcí, které nepotřebuju. :slight_smile:
To odečítání stovek, desítek,… mě už taky napadlo. Otázkou je, zdali to je rychlejší, než realizování nějakého 8b/8b dělení a následné použití tohoto dělení (ten postup jak jsem psal já, a pak i Martin).
piityy: Co je zač to horneovo schéma?

To vyhladavenie tabulkovou metodou by sa dalo zefektivnit (ak je cas na odpocitanie) nasledovne:

DIGIT_1 = ‘0’;
DIGIT_2 = ‘0’;
DIGIT_3 = ‘0’;
ak cislo > 200 potom DIGIT_3 = DIGIT_3 + 2, cislo = cislo -200
ak cislo > 100 potom DIGIT_3 = DIGIT_3 + 1, cislo = cislo -100
ak cislo < 10 potom DIGIT_1 = DIGIT_1 + cislo, koniec

A cislo je teraz mensie ako 100 a zaroven vacsie ako ako 9. To by som dal do tabulky TAB[90][2]

Tabulka sa tak skrati zo 768B na 180B

Je na str.7 Kdyby ti to hned nebylo jasny dodam doplnujici info :slight_smile:
Potrebujes tam ovsem provadet dekadickou korekci (DA u x51), kterou musis na avr napsat rucne…
pr07_viceb_arit_BCD.pdf (426 KB)

V tom dokumentu mě zaujala úplně poslední větička…
“násobení x10 lze nahradit posuvy”
Jen tak mě napadá, nelze i dělení nahradit posuvy?

Ovšem že lze. Dělení není nic jiného než odhad, odečet a posuv ve smyčce. A když se to trochu optimalizuje např. pro převod BIN do BCD vypadá to takto:[code]

.def AL = R16
.def AH = R17
.def CL = R20

.macro djnz
dec @0
brne @1
.endmacro

;Prevod AL do BCD formatu
BINtoBCD:
push AH
ldi CL,4
ldi AH,0xB0
BinLoop:
add AL,AH
brcs NoSub50
sub AL,AH
NoSub50:
rol AL
djnz CL,BinLoop
swap AL
pop AH
ret

[/code]
Pracuje to pro čísla v rozsahu 0 až 99. V BCD ani více nelze, ale pokud bys chtěl celý rozsah byte, pak bys musel odečítat stovky.
Ještě by mě zajímalo, proč ten požadavek na rychlost. Za dobu převodu se nestačí ani natočit krystaly v LCD natož aby to někdo přečetl.

To je sice hezký, že je to rychlejší než to LCD, ale co mi řekneš na tohle: Původně, když jsem ještě netušil, že AVRka neumí dělit, jsem předpokládal takový program na real-time úpravu signálu, dky to dělení mělo běžet asi 45 000x za sekundu. ALe to řešit nebudeme, jelikož jsem od toho odstoupil.
Spíš k tomu, co si právě napsal za zdroják. Jelikož sotva vím, co to má dělat, můžeš se mi o tom kusu kodu podrobněji rozepsat?
Dík moc

Ak sa jedna o realtime upravu signalu, na to sluzia procesory DSP. Na to su urcene.
Pre takuto cinnost by mozno stacil nejaky ARM7DTMI, alebo Ti skor doporucujem Cortex M3 (napr. STM32103xxx cena kusok nad 100,-Kc 128kB Flash/20kB RAM, ma nasobicku i delicku na takt :slight_smile: ), ktory v sebe obsahuje dva synchronizovane AD 12b prevodniky. Ale ci by stihali hociaku realtime upravu signalu, tazko povedat. Ale to hovorime o 32b procesoroch s vykonom 55-72MIPS. To uvadzam len tak pre inspiraciu :slight_smile:

Njn, 32 procesůrky… To už je jiný kafčo. Překvapuje mě ta cena co si psal… 100kč/kus? Neni to nějak málo?

Technik: Můžeš mi prosím podrobněji vysvětlit ten kus kódu co si sem vložil?

Potřebuju ještě nějak vyřešit dělení 100 a 10ti. Zkouším to nějak rozložit na posuvy, a stále se nedaří. Poradíte mi?
Dík, Honza

No ved ta cena ma dostala.

TME (ako neregistrovany)

STM32F103RBT6 133,-CZK /1ks 114,-CZK/10ks
STM32F101C8T6 101,-CZK/1ks 89,-CZK/10ks

tu sa da na prve zoznamenie nieco docitat o Cortex M3 (a nielen o nich)

hw.cz/teorie-praxe/art2111-porov … e-arm.html

kto ma problem so spajkovanim SMD, urcite sa da naletovat (pripadne niekym) na redukciu TQFP/DIL40

pripadne cena STM32-H103 na stranke:

olimex.com/dev/index.html

(treba vyhladat v skupine ARM\STM\CORTEX M3)

zakl. vlastnosti:

MCU: STM32F103RBT6 ARM 32 bit CORTEX M3™ with 128K Bytes Program Flash, 20K Bytes RAM, USB, CAN, x2 I2C, x2 ADC 12 bit, x3 UART, x2 SPI, x3 TIMERS, up to 72Mhz operation

standard JTAG connector with ARM 2x10 pin layout for programming/debugging with ARM-JTAG

USB connector

user button

RESET button

status LED

power supply LED

on board voltage regulator 3.3V with up to 800mA current

single power supply: takes power from USB port or extension connector pin

8 Mhz crystal oscillator

32768 Hz crystal and RTC backup battery connector

extension headers for all uC ports

Dimensions: 61x 34mm (2.4 x 1.3")

Distance between the exntension connectors: 25.4 mm (1")

je pre laborovanie a nestaranie sa o to ako mcu rozbehat a pouzit v s “klasickou” montazou celkom prijatelna. Ale urcite sa najdu i lacnejsie varianty, ako pripadne takyto procesor vo svojich aplikaciach nasadit.

S uvadzanym modulom sa da spravit napriklad toto:

teslachip.com/oslskp.html

a mat taky jednoduchy a lacny osiloskop do 750ksps - s nim sa daju analyzovat signaly na I2C, beznych UART rychlostiach, atd moze byt celkom zaujimave :slight_smile:

I kdyby AVR mělo dělení jako 51, bylo by to na nic, protože na dělení 16b/8b se to použít nedá, takže se opět musím nelichotivě vyjádřit k x51, že je to spackaný MCU. AVR a všechny 8biťáky jsou tak akorát pro zpracování telefonního signálu, tzn. do 4kHz max. do 6kHz a 8b vzorkování. Navíc svou rychlostí a instrukční výbavou nestačí. X51 nemá ani kousínek z DSP instrukcí na rozdíl od AVR.

Výklad by byl dost komlikovaný zvláště bez obrázů, takže jen stručně. Do AL zadáš číslo v rozsahu 0 až 99 v binárním formátu. Instrukce push AH a pop AH můžeš vynechat, pokud ti nevadí, že je procedůra modifikuje. Po vykonání je výsledek opět v AL v BCD formátu. Nejde o nic jiného než o dělení 10. Podíl je vyšší cifra, zbytek po dělení nižší. Algoritmus je zcela totožný s písemným dělením dekadických čísel, který znaš ze 4. třídy ZŠ. V bináru je to mnohem jednoduší, nepotřebujeme malou násobilku. Porovnává se dělenec s dělitelem. Jestliže se dělitel do dělence vejde, odečte se od něj a do výsledku se zapíše 1 jako nejnižší bit podílu. Pak se dělenec a podíl vynásobí 2 a proces se opakuje třeba do nekonečna. To co zůstáva v dělenci je zbytek po dělení, který lze dále dělit a získávat tak vyšší přesnost, třeba i na desetiny. Počet iterací totiž určuje mocninu podílu a taky přesnost.
Ještě je důležité, aby před započetím dělení byl dělenec menší než dvojnásobek dělitele. Jinak by algoritmus selhal.
Jestliže počet iterací určuje mocninu podílu, nebo chceteli pozici binární tečky(analogie k desetné čárce) lze dělení nerůznějším způsobem modifikovat. Když řeším podíl n/10, mohu jej upravit na 2n/20, nebo na 16n/160 a nebo třeba i na n/160 s tím, že změním počet iterací (počet cyklů). Šlo tedy o to udělat takovou úpravu podílu, aby se počet iterací minimalizoval na 4. Nejvyšší podíl je 9 a to lze vyjádřit min. 4 bity. Dělitele jsem zvolil 0x50 = 80 dek. címž je splněna podmínka pro dělení. Aby se příznak Cy negeneroval obráceně, je použita instrukce ADD s dvojkovým doplňkem. Výsledky odhadů (iterací) se zapisují do dělence zprava. Někdo by mohl namítnout, že to ovlivní výsledek následující iterace. Ne, protože o odhadu rozhoduje výšší teráda a dělitel má v nižší 0. Po 4 iteracích je v dolní tetrádě podíl, v horní zbytek. Takže je to obráceně než potřebujeme. Proto ta instrukce SWAP AL.

Jen tak mimo:
Proč mi tohle nechce kompiler sežrat :imp:
.db ‘Hello World!’,0xFE
a musim se s tim takhle rozepisovat?
.db ‘H’,‘e’,‘l’,‘l’,‘o’,’ ‘,‘W’,‘o’,‘r’,‘l’,‘d’,’!’,0xFE
Nebo jsem si jen zvyknul na zvláštnost 8051 kompileru? :slight_smile: Tam mi tohle prošlo bez problémů.

Stále nějak nevím, pro co se rozhodnout (myslím tu konverzi 8b/string)
Tabulku se mi dělat nechce, i když to bude řešení asi nejrychlejší (a taky největší - což se mi dost nelíbí). Našel jsem si v knížce rutinku na Uint8/Uint8 dělení, zatím jí tam použiju, protože o rychlost mi zrovna moc nejde.

Sakra :imp: :imp: , během 3 miut už mi 5x padlo AVR studio!

nerobim v ASM, ale nepomohli by uvozovky miesto apostrofov? Do apostrofov sa standartne uklada char, do uvozoviek string. Ale neviem, ako je to v ASM. Skus.

Divne, robim v nom kontinualne tak 6-10 hodin denne a nic take nezaznamenavam. Asi nejaky pes zakopany

No vida… uvozovky mě nenapdaly :smiley:
Dal sem je tam, a už je po problémech! Díkes :slight_smile:
Já jsem zvyklý z 8051, kde se stringy i chary psaly do uvozovek.