Sida 1 av 1
Hålla tiderna, hur dela ned klockan? (Atmel ASM, XTAL)
Postat: 19 mars 2005, 21:12:10
av Greensilver
När jag beställde STK500:an från SWC så bad jag honom (Bosse?) kasta med lite prylar som han trodde att jag skulle behöva för att lära mig uC teknik. Han kastade då bland annat med två kristaller, en på 8MHz och en på 3.686MHz, och när det gällde den sistnämnda så sa han något i stil med att den var extra bra när jag ville leka med tid eftersom den var jämt delbar till typ en Hz eller så.
Nu har jag försökt räkna lite på det här men jag blir inte klok på det...
Hur skall jag dela ned klockan för att få exempelvis 1 Hertz?
Inga siffror som varit multiplar av 2 som jag lekt med har givit något bra närmevärde. Hur gör man? Hur skall man räkna?
Någon som har några tips?
Postat: 19 mars 2005, 21:48:18
av PaNiC
3686400/1024 (prescaler lämpligtvis)
3600/225
16/16=1
Finns säkert ett närmare sätt. Men om du använder timer0 så delar först prescalern med 1024. Sedan sätter du att timern ger interrupt vid 225 (vet inte om man kan det, har glömt hur timer0 fungerar) och ökar ett register med ett. När sedan detta register blir 16 har det gått en sekund.
Finns säkert ett närmare sätt. Men detta fungerar

.
Postat: 19 mars 2005, 21:50:27
av Greensilver
Tror att jag fatta nu - kristallen var visst på 3,686400 MHz.
Håller på att testa att dela den med 1024 x 36 x 100, vilket borde ge 1Hz.
Postat: 19 mars 2005, 23:38:31
av babbage
Du har redan fått ett svar. Men till nästa gång du har ett liknande problem kommer här lite om hur man kan angripa det.
Om man vill dela ned en godtycklig frekvens till exakt 1Hz så kan det vara lämpligt att primtalsfaktorera. I ditt fall har du 3686400 vilket ger 3686400 = 2^14*3^2*5^2
2^14=2*2*2*2*2*2*2*2*2*2*2*2*2*2=16384
3^2=3*3=9
5^2=5*5=25
Eftersom multiplikation är kommutativ (= ordningen spelar ingen roll) kan man sedan enkelt dela upp hur man vill dela frekvensen om man vill göra det i flera steg. Faktorer av värdet 2 har attraktiva egenskaper om man vill dela värdet direkt med t.ex. flip-flops. Annars använder man sig av räknare/timer/watchdog/(känt antal instruktioner) eller annan hårdvara.
Ditt förslag att dela med 1024*36*100:
* Ansats:1024=2^10, då har man 4 st faktorer á 2 kvar
* Om man delar med 1024 har man kvar att dela med 2^4*3^2*5^2=2^4*9*25. Ur det ser man rätt enkelt att 25*4=100 vilket är ett jämt och snyggt värde. Människor gillar ju att dela med 10, 100, 1000 osv men för hårdvara har det ingen betydelse, om den föredrar något så är det antagligen att dela med faktorer av 2 som 2, 4, 8, 16 osv. 100=2*2*5*5, alltså har du kvar 2^2*3^2=36.
Svamlet ovan kan vara ett sätt att resonera sig fram till 1024*36*100 men det finns ju många alternativa sätt, det är ju bara att välja kombinationer av primtalsfaktorerna så att de passar den hårdvara man har.
(2^14)*(3^2*5^2)=16384*225. Det är ju väldigt likt PaNiCs förslag
Men vad händer om kristallen skulle vara specad 3686401Hz? Här uppstår ett problem, 3686401 är nämligen ett primtal. För att få exakt 1Hz måste man t.ex ha en räknare som räknar hela värdet. När man nu har 3686400 i minnet så är det ju uppenbart att om man delar med 1024*36*100 så får man en frekvens som är väldigt nära 1Hz. Hur kan man angripa detta problem? Tänk som en ingenjör och inte som en matematiker. Du vill ju ha viss noggrannhet eftersom du inser att man ändå inte lär få exakt 1Hz. Om du kan tänka dig t.ex. 1Hz +/- 1% (utöver den onoggrannhet som kristallen har) kan du dela med vilket värde x som helst i intervallet 3649537<x<3723265. Nu kan man primtalsfaktorera tal i detta intervall antingen slumpmässigt eller systematiskt tills man hittar ett tal som är bra. Urvalskriterier för ett bra tal kan vara t.ex.
* många faktorer á 2
* ej för stora faktorer
* många små faktorer
Om man nu köper färdiga kristaller så har någon antagligen tänkt till varför den ska vara på 3686401Hz eller något annat värde. Även om värdet ser lite konstigt ut vid en första anblick går det gissningsvis bra att primtalsfaktorera, men det är inte säkert.
Postat: 20 mars 2005, 09:32:04
av erixon
Den är oxå bra om man använder uarten då de ger lite fel i baudraten

Postat: 20 mars 2005, 11:31:39
av Greensilver
Det här blev en riktigt intressant tråd - kanske något att lägga i FAQ'n.
Kanske är jag en klåpare på att söka men innan jag frågade här på forumet sökte jag bland annat på AVR Freaks efter kodexempel men hittade inget (i ASM). Så jag tror att många (nybörjare) kan ha nytta av det som babbage mfl. skrev!
Postar den kod jag använt:
Kod: Markera allt
; ===============================================================================
; = FREKVENSRÄKNARE (inte klar, enbart en LED-blinkare just nu)
; = version 0.00.
; ===============================================================================
;
; Pin Port
; 1 Reset -
; 5 XTAL1
; 4 XTAL2
; 20 VCC
; 10 GND
;
; ===============================================================================
; = KONSTANTER I KOD
; ===============================================================================
;
; ===============================================================================
; = INITIERING
; ===============================================================================
.include "2313def.inc" ; Includera variabelnamn och definitioner
.org $0000 ; Börja skriva på första positionen
rjmp RESET ; Reset Handler
rjmp EXT_INT0 ; IRQ0 Handler
rjmp EXT_INT1 ; IRQ1 Handler
rjmp TIM_CAPT1 ; Timer1 Capture Handler
rjmp TIM_COMP1 ; Timer1 Compare Handler
rjmp TIMER1 ; Timer1 Overflow Handler
rjmp TIMER0 ; Timer0 Overflow Handler
rjmp UART_RXC ; UART RX Complete Handler
rjmp UART_DRE ; UDR Empty Handler
rjmp UART_TXC ; UART TX Complete Handler
rjmp ANA_COMP ; Analog Comparator Handler
.def Temp = r16 ; Temporärregister
.def Tmr1 = r17 ; Nedklockning1 (36)
.def Tmr2 = r18 ; Nedklockning2 (Base)
.def Base = r19 ; Tidbas, 1 = 100Hz, 10 = 10 Hz, 100 = 1 Hz
.def Flag = r20 ; Tidbas, 1 = 100Hz, 10 = 10 Hz, 100 = 1 Hz
; ===============================================================================
; = RESETVEKTOR - körs efter Power Off och Reset
; ===============================================================================
RESET:
; Initiera stacken:
ldi Temp, low(RAMEND) ; Initiera stacken med den sista SRAM adressen på chipet
out SPL, Temp ; SPL är stackpointer som pekar på returnvärdet från sub eller int
; Ställ in Timerfrekvens
ldi Temp,$02 ; xxxx x001 - CS02, CS01, CS00 (000 - stop, 001 - CK, 010 - CK/8, 011 - CK/64, 100 - CK/256, 101 - CK/1024, 110 - External Pin T0 Falling Edge, 111 - External Pin T0 Rising Edge)
out TCCR0,Temp ; TCCR0 Timer/Counter0 Control Register
; Nollställ variabler
ldi Tmr1,$00
; Konfigurera PortB för Output
ldi Temp,$FF ; xxx1 1111 - DDBn (4<= n <= 0)
out DDRB,Temp ; DDRB Data Direction Register port B, alla pins till output
out PORTB,Temp ; PORTB Port B, alla pins till logisk 1 = alla LED släckta
; Enabla sleep-kommando
ldi Temp,$20 ; xx10 xx00 - SE Sleep Enabled, SM SleepMode, ISC01, ISC00
out MCUCR, Temp ; MCU Control Register
; Starta timer:
ldi Temp,$02 ; xxxx xx1x - TOIE, Timer/counterOverflowInterruptEnabled
out TIMSK,Temp ; Timer/counterInterruptMaskRegister
ldi Base,100
; Aktivera interrupter:
sei ; Set global interrupt flag (Bit 7) i SREG, samma sak som bset 7
; ===============================================================================
; = MAIN - Huvudloop, systemets väntefunktion
; ===============================================================================
MAIN:
;cp Flag,1
;brne MAIN
; Om Flag = 1 Läs av ingången.
;ldi Flag,0
out PORTB,Flag
rjmp MAIN
; ===============================================================================
; = TIMER/COUNTER OVERFLOW - Anropas vid varje overflow på Timer/Counter
; ===============================================================================
TIMER0:
inc Tmr1 ; Öka Time
cpi Tmr1,36 ; Jämför Time med 36
brne NOCHANGE ; Om Time < 36 hoppa till NOCHANGE
; Tmr1 = 36
ldi Tmr1,0 ; Nollställ Tmr1
inc Tmr2 ; Öka Tmr2
cp Tmr2,Base ; Jämför Time med Base
brne NOCHANGE ; Om Time < Base hoppa till NOCHANGE
ldi Tmr2,0 ; Nollställ Tmr2
cpi Flag,$FF
brne OFF
ldi Flag,$00
rjmp NOCHANGE
OFF:
ldi Flag,$FF
NOCHANGE:
reti
EXT_INT0: ; IRQ0 Handler
EXT_INT1: ; IRQ1 Handler
TIM_CAPT1: ; Timer1 Capture Handler
TIM_COMP1: ; Timer1 Compare Handler
TIMER1: ; Timer0 Overflow Handler
UART_RXC: ; UART RX Complete Handler
UART_DRE: ; UDR Empty Handler
UART_TXC: ; UART TX Complete Handler
ANA_COMP: ; Analog