Stránka 1 z 13

Hodiny a časování

Napsal: 10 říj 2016, 10:20
od daton
zdravím všechny přítomné. Mám dotaz na kod. zkouším si různé variace hodin odvozených z taktu arduina. Z programu měřiče superkapacitorů jsem vykuchal zajímavé hodiny, které jsou asi velmi přesné kod zde:

Kód: Vybrat vše

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

// nastavena adresa 0x3F a poradi pinu displeje na prevodniku: en,rw,rs,d4,d5,d6,d7,bl,blpol
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
unsigned long pocatek, doba;    // pomocne mereni casu
unsigned long form = 86400000;   // doba formovani v ms

void setup() {
   lcd.begin(16, 2);            // zavedeni grafiky do RAM displeje
   lcd.clear();
   pocatek = millis();
   }

void loop() {

doba = millis() - pocatek;       
    if ((doba % 1000) == 0) {
    lcd.clear();
    lcd.print("Hodiny");
    long pom;                                   // vypis casu
    lcd.setCursor(0, 1);
    if (pom < 10) {
    lcd.print("0");}
    pom = (long(doba / 3600000));
    lcd.print(pom);
    lcd.print(":");
    pom = long(doba / 60000) % 60;
    if (pom < 10) {
    lcd.print("0");
    }
    lcd.print(pom);
    lcd.print(":");
    pom = long(doba / 1000) % 60;
    if (pom < 10) {
      lcd.print("0");
    }
    lcd.print(pom);
    }
    

U tohoto kodu se mi moc líbí ta podmínka na kterou jsem se ptal v jiné části
a to ((doba % 1000) == 0). Hodiny v originále se nulovaly po dosažení unsigned long 3600000 taktu a to jsem změnil na 86400000 což by mělo nulovat po 24 hod. Problém je že vše je odvozeno od počátku který se nastavuje jednou v setupu a tedy když přeteče vnitřní počet po cca 50 dnech ta to může být uprostřed dne a tím dojde k totálnímu chaosu tedy hodiny po 50 dnech zkolabují.
Tak jsem to předělal do této verze kdy po cca 50 dnech zkolabuje jen milisekunda což je nic:

Kód: Vybrat vše

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

// nastavena adresa 0x3F a poradi pinu displeje na prevodniku: en,rw,rs,d4,d5,d6,d7,bl,blpol
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
unsigned long pocatek, doba;    // pomocne mereni casu
unsigned long form = 86400000;   // doba formovani v ms
int hod;
int minut;
int sec;

void setup() {
    lcd.begin(16, 2);            // zavedeni grafiky do RAM displeje
    lcd.clear();
    pocatek = millis();

}


void loop() {
   
       doba = millis() ;   
    if ((doba - pocatek) >= 1000) {
      pocatek = millis();
      sec = sec +1;}
      if ( sec>59) {
        sec = 0;
        minut = minut + 1;}
      if ( minut >59) {
        minut = 0;
        hod = hod + 1;
       if (hod >23) {
        hod = 0; 
      }   
      }
       lcd.setCursor(0, 1);
       if (hod < 10) {
      lcd.print("0");}
      lcd.print(hod);
       lcd.print(":");
       if (minut < 10) {
       lcd.print("0");}
      lcd.print(minut);
       lcd.print(":");
       if (sec < 10) {
       lcd.print("0");}
      lcd.print(sec);
      }
      
HOdiny fungují ale co je pro mne zatím ne uplně pochopitelné je, že zde již nelze zařadit tu podmínku ((doba % 1000) == 0) protože s ní to prostě nechodí. Vysvětluji si to tak že tento kod ač se nezdá je delší a prostí to správnou číslici někdy přeskočí a tím se jakoby přeskočí hodnota přiníž vychází modulo které se rovná nule. Proto je zde podmínka ((doba - pocatek) >= 1000) která to ošetří a hodiny fungují ale již zřejmě méně přesně protože může dojít k přetečení 1000.
No a otázkou je proč se to tak děje. Proč u prvního příkladu to jde naprosto perfektně a u druhého už program zjevně tak ladně nefunguje i když je funkční i po cca 50 dnech.

Re: Hodiny a časování

Napsal: 10 říj 2016, 21:09
od Vladimir66
Ahoj,

zkousel jsem ten originalni kod a dokonce mi to vypisuje 2x za sekundu. (pouzivam Serial.print)
kdyz jsem dal do loopu delay(1) , tak to vypisuje 1x za sekundu. a kdyz jsem dal delay(3) tak jsou tam velke diry ve vypisu casu.
cimz jsem nasimuloval situaci, kdy millis() pri testovani nekonci na nulu ale "proskoci" behem delaye.
vsimnul jsem si, ze v tom originalnim vypisu se "pocatek" nemeni, kdezto v tvem kodu ho aktualizujes millisem.
bez chybejiciho setupu a inicializace promennych tezko presne testovat. doplnil jsem si podle uvazeni.

zkus otestovat v tvem programu situaci, kdy vynechas vypis hodin a minut a uvidis, jestli sekundy bezi v poradku.

nebo zkus tu "dobu" testovat na vice mistech/casteji. v nejhorsim pripade tak obcas vynechas jeden cyklus zapisu na LCD.

treba ti poradi zkuseni bardi..
-V66

Re: Hodiny a časování

Napsal: 11 říj 2016, 07:13
od daton
Dopnil jsem setup u obou příkladů. U původního kodu to bylo dělané pro par hodin (mě to vždy skončilo po 2hod 14 min). Natáhnul jsem nulování na 24 hod a chodilo to . MOžná je to rychlejší proto že jsem tam vynechal čtení vstupu co tam měl p.Černý implementované.
Mě jde hlavně o to abych pochopil proč se to děje a co tedy ovlivňuje "hladký chod kodu".

Re: Hodiny a časování

Napsal: 11 říj 2016, 08:57
od fulda
Ahoj,

moc jsem to nečetl, ale to je přece naprosto primitivní počítání času.
Vychází z toho, že millis() se zvětšuje po milisekundách.
Pokud neprochází tvúj kód podmínkou častěji než 1 za ms a zároveň vnitřek netrvá déle než 1 ms, tak to nefunguje.
V prvním případě proto, že prošvihneš podmínku a ve druhém proto, že projdeš vícekrát.


Pokud chceš tuhle slabinu odstranit, tak se to dělá zhruba takto:

Kód: Vybrat vše

void setup() {
   tik = millis() + 1000;  // další tik bude za vteřinu, lze použít i bez toho +1000, pak bude okamžitě
}

void loop() {
  if tik <= millis() {
    tik += 1000; // další tik bude za vteřinu od předchozího
    // tady se dělá co je potřeba
  }
Tenhle kód má jedno slabé místo a to je čas, kdy přeteče unsigned int (from 0 to 4,294,967,295 (2^32 - 1)). Je potřeba počítat s tím, že 65000 + 1000 = 465 na unsigned int16.

Re: Hodiny a časování

Napsal: 11 říj 2016, 11:20
od daton
Jo to jsem zkoušel trochu jinak ve druhém příkladu ale šlo mi především o to proč to v prvním původním kodu chodilo s tou podmínkou co tam je a v druhém mém podání to nechodí a je třeba podmínku rozšířit. Předpokládám že je to pro náročnost kodu který bude pomalejší ale proč když oba kody jsou podobně dlouhé a v podstatě dělají totéž.

Re: Hodiny a časování

Napsal: 11 říj 2016, 12:56
od pgerla
Protože se vykrádáním cizího kódu programovat nenaučíš. Jazyk Wire není strojový kód.

Je to marné, je to marné, je to marné

Re: Hodiny a časování

Napsal: 11 říj 2016, 14:33
od fulda
daton píše:Jo to jsem zkoušel trochu jinak ve druhém příkladu ale šlo mi především o to proč to v prvním původním kodu chodilo s tou podmínkou co tam je a v druhém mém podání to nechodí a je třeba podmínku rozšířit. Předpokládám že je to pro náročnost kodu který bude pomalejší ale proč když oba kody jsou podobně dlouhé a v podstatě dělají totéž.
OK, tak jsem si přečetl tvůj kód a je v něm spoustu drobných chyb.

První věc je, že píšeš: "nelze zařadit tu podmínku ((doba % 1000) == 0) protože s ní to prostě nechodí." Vysvěltení je tak prosté, jak je přesná provedená analýza: "máš to rozbité"

Vrátíme se k tomu co jsem napsal - jsi si u toho svého kódu jistý, že jsi dodržel podmínku, že "na prázdno" to projde častěji než jednou za milisekundu? (Podle Kotelníka raději dvakrát?)
A potom, pokud to vstupuje dovnitř, máš zajištěno, že to naopak trvá déle než milisekundu?

Tak a teď k chybě která zásadně ovlivňuje nepřesnost tvého kódu. Jedná se o konstrukci:

Kód: Vybrat vše

    if ((doba - pocatek) >= 1000) {
      pocatek = millis();
Za předpokladu, že jsi se do smyčky dostal o něco později, řekněme po čase 1002ms, tak to sice nevadí a program správně přičte sekundu. ALE tím že přiřadíš pocatek = millis();, tak to považuješ za správný výchozí stav od kterého začneš odpočítávat další sekundu, takže ona nastane za 1000ms od předchozí, která trvala 1002, takže hodiny nabírají 2ms zpoždění. Správné by bylo, aby jsi čekal jen 998ms a hodiny to dohnaly.

Proto já používám konstrukci, která ve tvojem označení bude: pocatek += 1000;, tedy od počátku se vzdaluji o dalších 1000ms a nikoli zahajuji nový počátek.

Pochopil jsi to??

A aby situace nebyla jenom růžová, tak se musíme podívat co se děje na konci.
Pro jednoduchost budeme uvažovat, že počítáme v unsigned int16 - tedy 0-65535, zároveň počátek nastal opravdu v čase 0.
Takže proběhlo 65sec a máme: pocatek = 65000 a zároveň doba=65000. Pokud provedu moje přičtení, tak pocatek=65000+1000 = 464, celé se to stane strašně rychle, takže další průchod podmínkou máme: doba(65000) - pocatek(464) = 64536 a to je větší než 1000 a pro další průchod to stále bude větší.
Takže moje konstrukce na konci velikosti datového typu, pokud není ošetřená, tak spustí hodiny maximální rychlostí.
Jak koukám na tvojí konstrukci, tak ta to udělá taky, takže je to vlastně jedno.

Pochopil jsi i tohle??

Re: Hodiny a časování

Napsal: 11 říj 2016, 14:35
od fulda
pgerla píše:Je to marné, je to marné, je to marné
Správně česky je to TAKHLE :mrgreen:

Re: Hodiny a časování

Napsal: 11 říj 2016, 17:30
od daton
Ahoj
Tak první část jsem pobral a ano v tomto případě máš pravdu že pokud testuji podmínku a přeskočí se mi například na 1002 a ne 1000 tak ano přibudou dvě milisekundy a tvůj algoritmus řeší její opravu tím že v dalším nepřičte 1000 ale jen 998. TO můj algoritmus nedělá a tedy pokud program celkově zpomaluje průchod smyčkou tak bude hodiny pomalu zpomalovat, což není nijak dobře a je to právě ten základ toho proč jsemzde psal proč se kod od Černého nezpomaluje a ten můj ano. Ten jeho se musí trefit do 1000 protože modulo netoleruje rozptyl a on se trefí kdežto u mne se netrefí prostě ten kod není tak čistý aby byl splněn Nymqvistův teorem o vzorkování (mimochodem z výšky si to pamatuji ;) a možná ještě něco dalšího :D ).
Ale ve druhé části si pochopením tvého vysvětlení nejsem zcela jist. U tvého algoritmu se čísla stále přičítají a to je se domnívám ta chyba, které jsem se chtěl vyvarovat. Dojde totiž k tomu co popisuješ, ale u mého algoritmu se čísla nepřičítají jen se testuje jestli je načtené číslo vyšší o tísíc než to které se načetlo před sekundou (která má uplynout).
U mého algoritmu je taky chyba ale ta způsobí spíše zastavení hodin. Mohlo by se to opravit takto:

Kód: Vybrat vše

doba = millis() ;   
    if ((doba - pocatek) >= 1000) {
      pocatek = millis();
      sec = sec +1;}
      if ((pocate -doba) > 65000) { pocatek = millis();}
      if ( sec>59) {
        sec = 0;
Tedy už nemůže dojít k tomu co popisuješ protože kdy dojde k přetečeni pro příklad uvažujme tech 65000 pak to bude takto načte se takto doba -počátek >=1000 v nejhorším tam bude např 455 - 65545 >=1000 podmínka se nesplní ale zafunguje druhá podmínka že 65545-455>65000 no a tím se načte okamžitý počátek a v dalším taktu se testuje už 1460-460>=1000 no a vše je OK.
PS s tím Tomem Marným se jdi bodnout.... :D

Re: Hodiny a časování

Napsal: 11 říj 2016, 19:26
od Vladimir66