Při nulování TCNT běží čítač rychleji, simulace ale funguje

Dobrý den,
snažím se naprogramovat binární hodiny na procesor Mega8A
program by se měl řídit 16bit čítačem a při každém přerušení jej nulovat,
Simulace funguje jak má, každou sekundu (cca, přerušení není přesně dopočítáno).
Na procesoru se však chová zcela jinak, čas najednou opravdu letí a jediné, co je člověk schopen počítat jsou hodiny.
Když se však nulování TCNT vynechá, program funguje ± normálně, jen nedodržuje 1s ale o něco víc, než přeteče celý registr čítače.
Nejspíš mi v kódu někde něco chybí, ale já už to asi nejsem schopen doplnit, proto prosím kohokoliv, kdo by tušil,co je špatně o radu.
(používám AVRstudio)
Děkuji

.NOLIST	
.INCLUDE "m8def.inc"	
.LIST	
				 
.ORG    0   
 RJMP    RESET   
.ORG    OC1Baddr
; ============================= 
;    delay loop generator 
;     49 cycles:
; ----------------------------- 
; delaying 48 cycles:
          ldi  R17, $10
WGLOOP0:  dec  R17
          brne WGLOOP0
; ----------------------------- 
; delaying 1 cycle:
          nop
; ============================= 
ldi tmp,0
out TCNT1L,tmp
ldi tmp,0
out TCNT1H,tmp
 
 rjmp zobrazeni

.DEF seco	=	R1
.DEF mino	=	R2
.DEF hro	=	R3
.DEF sys	=	R4
.DEF tmp	=	R16
.DEF hr		=	R20
.DEF mn		=	R21
.DEF sc		=	R22	
.DEF cycs	=	R23
.DEF cycm	=	R24
.DEF cych	=	R25


reset:

;******************
LDI tmp,LOW(RAMEND)
OUT SPL,tmp	
LDI tmp,HIGH(RAMEND)
OUT SPH,tmp
;******************


LDI tmp,255
OUT DDRB,tmp	
OUT DDRC,tmp
OUT DDRD,tmp

clr cycs
clr cycm
clr cych
clr sc
clr mn
clr hr
clr mino
clr seco
clr hro


start:

;******************

;nastaveni citace
LDI     R19,3    		 
OUT     TCCR1B,R19       
LDI     R19,0b00100011   
OUT     OCR1BL,R19      
LDI     R19,0b11110100   
OUT     OCR1BH,R19       
LDI     R19,8            
OUT     TIMSK,R19        

;*****************
sei

wait:
cpi tmp,2
brne scnd
rjmp wait

zobrazeni:
out portB,seco
out portD,mino
out portC,hro
reti

scnd:
ldi tmp,2
mov seco,cycs
add seco,sc
cpi cycs,0b1001
breq scnd2
inc cycs
rjmp wait

scnd2:
ldi cycs,0b10000
add sc,cycs
clr cycs
cpi sc,0b1100000
breq sres
rjmp wait

sres:
clr sc
rjmp min

min:
mov mino,cycm
add mino,mn
cpi cycm,0b1001
breq min2
inc cycm
rjmp wait

min2:
ldi cycm,0b10000
add mn,cycm
clr cycm
cpi mn,0b1100000
breq mres
rjmp wait

mres:
clr mn
rjmp hrs

hrs:
mov hro,cych
add hro,hr
ldi tmp,0b100101
cp 	hro,tmp
breq hres
cpi cych,0b1001
breq hrs2
inc cych
rjmp wait

hrs2:
ldi cych,0b10000
add hr,cych
clr cych
cpi hr,0b100100
rjmp wait

hres:
clr hr
clr cych
clr hro
clr tmp
rjmp wait


ret

delay:
; ============================= 
;    delay loop generator 
;     400000 cycles:
; ----------------------------- 
; delaying 399999 cycles:
          ldi  R17, $97
WGLOOP4:  ldi  R18, $06
WGLOOP1:  ldi  R19, $92
WGLOOP2:  dec  R19
          brne WGLOOP2
          dec  R18
          brne WGLOOP1
          dec  R17
          brne WGLOOP4
; ----------------------------- 
; delaying 1 cycle:
          nop
; ============================= 
 

 
ret

:arrow_right: administrator: přejmenováno z "Splašený čítač"

Asi máš špatně přerušení. Bude tam bordel se stackem. Do tabulky vektorů si dej pouze volání a obsluhu proveď celou vkuse. Proč tam máš v přerušení tu brzdu?.

Ovšem ve tvém případě by byl vhodnější mód timeru 14 nebo 15, kte si napevno nastavíš TOP a nemusíš se už o timer starat. Reakce na přerušení je totiž různá a bude ti to dělat nepřesnost v čase.

Předělal jsem přerušení tak, aby se jen přesměroval a vše udělal najednou.
Změna není žádná.
jestli se nepletu, tak módy 14 a 15 jsou závislé na externím oscilátoru, já bych to chtěl řešit pouze s interním.
brzda je tam právě z důvodu dorovnání času, přerušení pak dá dohromady 64 cyklů, což je právě ten jeden, který čírači chybí do přesné sekundy.
doufám, že je to řešitelné, děkuji
p0o9I

Všechny módy jsou závislé na zdroji hodin a je úplně jedno, který to je. Pokud pracuješ s interním oscilátorem, tak si na nějaké cykly hrát nemusíš. Má přesnost 10% a lítá jak splašená koza. Když to pověsíš na čítač, budeš mít stabilní 2 čísla (když budeš mít štěstí). To je pro hodiny naprosto k ničemu. Bez krystalu provozovat hodiny je nesmysl. Tedy alespoň pokud nejsou řízeny jiným referenčním zdrojem.

V přerušení používáš registr pojmenovaný tmp. Na začátku si ho ulož a na konci vyzvedni (push, pop).
Jakým mechanismem zjištujěš, kdy přičíst vteřinu?

díky, přidělám tedy k čipu nějaké ty periferie.
co se sekund týče, ty jsou hlídány právě registrem tmp.
Smyčka wait kontroluje, jestli je tmp,2 , pakliže ne, odbočí do cyklu pro výpočet hodin, na jehož začátku se registr tmp nastavuje právě na hodnotu 2. V přerušení se pak nuluje, díky čemuž se přičte další sekunda, popř, víc a čeká na zobrazení při dalším přerušení.

Připomínáš mi spolužáka. Zatímco běžně člověk napsal program na 200-300 řádek, on to dokázal na 1000! :smiley:

Jsou ta kvanta proměnných nezbytná? Nestačily by jen 3 registry (h, m, s) + tmp(možná dva)?
V běžném podání jsou hodiny vcelku jednoduché i pokud mají být čísla jako BCD (11 je uloženo jako 0x11, nikoli jako 0x0B).

  1. nová vteřina? Ne => zpět na 1), jinak 2)
  2. vteřina + 1
  3. DA(vteřina)
  4. vteřina = 0x60? Ne => konec, jinak 5)
  5. vteřina = 0, minuta +1
  6. DA(minuta)
  7. minuta = 0x60?..

    konec: zobrazit a zpět na 1)

DA(číslo) je tzv. dekadická korekce (upraví číslo do BCD po inkrementaci):
dolní 4 bity čísla = 0xA? Ne => nic, Ano => číslo + 6

Odvození časování od timeru je cesta správná(zmíněné módy 14 a 15 by byly velmi výhodné), jen tady by ses asi obešel i bez přerušení. Stačí testovat jeho příznak, ten posléze vynulovat a provést výpočet. Tvůj program stejně nic jiného nedělá.
Ovšem je pravda, že se v tom ztrácím. Třeba části scnd a scnd2 (analogicky i další). To je nějaká forma dekadické korekce?

Když jsi tedy zakomentoval:
“out TCNT1L,tmp” a “out TCNT1H,tmp” a timer jel až do konce tak to chodilo? Nezkoušel jsi mód CTC? To je totiž to, co jsi na začátku popsal akorát bez toho zpoždění.
To zpoždění několika taktů v přerušení je zbytečné pokud neresetuješ děličku timeru. On totiž začne od nuly, ale v děličce nula není a to zpoždění tedy nemá takový vliv, jaký bys předpokládal.
Buď tedy ještě potřebuješ zařídit reset děličky, nebo mít timer na plné rychlosti a mít ještě jednu proměnnou, kde si budeš počítat, kolikrát timer přetekl a když dojde na správné číslo, tak přidáš vteřinu.
Když ti například pojede timer na plné frekvenci 8MHz a nastavíš si TOP na 63999 (dékla timeru je 64000), tak přeteče za vteřinu PŘESNĚ 125x a nemusíš řešit nějaké takty, o které ti to teď nevychází.

Trochu jsem prolistoval datasheet a zistil jsem, že TOP je řízen registrem OCR1A, nikoliv mnou použitým OCR1B, zaměnil jsem je a teď již funguje časování.

scnd a scnd2 je opravdu forma korekce pro binární hodiny typu 6 sloupců,
2 sl. s, 2 sl. m a 2sl. h.
V této verzi kódu nepracují korektně, špatně zobrazuje při přechodu na minutu.
Když ale pracují korektně, hlídají, aby v prvním sloupci nebylo více jak 9 (0b1001), jakmile se této hodnoty dosáhne, přijde na řadu scnd2, kde se nastaví pracovní registr sc(desítky sekund…) a vynuluje pracovní registr cycs(jednotky sekund).
Jakmile se v druhém sloupci (sc) dosáhne hodnoty 6(0b1100000)
přeskočí na sres, kde se vynuluje i sc a přičte si minuta.
Doufám, že je to pochopitelné.

Dekadická korekce je pro mě novinka.
zkoušel jsem ji najít v mých materiálech, ale nepochodil jsem,
jedná se o jazyk C?
Ten bohužel neumím.
Děkuji

Já zas umím asm sotva na dostatečnou, ale u problémů s hw nebo algoritmem to často nevadí :slight_smile:.
Dekadická korekce je obecný termín z výpočetní techniky. Ovšem stará x51 na to má přímo instrukci :slight_smile:.
Chceš-li ušetřit místo a mít dekadické číslo o 2 cifrách v 1 8bit registru, může být uloženo jako BCD (desítky v horních 4 bitech, jednotky v dolních). Tedy 11 je 0x0B, ale v BCD je to 0x11.
Problém je, když chceš s takovým číslem počítat. Když máš devět (0x09) a přičteš 1, vyjde 10 (0x0A). Jenže teď už BCD uspořádaní neplatí, správně by bylo 0x10. Proto provedeš dekadickou korekci: Je dole “A”? => přičtu 6. Výsledek je 0x10. Hurá, jsme tam, kde jsme chtěli. Nepotřebuješ hejno registrů, stačí jen ten jeden, na kterém vždy po inkrementaci tu korekci provedeš :wink:. Patrně myslíš to samý, jen to máš programově trochu oklikou :smiley:.

Tím, že máš nepřesnost několik tiků (neupravil jsi timer na chod bez děličky a co jsem odhadl, tak jedeš na 8MHz), bude to dělat tak 1s/2dny.
Ta chyba je srovnatelná s přesností krystalu.
Při 8MHz a 30ppm je tolerance 240 tiků (jesli jem pochopil správně výpočet absolutní přesnosti z ppm).

1s/2dny není moc přívětivé číslo.
Patrně tedy musím připojit přesný hodinový krystal a řídit jím čítač.
Pak ale nemám dostatek pinů (pakliže nechci zakázat reset, ale jelikož mám seriový programátor tak nechci) pro připojení co pin to LEDka.
Musím tedy použít multiplexní, nebo souřadnicový systém, ten však nějak již nedokážu domyslet, takže asi zůstanu u multiplexu

Ta jedna sekunda za 2 dny je myšlená jen logická chyba způsobená programem za předpokladu přesnýho krystalu. Krystal sám má určitou toleranci, bude to ještě horší :wink:
Kdybys tedy napsal program aby počítal přesně (viz. 4 příspěvky nahoru), tak s krystalem 8MHz 10ppm máš odchylku 80 taktů/s. Odchylka 1s tedy vznikne za 8e6/80 = 1e5s = 28h. A 10ppm je už dost přesnej krystal.

Ovšem moc doslova mě neber. Na farnellu totiž mají krystal 32,768kHz, 0.034ppm. To by znamenalo chybu 0.0327680.034 tiku za vteřinu. Chyba 1s by tedy vznikla za 32768/(0.0327680.034) = 29e6 vteřin = 340 dní.
Nevim, jesli je to opravdu tak přesný, nebo to počítám blbě.

předpokládám správně, že těch 0.034ppm je Frequency stability?

Ve vyhledávači to mají jako přesnost frekvence. Když si to ovšem otevřeš, tak zjistíš, že tam maj blbosti. Ve skutečnasti má 20ppm. Těch 0.034 je konstanta u jakési kvadratické křivky. Nevím, co to znamená.

Oni krystaly a oscilátory obecně mají hejno parametrů. Přesnost frekvence, stabilitu, teplotní drift, stárnutí… Ta krátkodobá stabilita by tě u hodin snad trápit neměla.