Programová záhada

Raspberry, Arduino, Mini-PC a další

Moderátor: Moderátoři

Zpráva
Autor
Uživatelský avatar
Cust
Příspěvky: 5553
Registrován: 17 led 2007, 01:00
Bydliště: Husinec-Řež

#31 Příspěvek od Cust »

Celeron píše:To nebylo na Tebe, ale na Custa. Mezi tím jsi stihnul odpovědět.
Já to myslel vážně, mě se už stalo, že procesor nejel správně a stačilo vzít druhý kus...

Uživatelský avatar
Celeron
Příspěvky: 16140
Registrován: 02 dub 2011, 02:00
Bydliště: Nový Bydžov

#32 Příspěvek od Celeron »

Tak všechno je jinak. Mám na PC výběr systému 32 a 64 bitů. Kvůli starejm ovladačům některých periférek jedu převážně jen na 32 bitech.
Dnes jsem nainstaloval 64 bitový Arduino IDE 2.1.0 a projekt šel bez problémů zkompilovat a hlavně nahrát. 8O Bez jakýkoliv změny, se stejnejma knihovnama. Trochu divný je, že jsem musel přidat ke grafický knihovně <Adafruit_GFX.h> ještě navíc <Adafruit_I2CDevice.h>. Proč ji 32 bitovej IDE 1.8 nepotřeboval, mi není jasný.
Zůstanu u toho 64 bitovýho. Kompilace trvá a nahrání do Every je asi 5 x kratší než na 32 bitech. Akorát ta pakárna zase vše nastavit, zaktualizovat desky, knihovny a přidat doplňky pro v IDE nepodporovaný desky.
Cvičně jsem zkompiloval taky 46 KB dlouhej projektík se samejma SerialPrintama a taky bez problémů.
Ještě jsem zkusil odinstalovat 32 bitový IDE a znova ho nainstalovat. Stále stejná chyba, závěr je, že v IDE nebo v něm obsaženým Avrdude je pro Every něco shnilýho.
Ještě mě napadlo zkusit zkompilovat ten samej projekt na ProMini, kde byl problém s velikostí RAM. Ta je bohužel stále malá.
Jirka

Proč mi nemůže všechno chodit hned ?!!

Uživatelský avatar
Celeron
Příspěvky: 16140
Registrován: 02 dub 2011, 02:00
Bydliště: Nový Bydžov

#33 Příspěvek od Celeron »

Další projekt a zase problém s velikostí RAMky při použití OLED displeje s ProMini 328P.
Na zapojovací desce s displejem OLED 0,96" 128x64 I2C s knihovnou Adafruit_SSD1306.h a Adafruit_GFX.h projekt makal. Finál s větším displejem OLED 1,3" 128x64 I2C s knihovnou Adafruit_SH110X a Adafruit_GFX.h už ne. Displej jsem odzkoušel na krátkým testu, vše v pořádku. Tak jsem před všechny výpisy textů nacpal F aby se přesunuly z RAM do Flash. Uvolnění asi 100 byte RAM pomohlo, úvodní texty v Setup se začaly vypisovat ale v Loop se to zase kousalo, jako jsem popisoval jiný projekt na začátku tohoto vlákna. Prostě dynamický proměnný od displeje se zbouchnou se Stackem. V Setup toho není do Stacku moc k uložení, tak to projde ale v Loop je už spousta podprogramů, přerušení a stack nestačí. Když jsem natlačil ještě tabulky do Progmem, začalo to chodit ale nestabilně, občas restart. Podotýkám, že při překladu byl výpis 1420 byte RAM volných.
Pak jsem zjistil zajímavou věc, pokud se projekt přeloží v 64 bitovým IDE 2.x.x, tak není potřeba knihovna Adafruit_GFX.h. 8O Zase se uvolnilo pár byte v RAMce ale stejně není projekt stabilní, při rychlým otáčení n-codéru se občas zrestartuje. A to tam potřebuju ještě něco dopsat. Zkusil jsem projekt nahrát do vypůjčenýho Every a větší RAM všechny problémy zmizely.
Takže počkat na dodání Every a nebo to zkusit přenýst na Mini s 32U4 který má o 500 byte vetší RAM. Mám je k dispozici a jsou skoro o půlku levnější než Every.
Nojo, ale na 32U4 je I2C na stejnejch pinech jako INT0 a INT1 co potřebuju pro N-codér, sériová linka je taky trochu záhadná, USB je přímo v procíku a TTL serial je na pinech ale USART má být jen jeden. Takže kupu věcí přepsat a odzkoušet jak to vlastně maká....
Jirka

Proč mi nemůže všechno chodit hned ?!!

Uživatelský avatar
Pater71
Příspěvky: 7
Registrován: 07 čer 2023, 02:00

#34 Příspěvek od Pater71 »

Software moc nerozumím, ale když se mi bezdůvodně kousaly OLED displeje, tak chyba nebyla v programu. Pokud se nekousl program, ale jen displej, pomohlo mi přidat 10k pull-up odpory na I2C.

Uživatelský avatar
Celeron
Příspěvky: 16140
Registrován: 02 dub 2011, 02:00
Bydliště: Nový Bydžov

#35 Příspěvek od Celeron »

Tím to není, pokud je jednodušší program a méně float a long proměnných, tak je k dispozici více RAM a se stejným HW to maká. Však to tu již bylo Valdanem zdůvodněný, že knihovna displeje používá spoustu dynamických proměnných a že se to nakonec srazí se Stack na konci RAMky.

Ale utrpěl jsem vítězství nad tupou hmotou a to převedením na ProMicro 32U4 který má 2,5KB RAM, tedy o 500byte víc než 328P. Ale bylo to peklo hlavně protože I2C má na rozdíl od ProMini 328P na stejnejch pinech kde je INT0 a INT1 pro n-codér. Zkusil jsem použít alternativní piny I2C jak je popsáno na Arduino fóru pro Wire1. Kompilací to prolezlo ale I2C zůstávala tvrdošíjně na pinech 2 a 3. Tak jsem nakonec přerušení pro n-codér přesunul jinam, ještě že je na pinu 7 další vstup externího přerušení a n-codéru stačí int jen od jednoho kontaktu.
Pak jsem ještě vykoumal, že sériová linka se u 32U4 programuje podobně jako na Every, na USB se leze přes Serial a na TTL piny Tx a Rx přes Serial1. Nevím, jak to mají udělaný, vysílání jde na obou Serial najednou ale pokud se sejde příjem ze dvou linek, nějak se to podělá, nejspíš kvůli sdílení pouze jednoho USARTu.
Taky jsem se u té 32U4 potkal s zakousnutím při nahrávání do flash i když bylo nastavený správně Leonardo. 10 x se povedlo, pak už byl procík nedobytnej. Naštěstí se dá na netu nalýzt postup, jak dvojklikem na reset se nahodí na 8 sekund další COM bootloaderu, přes něj se nahraje pár řádků, třeba Blink a pak už to zase maká jak má. Taky to má dát dohromady nedostupnou 32U4 pokud se někdo sekne a zvolí špatnou desku, třeba ProMini. Mini, Micro, to se docela dobře splete nebo přehlídne.
Jirka

Proč mi nemůže všechno chodit hned ?!!

Uživatelský avatar
Crifodo
Příspěvky: 14471
Registrován: 11 říj 2005, 02:00

#36 Příspěvek od Crifodo »

V nuancích toho jakéhosi programu se nevyznám a v těch "projektech" už vůbec, ale když se to kouše po zatočení enkodérem, bude přidávání RAM spíš silové řešení než principiální, ne? Přerušení by po sobě mělo přece uklízet a případně nedovolit další přerušení. A zásobník si hlídat, asi jako PC zabučelo, že se nenechá uživatelem uhnat a přeplnit buffer.

Uživatelský avatar
Celeron
Příspěvky: 16140
Registrován: 02 dub 2011, 02:00
Bydliště: Nový Bydžov

#37 Příspěvek od Celeron »

Tys to koukám nečetl od začátku, jak Valdano spočítal, kolik knihovny OLED displejů používají dynamických proměnných. Od určité obsazenosti RAM, co se vypíše při kompilaci, začnou problémy. Nejdříve s tím rychlým točením n-codéru, pak se kouše nebo restartuje v určitých místech chodu programu a nakonec nejde vůbec. A taky záleží, kterej Oled displej SSD a knihovnu použiješ. SSD1306 je na Ramku míň náročnej než SH1107 i když mají stejnou rozlišovačku 128x64. Na Arduino fóru o tomhle problému taky pár lidí píše a doporučení je vždy přidat RAM nebo si napsat svoji obsluhu displeje, což by pro mě bylo práce na týdny.
Samozřejmě že mám při obsluze přerušení další přerušení zakázaný a globální proměnný jsou jen byte příznak kroku a směru, takže uklízet není co.
Jestli víš, jak vypsat při ladění, kde se pohybuje konec dynamických proměnných, jakej je rozsah haldy a kam dolezl stack, sem s tou informací. :)
Jirka

Proč mi nemůže všechno chodit hned ?!!

Uživatelský avatar
Valdano
Příspěvky: 695
Registrován: 01 led 2023, 01:00
Bydliště: Česká Lípa

#38 Příspěvek od Valdano »

Většina knihoven pro Arduino nepoužívá dynamickou alokaci paměti jelikož je to spíš problematické a pokud tedy používá třeba nějaký bafr tak jej má deklarován staticky viz třeba SoftwareSerial. Staticky definovaná velikost znamená, že je velikost pole bafru předem známá už při překladu. Vývojové prostředí pak celkovou velikost použité a zbylé paměti programu zobrazuje ve výpisu po provedení překladu programu.

Globální knihovna Adafruit pro OLED displeje ovšem pracuje z různými displeji a používá tak různé velikosti bafru pro vykreslování a zřejmě proto je to v ní řešeno přes dynamickou alokaci paměti voláním standardní funkce jazyka C malloc(velikost) přičemž se velikost vypočítává za běhu programu a dynamická alokace se neprojeví při překladu, ale až za běhu programu.

Kód: Vybrat vše

Adafruit_SH1106G display = Adafruit_SH1106G(128, 64, &Wire, -1);

... další globální deklarace ...

void setup() {

  Wire.begin();
  display.begin(0x3C,true);

  ... další kód v setup() ...
Volání display.begin je volání metody Adafruit_SH1106G::begin a uvnitř ní se volá metoda Adafruit_GrayOLED::_init a ta provádí dynamickou alokaci paměťového bafru voláním malloc(VELIKOST)

Kód: Vybrat vše

kde VELIKOST v bajtech = ((sirka + 7) / 8) * vyska;
1024 = ((128 + 7) / 8) * 64;
Funkce malloc interně sama kontroluje stav volné paměti a zjednodušeně řečeno pokud zjistí, že není k dispozici volný souvislý blok paměti o velikosti odpovídající hodnotě VELIKOST tak tato funkce vrací NULL a metoda Adafruit_GrayOLED::_init v takovém případě vrací false. Nicméně metoda Adafruit_SH1106G::begin na to nijak nereaguje a vrací true i když Adafruit_GrayOLED::_init vrátí false.

Předpokládám, že dynamická alokace uvnitř Adafruit_GrayOLED::_init se sice ještě podaří, ale později za běhu programu dojde k přetečení aktuálně používané části zásobníku do haldy a program pak následně zhavaruje.

Další možnost je ta, že je v programu nebo v některé z knihoven nějaká jiná záludná chyba a díky ní za určitých okolností dochází k hrábnutí někam do paměti, a to má za následek pád programu. Když se pak přeloží program pro jinou platformu nebo se změní pro tu samou platformu tak se může chyba zdánlivě ukrýt, protože hrabe do míst kde to při aktuálním rozložení v paměti prostě zrovna nevadí.

Zkuste si změřit volnou paměť za běhu programu viz níže zdroják

Pokud začne funkce freeRam() vracet záporné hodnoty tak to znamená, že halda a zásobník aktuálně používají zčásti stejný prostor paměti, a to je samozřejmě problém. Pokud to nastane tak pravděpodobně program dřív vytuhne než informaci o tom stihne odeslat na sériový port a možná i dříve než se v programu zavolá freeRam(). Pokud freeRam() proběhne a vrácená hodnota bude záporná tak se v příkladu níže rozsvítí LED signalizující problém ještě před odesíláním dat na sériový port.

V příkladu níže jsem k účelu signalizace aktuálního překrývání části haldy a zásobníku použil vestavěnou LED_BUILTIN. LED_BUILTIN může mít pro různé typy Arduina různé číslo, ale pokud máte ve vývojovém prostředí správně zvolenou desku, tak LED_BUILTIN bude odpovídat vestavěné LED, která by na příslušném Arduinu měla být standardně dostupná a pokud ji v programu nepoužíváte na nic jiného tak ji můžete pro tento účel použít a tím nemusíte používat výstup, na který by bylo potřeba připojovat externí LED s odporem.

Kód: Vybrat vše

#include <Wire.h>               
#include <Rotary.h>             
#include <si5351.h>               
#include <Adafruit_GFX.h>
#include <Adafruit_SH110X.h> 
#include <EEPROM.h>

#if (ARDUINO >= 100)
#include <Arduino.h>
#else
#include <WProgram.h>
#endif

unsigned long tick = 0;
int mem_stav = 0;

Adafruit_SH1106G display = Adafruit_SH1106G(128, 64, &Wire, -1);

... další globální deklarace a funkce ...

#ifdef __arm__
extern "C" char* sbrk(int incr);
#else
extern unsigned int __heap_start;
extern void *__brkval;
struct __freelist
{
  size_t sz;
  struct __freelist *nx;
};
extern struct __freelist *__flp;
int freeListSize()
{
  struct __freelist* current;
  int total = 0;
  for (current = __flp; current; current = current->nx)
  {
    total += 2;
    total += (int) current->sz;
  };
  return total;
}
#endif

int freeRam()
{
  int ram = 0;
#ifdef __arm__  
  ram = &ram - reinterpret_cast<char*>(sbrk(0));
#else
  if ((int)__brkval == 0) {
    ram = ((int)&ram) - ((int)&__heap_start);
  } else {
    ram = ((int)&ram) - ((int)__brkval);
    ram += freeListSize();
  };
#endif
  return ram;
}

void setup() {

  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);

  // stav volné paměti na počátku SETUP
  mem_stav = freeRam();
  if (mem_stav < 0) digitalWrite(LED_BUILTIN, HIGH);

  Serial.begin(9600);
  Serial.print(F("SETUP1 RAM = "));
  Serial.println(mem_stav, DEC);

  Wire.begin();
  display.begin(0x3C,true);

  // stav volné paměti v SETUP po volání display.begin
  mem_stav = freeRam();
  if (mem_stav < 0) digitalWrite(LED_BUILTIN, HIGH);
  Serial.print(F("SETUP2 RAM = "));
  Serial.println(mem_stav, DEC);

  ... další kód v setup() ...

  tick = millis();
}

void loop() {

  if ((millis() - tick) > 1000) {
    // stav volné paměti v LOOP cca jednou za sekundu
    mem_stav = freeRam();
    if (mem_stav < 0) digitalWrite(LED_BUILTIN, HIGH);
    Serial.print(F("LOOP RAM = "));
    Serial.println(mem_stav, DEC);
    tick = millis();
  };

  ... další kód v loop() ...

}
Naposledy upravil(a) Valdano dne 12 čer 2023, 21:53, celkem upraveno 4 x.

Uživatelský avatar
Celeron
Příspěvky: 16140
Registrován: 02 dub 2011, 02:00
Bydliště: Nový Bydžov

#39 Příspěvek od Celeron »

Díky za přínosné info. Příležitostně to vyzkouším, nejdříve na konci týdne. ProMicro taky není žádnej zázrak. Každý druhý až čtvrtý nahrání flash se kouše a pak je po USB nedostupnej a je potřeba udělat reset bootloaderu. Naštěstí to není tak složitý, jen je potřeba se s dvojklikem na pin reset trefit do správnýho okamžiku.
S Every žádnej problém není ale je za skoro dvojnásobek ProMini nebo ProMicro. Tak by bylo docela přínosný zjistit, co se v ProMini děje při spolupráci s Oled displejema v RAMce.
Jirka

Proč mi nemůže všechno chodit hned ?!!

Uživatelský avatar
Valdano
Příspěvky: 695
Registrován: 01 led 2023, 01:00
Bydliště: Česká Lípa

#40 Příspěvek od Valdano »

Nějak to tady usnulo. Už se za pomocí toho měření spotřeby paměti co jsem psal výše podařilo něco dalšího zjistit ohledně těch problémů s Arduino ProMini?

Uživatelský avatar
Celeron
Příspěvky: 16140
Registrován: 02 dub 2011, 02:00
Bydliště: Nový Bydžov

#41 Příspěvek od Celeron »

Zatím jsem se k tomu nedostal. Tam, kde byl s OLED problém, jsem dal Every a nebo 32U4. I těch 500 byte navíc 32U4 stačí na bezproblémový chod.

A je teplo a spousta jiných "venkovních" projektů. Tak se na to dostane až bude pršet nebo až bude zima. :)
Jirka

Proč mi nemůže všechno chodit hned ?!!

Odpovědět

Zpět na „Miniaturní počítače (Arduino, Raspberry a další)“