Binär klocka, några funderingar...

Elektronikrelaterade (på komponentnivå) frågor och funderingar.
ASTRX
Inlägg: 68
Blev medlem: 12 januari 2005, 16:58:13

Binär klocka, några funderingar...

Inlägg av ASTRX »

Håller på att kasta ihop mjukvaran för en binär klocka ála:

http://www.thinkgeek.com/homeoffice/lights/59e0/

Jag använder en ATmega8515 och kodar i C.
jag behöver alltså en väldigt exakt klocka som inte drar sig. det bästa valet är då en extern kristall vad jag har förstått.
men ju mer kod som skall exekveras i avbrottsrutinen (beräkningar, output till portar o.s.v.) kommer ju stjäla klockcykler och bidra till att klockan drar sig?

jag undrar också hur jag ska attackera antalet avbrott. säg att jag har en 8Mhz klocka, ska jag ha ett avbrott varje sekund eller skall de vara kortare, t.ex. 1 ms, och då räkna upp en variabel går upp till 1000 och sedan ökar klockan med 1 sekund?
det senare borde väl göra att klockan drar sig mer då man exekverar kod varje ms och inte bara varje sekund?

Vore bra om ni som har erfarenhet att klockbyggen berättar hur ni gjort :)
Användarvisningsbild
EagleSpirit
Inlägg: 1288
Blev medlem: 27 maj 2003, 23:15:48
Ort: Västerås
Kontakt:

Inlägg av EagleSpirit »

Det finns timerfunktioner i AVR-kretsar som passar perfekt till det du vill göra. Den gör så att klockan inte skulla dra sig någonting så länge du har angett rätt siffror.

Varje gång timerfunktionen har räknat till ett visst tal så skapar den ett interrupt, dvs den hoppar till en speciellt plats i programmet vad programmet än håller på med i övrigt. I interruptrutinen kan du sedan utföra vissa saker, t.ex. öka klockan med 1 sekund, därefter hoppar den tillbaka till platsen där den var innan. Mellan interrupten kan du alltså göra vad du vill, t.ex. uppdatera utgångarna så att klockan visar det den ska visa.

Hur du gör det här rent praktiskt finns det nog en hel del exempel om som du kan hitta på google och så kan jag nästan tänka mig att det finns i guideforumsdelen, men jag är inte helt säker. Annars står det en del i databladet för kretsen du håller på med, kolla lite på interrupt och timer.
ASTRX
Inlägg: 68
Blev medlem: 12 januari 2005, 16:58:13

Inlägg av ASTRX »

Ja jo så långt är jag med. jag använder overflow interrupt på timer/counter0.
men timern är ju beroende av systemklockan och det måste ju vara jäkligt stabil för att det sak bli exakt. jag antar att den interna oscillatorn inte duger som klockkälla då den är alldeles för oexakt.

ska man satsa på en såndär speciell klockkristall på typ 32,768kHz som är mer exakt?

och avbrotten maskeras ju under servicerutinen och då kan ju klockan dra sig om avbrottsrutinen tar längre tid att exekvera än mellanrummen mellan avbrotten (inte så sannolikt att så är fallet iofs om man har avbrott varje sekund). räknaren fortsätter väl dock att ticka upp oberoende av avbrottsrutinen.
Användarvisningsbild
Icecap
Inlägg: 26650
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Inlägg av Icecap »

Ska du ha en riktig exakt klocka ska du montera en DS32KHZ! Det är en 32768Hz oscillator som är temperaturkompenserat, om du kör "rumstemperatur" har du en avvikelse på max ±1 min/år och det är väl skapligt.

Vill du vara ännu häftigare monterar du också en DCF77-mottagare men det är en del mer pyssel med den biten.

Det lösa klockkristaller på 32768Hz är inte speciellt noga, ska de bli bra måste du trimma deras belastningskondingar och till detta ha en bra (exakt!) frekvensmätare... Ska man undvika DS32KHZ är det faktisk mer noga med det vanliga klockkristall (4MHz ELFA 74-515-29 ±50ppm) än ett 32KHz klockkristall (ELFA 74-530-04 ±50ppm @ 12,5pF) då deras lastkapicitans kan vara svår att få rätt.

Att ett avbrott maskas ut under ISR-tide betyder INTE att timern sluter köra eller hur? Näpp, det betyder bara att timern inte ger interrupt under den tid och då en ISR (Interrupt Service Routine) ska vara snabb (inden "delay"/"wait" eller skit) lär den rikligt hinna genom.

Ett sätt är att ställa timern till att ge interrupt varje sekund och då öka en flagga som anger att sekunderna ska ökas på. Efter det avsluter man ISR'n.

I "huvudrutinen" kollar man sedan om denna flagga är mer än noll. Är den mer än noll räknar man 1 ner och öker på sekunderna/minuterna/timmerna.

Om det nu tar så helvetes lång tid att det tar mer än 1 sekund kommer flaggan ju att visa att det åter ska läggas till osv. Om din uppdateringstid likaväl tar mer än 1 sek har du ett programmeringsproblem hur som helst eller en ovanligt seg µC.

Man räknar ju:

Kod: Markera allt

if(Flaggan) // Allt annat än noll är sant
  {
  Flaggan--; // Räkna bort 1 uppdatering
  if(++Seconds >= 60) // Räkna upp sekunder OCH kolla om den är 60 eller mer
    {
    Seconds = 0;
    if(++Minutes >= 60) // Samma grej med minuterna
      {
      Minutes = 0;
      if(++Hours >= 24) Hours = 0; // Nolla om den blir 24 eller mer
      }
    }
  }
ASTRX
Inlägg: 68
Blev medlem: 12 januari 2005, 16:58:13

Inlägg av ASTRX »

tack Icecap för mycket bra info. hittar dock inte någon DS32KHZ eller liknande på elfa?
men jag skall alltså koppla denna till T0/T1 på uC:n. men jag funderar på hur jag ska generera ett exakt 1s avbrott. måste ju konfigurera räknaren rätt för att få overflow precis varje sekund, men jag kommer inte riktigt på hur jag skall åstadkomma detta? nån som vill förklara?

här är min kod för övrigt där timern är inställd för att passa i simulatorn med en 4MHz systemklocka.

Kod: Markera allt

#include <avr/io.h>
#include <avr/delay.h>
#include <avr/interrupt.h>

static volatile unsigned char sec,min,hour;

void init_clock()
{
	sec=0x00;
	min=0x00;
	hour=0x00;
	
	DDRA=0xFF;
	DDRB=0xFF;
	DDRC=0xFF;
}

unsigned char bin2nbcd(unsigned char n)
{
	unsigned char c=0x00;
	
	c=n%10;
	n=n-c;
	c|=(n/10<<4);

	return c;
}

ISR(SIG_OVERFLOW0)
{
	sec++;
	sec=sec%60;
	PORTA=bin2nbcd(sec);

	if(sec==0){
		min++;
		min=min%60;
		PORTB=bin2nbcd(min);
		if(min==0){
			hour++;
			hour=hour%24;
			PORTC=bin2nbcd(hour);
		}
	}

}


int main()
{
	/* Initalize Timer */
	cli();
	TCNT0=0x00;
	TCCR0=(1<<CS00);
	TIMSK=(1<<TOIE0);
	sei();

	init_clock();

	while(1);

	return 0;
}
Användarvisningsbild
Icecap
Inlägg: 26650
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Inlägg av Icecap »

Om den inte går att ställa till hela sekunder kan man ta delar!

DS32KHZ kan samplas på hemsidan... ;-)

Då jag inte pillar med AVR kan jag inte hjälpa med resten förutom detta:
De flesta 16-bitars timrar har en funktion där de antingen jämförs med eller laddas från ett 16-bitars (eller 2 8-bitars) register som man kan ställa själv.

Laddningen utförs när räknaren "rullar över" från 0xFFFF till 0x0000.

Om vi tar reload-funktionen gäller det alltså att klura ut vilket tal som räknaren ska räkna upp till OCH MED 0xFFFF. Om vi utgår ifrån en 32768Hz klocka (skapad på ena eller andra sättet) behöver räknaren alltså att återställas till -32768 vilket är 0x8000! Därmed kommer timern att ge interrupt varje sekund.

Detta är en generell beskrivning, det finns många typer av timrar men finns det en 16-bitars är det en mycket bra chans att den har en sådan eller liknande funktion.
ASTRX
Inlägg: 68
Blev medlem: 12 januari 2005, 16:58:13

Inlägg av ASTRX »

ahh gött då fattar jag. mycket bra förklarat. men då måste jag alltså ha en 16-bitars räknare om jag vill ha 1s avbrott, annars får jag räkna ett antal små avbrott.
Användarvisningsbild
Icecap
Inlägg: 26650
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Inlägg av Icecap »

Nja.... på ett ungefär. Jooo.... det är ju så faktisk. Det viktiga är oftast att man kan ställa in vilken pulstid timern kan ge och/eller det passar med en binär delning vilket det kommer att göra med 32768Hz.
Användarvisningsbild
cykze
EF Sponsor
Inlägg: 1539
Blev medlem: 8 april 2004, 10:40:28
Ort: Uppsala

Inlägg av cykze »

Istället för att återställa timern manuellt till 32768 (0x8000) vid varje interrupt bör du istället använda "Output Compare Match" i CTC-läge. Annars riskerar klockan att dra sig.

Gör så här:

1. Koppla DS32kHz:en till T1-pinnen.
2. Ställ in "Clock Select" till att räkna pulser på T1.
3. Sätt OCR1A = 32768
4. Ställ in de fyra inställningsbitarna som läge Mode=4 i listan "Waveform Generation Mode..." visar.
5. Sätt OCIE1A i TIMSK
6. Lägg in interruptrutinen för "Output Compare Match A" (Timer1)

Det fungerar så att timervärdesregistret TCNT1 ökar med 1 vid varje puls. När den kommer upp till 32768 så triggas interruptrutinen för "Output Compare Match" och TCNT1 sätts samtidigt till 0.

Det borde fungera. Men jag kan inte garantera att värdet ska vara just 32768 och inte 32768 +- 1.
Användarvisningsbild
karlstedt
EF Sponsor
Inlägg: 966
Blev medlem: 14 oktober 2003, 16:55:23
Ort: Lund
Kontakt:

Inlägg av karlstedt »

Hur menar du att klockan ska dra sig om man triggar på owerflow? (om det är det du menar)

Det är väl bara att lista ut exakt hur många instruktioner interrupt + återställning av räknaren tar och sedan minska det från det värde man återställer räknaren med?

Fast det är väl kanske besvärligare än din metod :)
Användarvisningsbild
cykze
EF Sponsor
Inlägg: 1539
Blev medlem: 8 april 2004, 10:40:28
Ort: Uppsala

Inlägg av cykze »

Problemet är väl främst när interrupts är tillfälligt inaktiverade av någon anledning. T ex när man är inne i en annan interruptrutin eller att man har inaktiverat dom själv.

Sen tillkommer ju alltid en viss "inledning" på interruptrutinen som avr-gcc lägger in. Den är olika stor beroende på vilka register som används i interruptrutinen.

Overflow behöver man väl egentligen aldrig använda, på dom Timers där "Output Compare Match" finns.
Användarvisningsbild
Icecap
Inlägg: 26650
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Inlägg av Icecap »

Jag kan bara hålla med, börjar man "omladda" timern i software är det skit med det! Jag skrev ju också att det i de flesta fall finns en funktion som antingen omladdar automatisk från ett par skriv/läsbara registre eller som jämför och nollar timern automatisk. Finns det inte i processorn är det bara att byta till en med den funktion.
Pjoms
EF Sponsor
Inlägg: 644
Blev medlem: 24 maj 2004, 12:18:40
Ort: Ö-vik

Inlägg av Pjoms »

Hmm... Intresant.
Är detta något som även finns i PIC-världen? 18-serien kanske?
Kan inte minnas att jag sett annat än rollower-interrupt på timers i t.ex. 16F876, eller är jag fel ute?
ASTRX
Inlägg: 68
Blev medlem: 12 januari 2005, 16:58:13

Inlägg av ASTRX »

cykze skrev:Problemet är väl främst när interrupts är tillfälligt inaktiverade av någon anledning. T ex när man är inne i en annan interruptrutin eller att man har inaktiverat dom själv.
Man får ju anta att man byggt systemet på så sätt att inga högre prioriterade avbrott är möjliga. man kan ju tänka sig att man har avbrott för att ställa om klockan t.ex. men dessa spelar ju ingen roll eftersom klockan ändå ställs om och börjar om.

Sen tillkommer ju alltid en viss "inledning" på interruptrutinen som avr-gcc lägger in. Den är olika stor beroende på vilka register som används i interruptrutinen.
Denna "inledning" (i princip då processorn sparar programmets "state" på stacken) som du pratar om är ju oviktig i detta sammanhang. om jag har avbrott med 1 sekunds mellanrum så krävs det en massa kod inkl. inledningen för att det skall överstiga 1s i exekveringstid. räknaren fortsätter ju att ticka eftersom den är oberoende av programexekveringen, och endast beror på klockan..

Icecap: Maxin/Dallas samplings-"shop" verkar vara ganska risigt skick och fungerar inte :(
Användarvisningsbild
cykze
EF Sponsor
Inlägg: 1539
Blev medlem: 8 april 2004, 10:40:28
Ort: Uppsala

Inlägg av cykze »

> Man får ju anta att man byggt systemet på så sätt att inga högre prioriterade avbrott är möjliga. man kan ju tänka sig att man har avbrott för att ställa om klockan t.ex. men dessa spelar ju ingen roll eftersom klockan ändå ställs om och börjar om.

Om man använder "Overflow" istället för "Output Compare Match" så måste man sätta TCNT1 till 32768 (eller vad det blir) varje gång en overflow har skett (dvs TCNT1 har slagit om från 65536 till 0). Det är i så fall det första som ska göras i interruptrutinen. Men körs inte "TCNT1 = 32768" exakt vid den tidpunkten som man har tänkt sig så kommer tidräkningen att dra sig något. Visst kan man köra med något liknande "TCNT1 = 32768 - TCNT1;", för att komma runt det. Men med "Output Compare Match" finns den funktionen inbyggd. Det är mycket smidigare.

> Denna "inledning" (i princip då processorn sparar programmets "state" på stacken) som du pratar om är ju oviktig i detta sammanhang. om jag har avbrott med 1 sekunds mellanrum så krävs det en massa kod inkl. inledningen för att det skall överstiga 1s i exekveringstid. räknaren fortsätter ju att ticka eftersom den är oberoende av programexekveringen, och endast beror på klockan..

Du har rätt så länge man inte kör med den första metoden med "Overflow" som jag skrev om. Men med "Overflow" + "TCNT1 = 32768 - TCNT1" (i början av Overflow-rutinen) så borde det bli rätt. Men som sagt behöver man inte det om man kör med "Output Compare Match".
Skriv svar