probleem met timer0 PIC

hallo allemaal,
ik gebruik een PIC16F877 met een 20MHZ kristal.
om lage frequenties te meten met timer1 als counter,
heb ik een interrupt nodig heb van 1 seconden.
nu probeer ik dat te doen met timer0.

Fosc/4= 4MHz / prescaler / timer0 = interrupt frequentie
prescaler staat op /16
preload timer0 = 6 (256-6=250)

maar dit werkt niet zoals gepland

; Fosc = 20000000 Hz ( 20MHz)
; Fosc/4 = 4000000 Hz ( 4MHz)
; 4MHz/16 = 250000 Hz (250KHz)
; 250KHz/250= 1000 Hz ( 1KHz)
; 1000/1000 = 1 Hz

ik ben al dagen bezig om uit te zoeken hoe dit komt.
daar ben ik net achter gekomen.
timer0 blijft doorlopen.
daar tel ik 6 bij op.
en juist hier gaat het fout!
in de datasheet staat het volgende:

Note: Writing to TMR0, when the prescaler is
assigned to Timer0, will clear the prescaler
count, but will not change the prescaler
assignment.

de prescaler wordt dus gewist!!
en dat was niet de bedoeling, hierdoor klopt de timing niet meer.

hoe moet ik dit gaan oplossen?
ik heb echt geen idee meer.
hopelijk kunnen jullie mij helpen.

hieronder voorbeeld van mijn code:

code:


;***************************************************************************************************************************
;					interrupt routine
;***************************************************************************************************************************

		org	04h		; begin van interrupt

		movwf	INT_tempW	;;
		swapf	STATUS,w	;;
		clrf	STATUS		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		movwf	INT_tempSTAT	;; opslaan van waardes in W, STATUS en PCLATH register ;;
		movf	PCLATH,w	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		movwf	INT_tempPCLATH	;;
		clrf	PCLATH		;;

;---------------begin van interrupt routine------------------

		btfsc	INTCON,T0IF	; T0IF: TMR0 Overflow Interrupt Flag bit (must be cleared in software)
		goto	ADD_CLOCK_PULS
		btfsc	INTCON,INTF	; INTF: RB0/INT External Interrupt Flag bit (must be cleared in software)
		goto	$+2;xxx
		btfsc	INTCON,RBIF	; RBIF: RB Port Change Interrupt Flag bit (must be cleared in software)
		nop;goto	;xxx
		bcf	INTCON,T0IF	; clear TMR0 Overflow Interrupt Flag bit (must be cleared in software)
		bcf	INTCON,INTF	; clear RB0/INT External Interrupt Flag bit (must be cleared in software)
		bcf	INTCON,RBIF	; clear RB Port Change Interrupt Flag bit (must be cleared in software)

;---------------einde van interrupt routine------------------

INT_return	movf	INT_tempPCLATH,w;;
		movwf	PCLATH		;;
		swapf	INT_tempSTAT,w	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		movwf	STATUS		;; herstellen van waardes in W, STATUS en PCLATH register ;;
		swapf	INT_tempW,f	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		swapf	INT_tempW,w	;;
		bcf	INTCON,RBIF	;;
		retfie			;;

;***************************************************************************************************************************
;	init clock
;***************************************************************************************************************************
INIT_CLOCK
    BANK1
		movlw   b'00000011'		; set prescular at /16 on internal osc.
		movwf   OPTION_REG		; 4000000/16= 250000 CYCLES. /250= 1000 CYCLES
    BANK0
		clrf    seconden
		clrf    minuten
		clrf    uren
		clrf    dagen
		clrf	TMR0			; timer0 interrupt will come when timer0 overflows.! (only on startop uP)

		movlw	0x03			; add value of 1000 to devider
		movwf	CLK_devider_H
		movlw	0xe8
		movwf	CLK_devider_L

		bsf	INTCON,T0IE		; Enables the TMR0 interrupt
		bsf 	INTCON,GIE		; Enable Global Interrupt.
		return

;***************************************************************************************************************************
;	clock ADD INT PULS
;***************************************************************************************************************************
ADD_CLOCK_PULS					; from INTERRUPT

    LED_gr_ON
    LED_gr_OFF
		bcf	INTCON,T0IF		; clear TMR0 Overflow Interrupt Flag bit (must be cleared in software)

		decfsz	CLK_devider_L,f		; Fosc	    = 20000000 Hz ( 20MHz)
		goto	$+4			; Fosc/4    =  4000000 Hz (  4MHz)
		decfsz	CLK_devider_H,f		; 4MHz/16   =   250000 Hz (250KHz)
		goto	$+2			; 250KHz/250=     1000 Hz (  1KHz)
		goto	$+4			; 1000/1000 =        1 Hz 

		movlw	.5			; preset timer0 to count to 250 (256-6=250)
		addwf	TMR0
		goto	INT_return

		movlw	0x03		
		movwf	CLK_devider_H
		movlw	0xe8
		movwf	CLK_devider_L

		movlw	.5			; preset timer0 to count to 250 (256-6=250)
		addwf	TMR0

		call	MAIN_COUNTER_TMR1	; take counter sample
;***************************************************************************************************************************
;	add e second to clock
;***************************************************************************************************************************
CLOCK_ADD
		bsf	log_byte,0		; mus be cleared after used.
		incf    seconden,f
		movlw   .60
		xorwf   seconden,w
		btfss   STATUS,Z
	goto	DONE_A_second
		clrf    seconden
		incf    minuten,f
		movlw   .60
		xorwf   minuten,w
		btfss   STATUS,Z
	goto	DONE_A_second
		clrf    minuten
		incf    uren,f
		movlw   .24
		xorwf   uren,w
		btfss   STATUS,Z
	goto	DONE_A_second
		clrf    uren
		incf    dagen,f
DONE_A_second
		goto	INT_return
;***************************************************************************************************************************
;	END
;***************************************************************************************************************************
ik wil alles weten over pic stap voor stap

uw Fosc/4 klopt ook niet bij mijn weten is 20000000/4 = 5000000Hz

JoWi

Special Member

Lage frequenties meten met een resolutie van bijv .01 Hz, zonder een gate tijd van 100 seconden te gebruiken doe je door van een aantal pulsen de tijd te meten en dan reken je de frequentie uit.

Ik kan morgen wel een programma posten dat dit doet (geschreven in een mix van assembly en PicBasic) als je belangstelling hebt.

Ignorance is bliss

etienne_on4eg, u heeft ook nog eens gelijk!!
ik vraag mezelf af hoe ik nu weer aan de 4KHz ben gekomen.
ik heb hier ook mee gerekend.
echter heb ik mijn programma aangepast, meer werkt nu nog steeds niet goed.
zo ben ik dus al dagen bezig!

JoWi, als je tijd hebt om uw voorbeeld te posten, dan is die ook erg welkom.
ik heb namelijk nog een project in de kast staan waar ik dat in zou kunnen gebruiken.
alvast bedankt.

code:


;***************************************************************************************************************************
;	init clock
;***************************************************************************************************************************
INIT_CLOCK
    BANK1
		movlw   b'00000100'		; set prescular at /32 on internal osc.
		movwf   OPTION_REG		; 
    BANK0
		clrf    seconden
		clrf    minuten
		clrf    uren
		clrf    dagen
		clrf	TMR0			; timer0 interrupt will come when timer0 overflows.! (only on startop uP)

		movlw	0x02			; add value of 625 to devider
		movwf	CLK_devider_H
		movlw	0x71
		movwf	CLK_devider_L

		bsf	INTCON,T0IE		; Enables the TMR0 interrupt
		bsf 	INTCON,GIE		; Enable Global Interrupt.
		return

;***************************************************************************************************************************
;	clock ADD INT PULS
;***************************************************************************************************************************
ADD_CLOCK_PULS					; from INTERRUPT

    LED_gr_ON
    LED_gr_OFF
		bcf	INTCON,T0IF		; clear TMR0 Overflow Interrupt Flag bit (must be cleared in software)

		decfsz	CLK_devider_L,f		; Fosc	    = 20000000 Hz ( 20MHz)
		goto	$+4			; Fosc/4    =  5000000 Hz (  5MHz)
		decfsz	CLK_devider_H,f		; 5MHz/32   =   156250 Hz (KHz)
		goto	$+2			; 156250/250=      625 Hz (625KHz)
		goto	$+4			; 625/625   =        1 Hz 

		movlw	.5			; preset timer0 to count to 250 (256-6=250)
		addwf	TMR0
		goto	INT_return

		movlw	0x02		
		movwf	CLK_devider_H
		movlw	0x71
		movwf	CLK_devider_L

		movlw	.5			; preset timer0 to count to 250 (256-6=250)
		addwf	TMR0

		call	MAIN_COUNTER_TMR1	; take counter sample
;***************************************************************************************************************************
;	add e second to clock
;***************************************************************************************************************************
CLOCK_ADD
		bsf	log_byte,0		; mus be cleared after used.
		incf    seconden,f
		movlw   .60
		xorwf   seconden,w
		btfss   STATUS,Z
	goto	DONE_A_second
		clrf    seconden
		incf    minuten,f
		movlw   .60
		xorwf   minuten,w
		btfss   STATUS,Z
	goto	DONE_A_second
		clrf    minuten
		incf    uren,f
		movlw   .24
		xorwf   uren,w
		btfss   STATUS,Z
	goto	DONE_A_second
		clrf    uren
		incf    dagen,f
DONE_A_second
		goto	INT_return
;***************************************************************************************************************************
;	END
;***************************************************************************************************************************
ik wil alles weten over pic stap voor stap
JoWi

Special Member

@kevinonline:

Het was even zoeken, maar het is voor een counter met een LF en HF input. HF tellen gebeurt door TMR0 extern te klokken en na een gate tijd van 0.25 of 1.00 seconde het aantal pulsen te displayen (voor een gate tijd van 1 seconde is dat de frequentie). Dit werkt tot zo'n 50 Mhz.
Voor LF wordt de CCP module gebruikt: je telt gedurende een bepaalde tijd het aantal pulsen en meet de van een die pulsen de tijd in microseconden. Bijvoorbeeld: 48 pulsen in 498323 uSec is een periodetijd van 10381.8 uSec en een frequentie van 9.632 Hz.
Dit wordt gebruikt om frequenties van een paar Hz tot 25kHz te meten, om lager te gaan moet de meettijd verlengt worden.
De lap code is bijgevoegd, het was een beetje te lang om in de post te plaatsen.

Ignorance is bliss

Twee opmerkingen:
De timer loopt na het zetten van de interruptvlag door. Met het ophogen van het timerregister op een moment enige instructies later is er in werkelijkheid meer tijd verstreken. Dat ophogen moet dus gecorrigeerd worden.
Ik werk zelf als volgt (in de interruptroutine):
-test op int-vlag
-clear int-vlag
-zet vaste waarde in timerregister (gecorrigeerd voor tijdverlies)
-hoog teller(s) op
-test op te bereiken waarde (in dit geval tot 1 sec bereikt is)
-doe 1 sec routine
-reset tellers

Arco

Special Member

Meestal is het handiger om Timer2 te gebruiken, dit is een auto-reload timer...

Arco - "Simplicity is a prerequisite for reliability" - hard-, firm-, en software ontwikkeling: www.arcovox.com

Op 31 januari 2017 23:07:12 schreef kevinonline:

Note: Writing to TMR0, when the prescaler is
assigned to Timer0, will clear the prescaler
count, but will not change the prescaler
assignment.

de prescaler wordt dus gewist!!
en dat was niet de bedoeling, hierdoor klopt de timing niet meer.

Als direct na het resetten van de interruptvlag de timer geladen wordt, start het proces vanaf dat moment, inclusief de prescaler. Waarom zou dat fout gaan (afgezien van enige vertraging)?

Maar wat gaat er eigenlijk niet goed? Doet hij het wel maar onnauwkeurig, of doet ie het helemaal niet?

[Bericht gewijzigd door BenZ op vrijdag 3 februari 2017 10:04:36 (30%)

Timer 0 is waardeloos, en extreem waardeloos voor iets wat nauwkeurig moet zijn. Dat ding zit alleen nog in PIC controllers voor de backward compatibility, nergens anders voor. Gebruik dat ding gewoon niet, je verprutst je tijd ;) .

Mijn echte naam: Joris | Mijn elektronica website: Fuzzcraft.com
Lambiek

Special Member

Op 3 februari 2017 10:36:37 schreef Fuzzbass:
Timer 0 is waardeloos, en extreem waardeloos voor iets wat nauwkeurig moet zijn.

Dat ben ik niet helemaal met je eens, hoe verklaar je dan dat ik er een timer mee maak die bijv. om de 12 uur een puls geeft die precies op de seconden loopt. En gewoon met een interrupt op timer0.

Als je haar maar goed zit, GROETEN LAMBIEK.
Arco

Special Member

Het kan ook wel prima, maar als je Timer2 gebruikt is het allemaal een stuk simpeler... ;)
Meeste nieuwere pics hebben een RTCC, dus dan wordt alles nog simpeler...

[Bericht gewijzigd door Arco op vrijdag 3 februari 2017 13:46:56 (29%)

Arco - "Simplicity is a prerequisite for reliability" - hard-, firm-, en software ontwikkeling: www.arcovox.com

Als je het leuk vindt om cycli te zitten tellen om die timer nauwkeurig te laten lopen, wat je dan opnieuw moet doen als je dat deel van je code wijzigt, en je dat in (in-line) assembly wil doen, dan kun je gerust timer 0 gebruiken. Deed ik ook, tot er wat beters kwam.

Mijn echte naam: Joris | Mijn elektronica website: Fuzzcraft.com
Lambiek

Special Member

Op 3 februari 2017 13:46:38 schreef Fuzzbass:
......, wat je dan opnieuw moet doen als je dat deel van je code wijzigt, en je dat in (in-line) assembly wil doen, dan kun je gerust timer 0 gebruiken.

Sterker nog, ik doe het in basic.

Deed ik ook, tot er wat beters kwam.

Dat is ook zo, maar het kan wel en zoveel werk is het niet. Je moet alleen goed kijken wat je doet.

Als je haar maar goed zit, GROETEN LAMBIEK.
Arco

Special Member

Het lastige met timer0 is dat je die moet 'tweaken'. Zeker bij een hogere programmeertaal lastig, want je bent afhankelijk van wat voor code de compiler bakt...
Daarbij nog de incompabiliteit tussen oudere en nieuwere pics. Bijv de enhanced 16F1xxx serie heeft geen extra cycles meer nodig voor save/restore context.

Arco - "Simplicity is a prerequisite for reliability" - hard-, firm-, en software ontwikkeling: www.arcovox.com

Gaat er even voor zitten,...

Nauwkeurigheid wordt niet door de timer bepaald maar door : keuze componenten, temperatuur, luchtvochtigheid, luchtdruk,zwaartekracht en schoonmoeders. Deze Timer0 is dus extreem waardevol en uitermate geschikt voor een seconde puls zodat je die andere timers voor andere dingen kunt gebruiken. Het gaat er om hoe je die timer/cpu aanstuurt !! gebruik je een kristal met een afwijking van 150 ppm(parts per million) op een hoge frequentie of zet je er een aparte oscillator naast met een horloge kristal van 32768Hz ? hangen er C's aan het kristal van 22pF geen NP0 maar NP750 is ie behoorlijk beinvloedbaar door temperatuur(NPx is maat temp coefficient van C). Je kunt een C-tje vervangen door een trimmer van 5-30pF en veel discussies omtrent het opvangen van tijdsverschil kun je dan sluiten.

Of je wekt een interrupt op via de netfrequentie van 100Hz, iedere 10ms een interrupt, succes verzekerd en redelijk nauwkeurig op den lange duur.Een GPS DSO gestuurde oscillator helemaal nauwkeurig...alleen die GPS unit moet de SKY zien...
TS wil iets maken, kennelijk een klok om er van te leren . (zie z'n TAG) heeft een pic gekocht en een 20MHz kristal en denkt gaan met die banaan...lekker aan de gang met assembler (helemaal TOP).

Dat de keuze een 20MHz kristal is geworden is waarschijnlijk te wijten aan het feit dat in veel AVR/PIC projecten een 20MHz kristal wordt gebruikt maar dan zit er ook mogelijk een PLL in waarmee je diverse frequenties kunt maken enof het project is niet afhankelijk van nauwkeurige tijdmeting.

Waarom is 20MHz een slechte frequentie? 20MHz gedeeld door 4(cpu cycles) = 5MHz ...niet bepaald een getal wat een integer opleverd gedeeld door 2 gedeeld door 2 etc, maar een fractioneel getal.

Er zijn Kristalfrequentie-reeksen die speciaal op bepaalde frequenties worden geslepen (A-T Cut) waarvan de grondfreqentie deelbaar is door een getal uit de binaire reeks(16,32,64,128,256,etc) De meest bekende is 32,768KHz uit oa horloges. 4,194.304 MHz is ook een bekend tijd kristal maar onze gebruikte cpu's lopen iets sneller dan 4 en een beetje.
Bijvoorbeeld Kristal 19,6608 MHz dit is een binaire reeks kristal en het ligt in de hoge regionen( dicht bij 20MHz) om je cpu aan te sturen.

Je kunt de "reload methode" toepassen waarmee je een kettingmaat veroorzaakt, je meet zeg maar 1 Meter 10 keer op met 10 centimeter maatstokje inclusief de tolerantie(reload) van het meten.

Voorbeeld met decimale prescaler van 100 en counter van 200 aangestuurd met kristal van 4MHz en cpucycle van 4.
De frequentie op de prescaler is dus 1MHz gedeeld door 100 geeft 10KHz . De reload counter waarde is 100 en bij 199>>0 geeft ie een interrupt . Ik deel dus die 10KHz nog een keer door 100 is 100Hz.
Nu moet ik de counter en prescaler weer reloaden en dat kost ook tijd qua extra instructies.
Je kunt dit corrigeren door de Counter (99 cycles) eerder te laten stoppen en de resterende tijd optevullen met "NOP's or whatever. Dit betekent dus wel dat als je 10 Instructiecycles kwijt was aan het reloaden je er nog 90 nop's moet doorlopen om op moment T=0 uit te komen, want 1 signaal uit je prescaler kost 100 cycles.
En die 90 NOP instructies extra voldoen niet meer aan de pracht van het boekje PIC assembler programming hoofdstukje: "interrupts ERIN en ERUIT...."
Kun je nagaan als je een prescaler waarde van 256 zou gebruiken moet je 256-10=246 cycles wegwerken!!!!!

Waar ik naar toe wil.......
Ik stel de counter (256) en prescaler(256) waarde in in m'n init en zeg tegen de timer0: "ga maar lopen".
Met een kristal van 19.6608 MHz/4 =4915200 / 256 / 256 = 75 wauw !!!! een integere waarde
Dus iedere 1/75-ste seconde wordt er een interrupt gegenereerd , in een tellervariable houd ik bij of ik al op nul zit zoja dan reload ik de variable met 75 ik geef een seintje door middel van een FLAG naar buiten en zo,nee?
doe een decfsz teller_var .Deze reload zit buiten de tijdmeting!!!!! omdat je counter (de tijdmeter) doorloopt.
Wil je een halve seconde? verlaag de prescaler(128 ipv 256) met factor 2.

Bij een 4MHZ kristal of veelvoud ervan op een 16F of 18F Core krijg ik de seconde op een nauwkeurigheid van 1.000009143 sec dat laatste restje regel je weg door tegen het kristal te blazen.... :-)
Dat is een verloop van 1 seconde in 30,38146 uur

Belangrijke opmerkingen met betrekking tot je werk

Een "Goto" in een interrupt naar een proc buiten de interrupt om vervolgens weer terug te keren, is zogezegd NOT DONE. Als het toch "moet" gebruikt dan een Call/ret
Als je een flag zet [bsf flag_reg,0] hoef je niet eens uit je interrupt te springen , je kijkt in je mainloop of die flag gezet is en doet z'n ding wat ie moet doen en reset de flag.(processor staat voor 99,999999999% uit z'n neus te grutten]
Interrupt bezoek is net als de HEMA .. get in.. do your thing and get out ....

Als je door de bomen het bos niet meer ziet , sloop dan alles uit je code wat niet belangrijk is voor het resultaat!!!
In dit geval wil je een seconde puls ,..laat dan die minuten uren dagen functies weg. en concentreer je op je probleem.
Neem de POORMAN's debugger : ledje of pieper of maak gebruik van breakpoints in je simulator.
En werk gestructureerd, maak een initalisatie functie aan voor de PIC functies in algemeen en z'n timer settings

Re-integratiecoach uitgerangeerde en degoutante electronen
Lambiek

Special Member

Op 3 februari 2017 14:27:03 schreef Arco:
Het lastige met timer0 is dat je die moet 'tweaken'. Zeker bij een hogere programmeertaal lastig, want je bent afhankelijk van wat voor code de compiler bakt...

Oké dat is zo, maar het werkt wel. En daar gaat het om, maar via timer twee gaat het makkelijker dat is zo.

Op 3 februari 2017 14:35:49 schreef Arabel:
Deze Timer0 is dus extreem waardevol en uitermate geschikt voor een seconde puls zodat je die andere timers voor andere dingen kunt gebruiken.

En dat is nu precies waar ik hem voor gebruik. :)

En voor de rest helemaal met je eens, je moet op een mooi deelbaar getal uitkomen en dan werkt het prima.

Als je haar maar goed zit, GROETEN LAMBIEK.
JoWi

Special Member

Op 3 februari 2017 10:36:37 schreef Fuzzbass:
Timer 0 is waardeloos, en extreem waardeloos voor iets wat nauwkeurig moet zijn.

TMR0 is wel de enige timer die 50Mhz op zijn input aankan op een PIC ongeacht van op welke frequentie de rest van het systeem draait.

[Bericht gewijzigd door JoWi op vrijdag 3 februari 2017 14:41:32 (12%)

Ignorance is bliss