unable to toggle a led on port pin using interrupt, went through code & can't find the problem

Thread Starter

chaosenvoy

Joined Aug 6, 2017
49
trying to use interrupt on port pin to toggle a led & when i run the code the led stays on , i increase the counter to 50 & the led stays off i can't seem to figure out whats wrong in the code or what i'm doing wrong
C:
// pic 18f2500
// pckit2 programmer
// MikrocPro compiler

int counter ;
void interrupt (){
INTCON.GIE = 0;                   // disable global interrupt
if (INTCON.TMR0IF == 1)      // if TMR0IF overflow
{
count ++;                              // increment overflow counter
if (count ==20) {                  // after counting 20 TMR0IF overflow
  PORTA.B0 = ~ PORTA.B0;    // toggle PORTA.B0 pin
  counter = 0;                       // reset counter to 0
{
INTCON.TMR0IF = 0;            // reset TMR0IF
INTCON.GIE = 1;                  // enable global interrupt
TMR0L = 255 - 250; // reset TMR0 counter
  }
}
}
}
void main (){
  TRISA .B0 = 0;                      // PORTA.B0 configure as output
  PORTA.B0 = 0;;                    // turn off  PORTA.B0
  T0CON = 0b11100010;          // external oscillator  8Mhz  1:8 prescaler
  INTCON.TMR0IE = 1;            // enable TMR0 interrupt
  INTCON.TMR0IP = 1;            // enable TRM0 interrupt priority
  INTCON.GIE = 1;                  // enable global interrupt
  INTCON.GIEH = 1;                // not sure if i need to enable this  since GIE  is enable
  while (1){
  }
}
Mod edit: code tags
 
Last edited by a moderator:

JohnInTX

Joined Jun 26, 2012
4,379
Welcome to AAC!
PIC18F2500? I think that's a typo. Which one are you using?

A few things though:
You don't want to manipulate GIE in the interrupt routine. The PIC does that automatically.
Most 18Fxxxx have analog inputs on PORTA. That means you need to configure those pins to digital. See the chapter on Analog-to-Digital converter in the datasheet for the register(s) to use. For example, the 18F4520 uses ADCON1 to change the pins from analog to digital.
Always write to LAT, not PORT when using read-modify-write instructions like PORTA.B0 = ~ PORTA.B0 and PORTA.B0 = 0. These should be LATA.B0 = ~ LATA.B0 and LATA.B0 = 0. If you don't you can get corrupt or inoperative operation on the output pin.
Your timer is expecting a clocking signal on TOCKI. Most periodic timers use the internal clock.
Set up your interrupt priority first. Then load the timer. Then clear TMR0IF. Then enable the timer interrupt then global interrupts.
In the interrupt priority mode, GIE and GIEH are the same bit.
I would consider using the non-prioritized interrupts.
8MHz /8 prescaler = 1us timer tick. Your timer tick is only 5us long (255-250) Is that enough to get through the interrupt routine and return before the timer overflows again?
At any rate, 5us * count == 20 is only 100us LED flash period, you can't see that with your eye.

That's all I can think of. Hope it helps.
 

Thread Starter

chaosenvoy

Joined Aug 6, 2017
49
Welcome to AAC!
PIC18F2500? I think that's a typo. Which one are you using?

A few things though:
You don't want to manipulate GIE in the interrupt routine. The PIC does that automatically.
Most 18Fxxxx have analog inputs on PORTA. That means you need to configure those pins to digital. See the chapter on Analog-to-Digital converter in the datasheet for the register(s) to use. For example, the 18F4520 uses ADCON1 to change the pins from analog to digital.
Always write to LAT, not PORT when using read-modify-write instructions like PORTA.B0 = ~ PORTA.B0 and PORTA.B0 = 0. These should be LATA.B0 = ~ LATA.B0 and LATA.B0 = 0. If you don't you can get corrupt or inoperative operation on the output pin.
Your timer is expecting a clocking signal on TOCKI. Most periodic timers use the internal clock.
Set up your interrupt priority first. Then load the timer. Then clear TMR0IF. Then enable the timer interrupt then global interrupts.
In the interrupt priority mode, GIE and GIEH are the same bit.
I would consider using the non-prioritized interrupts.
8MHz /8 prescaler = 1us timer tick. Your timer tick is only 5us long (255-250) Is that enough to get through the interrupt routine and return before the timer overflows again?
At any rate, 5us * count == 20 is only 100us LED flash period, you can't see that with your eye.

That's all I can think of. Hope it helps.
thank you JohnlnTX for your speedy reply, think i understand most of what you are saying, sorry i meant to type 18f2550, as for the pin being analog or digital i assume by the TRISA.B0 line of code i made that 1 pin digital, i will look into it & use the ADCON0 to configure them. Some of the tutorials i learnt from the guy said to disable the interrupt so they don't fire off at the start up of the circuit so that's why i did it that way, i learn something new thank you. i have an 8 Mtz crystal on pins 9 & 10 according to the data sheet.
i will try to write the code as you say with regards to what to write first & so on.
As for the non- prioritize interrupt i look at the data sheet & thought i needed to initialize it ( INTCON.TMROIP = 1) for the interrupt to fire.
Thank you for clarifying my gross miscalculation on the timer tick i will look at it & try to calculate it properly & get it working.
It's my first time on a forum i have learnt all that i know by watch tutorials as much as i can on the net & i've been trying to learn interrupts & get them working & i was stuck for a few months now i feel so relieve .
i will work on my code & once i get it working i will post my results here, if not i know where i can get much needed assistance.

Thank you again JohnlnTX
 
Last edited by a moderator:

JohnInTX

Joined Jun 26, 2012
4,379
You're welcome. Feel free to post your updates, especially when it works. If it is still giving you trouble, let us know.

Some clarification: you can use the prioritized interrupt as is for now. The reason I recommended not using it is that it generates two interrupt vectors at 0008h (high priority) and 0018h (low priority). You don't have a low priority interrupt handler i.e. void interrupt_low(void) so if it ever happened that you got an unexpected interrupt (by accidentally turning on another interrupt) you wouldn't have anything to service the IRQ and your code would go into the weeds.

After RESET, all interrupts are disabled so you don't have to worry about disabling them. As I indicated though, it is important to get everything fully configured, clear all active interrupt flags (in case they got set while you were doing the init) and only then enable first the timer's interrupt then finally GIE. Otherwise you run the risk of getting an interrupt before you are fully prepared for it.

Don't forget to change PORT to LAT.

It is a good idea not to change TRIS bits one by one. It's better to write the register as a byte i.e. TRISB = 0b'11110000' or whatever directions you need. Flipping bits one by one can have issues when peripherals share the IO pins.

You can tag a member using the 'at sign' and member name as in @chaosenvoy . The member will get an alert and can visit the thread easily. Quotes and 'likes' generate similar alerts. As you probably found out, you need to post a few times before you can edit..

Good luck!
 
Last edited:

Thread Starter

chaosenvoy

Joined Aug 6, 2017
49
You're welcome. Feel free to post your updates, especially when it works. If it is still giving you trouble, let us know.

Some clarification: you can use the prioritized interrupt as is for now. The reason I recommended not using it is that it generates two interrupt vectors at 0008h (high priority) and 0018h (low priority). You don't have a low priority interrupt handler i.e. void interrupt_low(void) so if it ever happened that you got an unexpected interrupt (by accidentally turning on another interrupt) you wouldn't have anything to service the IRQ and your code would go into the weeds.

After RESET, all interrupts are disabled so you don't have to worry about disabling them. As I indicated though, it is important to get everything fully configured, clear all active interrupt flags (in case they got set while you were doing the init) and only then enable first the timer's interrupt then finally GIE. Otherwise you run the risk of getting an interrupt before you are fully prepared for it.

Don't forget to change PORT to LAT.

It is a good idea not to change TRIS bits one by one. It's better to write the register as a byte i.e. TRISB = 0b'11110000' or whatever directions you need. Flipping bits one by one can have issues when peripherals share the IO pins.

You can tag a member using the 'at sign' and member name as in @chaosenvoy . The member will get an alert and can visit the thread easily. Quotes and 'likes' generate similar alerts. As you probably found out, you need to post a few times before you can edit..

Good luck!
@JohnlnTX
Thank you again i did what you said (i hope) & no results i'm going through the configuration of the TMR0 to make sure i'm doing it right & then i'm going through the code, if i can't figure it out i will post the way i configure the prescaler, timer & overflow & it's calculations, if i did it right i will go back to the code & will try to see where i want wrong.
Just keeping you updated on my progress
 
Last edited by a moderator:

jayanthd

Joined Jul 4, 2015
905
What duration Timer0 Interrupt you need and at what delay the Led has to blink ? If toggling an Led then it can be toggled inside the ISR itself.

If you can't create a timer0 interrupt of more than say 10ms for your Crystal frequency then you can use 10ms timer0 interrupt and then use a unsigned int counter variable inside the ISR and increment it on every interrupt. When counter becomes 100 (100 * 10ms = 1 sec) then you can toggle the Led and clear the counter.


Here is the code which I made. Check the attached file. You problem is using PORTx instead of LATx for Led.

C:
sbit Led at LATA0_bit;
sbit Led_Direction at TRISA0_bit;

unsigned char delay_counter = 0, delay_count = 2;

//Timer0
//Prescaler 1:16; TMR0 Preload = 3036; Actual Interrupt Time : 500 ms
void InitTimer0() {
    T0CON = 0x83;
    TMR0H = 0x0B;
    TMR0L = 0xDC;
    GIE_bit = 1;
    TMR0IE_bit = 1;
}

void Interrupt() {
    if((TMR0IE_bit) && (TMR0IF_bit)) {
        TMR0IF_bit = 0;
        TMR0H = 0x0B;
        TMR0L = 0xDC;
        //Enter your code here
        if(++delay_counter >= delay_count) {
            Led = ~Led;
            delay_counter = 0;
        }
    }
}

void main() {

    asm clrwdt

    CMCON = 0x07;

    ADCON1 = 0x0F;

    TRISA = 0x00;
    TRISB = 0x00;
    TRISC = 0x00;
    /*
    PORTA = 0x00;
    PORTB = 0x00;
    PORTC = 0x00;

    LATA = 0x00;
    LATB = 0x00;
    LATC = 0x00;
    */
    Delay_ms(100);

    InitTimer0();

    while(1) {
        asm clrwdt
    }
}
 

Attachments

Last edited:

Thread Starter

chaosenvoy

Joined Aug 6, 2017
49
@jayanthd & @JohnlnTX

Thank you very much for the help & the code Jayanthd i'm now learning & i don't understand all that you did in your code.
the duration of my blink on the led doesn't really matter i just started to learning about interrupts & wanted to blink a led with it
once the blink is visible & not
( PORTA.B0 = 1;
delay_ms(200);
PORTA.B0 = 0;
delay_ms(200);
& play around with the code to get different speed of the blink
(1) in line 18 if((TMR0IE_bit) && (TMR0IF_bit)) i understand if both the TMR0IF & TMR0IE are true it continue to the next line but if i writing
TMR0IF ==1;work as well to be true or do i have to do it like that
(2) i looked at CMCON = 0x07; in the data sheet & i'm a bit lost as to what you did & why
(3) TMR0H= 0x0B & TMR0L = 0xDC is it the address of the H & L registers & why do you put it in both TMR0 Init & ISR
(4) why do you need delay_ms(100) at the bottom of the code since you are using interrupt to count to toggle led time
sorry for asking so many questions but i really want to understand all that you do & why you did it
i was wondering if i could of done it in a simpler way & maybe move on to the way you did it for example

sbit Led at LATA0_bit;
sbit Led_Direction at TRISA0_bit;
unsigned char counter = 0, count = 2;
void InitTimer0(){
T0CON = 0xE3;
INTCON.GIE =1;
INTCON.TMR0IE =1;
}
void interrupt (){
if (INTCON.TMR0IF == 1) { // if TMR0IF overflow
INTCON.TMR0IF =0;
if (++counter >= count ) {
Led = ~Led;
counter = 0; // reset counter to 0
}



}
}
void main (){
TRISA = 0b00000000; // PORTA configure as output
PORTA = 0; // turn off PORTA.B0
ADCON1 = 0b00001111; // configure all Analog pins to digital
CMCON = 0b00000111; //
while (1){
}
}
i know my code isn't working & i'm having some trouble to calculate the timing with regards to the crystal i'm using & prescaler but i want to know if i can do it along that line
really willing to learn & thank you very much again
 

jayanthd

Joined Jul 4, 2015
905
You need to code like this

Code:
if((TMR0IE_bit) && (TMR0IF_bit))
if you have multiple interrupts in ISR.

Use mikroE Timer Calculator tool to generate Timer Interrupt code.
 

Thread Starter

chaosenvoy

Joined Aug 6, 2017
49
You need to code like this

Code:
if((TMR0IE_bit) && (TMR0IF_bit))
if you have multiple interrupts in ISR.

Use mikroE Timer Calculator tool to generate Timer Interrupt code.
@jayanthd
Thank you for all the help i got my code working i have the code generator & used it, my code is below to blink a led
sbit Led at LATA0_bit;
sbit Led_Direction at TRISA0_bit;
unsigned char counter = 0, count = 1;
void InitTimer0(){
T0CON = 0b10000001;
TMR0H = 0b111100;
TMR0L = 0b10110000;
INTCON.GIE =1;
INTCON.TMR0IE =1;
}
void interrupt (){
if ((TMR0IE_bit)&&(TMR0IF_bit)){
TMR0IF_bit = 0;
TMR0H = 0b111100;
TMR0L = 0b10110000;
if (++counter >= count ) {
Led = !Led;
counter = 0; // reset counter to 0
}
}
}
void main (){
TRISA = 0b00000000; // PORTA configure as output
PORTA = 0; // turn off PORTA.B0
ADCON1 = 0b00001111; // configure all Analog pins to digital
CMCON = 0b00000111;
InitTimer0();
while (1){
}
}
 

Thread Starter

chaosenvoy

Joined Aug 6, 2017
49
@JohnlnTX
Thank you for all the help i got a sample code from Jayanthd & i forgot i have the timer calculator ;i'm not just about coping code that's why i ask too many questions i'm not the sharpest tack in the box sometimes minor parts of coding gives me trouble to figure out .
i just want know ( TMR0H = 0x3C & TMR0L=oxB0) in the code what am i doing & why do i need it in the ISR & InitTimer0? i looked at the data sheet trying to figure it out & if you can't explain it to me . where can i read up on it,
still not sure if i'm doing calculations with regards to crystal & prescaler right
8 Mhz crystal HS external
this is my calculation
so 8000000 divide by 1000 = 8000
to count 256 ticks in 8000 = 8000 divide by prescaler = 8000 divide by 32 = 250 which is within the 256 ticks
so interrupt fires every 32ms
 

jayanthd

Joined Jul 4, 2015
905
QUOTE="chaosenvoy, post: 1167026, member: 465575"]
just want know ( TMR0H = 0x3C & TMR0L=oxB0) in the code what am i doing & why do i need it in the ISR & InitTimer0?
[/QUOTE]

They are timer reload register initial values. They have to be calculated based on your Fosc.

They count up from 0x3CB0 to 0xFFFF (in your case) and overflow and when overflow occurs TMR1IF_bit (Timer1 Interrupt Flag bit) is set and code execution is diverted to Timer1 ISR. Here after clearing the TMR1IF_bit you have to reload the Timer registers so that another Timer1 Interrupt can occur at the required duration.
 

Thread Starter

chaosenvoy

Joined Aug 6, 2017
49
@jayanthd
thank you for the very quick & helpful response, i try my best to learn all i can on my own till i was stuck for a few months on the interrupt, i'm still fighting with the calculations in regards to crystal, prescaler & now TMR0Hxx &TMR0Lxx.
i will go through the data sheet & see what i can figure it out, thank you for pointing me in the right direction.
as for Led = !Led , it works,i saw different ways to use toggle = ! = ~ and ^, i was just trying it out, learning process.
my next step is to be able to create any interrupt time using the timer hassle free.
i use the 8 bit timer/counter because i think it may be easier for me to calculate with it (256), instead of 16 bit (65535) timer/counter.
Thank you again & if i get into any problem i know where to look .
 

Thread Starter

chaosenvoy

Joined Aug 6, 2017
49
@jayanthd
one more thing this part in the code
unsigned char counter = 0, count = 1;

if (++counter >= count ) {
Led = !Led;
counter = 0; // reset counter to 0
vs
creating 1 variable unsigned char counter = 0;& coding
counter ++;
if (counter ==50) {
Led = !Led;
counter = 0;
is it wrong to do it the second way & i will run into problems ?
 

jayanthd

Joined Jul 4, 2015
905
In the first method you have a variable where you can change the value for different delays and you can use that variable at other places in code if required.

In second method you use value 50 and as it is not a variable like count you cannot use it at other places of the program.
 

Thread Starter

chaosenvoy

Joined Aug 6, 2017
49
@jayanthd
hello sorry but i'm having some problem in figuring out how you do the calculation to get the value of ( TMR0H & TMR0l) when i use the timer calculator ,
i know the timer calculator does it for me but i searched & couldn't find any help on calculating it for myself .
The calculator uses the 16 bit timer & it doesn't have any for the 8 bit timer i just want to be able to use any one of the timers so i can have a better understanding of the configuration bits .
Is there a place you can point me to or can you give me an example using the information below, these are the pics i have but i'm concentration on the 18f2550 for now ( 16f690 , 18f2550 & 16f997a) .

//18f2550
//MikroC Pro
// pickit2
T0CON = 0b11100100; // enable TMR0,configure as 8 bit timer,external crystal (8Mhz). low to high transition,enable prescaler .set to 1:32
this is my interrupt calculation 8000000 / 256 =31.25ms
so if i do my code like this
unsigned char counter =0, count = 100;
so that if (++counter >= count ) = 31.250 x 100 = 31.25 ms my interrupt should fire & if so how do i get the value of
TMR0H & TMR0L from here.
thank you in advance @jayanthd.
 
Top