Timer1 op PIC18F4550 wilt niet in 16bit mode werken?

Ondanks dat de timer1 duidelijk een 16 bit timer is, werkt hij slechts met 8 bit in mijn implementatie, en dat wil ik niet, ik vind mijn fout echter niet.

Ik werk met de CCP compare module om de interrupt te genereren. met een laag getal in de ccpr-registers (onder 0xFF) werkt de timer dus, maar hij zou in 16bit moeten werken. Mijn code om timer1 te initialiseren ziet er zo uit:

void setupTimer()
{
	const tLong constValueForCCPR1 = ((configTICK_TIME	 * (OSC_FREQ / OSC_PER_INST)) / 1000 );	//1000 is om msec van tick_time aan te passen
	tLong valueForCCPR1;

	//timecount clearen
	TMR1H = (tByte) 0x00;
	TMR1L = (tByte) 0x00;
	
	//waarde in CCPR1 register steken
	valueForCCPR1 = constValueForCCPR1;
	CCPR1L = ( tByte ) ( valueForCCPR1 & ( tLong ) 0xff );
	valueForCCPR1 >>= (tLong) 8;
	CCPR1H = ( tByte ) ( valueForCCPR1 & ( tLong ) 0xff );	

	//Compare mode
	CCP1CONbits.CCP1M0 = SET_BIT;
	CCP1CONbits.CCP1M1 = SET_BIT;
	CCP1CONbits.CCP1M2 = CLEAR_BIT;
	CCP1CONbits.CCP1M3 = SET_BIT;

	//alle instellingen qua timer1 en interrupts voor timer1
	PIE1bits.CCP1IE = SET_BIT;		// CCP1 Interrupt Enable
	//IPR1bits.TMR1IP = SET_BIT;		// CCP1 Interrupt Priority High

	T1CONbits.TMR1CS = CLEAR_BIT;		// Timer 1 Clock Source Bit -- zero equals internal clock
	T1CONbits.T1CKPS1 =	CLEAR_BIT;		// Prescale value
	T1CONbits.T1CKPS0 = CLEAR_BIT;		// Prescale value
	T1CONbits.RD16 = SET_BIT;			// Enable 16bit mode

	T3CONbits.T3CCP2 = CLEAR_BIT;		//Timer3 and Timer1 to CCPx Enable bits
	T3CONbits.T3CCP1 = SET_BIT;			//Timer3 and Timer1 to CCPx Enable bits

	T1CONbits.TMR1ON = SET_BIT;			// Timer 1 On Bit
	//freeRTOS gebruikt 	INTCONbits.GIEL  nog, ma ik denk dak die ni moet gebruiken?

	//INTCONbits.INT0IE = CLEAR_BIT; //deze regel moet eerder bij startscheduler
	INTCONbits.GIEL = SET_BIT;

	//RCONbits.IPEN = 0;
	INTCONbits.GIEH = SET_BIT;
}

kan iemand mij helpen?

is de vraag te gedetailleerd? of geef ik niet genoeg informatie misschien?

nog even wat meer info dan: ik gebruik pic18f4550, c18 compiler en mplab ide omgeving.

tByte in de code is unsigned char
clear_bit en set_bit zijn 0 en 1 respectievelijk

de commentaar bij de code is niet altijd relevant.

en de rest van het programma?

of is het te lang?

mvg,
noone.

Op 6 september 2010 23:05:46 schreef stasse:
is de vraag te gedetailleerd? of geef ik niet genoeg informatie misschien?

Beetje minder haastig? Het is hier niet klaar terwijl je wacht...
http://www.circuitsonline.net/forum/faq#topickicks

sorry voor kicken, het is gewoon frustrerend dat zoiets simpel niet lukt, zal er op letten in't vervolg.

rest van de code(hier moet je dus nog setupTimer() van in de eerste post bij denken:

//main
#include <p18f4550.h>

typedef unsigned char 	tByte;
typedef unsigned int 	tWord;
typedef unsigned long 	tLong;

// Defines
#define SET_BIT				1
#define CLEAR_BIT			0

#define configTICK_TIME				20
#define OSC_FREQ (8000000UL)
#define OSC_PER_INST (4)

int i;

void InterruptVectorHigh(void);
void InterruptHandler(void);
void setupTimer(void);
void knipperLed(void);

//==========================================================
void main()
{		
	i = 0;
	TRISD	= 0x00;		
	LATD	= 0x00;
	setupTimer();
	while(1)
	{
		INTCONbits.GIEH = SET_BIT;
	}
}
//==========================================================


//==========================================================
#pragma code InterruptVectorHigh = 0x08
void InterruptVectorHigh()
{
	_asm
		goto InterruptHandler
	_endasm
}
#pragma code
//==========================================================


//==========================================================
void InterruptHandler()
{
	PIR1bits.CCP1IF = CLEAR_BIT; //clear de CCP1 interrupt vlag
	knipperLed();	
}
//==========================================================


//==========================================================
void knipperLed()
{	
	i++;
	if( i > 49)
	{				
		LATDbits.LATD2 = 1;
		if (i > 99)
		{
			LATDbits.LATD2 = 0;
			i = 0;
		}
	}
}
//==========================================================

Mijn C is niet je dat, maar ik zie een instructie "goto Interrupthandler" terwijl dit een functie is. Ik vraag me af of dat zomaar mag zonder stackproblemen te veroorzaken.

dat doet hij gewoon hoor, ik kan daar ipv die assembly ook gewoon InterruptHandler(); zetten, dat komt op hetzelfde neer

maar dat is het probleem niet, om 1 of andere reden overflowed hij niet van de lage byte naar de hoge byte in timer1. Waarschijnlijk omdat er een register nog niet goed staat, maar ik heb alles afgelopen, op google vind ik geen gelijkaardig probleem...ik snap het niet...

Je zult wel gelijk hebben, maar een taal waarin een "GOTO" en een "CALL" op dezelfde manier werken? Kijk voor de lol eens in de gegenereerde assembler, aan het einde van de functie zal naar alle waarschijnlijkheid toch echt een 'return' staan. Als daar geen 'call' of 'rcall' tegenover staat krijg je een extra pop van de stack, dus een stackoverflow en uiteindelijk een reset.

Ik zie in de interruptroutine het volgende staan:
PIR1bits.CCP1IF = CLEAR_BIT
Maar je cleart PIR1bits.TMR1IF niet. Overigens check je ook niet welke interrupt flag aan staat, maar dat terzijde.

Ik zou alleen in de setup wel eerst de interrupts enablen voor je Timer1 start.
Ik ken de 4550 niet zo goed, maar ik zou in ieder geval wel even checken of je CCP setup wel goed is. CCP en Timer1/3 werken nl samen.

Op uw interruptvector mag je wel een goto gebruiken, maar je moet daarna op de juiste manier terugkeren nadat de interruptroutine is afgehandeld.

Waar gaat uw programma na de functie void InterruptHandler() dan heen?

Volgens mij niet terug naar de plaats in het programma waar de microcontroller zat wanneer de interrupt ontstaan is.

Via een compiler directieve moet de functie meegegeven worden dat het om een interrupt service routine gaat.


#pragma interrupt Interrupthandler
void InterruptHandler()
{
//Uw interrupt handler code
} //hier zal de compiler nu een RETFIE instructie plaatsen ipv een gewone RETURN

#pragma code

Op 7 september 2010 17:45:25 schreef hadv:
Je zult wel gelijk hebben, maar een taal waarin een "GOTO" en een "CALL" op dezelfde manier werken? Kijk voor de lol eens in de gegenereerde assembler

*knip*

Ik heb er eens naar gekeken omdat ik vond dat je een goed punt had waar ik niet aan gedacht had.

het verschil is: bij mijn code (met de assembly goto) doet hij dit:

38:                //==========================================================
39:                #pragma code InterruptVectorHigh = 0x08
40:                void InterruptVectorHigh()
41:                {
42:                	_asm
43:                		goto InterruptHandler
  0008    EF10     GOTO 0x20
  000A    F000     NOP
44:                	_endasm
45:                }
  000C    0012     RETURN 0
46:                #pragma code
47:                //==========================================================

en bij "InterruptHandler()" staat die "return 0" daar niet in (maar hij gebruikt ook een goto en geen call). de rest van de gegenereerde assembly is hetzelfde, en beide codes gaan allebei terug naar de while

ik heb wel een probleem met dit te begrijpen, wat wilt dit nu zeggen? geen vuiltje aan de lucht of toch wel? in de hardware stack zie ik trouwens geen verschil.

Ik zie in de interruptroutine het volgende staan:
PIR1bits.CCP1IF = CLEAR_BIT
Maar je cleart PIR1bits.TMR1IF niet. Overigens check je ook niet welke interrupt flag aan staat, maar dat terzijde.

móet ik die TMR1IF clearen? normaalgezien zou hij nooit mogen overflowen? en in mijn geval overflowed hij zelfs nooit (omdat hij juist niet naar de high byte gaat) dus heeft dat dan ook zin om te checken welke interruptflag aan staat? of zijn deze twee dingen gewoon goede programmeergewoontes om toe te passen?

@ peter claus: ik denk niet dat er een compiler directive moet gebruikt worden bij interruptHandler want "InterruptVectorHigh()" is eigenlijk de interrupt handler hé. Ik heb het ook getest en als ik ook nog eens een directive rond interrupthandler zet dan springt hij gewoon telkens terug (na interrupthandler) naar "goto interrupthandler" maw infinite loop in de interrupts.
Bedoel je dit of iets anders?

Op 8 september 2010 17:03:34 schreef stasse:
móet ik die TMR1IF clearen? normaalgezien zou hij nooit mogen overflowen? en in mijn geval overflowed hij zelfs nooit (omdat hij juist niet naar de high byte gaat) dus heeft dat dan ook zin om te checken welke interruptflag aan staat? of zijn deze twee dingen gewoon goede programmeergewoontes om toe te passen?

Alle interuptvlaggen van de interrupts die je ge-enabled hebt dien je te checken.

Op 8 september 2010 17:03:34 schreef stasse:
@ peter claus: ik denk niet dat er een compiler directive moet gebruikt worden bij interruptHandler want "InterruptVectorHigh()" is eigenlijk de interrupt handler hé. Ik heb het ook getest en als ik ook nog eens een directive rond interrupthandler zet dan springt hij gewoon telkens terug (na interrupthandler) naar "goto interrupthandler" maw infinite loop in de interrupts.
Bedoel je dit of iets anders?

Dat is teken dat er nog ergens een interrupt vlag aanstaat, als hij telkens weer naar de interrupthandler gaat. Kijk welke interrupts er ge-enalbled zijn en controleer de vlaggen. Maar op deze manier sluit je de interruptroutine correct af.

Nu zit je continu in de interrupt rond te zwerven zonder dat die afgesloten wordt. Natuurlijk zal er ogenschijnlijk geen vuiltje aan de lucht hangen, maar het programma doet toch niet wat het hoort te doen.

De compiler zet code neer op het adres dat je opgeeft met de #pragma code naam=adres. Maar de compiler weet daarmee niet dat het een interrupt routine is. Hij ziet dat dan als een gewone functie en plaatst op het einde een gewone RETURN. Een interrupt routine moet aan het einde een RETFIE (RETurn From IntErrupt) instructie hebben

Ik heb nog eens in de documentatie van MC's C18 compiler gekeken en zag daar in de voorbeelden dezelfde constructie: vanuit de interrupt service routine een goto naar een functie. Dan zal het, denk ik maar, wel kloppen. (Maar ik blijf er moeite mee hebben)

Ten aanzien van het clearen: kijk maar in de datasheet, 9.3 en 9.4.
Verder: zodra je meerdere interrupts gebruikt moet je namelijk wel controleren welke er aan staat en moet je er ook voor zorgen dat die bij het einde van de ISR weer is uitgezet. Anders blijf je dezelfde interrupt afhandelen.

Kijk verder eens in de documentatie van je compiler (en wellicht voorbeeld programma's) hoe je er precies voor moet zorgen dat wat je op adres 0x08 zet ook feitelijk als ISR wordt behandeld. Pragma's en zo.

Op 9 september 2010 02:55:12 schreef hadv:
vanuit de interrupt service routine een goto naar een functie. Dan zal het, denk ik maar, wel kloppen. (Maar ik blijf er moeite mee hebben)

Dit is heel normaal en wordt heel vaak gedaan.
Er komt een interrupt, en het return adres wordt op de stack gezet.
De program counter springt naar 0008.
Je wilt/kunt je ISR niet direct op 0008 zetten omdat vlak daarboven vaak adressen in gebruik zijn.
Dus plaats je op 0008 een goto naar je ISR.
Niks aan de hand, zodra de ISR afgehandeld is, en wordt afgesloten met een RETFIE (in ASM noodzakelijk, van C heb ik geen verstand) wordt het return adres van de stack gehaald en springt de PC terug naar die plek.

Na nog wat nadenken besefte ik mij het volgende:
Zodra er een interrupt optreedt wordt de PC op de stack gezet en wordt de instructie op 0x08 uitgevoerd. Dat is een goto, dus geen invloed op de stack. Vervolgens gaat-ie verder vanaf het aangegeven adres. Dat is in de regel een subroutine/functie. Die eindigt op enig moment in een Return. De oude PC wordt van de stack gehaald en het programma gaat door waar het vandaan kwam.

Het belangrijkste verschil, zoals Zonnepaneeltje al aangaf, zit in Return versus Retfie.
Retfie enablet de interrupts weer, dit doet een Return niet.
Tenzij, zoals Zonnepaneeltje ook al aangaf, de compiler hier iets slims mee doet.

Mag ik jullie eerst en vooral al bedanken voor de tijd en kennis die jullie hierin steken.

Dan gaan we verder, eerst over de flags:
bij het PIE1 register (9.4 in datasheet) zet ik TMR1IE niet hoog (enkel bit 2 CCP1IE: CCP1 Interrupt Enable bit), dus volgens mij moet ik geen enkele andere flag clearen dan de CCP1IF flag...
Dus ik snap niet goed waar er dan een andere flag gezet zou zijn die gecleared moet worden.

dan over de ISR met pragmas:
ik heb nog eens gedaan zoals peter claus zei, want inderdaad in de C18 user guide staat het ook zo:

//==========================================================
#pragma code InterruptVectorHigh = 0x08
void InterruptVectorHigh()
{
	//InterruptHandler();
	_asm
		goto InterruptHandler
	_endasm
}
#pragma code
//==========================================================


//==========================================================
#pragma interrupt InterruptHandler
void InterruptHandler()
{
	PIR1bits.CCP1IF = CLEAR_BIT; //clear de CCP1 interrupt vlag	
	knipperLed();

}
//==========================================================

Maar ik herhaal, in dit geval (mét pragma) gaat hij -als hij de interrupthandler uit gaat- terug naar de regel "goto InterruptHandler" terwijl hij de PIR1bits.CCP1IFflag terug hoog zet(alleen die flag), dus ik kan die nergens clearen om deze oneindige lus te doorbreken. (het heeft geen zin om na de regel "knipperLed();" die flag nog eens te clearen want de flag wordt daarna pas terug hoog gezet.

En dan nog altijd het initiële probleem natuurlijk, hij overflowt niet van de timer1 lage byte naar de hoge byte terwijl ik denk dat de CCPbits goed staan.

Ik heb nog eens naar de initialisatie gekeken. Je gebruikt CCP1 in Compare mode met Timer1 als bron.
Wat ik ook zie is dat bij jouw initialisatie van CCP1CON er geen interrupt plaats zal vinden! (zie 15.3.3).

CCPxM3:CCPxM0
1010 = Compare mode: generate software interrupt on compare match (CCPxIF bit is set,
CCPx pin reflects I/O state)
1011 = Compare mode: trigger special event, reset timer, start A/D conversion on CCP2 match
(CCPxIF bit is set)

En dat zou dus best eens de verklaring kunnen zijn waarom je led niet knippert.

Wat je met CCP1 doet is mij echter niet duidelijk. (Maar dat kan al gauw want ik begrijp maar heel weinig van dat Capture/Compare gedoe).

Als ik heel eerlijk mag zijn begrijp ik niet wat je nu eigenlijk voor functionaliteit aan het realiseren bent.
Als je alleen maar een ledje wilt laten knipperen is een simpele timer1 configuratie, met TMR1IF voldoende.

Ik denk dat je het probleem niet vat: het ding is dat mijn timer wél werkt als ik een waarde tussen 0 en 256 kies, maar niet als ik een hogere waarde kies omdat TMR1 niet overflowed naar zijn hoge byte.

over de ccpmodule:
De CCPmodule is gewoon iets gemakkelijker, omdat je TMR1 niet meer opnieuw moet instellen in de interruptHandler (om een exacte timing te krijgen). Als je de CCP1IE bit set, dan heb je wel gewoon interrupts en moet je de CCP1IF in de interrupthandler clearen.

met andere woorden ik kan idd ook gewoon een simpele timer1 configuratie gebruiken en dit heb ik ook getest, maar het probleem blijft. Als ik het trouwens met een simpele overflowinterrupt doet, dan interrupt hij nooit(!) omdat TMR1H niet increment, en dus ook niet overflowed.

PS: dit simpele programmaatje is eigenlijk bedoeld om te integreren in een groter geheel (een task scheduler met ticks), dus ik zou graag interrupts om de 20 msec krijgen, maar ik dacht dat dit voor het probleem niet ter zake doet.

Soms helpt het om te snappen wat de vrager wil.

Maar, ik heb wel heel ander nieuws voor je: er zit iets stevig mis in Timer1 in een heleboel devices.

http://ww1.microchip.com/downloads/en/DeviceDoc/80329B.pdf

Daar ben ik achter gekomen omdat ik
a) het verhaal wou naspelen en dus even een programmaatje ben gaan schrijven
b) bij een ander project ook een raar fenomeen met Timer1 meende te ontwaren
c) me te laat herinnerde de laatste errata up te halen

Edit enkele uren later:
Na heel wat gepiel in assembler heb ik min of meer de moed opgegeven. Het errata verhaal gaat toch over een ander probleem.
Een simpele Timer1 configuratie op 16 bits doet zelfs in SIM niet aan ophogen van TMR1H. Ik ben het spoor even helemaal bijster.

[Bericht gewijzigd door hadv op (23%)]

Die errata sheet gaat over de asynchronous counter, en ik gebruik de timer optie, dus is (denk ik) niet van toepassing. Echter, heb ik ook al overal problemen met timer 1 gelezen zoals je zei...

Ik heb nog eens wat zitten prutsen, maar het komt erop neer dat ik code heb toegevoegd en terug weggehaald (dus eigenlijk niks gedaan), maar het werkt ineens nu wel...
Dus ik heb nu een werkende timer (en dus LED) met timer1 via de normale overflow mode, een werkende timer met de CCP registers en ook heb ik een werkende gemaakt met timer0.

Op zich is het dus opgelost, ik weet alleen niet hoe, en da zie ik liever ni, want dan denk ik altijd dat mijn systeem niet stabiel is, maar goed...

als er nog reacties zijn over het compiler directive gedeelte, mag altijd hé, want ik snap jullie punt wel, alleen werkt het ni in de praktijk, en ook da vind ik wat louche...

btw TIP voor iedereen die met mplab werkt: lees de TMR1L en H uit via een variabele dan ga je de TMR1 ook effectief zien lopen in View > SFR registers.

Nogmaals iedereen bedankt voor jullie hulp!!!!

Nog iets. In je interrupthandler gebruik je de variabele "int i". Het is best om deze als "volatile" te declareren "volatile int i". Dan ben je zeker dat deze variabele niet onverwacht veranderd wordt.

true, had ik ook gelezen, merci.

[Bericht gewijzigd door Henry S. op (83%)]

Is de fout hiermee nu opgelost?

de fout is opgelost maar niet door die volatile, maar ik weet niet waardoor.

en over die compiler directives geraak ik niet uit. Het werkt hoe ik het initieel deed, en niet met die pragmas rond de ISR (rond interrupthandler) maar dat snap ik ook niet waarom dat em da ni doet...

Toch even nadenken, en de stappen en acties nog eens bekijken die voorafgingen voordat het werkte. Een fout oplossen en nog niet ontdekt hebben wat de oorzaak is, geeft geen zekerheid of het probleem nog zal terugkeren of niet. Goed overlopen tot in het kleinste detail.