Formatovany tisk pro 8bit

Pavel Hudeček edizon na seznam.cz
Sobota Duben 20 16:26:59 CEST 2024


Jsem zvyklý spíš používat float a teprve když není místo, se floatu 
zbavit za cenu zesložitění programu. Třeba na ATtiny416 je float skoro 
nereálnej, na ATmega32, nebo AVR64DD32 s ním není problém. Ono to hodně 
naběhne prvním použitím, ale pak už moc nepřibývá. A přehlednost výpočtů 
se dramaticky zlepší.

A ano, při zobrazování či odesílání postupně dělím deseti, to snad ani 
jinak nejde:-)
Tzn. z mist se udělá násobitel, např. ze 2 je 100, tím se floatový vstup 
vynásobí a předhodí se funkci cislo(long). Ale důležitý je, že se to 
stane až v uartSend.

Pro mě je důležité, že mezi změřením a odesláním jsou floatové výpočty. 
Když se vejdou, zmizí veškeré starosti s přetýkáním, co nastávají při 
celočíselných výpočtech a snaze o zachování přesnosti.
takže pak máme třeba:

#define AD_samples (1024.0)
#define AD_uInDiv   (1 + 110.0/10.0)
#define AD_uOutDiv (1 + 110.0/22.0)
#define AD_ref           (4.096)
...

kde ve funkci cekej je:

if (adSync==1) {
     adSync=0;
     adUin = (adData[AD_first] - offset) * násobitel
     ...
přičemž offset a násobitel jsou z těch definů, nebo z EEPROM float 
eCalConst..., kde je výchozí hodnota z těch definů.
A zajímavější je, když je výstup třeba teplota z termistoru s 
charakteristikou B3950, nebo konduktometr, kde je ale z EMC a dalších 
důvodů na vstupu ještě sériovej a paralaelní R.

Tabulky mívám na grafickém displeji, na to je pak tab(x), která posune 
příští psaní na požadovanou pozici, font je třeba i proporcionální. Nebo 
jsem měl pole dispTabs, kde byly začátky na který skočí \t. A to se dá 
zrovna celkem dobře použít i na sériáku, dělá to pak až putchar2, když 
mu přijde \t a on ví kolik bylo od posledního \n.
Ale vizuální tabulky na sériáku moc nemívám, většinou max. takový napůl, 
kde se jen normálně použije \t. Až když to začne vadit, přidám uartTabs[].

PH

Dne 20.04.2024 v 14:01 Martin Záruba napsal(a):
>
> Nojo, jenže pak s tím neuděláte tabulku. Je potřeba, aby například 
> pokud je hodnota 3753, napsalo to |3753mV| ale pokud je 53, napsalo to 
> |  53mV| a ne |53mV|. Tedy aby celý řetězec (včetně toho mV) byl stále 
> (například) 6 znaků.
>
> Zkusil jste, kolik to zabere paměti? Podle toho, co píšete to dělá 
> převod na float. To ale na osmibitu je hrůza. Já si hraju jen s 
> "grafikou". Číslo stále dělím deseti, to je i na osmibitu snadné. A 
> vypisuji zbytek po dělení. No a někam vrazím jakoukoli "výplň", třeba 
> tu tečku. Pak to vypadá jako desetinné číslo, které ale je v 
> celočíselném formátu uloženo třeba 10x 100x nebo jak se chce větší, 
> což je myslím ten nejefektivnější způsob, jak na malinkém procesoru 
> zobrazit desetinné číslo.
>
> Zkoušel jsem také použít ultoa(). Ale je to větší a stejně musíte 
> výsledek formátovat.
>
> Napadlo mě ještě nejprve první smyčkou zjistit délku masky a pak 
> nadefinovat pole buf pomocí malloc. A na konci ho uvolnit. Pak by 
> většinou bylo malinké. Co si myslíte o tomto nápadu?
>
> Martin Záruba
> Dne 20.4.2024 v 12:29 Pavel Hudeček napsal(a):
>> Ne, v mém příkladu je přeci stav uBat1=3753, tak uartSend("\1mV", 
>> adUbat1, 0); vypíše 3753mV
>> Pokud by adUbat bylo 53, vypíše 53mV
>> a pokud by bylo zadáno uartSend("\1mV", adUbat1, 3);
>> vyleze 3753,000mV
>> nebo třeba 3753,001, pokud float hodnota nevyšla přesně 3753,000
>>
>> Nějak tak to fungovalo, už je to dlouho.
>> Ale jak se tu o tom teď diskutuje a ještě jsme na kroužku došli k 
>> sériáku, tak si asi konečně udělám tu knihovnu, kde tohle bude jedno 
>> z přetížení.
>>
>> PH
>>
>> Dne 20.04.2024 v 11:27 Martin Záruba napsal(a):
>>>
>>> Jen dotaz: Co vypíše:
>>>
>>> uBat1 je 53
>>>
>>> uartSend("\1mV",adUbat1,0);
>>>
>>> nevypíše to
>>>
>>> 53mV      ?
>>>
>>> Martin Záruba
>>> Dne 20.4.2024 v 10:36 Pavel Hudeček napsal(a):
>>>> Tady asi došlo k mírnému nedorozumění:
>>>> Oba umíme zobrazit jen jednu hodnotu.
>>>> Oba umíme požadovaný počet des. míst.
>>>> Oba umíme text před ním i po něm.
>>>> Já nemám formátování tisíců, ale zas můžu ve výstupním textu použít #.
>>>> Zas to formátování s # se asi dá ohackovat, že zobrazí třeba 2 
>>>> čísla zakódovaný do jednoho longu?
>>>>
>>>> // adUin je 12,3456  uBat1 je 3753
>>>> // UART_sendDT je ','  UART_sendPosChr je '\1'
>>>> uartSend("Uin=\1 V\n", adUin, 2);
>>>> uartSend("\1mV", adUbat1, 0);
>>>> výsledek je:
>>>> Uin=12,34 V
>>>> 3753mV
>>>>
>>>> Ten uartSend byl trochu prasáckej, mist nakopíruje do globální 
>>>> uartSendMist a pak zavolá cislo((long)(v * m)), kde m je třeba 1000 
>>>> pro mist=3. A cislo pak do požadovaného místa vnutí oddělovač a 
>>>> uartSendMist vynuluje. Tohle jsem už někde měl i na ty tisíce, jen 
>>>> se to nevypínalo samo.
>>>>
>>>> Chystám se že bych si z těhle věcí konečně udělal nějakou knihovnu, 
>>>> zatím to jen kopíruju mezi projekty a nejčastějc si vystačím s 
>>>> putchar2, text na poslání textu a cislo na poslání longu.
>>>> takže posledně to bylo ve stylu
>>>> text("Uin="); cislo(static_cast<long>(1000.0*adUin)); text(" mV\r\n");
>>>>
>>>> Buffer je na zásobníku, takže sám vznikne a sám zmizí.
>>>> Ale 100 B, to je 20 % RAMky na ATtiny816, což je hodně, případně 
>>>> 1,25 % na AVR64DD32, to už by bylo v pohodě.
>>>> Mě se to obecně nelíbí, protože rád používám děje na pozadí. Funkce 
>>>> cekej(uint32_t ms) je i víc než půlka programu a pokud možno 
>>>> všechno čekání volá aspoň cekej(0). No a když se těch čekání sejde 
>>>> více takových, kde mezitím vzniknul buffer..
>>>> Takže mám snahu takové buffery nedělat.
>>>> Další věc je, že mám snahu minimalizovat místa, kde je potřeba 
>>>> omezovat velikost stringu. Takže mě stačí buffer na 10B celej vstup 
>>>> může mít 254, výstup až 264 a ve verzi s txtPred + txtPo můžou 
>>>> klidně oba texty mít do 254.
>>>>
>>>> Ovšem to s F a PSTR je pro mě novinka, za to díky.
>>>>
>>>> Byl jsem zvyklý na codevision, tam se daly deklarovat proměnné do 
>>>> RAM, EEPROM i Flash, "text" byl ve flash a všechno fungovalo do 
>>>> velké míry samo, včetně toho, že v EEPROM je pointer do flash. 
>>>> Super věc, pokud se po kompilaci k binárce sprostě přikopírujou 
>>>> UTF8 texty ve 4 jazycích a za ně bitmapa fontů. Při prvním spuštění 
>>>> program ve flash najde "Tady>>>", pak dohledá začátky jazyků, 
>>>> hlášky v default jazyku a nakonec font. Pointery hodí do EEPROM a 
>>>> odteď se může na displeji 10 ms po zapnutí objevit třeba normální 
>>>> čeština.
>>>> A v případě GCC jsem si myslel, že nic už takhle elegantně nepůjde.
>>>> Vlastně opravdu nejde, protože si sice můžu nadeklarovat proměnnou 
>>>> v EEPROM, ale nemůžu jí přímo používat, jen přes funkce na čtení a 
>>>> zápis EEPROM, který maj navíc hrozně dlouhý názvy. "Super" do 
>>>> vzorce se 3 kalibračníma konstantama.
>>>> Tak aspoň snad zas můžu mít texty ve flash:-)
>>>>
>>>> PH
>>>>
>>>> Dne 19.04.2024 v 21:23 Martin Záruba napsal(a):
>>>>>
>>>>> Já to tak původně dělal, jenže byl to mnohem složitější a vlastně 
>>>>> můžete takto udělat jen část než narazíte na první formátovací 
>>>>> znak. Pak už je to stejně jen odeslání čísla. protože nevíte, zda 
>>>>> nebude následovat další formátovací znak po libovolné počtu 
>>>>> "výplní". Tvar text,val, míst se mi nelíbil, protože s ním 
>>>>> neuděláte třeba 10mV. Takto je text kdekoli, dokonce je kdekoli i 
>>>>> třeba \n, takže nemá smysl println. A varinta txtPred, val, míst, 
>>>>> txtPo to sice umí, ale jsou 4 parametry a stejně nejde udělat 
>>>>> třeba odskočené tisíce. Taky se mi líbí, že to sežere i float. 
>>>>> Předpokládím, že buffer se vytvoří na zásobníku a po opuštění 
>>>>> funkce zmizí. Je to tak?
>>>>>
>>>>> Ještě to chci doplnit o možnost mít formátovací řetězec ve flash. 
>>>>> Není mi ale jasné, jaký je rozdíl mezi PSTR("V paměti flash") a 
>>>>> F("V paměti flash"). Asi se mi víc líbí F(" "), je to kratší zápis 
>>>>> a dovoluje přetížení funkce. Ale možná má nějakou nevýhodu, na 
>>>>> kterou jsem nepřišel.
>>>>>
>>>>> Martin Záruba
>>>>> Dne 19.4.2024 v 20:50 Pavel Hudeček napsal(a):
>>>>>> Moc pěkný.
>>>>>> Já bych teda akorát nekopíroval celý text do velkého bufferu a 
>>>>>> místo toho text před číslem rovnou odesílal v prvním do/while, 
>>>>>> pak převed a odeslal číslo a nakonec odeslal zbytek vstupního textu.
>>>>>>
>>>>>> Už jsem taky kdysi použil variantu, kdy se zadávají parametry 
>>>>>> (text, val, mist) a v textu je pak znak pro umístění čísla a des. 
>>>>>> míst se odešle podle hodnoty mist. Častějc mám ale (txtPred, val, 
>>>>>> mist, txtPo).
>>>>>>
>>>>>> Odesílání mám teda téměř vždy rovnou průběžně po znacích a 
>>>>>> odesílací funkce čeká jen na dokončení předchozího znaku, takže 
>>>>>> konverze na výstupní text probíhá paralelně s odesíláním.
>>>>>>
>>>>>> PH
>>>>>>
>>>>>> Dne 19.04.2024 v 19:23 Martin Záruba napsal(a):
>>>>>>> Už jsem se Vás dost natrápil na toto téma a měl jsem pocit, že 
>>>>>>> nic moc úsporného a jednoduše použitelného není. Jenže jsem 
>>>>>>> paličatý a zkusil jsem přece něco vymyslet. Požadavek byl:
>>>>>>>
>>>>>>> Výpis na požadovaný počet míst s možností textu před i za číslem.
>>>>>>>
>>>>>>> Potlačení nevýznamných nul.
>>>>>>>
>>>>>>> Co nejúspornější kód jak funkce, tak volání, vhodný pro malinky 
>>>>>>> procesor.
>>>>>>>
>>>>>>> Přímý tisk bez nutnosti psaní Serial.print();
>>>>>>>
>>>>>>> Vyrobil jsem toto, posuďte a navrhněte prosím co by ještě šlo líp.
>>>>>>>
>>>>>>>
>>>>>>> void pr(int32_t h, const char* f) {
>>>>>>>   char buf[100];
>>>>>>>   uint8_t i = 0xFF;
>>>>>>>   int32_t x = abs(h);
>>>>>>>   do {
>>>>>>>     i++;
>>>>>>>     buf[i] = f[i];
>>>>>>>   } while (f[i] != 0);
>>>>>>>
>>>>>>>   do {
>>>>>>>     i--;
>>>>>>>     if (buf[i] == '#') {
>>>>>>>       if (x != 0) {
>>>>>>>         buf[i] = x % 10 + (uint8_t)'0';
>>>>>>>         if (h < 0 && x < 10) {
>>>>>>>           i--;
>>>>>>>           buf[i] = '-';
>>>>>>>         }
>>>>>>>       } else {
>>>>>>>         buf[i] = ' ';
>>>>>>>       }
>>>>>>>       x /= 10;
>>>>>>>     }
>>>>>>>   } while (i != 0);
>>>>>>>   Serial.print(buf);
>>>>>>> }
>>>>>>>
>>>>>>>
>>>>>>> Funkce má jediný formátovací znak #
>>>>>>>
>>>>>>> Příklady:
>>>>>>>
>>>>>>> int32_t napetimV = 5432;
>>>>>>> pr(napetimV, "Pokus1=###.###V\n");
>>>>>>> pr(-21,      "Pokus2=### zaporne cislo\n");
>>>>>>> pr(9876543,  "Pokus3=# ### ### cislo s mezerami po 1000\n");
>>>>>>> float a = 54.3;
>>>>>>> pr(a*10,     "Pokus4=####.# vypis float na 1 desetinne misto\n");
>>>>>>>
>>>>>>> Výsledek vypadá takto:
>>>>>>>
>>>>>>> Pokus1=  5.432V
>>>>>>> Pokus2=-21 zaporne cislo
>>>>>>> Pokus3=9 876 543 cislo s mezerami po 1000
>>>>>>> Pokus4=   54.3 vypis float na 1 desetinne misto 
------------- další část ---------------
HTML příloha byla odstraněna...
URL: <http://list.hw.cz/pipermail/hw-list/attachments/20240420/f55e08cf/attachment.htm>


Další informace o konferenci Hw-list