ako naprogramovat

Zuffa Jan ZuffaJ na cgc.sk
Pondělí Duben 15 00:40:13 CEST 2024


Ja k tomu dodam len, ze je potrebne mysliet aj na "false sync"
to znamena ze ked paket zacina B5 tak ked sa docita zvysok dat
je mozne povazovat ho za platny az ked dalsi zacina tiez bajtom B5 inak
moze dojst k situacii kedy je zaciatok zly a potom uz nesedi nic. Netreba sa spoliehat 
na to ze komunikacia  a zariadenie su  idealne. V mojom profesionalnom zivote som sa stretol uz velakrat s tym, ze na stole vsetko fungovalo ale v realnych podmienkach nie.

J.



-----Original Message-----
From: Hw-list <hw-list-bounces na list.hw.cz> On Behalf Of David Obdrzalek
Sent: Sunday, April 14, 2024 11:34 PM
To: HW-news <hw-list na list.hw.cz>
Subject: Re: ako naprogramovat

Já bych ještě s dovolením podotknul, že používat String v situaci, kdy se do něj inkrementálně neco strká, je dost neefektivní jak paměťově, tak výkonově, protože tempString += inChar; znamená, že se v zásadě udělá nová alokace na velikost součtu délek (v tomhle přípaně jen o 1 větší) a okopíruje se tam všechno plus ten nový znak. A tak pořád dokola.
Plus je potřeba připočítat management okolo vlastní existence instance String.

Pokud vím, jak je zpráva dlouhá, tak je šetrnější deklarovat rovnou buffer potřebné délky a do něj to postupně ukládat. Nebo zprávu dokonce zpracovávat za letu tak jak přicházejí jednotlivé znaky a nepotřebuju ani buffer na celou zprávu.

Ono sice né že by to byl ve spojení se seriovou linkou nějaký výkonnostní problém (seriová komunikace je obvykle výrazně pomalejší, než jak běží mikrokontroler, takže se mezitím ta realokace v pohodě stihne). Také obvykle string bude krátký, takže nejspíš ani nějaké potíže, že by se to do paměti nevešlo dvakrát nebo došlo k nějaké fatální vnitřní fragmentaci paměti, asi taky nenastanou. Ale myslím, že ani tehdy se prostě nemá plýtvat :-)

Na druhou stranu souhlasím, že pokud se jedná o nějakou jednorázovku nebo něco fakt triviálního, kde i těch 32 kB programové paměti Arduino Uno je řádově víc, než potřebuju, a 16MHz hodiny to ženou tak rychle, že se to stejně vlastně pořád jen točí dokola v loop, aniž by to dělalo něco rozumného, a navíc ani nepotřebuju nijak šetřit energií, tak se zohledněním ceny času programátora může být prostě efektivnější se s tím nebabrat a namastit to tam víceméně jakkoli... (ovšem když se to namastí ne plýtvavě, tak to asi vadit nebude, žejo)

D.O.




PS: Pro srovnání:
A)
volatile String retezec;
void loop() {
  while(Serial.available())
  {
    char novyZnak;
    novyZnak = Serial.read();
    retezec += novyZnak;
  }
}

B)
const unsigned maxDelka=100;
volatile char retezec[maxDelka];
uint8_t delka;
void loop() {
  while(Serial.available())
  {
    char novyZnak;
    novyZnak = Serial.read();
    if(delka<maxDelka-1) {
      retezec[delka++] = novyZnak;
      retezec[delka] = 0;
    }
  }
}
(volatile je v obou případech jen aby se to fakt uložilo, jinak by to přinejmenším v tom druhém případě teď bez dalšího použití pole retezec překladač vyoptimalizoval)

Na mé instalaci se pro Arduino Uno A) přeloží do 2826 B programové paměti + 202 B gbálních proměnných, B) do 1388 B + 285 B. V B) jsem dal 100 jen tak od oka, neznaje max délku té věty v ASCII, tam by samozřejmě mělo být něco rozumnějšího. A) má proti
B) větší program o cca kilo a půl. Na proměnných to je sice na první pohled naopak,
B) teď potřebuje o 83 B víc místa pro globální proměnné, ale za běhu už pak nebude potřebovat téměř žádné lokální nebo dynamické alokování, zatímco varianta A) potřebuje vždy naalokovat dvakrát tolik místa, než je délka věty, kterou přijímá. 
Protože jak globální, tak lokální a dynamicky naalokované proměnné jsou ale ve stejné paměti, je potřeba porovnávat součet, takže B) bude proti A) úspornější i z tohoto pohledu.
(běhovou analýzu, jak přesně moc ten heap roste dělat nebudu) 
 

Detailnější pohled na přidání 1 písmene do Stringu: V případě A) se to retezec += novyZnak; přeloží zavoláním metody retezce concat, která uvnitř vytvoří z toho jednoho znaku céčkový řetězec a zavolá concat stávajícího Stringu s ním:
unsigned char String::concat(char c)
{
	char buf[2];
	buf[0] = c;
	buf[1] = 0;
	return concat(s.buffer, s.len);
}

Tehnle druhý concat si vyžádá rezervaci délky bufferu na požadovanou novou délku a strcpy pak na konec původního přikopíruje nový:
unsigned char String::concat(const char *cstr, unsigned int length) {
	unsigned int newlen = len + length;
	if (!cstr) return 0;
	if (length == 0) return 1;
	if (!reserve(newlen)) return 0;
	strcpy(buffer + len, cstr);
	len = newlen;
	return 1;
}

Metoda reserve zavolá v případě zvětšování metodu changeBuffer:
unsigned char String::reserve(unsigned int size) {
	if (buffer && capacity >= size) return 1;
	if (changeBuffer(size)) {
		if (len == 0) buffer[0] = 0;
		return 1;
	}
	return 0;
}

Metoda changeBuffer zavolá realloc:
unsigned char String::changeBuffer(unsigned int maxStrLen) {
	char *newbuffer = (char *)realloc(buffer, maxStrLen + 1);
	if (newbuffer) {
		buffer = newbuffer;
		capacity = maxStrLen;
		return 1;
	}
	return 0;
}
Tady připouštím, že je možné, že realloc uvnitř zjistí, že za tím řetězcem je ještě volno, takže není potřeba přesouvat. To nevím, jak je pro avr implementované, ale i kdyby, tak za tím řetězcem volno principiálně být nemusí a tak to čert bude nutit přesouvat pořád znovu a znovu.

On 12 Apr 2024 at 18:19, Daniel Valuch wrote:

> zdravim osadenstvo,
> 
> programovat viem len velmi zakladne, preto by som mal na piatok 
> algoritmicku... Dokoncujem ziskavanie presneho casu z GNSS modulu o 
> ktorych sme tu uz nejaky cas rozpravali.
> 
> Ten generuje dva typy vystupu po seriovej linke. Klasicky ascii, ktory 
> je ukonceny \n znakom a parsovanie tohoto stringu je trivialne (robim 
> v arduino ide). Prijimaju sa data, tie sa pridavaju do stringu a ked 
> pride \n tak sa to rozobere a urobi co treba
> 
> void serialEvent() {
>    while (Serial.available()) {
>      // get the new byte:
>      char inChar = (char)Serial.read();
>      if (inChar == '\n') {
>          // sprava/retazec je hotovy, rozparsuj a urob co treba
>        tempString = "";
>      }
>      // keep receiving until \n arrives
>      else {
>        // add it to the inputString and keep receiving:
>        tempString += inChar;
>      }
> 
> Druhy vystup je ale binarny, prichadzaju pakety, ktore maju strukturu 
> definovanu, ale paket zacina opacne.
> 
> 0000  B5 62 06 8A 18 00 00 05 00 00 01 00 76 10 01 05
> 0010  00 53 10 01 7E 01 91 20 00 7F 01 91 20 01 00 B6
> 
> Prve dva bajty na zaciatku B5 62 vzdy oznacuju zaciatok paketu
> 
> dalsie dva 06 8A oznacuju o aky paket ide
> 
> nasledujuce dva 18 00 dlzku kolko byteov spravy nasleduje (v tomto 
> pripade 24)
> 
> potom je samotna sprava
> 
> a na konci 00 B6 su dva bajty checksum
> 
> a s tymto si neviem poradit, ako to zapisat aby to pocuvalo kedy pride
> B5 62, pockalo ako dlha bude sprava a potom zapisalo do nejakeho 
> stringu data.
> 
> Ako na to?
> 
> dakujem,
> 
> b.
> 
> 



_______________________________________________
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