Sida 2 av 4
Postat: 20 januari 2008, 23:17:38
av chrille112
Nu har jag fått igång tmr1, och den verkar gå mer exakt, men är fortfarande efter några sekunder varje minut. Så här gör jag nu:
Prescaler 1:8
Kod: Markera allt
void interrupt() {
T1 = -2500000000;
if(ms==10)
{
timeSECOND++;
ms=1;
}
else
ms++;
PIR1.TMR1IF = 0;
}
void main() {
ms=1;
INTCON.GIE = 1; //enable Interrupt
INTCON.PEIE = 1; //enable interrupt
T1CON = 0b00110001; // 1/8 prescaler
PIE1.TMR1IE = 1; // Interrupt bit Timer1
PIR1.TMR1IF = 0; // clear FLAG
T1 = -2500000000;
Skulle gärna vilja köra "free-running", men förstår inte hur man får till det i praktiken...

Postat: 20 januari 2008, 23:24:03
av sodjan
> och den verkar gå mer exakt,
TMR1 går *exakt* lika "exakt" som TMR0.
> Skulle gärna vilja köra "free-running", men förstår inte hur man får till det i praktiken...
Du skulle ju kunna läsa mitt föregående inlägg, men det är kanske
roligare att skriva nya...
Sen, om det ska vara någon mening alls att posta kod, så bör du nog
kommentera och tala om vad det är du gör på varje rad. T.ex
"T1 = -2500000000;" är inte så där direkt helt uppenbart för mig.
Vad är "T1" ? Och vad står "-2500000000" för ? Hur har du kommit fram
till (räknat ut) det värdet ??
Postat: 20 januari 2008, 23:51:52
av chrille112
>TMR1 går *exakt* lika "exakt" som TMR0.
Jag menade att koden jag skrivit nu verkar vara mer exakt än den som jag gjorde förra gången
>>Du skulle ju kunna läsa mitt föregående inlägg, men det är kanske
roligare att skriva nya... Smile Smile
Jag läste din inlägg, men jag tycker att detta borde gå att göra utan en kristall till?
Jag har läst det här inlägget, det är därifrån jag fått kod
http://www.mikroe.com/forum/viewtopic.php?t=6067
>>Vad är "T1" ?
unsigned T1 absolute 0x0E;
T1 is same address as TMR1L and TMR1H
>>Och vad står "-2500000000" för ? Hur har du kommit fram
till (räknat ut) det värdet ??
Kommer inte riktigt ihåg hur jag fick fram det, så jag gick in på länken och räknade om:
Om jag vill ha interrupt varje 1000 µS och kör prescaler 1:8 så räknar jag:
1000/1.6 = 625;
Nu har jag en interrupt på varje 1 ms, ökar en räknare, och när räknaren är uppe i 1000 så nollställer jag och ökar sekunder.
Med denna metoden har jag bäst resultat hittils, runt 1 sekund fel efter 5 minuters körning. Kan detta vara lösningen, eller vad tror ni? Låter den stå på under natten och hoppas på att den går lika rätt imorgon

Postat: 21 januari 2008, 00:32:40
av sodjan
1 sek på 5 min är ca 3.300 ppm fel.
En standard kristall bör ligga på ca 50 ppm, så du ligger
väl över 50 gånger sämre än vad du borde göra.
> unsigned T1 absolute 0x0E;
> T1 is same address as TMR1L and TMR1H
TMR1L = h'0E', TMR1H = h'0F'. Jag förstår inte vad de menar med "same address" !?
> Kommer inte riktigt ihåg hur jag fick fram det,
Precis !! Gissa nu varför man *SKA* kommentera sin kod !!
> Kan detta vara lösningen, eller vad tror ni?
Det verkar ju inte så, med tanke på hur fel det fortfarande går.
Det är också lite svårt att kommentera metoden bättre, utan att veta
var -2500000000 kommer ifrån.
Postat: 21 januari 2008, 07:25:02
av Icecap
Då du FORTFARANDE sätter TMR1-värdet till ett visst värde är det FORTFARANDE fel väg att gå...
Och att du kan peta in ett 40-bitars värde (-2500000000) i ett 16-bitars register verkar ganska häftigt, det är mer än jag klarar av.
Nåväl, du har ett 20MHz kristall som klockbas, detta ger 5MHz som systemklocka.
Kod: Markera allt
void interrupt()
{ // Rate = 104857,6µs
static unsigned long Sum; // Must be a big one
PIR1.TMR1IF = 0; // Acknowledge interrupt
Sum += 1048576; // Add the current part
if(Sum >= 10000000)
{
timeSECOND++;
Sum -= 10000000;
}
}
void main()
{
INTCON.GIE = 1; //enable Interrupt
INTCON.PEIE = 1; //enable interrupt
T1CON = 0b00110001; // 1/8 prescaler
PIE1.TMR1IE = 1; // Interrupt bit Timer1
PIR1.TMR1IF = 0; // clear FLAG
while(1)
{
}
}
Då får du en klocka som är långtidsstabil.
Ett alternativ är självklart att välja ett kristall som är "binärt" som sodjan skriver om, ytterligare ett alternativ är att använda Timer2 där du kan ställa vad den ska räkna till innan den ska flöda över och ge interrupt. Man kan även ställa prescale och postscale och det är ju perfekt till detta.
Postat: 21 januari 2008, 08:01:59
av chrille112
Tack för koden Icecap! Jag ska testa den det första jag gör ikväll.
Hur kom du fram till Rate = 104857,6µs?
Nu när jag jämför klockan så går den 1 min 49 sekunder före. Inte bra, men helt klart mitt bästa resultat hittils

Postat: 21 januari 2008, 08:57:54
av Icecap
5MHz delad med 65536 (en 16-bit räknare) = 0,0131072s (65536/5000000) intervall = 13107,2µs mellan varje puls. Du har en prescaler på 8 inkopplat = 13107,2µs * 8 = 104857,6µs mellan varje puls.
Skyller på tidig morgon efter en natt med en snart-2-månaders-tjej om jag har räknat lite fel...
Postat: 21 januari 2008, 16:47:40
av bos
sodjan skrev:TMR1L = h'0E', TMR1H = h'0F'. Jag förstår inte vad de menar med "same address" !?
Jag skulle gissa på att T1 är ett 16bit-värde som innehåller TMR1H:TMR1L, men "same address" är äckligt luddigt formulerat isåfall.
Postat: 21 januari 2008, 17:22:26
av sodjan
Jo, det kan det vara, men det är inget som går att adressera direkt
i processorn. Så på något sätt måste värdet delas upp på "H" resp "L"
och skrivas via flera register accesser. Tyvärr däöljer det bara ytterligare
vad som igentligen händer i koden.
Postat: 21 januari 2008, 17:45:20
av chrille112
Icecap: Det är något som inte stämmer med din kod. Först så går klockan väldigt snabbt, tippar på att sekunderna går 10 gånger snabbare än vad de borde. Sedan, efter en varierande tid runt 2-5 minuter så börjar klockan gå i vad som verkar vara rätt fart.
Jag kan filma och lägga upp om ni vill se?
Mycket tacksam för din hjälp!
Postat: 21 januari 2008, 18:41:09
av Icecap
Flytta då ut 'Sum' till main och nolla den:
Kod: Markera allt
unsigned long Sum; // Must be a big one
void interrupt()
{ // Rate = 104857,6µs
PIR1.TMR1IF = 0; // Acknowledge interrupt
Sum += 1048576; // Add the current part
if(Sum >= 10000000)
{
timeSECOND++;
Sum -= 10000000;
}
}
void main()
{
Sum = 0; // Make it start from the beginning
INTCON.GIE = 1; //enable Interrupt
INTCON.PEIE = 1; //enable interrupt
T1CON = 0b00110001; // 1/8 prescaler
PIE1.TMR1IE = 1; // Interrupt bit Timer1
PIR1.TMR1IF = 0; // clear FLAG
while(1)
{
}
}
Om 'Sum' startar som 0xFFFFFFFF blir det 429 sekunder ganska snabbt och det är ju 0:07:09... Så det beror nog på att 'Sum' inte starter på 0, RAM'en på en PIC (och alla andra µC jag har jobbat med) är odefinierat vid power-on.
Postat: 21 januari 2008, 20:20:03
av chrille112
Koden fungerar utmärkt, och vad jag kan se så går den helt rätt nu efter 1½ timme. Tack för all hjälp!

Postat: 21 januari 2008, 20:47:46
av Icecap
Efter vad jag förstår är detta sätt just det som Marta har förespråkad hur många gånger som helst...
Postat: 26 januari 2008, 20:19:01
av chrille112
Jag har lagt in lite kod i interrupt som kollar knappars lägen var 100 ms. Behöver jag kompensera för detta i tidskoden? Jag har fått för mig att den inte går lika exakt som innan.
Så här ser interupt-funktionen ut
Kod: Markera allt
PIR1.TMR1IF = 0; // Acknowledge interrupt
Sum += 1048576; // Add the current part
checkButtons();
if(Sum >= 10000000)
{
timeSECOND++;
Sum -= 10000000;
}
Gör det någon skillnad om jag lägger checkButtons() efter if-satsen istället? Eftersom timern hela tiden är igång så borde det inte spela någon roll, så länge man lägger det efter PIR1.TMR1IF = 0, eller?
Postat: 26 januari 2008, 20:56:07
av Icecap
Då du inte återladdar Timern men uteslutande använder interrupten kan du göra vad du vill (om det går snabbt!) i ISR'n.