Problém s nastavením přesného času - čas běží rychleji

Ahoj, jsem amatérský programátor(začátečník) a našel jsem tento program na generování času. Když jsem vše zapojil, tak to běží správně, ale o 5 s rychleji než má. A nevím, jak mám toto upravit. Doufám, že mi někdo pomůže :slight_smile:.

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>
#include <stdio.h>

#define _s_A 2
#define _s_B 0
#define _s_C 6
#define _s_D 4
#define _s_E 3
#define _s_F 1
#define _s_G 7
#define _s_dot 5

const unsigned char segs] =
{
_BV(_s_A) | _BV(_s_B) | _BV(_s_C) | _BV(_s_D) | _BV(_s_E) | _BV(_s_F), //0
_BV(_s_B) | _BV(_s_C), //1
_BV(_s_A) | _BV(_s_B) | _BV(_s_D) | _BV(_s_E) | _BV(_s_G), //2
_BV(_s_A) | _BV(_s_B) | _BV(_s_C) | _BV(_s_D) | _BV(_s_G), //3
_BV(_s_B) | _BV(_s_C) | _BV(_s_F) | _BV(_s_G), //4
_BV(_s_A) | _BV(_s_C) | _BV(_s_D) | _BV(_s_F) | _BV(_s_G), //5
_BV(_s_A) | _BV(_s_C) | _BV(_s_D) | _BV(_s_E) | _BV(_s_F) | _BV(_s_G), //6
_BV(_s_A) | _BV(_s_B) | _BV(_s_C), //7
_BV(_s_A) | _BV(_s_B) | _BV(_s_C) | _BV(_s_D) | _BV(_s_E) | _BV(_s_F) | _BV(_s_G),//8
_BV(_s_A) | _BV(_s_B) | _BV(_s_C) | _BV(_s_F) | _BV(_s_G),//9

_BV(_s_A) | _BV(_s_B) | _BV(_s_C) | _BV(_s_E) | _BV(_s_F) | _BV(_s_G),    //A
_BV(_s_C) | _BV(_s_D) | _BV(_s_E) | _BV(_s_F) | _BV(_s_G),    //B
_BV(_s_A) | _BV(_s_D) | _BV(_s_E) | _BV(_s_F), //C
_BV(_s_B) | _BV(_s_C) | _BV(_s_D) | _BV(_s_E) | _BV(_s_G), //D
_BV(_s_A) | _BV(_s_D) | _BV(_s_E) | _BV(_s_F) | _BV(_s_G), //E
_BV(_s_A) | _BV(_s_E) | _BV(_s_F) | _BV(_s_G) //F

};

#define _ms(n) (17*n)

void wait(unsigned int a) //basic wait
{
volatile unsigned int b,c;
for(b=0;b!= a; b++)for(c=0;c!= 50;c++);
return;
}

unsigned char prescale=0;
unsigned char sec=0;
unsigned char min_1=0;
unsigned char min_10=0;
unsigned char hour_1=0;
unsigned char hour_10=0;
unsigned char show_t=0;

ISR(TIMER1_OVF_vect)
{
if(++prescale == 225){prescale = 0;sec++;};
if(sec>59){min_1++;sec=0;};
if(min_1>9){min_1=0;min_10++;};
if(min_10>5){min_10=0;hour_1++;};
if(hour_1>9){hour_1=0;hour_10++;};
if(hour_10>1 && hour_1>3){hour_1=0;hour_10=0;};

if(++show_t==4) show_t=0;
switch(show_t)
{
    case 0:    //show minutes
        PORTC = 0x04;
        PORTD = (~segs[min_1]);
        break;
    case 1:    //show 10 minutes
        PORTC = 0x08;
        PORTD = (~segs[min_10]);
        break;
    case 2:    //show hours
        PORTC = 0x10;
        PORTD = (~segs[hour_1]) & ~_BV(_s_dot);
        break;
    case 3:    //show 10hours
        PORTC = 0x20;
        PORTD = (~segs[hour_10]);
        break;
    default:
        show_t = 0;
        break;
}   
return;

}

#define B1() (bit_is_clear(PINB,3))
#define B2() (bit_is_clear(PINB,4))
#define B_WAIT 300

#define nop() asm volatile (“nop;”)

int main(void)
{

TIMSK = 0x04;
TCCR1B = 0x01;

DDRD = 0xFF;
DDRC = 0x3F;
DDRB = 0x00;
PORTB = 0xFF;

sei();
while(1)
{
    if(B1())
    {
        wait(_ms(B_WAIT));
        min_1++;
        sec=0;
    }
    if(B2())
    {
        wait(_ms(B_WAIT));
        hour_1++;
        sec=0;
    }
}

}

:arrow_right: administrator: přejmenováno z "problém s nastavením přsného času"

řekl bych, že problém bude v tom prescale, ale nevím jak se k tomuto číslu dopočítat…

Ta chyba to znamená předbíhání 5 sekund za den? To není moc.

Doplním zdroj: daqq.eu/index.php?show=prj_avr_clock_1

Procesor je ATmega8, krystal 14,74550 MHz. Předdělička je vypnutá (tj. hodiny jsou 14745500 Hz), časovač Timer1 běží s periodou 65536 (přerušení 224,998 Hz), se softwarovým dělením 225 je perioda 0,999993 Hz, což je dost přesné, v tom je software navržený dobře, tam problém není.

Problém je v hardware - tedy přesněji, krystal kmitá správně v povolených tolerancích, ale od výrobce bývá často přednastavený malinko rychlejší, aby se dal dokorigovat. Jestli se předbíhá o 5 sekund za den, lze přesnost dokorigovat kapacitním trimrem přiletovaným paralelně ke krystalu. Nebo lze zkušebně připojovat malé kondenzátory o velikosti jednotek až desítek pF.

ale já mám krystal 16 MHz a jde mi o to, jak mám vypočítat to dělení

už jsem na to přišel :slight_smile: je to 244, že?

a ještě bych se chtěl zeptat, co znamená tento řádek
#define _ms(n) (17*n)

proč je to vynásobené 17? a kde to pak používám?

Proč jsi to nenapsal hned že používáš krystal 16 MHz místo původního?

Dělení 244 dělá dost velkou chybu, minutu denně.

(17*n) je zkusmo nalezená korekce, aby ta funkce dělala prodlevu v milisekundách.

S takovým krystalem je situace o dost složitější, nestačí jen změnit dělicí konstantu. Máš 2 možnosti:
1.- použít zkrácený cyklus pro přerušení pomocí módu čítače s komparátorem (používá se i jiný vektor přerušení)
2.- nebo při přerušení čítat čas ve zlomcích

V prvním případě by se dal zkrátit cyklus na periodu 64000 - nastaví se do registrů OCR1AH a OCR1AL, v registru TCCR1A nastavit mód čítače 4, vektor přerušení TIMER1_COMPA_vect. Přerušení se bude vyvolávat s 250 Hz, konstanta pro prescale tedy bude 250. S tím módem ale nemám moc zkušenosti abych poradil kdyby to nejelo.

Druhý případ, počítání času ve zlomcích:

Krystal má 16 MHz, dělička 65536, přerušení je s frekvencí 244,140625 Hz, interval mezi přerušeními je 4,096 ms. Jedna z možností úpravy spočívá v tom, že prescale se změní z char na long, místo inkrementace se bude přičítat čas intervalu v mikrosekundách (tedy 4096) a přetečení nastane po překročení hodnoty 1000000 (tj. počet mikrosekund do sekundy). Namísto nulování registru se musí hodnota 1000000 odečíst, protože zůstane zlomková část. Posun sekund bude nastávat většinou po 244 přerušeních, ale občas po 245 přerušeních.

unsigned long prescale=0;
....
ISR(TIMER1_OVF_vect)
{
    prescale += 4096;
    if (prescale >= 1000000) { prescale -= 1000000; sec++; }

moc děkuji za informace a ještě nechápu tuto část základního časování

void wait(unsigned int a) //basic wait
{
volatile unsigned int b,c;
for(b=0;b!= a; b++)for(c=0;c!= 50;c++);
return;
}

tuto fci volám při zmáčknutí tlačítek? ale nechápu proč je tam zrovna 50?

To jsou zas jen experimentálně zjištěné hodnoty, je to jen zpožďovací smyčka. Volá se při stisku tlačítka kvůli tomu, aby se udělala prodleva asi 0,3 sekundy. Jednak se tak ošetří zákmity tlačítek a jednak to slouží jako opakování při držení tlačítka (čísla naskakují rychlostí 3 za sekundu).

ale tak to je ošetřeno v tomto řádku ne?
#define B_WAIT 300