Timer - Interruptafhandeling

Postby arjen h. » Thu Jan 10, 2008 12:00 am

Twee avonden aan het prutsen geweest om Timer0 werkend te krijgen, niet gelukt dus. Of misschien wel, maar zie ik het resultaat alleen niet. Wellicht dat jullie mij kunnen helpen.

In eerste instantie is het doel een ledje te laten knipperen met een frequentie van 1 Hz.

Ik maak gebruik van Timer0 met interrupt om een interrupt-frequentie te maken van 0,1 ms (resolutie die ik later nog een keer nodig heb). Hiervoor moet (volgens mij) de prescaler op 2 gezet worden en de counter van timer 0 op 5.
(255-5 tikken * prescaler 2 * 5 Mhz = 250 * 2 * 0,2 us = 0,1 ms)

In de interrupt routine vervolgens elke keer een counter++.
Bij counter == 5000 gaat ledje aan, bij counter == 10000 ledje uit en counter = 0.

De code:
OpenTimer0(TIMER_INT_ON & T0_8BIT & T0_SOURCE_INT & T0_EDGE_RISE & T0_PS_1_2); 
WriteTimer0(5); // interrupt each 0.1 ms
INTCONbits.GIE = 1;

en
static unsigned int t01msDelay = 0; 
if (INTCONbits.TMR0IF==1)
{
INTCONbits.TMR0IF=0; //clear interrupt flag
WriteTimer0(5);
t01msDelay++;
if (t01msDelay == 5000)
{
LATDbits.LATD0 = 1;
}
if (t01msDelay == 10000)
{
LATDbits.LATD0 = 0;
t01msDelay = 0;
}
}


Het stukje met OpenTimer0 heb ik onderin void UserInit(void) in user.c geplaatst.
De interrupt afhandeling in void low_isr(void) in interrupt.c.

Ook heb ik bovenstaande code geprobeerd in main.c, respectievelijk onderin static void InitializeSystem(void) en in void _low_ISR (void).

Met de code INTCONbits.GIE = 1; meldt de computer dat Het USB-apparaat niet wordt herkend. Deze regel dus maar verwijderd, maar het resultaat is er dus nog niet.

Groeten, Arjen.
Het Elektor forum is voor de lezers; voor een vraag aan de redactie kun je het beste Elektor een email sturen.
arjen h.
Moderator
 
Posts: 1290
Joined: Thu Jan 02, 2014 10:40 am

Postby pjongen » Thu Jan 10, 2008 12:00 am

Hoi Arjen,

Ik ga niet proberen te analyseren wat er bij jou mist, maar neem hier gelijk de code over zoals die in mijn programma staat en zoals die werkt.

In user.c staat als laatste:
  
// We use timer 0 to generate interupt to update display and scan keys
OpenTimer0 (TIMER_INT_ON & T0_SOURCE_INT & T0_8BIT & T0_PS_1_64);
// Timer 0, CPU is OSC/2 = 96MHz/2 = 48MHz
// Timer 0 is CPUCLock/4
// Prescale 64 = (48/4)/64 -> counter
// 8 Bits Cntr = ((48/4)/64)/256
// Thus freq = 732 Hz, Interval= 1,365 mSec
// This means button input is checked every 10,9 mS.
//
INTCONbits.GIE = 1; // Enable interupts

} //end User Intit


In main.c staat:

#pragma code _HIGH_INTERRUPT_VECTOR = 0x000808 
void _high_ISR (void)
{
_asm goto _isrPJ _endasm
}


Let even niet op die 0x000808, dat is ivm de usb boot, dat moet bij jou 0x00008 zijn. Want dat is de locatie van de interupt vector.

De interupt gaat dus eerst naar een routine (die ook in main.c) staat, dit is de Interupt Service Routine PJ. (isrPJ)
Ik test eerst of het wel een timero interupt is. Strikt genomen hoeft dat niet als je maar een interupt verwacht, maar het is wel zo elegant. Daarom heb ik ook deze extra tussenstap genomen. Eerst kijken welke interupt en dan pas afhandelen.

#pragma interrupthigh _isrPJ 
void _isrPJ (void)
{
if (INTCONbits.TMR0IF==1)
{
updateI(); // Handle the interupt in USER.C
INTCONbits.TMR0IF = 0;
}
}


De eigenlijke functie die tijdens de interupt wordt afgehandeld heet dus updateI() en die staat in user.c

void updateI(void) 
{

//Hier handel ik alle scaning van displays en buttons af.
.....


Jammer dat de site me niet toestaat het bericht eerst te zien zoals het geplaatst wordt voor ik opsla, nu ben ik nog even teruggegaan om een en ander wat leesbaarder te maken.


De interupt timing is iets anders dan jij wil, maar probeer het eerst hiermee, voordat je in te interupt de timer waarde gaat zetten. Dus eerst even zonder timer 0 te veranderen in de interupt service routine.

Ik hoop dat het je hiermee lukt.

Peter
pjongen
 
Posts: 64
Joined: Thu Jan 02, 2014 3:37 pm

Postby arjen h. » Fri Jan 11, 2008 12:00 am

Hallo Peter,

Ik heb je code geknipt en geplakt, helaas nog niet werkend.
De verschijnselen:

Met INTCONbits.GIE = 1; in de code blijft het probleem dat het USB-apparaat niet wordt herkend.

De compiler klaagt over #pragma interrupthigh _isrPJ, de melding is Warning [2002] unknown pragma 'interrupthigh'.

De code compileert wel, echter doet hij het dus niet.


Bij mij was de regel met 0x000808 uitgecommentarieerd. Als proef heb ik de regel met 0x000008 toegevoegd.
#pragma code _HIGH_INTERRUPT_VECTOR = 0x000008     
//#pragma code _HIGH_INTERRUPT_VECTOR = 0x000808
Echter dan verschijnt de melding Error - section 'high_vector' can not fit the absolute section. Section 'high_vector' start=0x00000008, length=0x00000006
.

Misschien kun je toch een blik op de code werpen?
Main.c en User.c heb ik ook bijgevoegd.

#pragma interrupthigh _isrPJ 
void _isrPJ (void)
{
if (INTCONbits.TMR0IF==1)
{
UpdateI(); // Handle the interupt in USER.C
INTCONbits.TMR0IF = 0;
}
}

//#pragma code _HIGH_INTERRUPT_VECTOR = 0x000008
//#pragma code _HIGH_INTERRUPT_VECTOR = 0x000808
void _high_ISR (void)
{
mLCD_DATA = 1; // ledje aan
_asm goto _isrPJ _endasm
;
}

en
void UserInit(void) 
{
...
OpenTimer0 (TIMER_INT_ON & T0_SOURCE_INT & T0_8BIT & T0_PS_1_64);
INTCONbits.GIE = 1; // Enable interupts
mLCD_DATA = 0; // ledje uit
mLCD_CLOCK = 0; // ledje uit
}//end UserInit

void UpdateI(void)
{
mLCD_CLOCK = 1; // ledje aan
}


Groeten, Arjen.
(en nu is het bedtijd)
Attachments
main.c
(8.86 KiB) Downloaded 60 times
user.c
(20.95 KiB) Downloaded 59 times
Het Elektor forum is voor de lezers; voor een vraag aan de redactie kun je het beste Elektor een email sturen.
arjen h.
Moderator
 
Posts: 1290
Joined: Thu Jan 02, 2014 10:40 am

Postby pjongen » Fri Jan 11, 2008 12:00 am

Arjen,

Dat krijg je als je op de late avond nog even een antwoordje maakt met copy/past en dan ziet dat als het er staat dat het onleesbaar is. Bij het nabewerken van alles dacht ik dat ik ‘high’ had uitgewist (ik twijfelde) dus bijgetyped. Stom moet ik niet doen.
In de nabewerking dus foutje gemaakt;

Er bestaat: “#pragma interrupt” en “pragma interuptlow”, enfin hieronder nu correct.

#pragma interrupt _isrPJ 
void _isrPJ (void)
{
if (INTCONbits.TMR0IF==1)
{
updateI();// Handle the interupt in USER.C
INTCONbits.TMR0IF = 0;
}
}


Maar het volgende is de echte oorzaak van je ellende:
Error - section 'high_vector' can not fit the absolute section. Section 'high_vector' start=0x00000008, length=0x00000006Dit zegt er is geen ruimte voor de interupt vector.

Dat klopt, want nu ik in jouw main.c zie ik dat je de routine _isrPJ op de interrupt vector plaatst (proberen te leggen). Maar tussen 0X008 en 0X0018 is geen ruimte om veel meer te doen dan het saven van W en een jump/goto naar de eigenlijke interrupt service routine.

De routine _isrPJ moet dus ergens anders in de source staan, dus ergens in de code page. Nu mag dat ook weer niet al te willekeurig want deze routine moet dan wel weer binnen het (relatieve) goto bereik van de interrupt vector geplaatst moet worden.
Dat doe je met #pragma interrupt(low)

Maar ook dan zal het nog niet werken want,
Voor de interrupt vectoren heb je “ #pragma code“ staan. Dat betekent dat de linkers, zonder verder aanwijzingen, dat stukje code in de code page zal leggen, (na adres 0X2A, zie linker script). Dus zo komt het niet op niet op de interrupt vector terecht.

Vóór alles moet je ervoor zorgen dat de interrupt vectoren op de absolute adressen 0X0008 en 0X00018 terecht komen. Daar stuurt de hardware de processor immers naar toe bij een interrupt.

Dat doe je door
#pragma code _HIGH_INTERRUPT_VECTOR = 0x000008

Dit vertelt de compiler/linker om het volgende stukje code op adres 0X008 te beginnen.

In de originele code is die pragma eruit-gehaald. (Duidelijk is te zien dat de maker van de originele code gebruik heeft gemaakt van de USB-bootloader.) Omdat hij geen interrupts gebruikt kon hij ze ook weglaten. De linker laat die adressen vanzelf vrij omdat hij de code op 0X02A begint te plaatsen (zie linkscript)

Dus moet je het volgende programmeren in de source:
 
#pragma code _HIGH_INTERRUPT_VECTOR = 0x000008
void _high_ISR (void)
{
_asm goto _isrPJ _endasm
}

#pragma code _LOW_INTERRUPT_VECTOR = 0x000018
void _low_ISR (void)
{
;
}


Bij een interrupt gaat je programma onmiddelijk naar de routine _isrPJ Die doet het nodige voorwerk en gaat de interupt de distibueren. (in mijn geval naar updateI().

Dus:
Interupt -> Meteen goto naar de ISR
In de ISR zoek je uit waar die interupt vandaan komt en wie die moet behandelen. Ook al kan dat in jou geval alleen maar de timer0 zijn, dan noch zou ik deze werkwijze aanhouden.

Ik kan jouw source niet testcompileren omdat ik de nodige refs mis uit andere delen, maar met deze uitleg moet je er wel komen denk ik.

Lukt het nu wel?

Peter
pjongen
 
Posts: 64
Joined: Thu Jan 02, 2014 3:37 pm

Postby arjen h. » Fri Jan 11, 2008 12:00 am

pjongenLukt het nu wel?

Nu even geen tijd (meer), maar vanavond ga ik nog wel even wat proberen.

Vanochtend heb ik dit geprobeerd om te kijken of de timer uberhaupt wel loopt.
void main(void) 
{
int result;

InitializeSystem();

OpenTimer0 (TIMER_INT_OFF & T0_SOURCE_INT & T0_16BIT & T0_PS_1_128);
WriteTimer0(0);

while(1)
{
USBTasks(); // USB Tasks
ProcessIO(); // See user\user.c & .h

result = ReadTimer0();
mLCD_DATA = 0;
if (result > 20000)
{
mLCD_DATA = 1;
if (result > 40000)
{
WriteTimer0(0);
}
}

}//end while
}//end main
Het ledje gaat keurig aan en uit, timer loopt .

Ik zal het laten weten zodra het met de interrupt ook gelukt is.
In ieder geval bedankt.

Groeten, Arjen.
Het Elektor forum is voor de lezers; voor een vraag aan de redactie kun je het beste Elektor een email sturen.
arjen h.
Moderator
 
Posts: 1290
Joined: Thu Jan 02, 2014 10:40 am

Postby arjen h. » Fri Jan 11, 2008 12:00 am

Je verhaal is verhelderend , maar het is nog niet gelukt, waarschijnlijk iets kleins...

Met onderstaande code houd ik de melding Error - section high_vector' can not fit the absolute section. Section 'high_vector' start=0x00000008, length=0x00000006
.

#pragma code _HIGH_INTERRUPT_VECTOR = 0x000008 
void _isrPJ (void);
void _high_ISR (void)
{
_asm goto _isrPJ _endasm
}

#pragma code _LOW_INTERRUPT_VECTOR = 0x000018
void _low_ISR (void)
{
;
}

#pragma code
#pragma interrupt _isrPJ
void _isrPJ (void)
{
if (INTCONbits.TMR0IF==1)
{
UpdateI(); // Handle the interupt in USER.C
INTCONbits.TMR0IF = 0;
}
}



Ik ben een poos op internet wezen zoeken en vergelijken, maar de code die ik heb gevonden biedt voor mij geen oplossing (allemaal vergelijkbaar).

Heb je nog een suggestie?

Groeten, Arjen.'
Attachments
main_1.c
(8.68 KiB) Downloaded 46 times
Het Elektor forum is voor de lezers; voor een vraag aan de redactie kun je het beste Elektor een email sturen.
arjen h.
Moderator
 
Posts: 1290
Joined: Thu Jan 02, 2014 10:40 am

Postby pjongen » Fri Jan 11, 2008 12:00 am

Even snel, want ik moet nu weg.
Ik heb wat aangepast.


Peter
Attachments
main_5f1.c
(8.65 KiB) Downloaded 50 times
pjongen
 
Posts: 64
Joined: Thu Jan 02, 2014 3:37 pm

Postby arjen h. » Fri Jan 11, 2008 12:00 am

Werkend !

Eigenlijk te simpel voor woorden, maar zo is het meestal.

In het project had ik interrupt.h en interrupt.c zitten.
Hier zitten de interruptvectoren en functies al in.
Nu deze vectoren en functies ook in main.c zitten is dat dubbelop en loopt de boel kennelijk in het honderd.
Oplossing: vectoren en functies in main.c verwijderen en de routines in interrupt.h en interrupt.c gebruiken.

In interrupt.c

void high_isr(void) 
{
if (INTCONbits.TMR0IF==1)
{
INTCONbits.TMR0IF = 0; // reset interrupt
WriteTimer0(0); // reset timer

mLCD_DATA = !mLCD_DATA; // toggle led
}
}


en in void UserInit(void) in user.c

OpenTimer0 (TIMER_INT_ON & T0_SOURCE_INT & T0_16BIT & T0_PS_1_128); 
WriteTimer0(0);
mEnableInterrupt();


In main.c en user.c nog wel even de include toevoegen.
#include "system\interrupt\interrupt.h"


En in interrupt.c de include naar user.h als de interruptafhandeling in user.c gedaan gaat worden.
#include "user\user.h"


Peter, bedankt voor je hulp, je had wel net niet de gouden tip (had je gedacht dat het aan interrupt.h en .c kon liggen?), maar ik ben er wel een stuk wijzer van geworden .

Groeten, Arjen.

(ik ga Thijs vragen om de titel van het topic te wijzigen in "Interruptafhandeling" wat gezien de oorzaak en oplossing van mijn probleem meer voor de hand ligt)
Het Elektor forum is voor de lezers; voor een vraag aan de redactie kun je het beste Elektor een email sturen.
arjen h.
Moderator
 
Posts: 1290
Joined: Thu Jan 02, 2014 10:40 am

Postby pjongen » Sat Jan 12, 2008 12:00 am

Arjen,

Ik kon niet weten wat jij wel en niet in je project had zitten.

Wat ik je voorstelde te doen was exact hetzelfde als in interrupt.c staat, alleen dan opgenomen in main. Dat mag geen verschil uitmaken. Maar ja, niet op 2 plaatsen hetzelfde, dat werkt niet.

Ben blij voor je dat het is opgelost en je verder kunt met je project.

Dit onderwerp hoort inderdaad meer onder microprocessors algemeen thuis dan onder data acquisitie.

Peter
pjongen
 
Posts: 64
Joined: Thu Jan 02, 2014 3:37 pm

Postby arjen h. » Sat Jan 12, 2008 12:00 am

pjongenWat ik je voorstelde te doen was exact hetzelfde als in interrupt.c staat, alleen dan opgenomen in main.

Inderdaad, dit kwam ik dus ook meerdere malen op internet tegen. Oftewel jouw code was gewoon goed en het moest dus aan iets anders liggen.

Ik ben (bijna ten einde raad) alle files nog eens langs gelopen en zag toen (weer) de code in interrupt.c. Deze was identiek aan jouw code. Vervolgens interrupt.h en interrupt.c uit het project verwijderd en werken dus.

De sleutel tot de oplossing stond dus eigenlijk al in mijn eerste bericht...

Het viel mij verder op dat er op internet weinig uitleg over interrupts is te vinden. Het was puur vergelijken van stukjes code. Jouw verhaal is dus een prima stukje proza om wat van te leren...
Het Elektor forum is voor de lezers; voor een vraag aan de redactie kun je het beste Elektor een email sturen.
arjen h.
Moderator
 
Posts: 1290
Joined: Thu Jan 02, 2014 10:40 am

Next

Return to 2007-11 USB data-acquisitiekaart

Who is online

Users browsing this forum: No registered users and 1 guest