Formatovany tisk pro 8bit

Jan Waclawek konfera na efton.sk
Pondělí Duben 22 00:27:29 CEST 2024


Pekne.

Ma to vsak tu istu chybu na ktoru som uz upozornoval predtym: odstranujete
prilis vela nul. 
Skuste
pr(0,  "Pokus1=###\n");
pr(23, "Pokus2=###.###\n");
Dalsia, suvisiaca chyba je, ze minus moze prepisat desatinnu bodku:
pr(-123, "Pokus3=###.###\n");

Obe chyby sa daju odstranit bud trikom, ktory mi napisal Andy Jancura mimo
konfery, kde sa tie nuly, ktore netreba odstranovat, doplnia vopred, tu
priamo do toho formatovacieho retazca - napr. ten retazec bude teda
vyzerat takto 
pr(0,  "Pokus1=##0\n");
pr(-123, "Pokus3=##0.000\n");
a funkcia bude v duchu

void pr(int32_t h, const char* f) {
  char buf[100];
  uint8_t i;
  uint32_t uh;
  _Bool isNegative;  // C++ : bool

  i = strlen(f) + 1;  // these two functions do have the FLASH (_P) variants
  strcpy(buf, f);

  if (h < 0) {
    uh = -h; 
    isNegative = 1;
  } else {
    uh = h;
    isNegative = 0;
  }

  do {
    i--;
    if ((buf[i] == '#') || (buf[i] == '0')) {
      if (uh != 0) {
        buf[i] = uh % 10 + (uint8_t)'0';
        uh /= 10;
      } else if (buf[i] == '#') {  // don't touch the zeros in formatting
string
        if (isNegative) {
          buf[i] = '-';
          isNegative = 0;
        } else {
          buf[i] = ' ';
        }
      } // (*)
    }
  } while (i > 0);
  Serial.print(buf);
}

Nevyhoda je, ze sa v texte nemoze vyskytnut okrem # ani nula.

Pouzil som funkcie strlen() a strcpy(), jednak preto, lebo su napisane
optimalne v asm, a druhak preto, lebo maju aj _P verziu. Ak sa nepouziju
ale sa kopiruje po znakoch ako v povodnej verzii, treba vo FLASH verzii
funkcie pouzivat pgm_read_byte(), 
https://www.nongnu.org/avr-libc/user-manual/pgmspace.html , co je otravne.
V C je moznost pouzit namiesto prostriedkov z <avr/pgmspace.h> named
address space __flash
https://gcc.gnu.org/onlinedocs/gcc/Named-Address-Spaces.html#AVR-Named-Address-Spaces
, ale v C++ nie.

Da sa ta funkcia rozbit na dve polovice, kde prva len kopiruje do toho
docasneho bufra, a tym padom sa da verzia pre FLASH a RAM napisat tak, ze
ma rozdielny len ten zaciatok s kopirovanim a potom sa vola jeden spolocny
koniec. Ale aj tak asi verziu pre RAM nikdy nepouzijete, takze nad tym
zase netreba tolko spekulovat.

Tiez som pre "odomielanie" pouzil neznamienkovu premennu (uh), mam nicim
neodovodneny pocit, ze neznamienkove % bude efektivnejsie. gcc za urcitych
okolnosti vie pouzit jednu funkciu z internej kniznice pre obe operacie
div mod, namiesto separatneho delenia a modula, treba si ustrazit, aby
bola pouzita (nepamatam si presne tie okolnosti, to avr-gcc uz aktivne
nepouzivam zo 10 rokov).

Ak vadi to, ze sa nuly nedaju pouzit v texte, da sa pouzit nejaka ina
znacka, ktora sa v mieste oznacenom (*) prepise na nuly.

Ak sa Vam nepacia formatovacie stringy s nulami alebo nahradnou znackou,
tak je tu algoritmicka moznost, ze sa napocita pocet potrebnych nul pri
tom prechode "dopredu" (rovna sa pocet # za desatinnou bodkou plus jedna,
ak nie je desatinna bodka tak jedna); a pri "tlaceni" v smere dozadu sa
pri kazdom "vytlacenom" znaku odcita z tohoto poctu nul, a ak je uh
nulove, tlacia sa nuly az kym ten pocet nevynuluje. Je to viac programu a
podla mna sa tym nic take vyznamne neziska. 

Tych pausalnych 100 byte zo stacku sa mi tiez nepaci. A malloc() v mcu si
vyzaduje velmi dobre odovodnenie (vo vacsine pripadov to odovodnene nie je
a malloc() sa jednoducho nema pouzivat uplne rovnako ako ten printf()).
Jedna z moznosti je buf = alloca(i);
https://www.nongnu.org/avr-libc/user-manual/group__alloca.html, ale treba
mysliet na to, ze takto moze zasobnik pretiect; v mcu musi clovek mat
vsetky pamate pod plnou kontrolou (t.j. musi ten zasobnik mat vypocitany).
V konecnom dosledku, ak si ten zasobnik skutocne vypocitate, zistite, ze
aj tak musite vediet, aky najdlhsi string budete tlacit, takze tam ten
konstantny buffer moze byt a moze to byt najefektivnejsie riesenie.

Narocnejsia moznost, ktora setri pamat/zasobnik za cenu dlhsieho programu,
je to spominane rozbitie na text a cislo: text pred cislom je jednoduchy,
ide sa az po prvu znacku a moze sa tlacit znak po znaku t.j. netreba
ziadnu extra pamat; text za cislom  je vlastne tiez jednoduchy, ide sa od
konca po prvu (t.j. poslednu) znacku a zapamata sa, kde ta znacka je,
nakopiruje sa len kus medzi prvou a poslednou znackou, urobi sa konverzia,
vytlaci sa; a potom sa vytlaci znak po znaku aj ten text za poslednou
znackou. Cislo nebude vacsie ako cojaviem 10 cislic 2 mezery a jedna
desatinna bodka a jedno minus, to je 14, to sa da.

Ale samozrejme, ako vzdy, detaily zavisia od poziadaviek. V mcu nikdy
neexistuje jedno "najlepsie" riesenie.

wek

PS. Disasm je avr-objdump -d -S program.elf >program.lss
Myslim, ze som tu pred par mesiacmi o tom pisal, nechce sa mi to hladat. Tu
https://sourceware.org/binutils/docs-2.42/binutils/objdump.html je
dokumentacia k objdump; on sam vypise vacsinu pouzitelnych prepinacov pri
pouziti --help.



----- Original Message ---------------

Subject: Formatovany tisk pro 8bit
   From: Martin Záruba <swz na volny.cz>
   Date: Fri, 19 Apr 2024 19:23:12 +0200
     To: Martin Zaruba <hw-list na list.hw.cz>

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
-- 

Martin Záruba

_______________________________________________
HW-list mailing list  -  sponsored by www.HW.cz
Hw-list na list.hw.cz
http://list.hw.cz/mailman/listinfo/hw-list



Další informace o konferenci Hw-list