Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

[PIC] TIMER and Interrupt : Using Timer inside Hardware ISR

Status
Not open for further replies.

shubham kumar

Member level 3
Joined
Sep 11, 2014
Messages
59
Helped
1
Reputation
2
Reaction score
1
Trophy points
8
Location
bangalore
Activity points
511
Hello friends,
I am using PIC18F452 and MikroC as compiler.
I am trying to use a timer inside a hardware Interrupt. i.e. : implementing the functionality like when a hardware interrupt is triggered, it should give 5 cycles of square wave square wave.

Here I have disabled the interrupt for TMR1 and only checking for hardware interrupt

Code:
 int cnt=0,i=0,j=0;
 
 void interrupt()         //hardware interrupt ISR
{               //for high priority interrupt
  INTCON.GIE=0;
  TMR1ON_bit= 1;   //  start the timer1
 
for(i=0;i<10;i++)                       //this loop provides 5 cycles
{
LATC.B5= ~ LATC.B5;         // toggle the output  // Blink LED
for(j=0;j<5;j++)      // Loop to provide 500ms delay;
{
while(TMR1IF_bit==0);
TMR1IF_bit=0;
}
}
TMR1ON_bit=0;
INTCON.GIE=1;
}

void main() {

    ADCON0=0;
 ADCON1=0x0F;  // no analog inputs

PORTC.B7=0;      //PortC.B7= input
TRISC.B7=1;

PORTC.B6=0;       //PortC.B6= output
TRISC.B6=0;

PORTC.B5=0;       //PortC.B5= output for continuous wave
TRISC.B5=0;

PORTB=0;       //PortB= input for interrupt
TRISB=255;

INTCON.PEIE=1;   //Enable global interrupt
INTCON.GIE=1;


        // Settings For INT1
INT1IE_bit=1;      // enable interrupt1
INT1IP_bit=1;      //Enable priority  high
INT1IF_bit=0;      // clear flag bit
INTEDG1_bit=1;          // interrupt on rising edge

// settings for Timer1
           INTCON =0xC0;   // since PEIE and GIE are already enabled.
      TMR1H= 0x0B;      // set timer values for 100ms
           TMR1L= 0xDC;
           TMR1IF_bit=0;        // clear flag bit
           TMR1IE_bit=0;         // disable timer1 interrupt
           TMR1IP_bit=0;         //priority  as low
           T1CON= 0x30;        // 16bit mode with pre-scalar of 1:8
                                // timer 1 not started

        do
        {
LATC.B6 = PORTC.B7;
        }  while(1)  ;
      }


This program is entering the ISR and also LED is blinking, but it is blinking indefinitely. Program is not coming out of the ISR.
I didn't found the way.
Is it because it may be storing the interrupt signals because practically I can't provide a perfect single pulse for interrupt.
Is there something with my hardware switch. Do I need to connect a capacitor along the way with switch for interrupt.

Or there is something wrong with the code.
 

You don't appear to be clearing the interrupt bit after entering the interrupt routine so it will immediately retrigger as soon as GIE is set again.

Note that you do not need to disable GIE yourself, it is automatically turned off when the interrupt is triggered and turned back on again at the end of the ISR.

Brian.
 

hello,


you did not define the purpose of your interrupt
You must go into the interrupt when pressing the switch , isn't it ?
so interrupt is treated only when the correct flag is ON
and you must RAZ this flag before end of interrupt
like this:


Code:
void interrupt()         //hardware interrupt ISR
{               //for high priority interrupt
if(INT1IF_bit==1)  // not an obligation, but recommanded 
{
 TMR1ON_bit= 1;   //  start the timer1
 for(i=0;i<10;i++)                       //this loop provides 5 cycles
 {
  LATC.B5= ~ LATC.B5;         // toggle the output  // Blink LED
  for(j=0;j<5;j++)      // Loop to provide 500ms delay;
  {
    while(TMR1IF_bit==0);
    TMR1IF_bit=0;
  }
 }
 TMR1ON_bit=0;
 INT1IF_bit=0; // Raz the interrupt flag = obligation !
}
}

nota: i din't test all your program.
 

@Brian

Note that you do not need to disable GIE yourself, it is automatically turned off when the interrupt is triggered and turned back on again at the end of the ISR.

May be my interpretation is wrong but suppose we are in an ISR and if a higher priority interrupt arrives, if GIE is automatically turned off then how would processor detect the higher interrupt.
As when higher priority arrives then processor must respond to it.

After this program, I want to test that how will the controller switches between higher and lower priority two interrupts. Your answer will help me for the next program also :)

@paulfjujo
You must go into the interrupt when pressing the switch , isn't it ?
Yaah I am doing the same.
 

Hello,

@betwixt @paulfjujo . yaah you were right I wasn't clearing the interrupt flag bit in ISR. Thanks.
It was such a silly mistake. Next time I will try to overcome these silly mistakes and will post a level problem.

@betwixt: Please clear me on this.
Note that you do not need to disable GIE yourself, it is automatically turned off when the interrupt is triggered and turned back on again at the end of the ISR.

As per my interpretation
suppose we are in an ISR and if a higher priority interrupt arrives, if GIE is automatically turned off then how would processor detect the higher interrupt.
As when higher priority arrives then processor must respond to it.

After this program, I want to test that how will the controller switches between higher and lower priority interrupts. Your answer will help me for the next program also :)
 

Well done for fixing the original problem.

The GIE (bit 7 in INTCON) has two purposes. If you are using 'flat' interrupts it works as a single switch to turn all the interrupts off or on, as it's name suggests, it is 'Global Interrupt Enable'. If you enable prioritized interrupts it does something different, instead of being global it becomes the high priority enable bit (GIEH) and INTCON bit 6 changes from being the peripheral interrupt enable to being the low priority interrupt enable (GIEL). You can of course enable and disable them at any time you want but during an interrupt process, either GIE (if priorities are off) or GIEH and GIEL are turned off automatically and restored when you use the RETFIE instruction or leave the ISR.

So a low priority interrupt which is itself interrupted by a higher priority one does this:

1. something triggers the low priority interrupt
2. GIEL is set to zero so no other low priority interrupts can be serviced (although their interrupt flags may still be set)
3. the low priority ISR runs
4. something triggers a high priority interrupt
5. GIEH is set to zero so no other high priority interrupts can be serviced (again, flags may still be set)
6. the high priority ISR code runs
7. At the end of the high priority ISR the GIEH is restored and if any other high priority interrupt flags are set, the ISR services them
8. when all the high priority interrupts are finished, GIEL is turned back on
9. the low priority routine resumes and after finishing if no other low priority flags are set, the ISR ends

There is quite a good explanation in section 8 of the data sheet.

Brian.
 
@betwixt

I made the program where timer0 is used as low priority interrupt and generating a square wave. when an interrupt (high priority) INT1 arrives, controller jumps to another ISR and there with the help of timer1 it is generating 5 cycles of a wave.

It functioning correctly. Please tell me if there something which I can improve as better programming style {{just as you told about no need of enabling and disabling GIE in ISR }} so that I'll not repeat them in future.

[[other than handling ISR functionality in main(). I made the same program with ISR handling in main also but this code was more relevant to the post so I am updating this one.

Code:
 int cnt=0,cnt1=0,i=0,j=0;

void interrupt (void)                        //hardware interrupt ISR
{
 if(INT1IF_bit==1)
{                                       //for high priority interrupt
  TMR1ON_bit= 1;        //  start the timer1
  INT1IF_bit=0;
for(i=0;i<10;i++)                       //this loop provides 5 cycles
{
LATC.B5= ~LATC.B5;                        // toggle the output
for(j=0;j<5;j++)                // Loop to provide 500ms delay;
{
while(TMR1IF_bit==0);
TMR1IF_bit=0;
}
}
TMR1ON_bit= 0;
TMR1H= 0x0B;                // set timer values for 100ms
TMR1L= 0xDC;
}
}

void interrupt_low ()                //timer interrupt ISR     // for low priority interrupt
 {
 if(TMR0IF_bit==1)
 {
cnt++;
if(cnt>=5)
{
cnt=0;
LATC.B4= ~LATC.B4;                 // toggle PORTC.B5
}
TMR0IF_bit=0;
TMR0H= 0x0B;
TMR0L=0xDC;
}
  }

void main() {

    ADCON0=0;
 ADCON1=0x0F;         // no analog inputs

PORTC.B7=0;                //PortC.B7= input
TRISC.B7=1;

PORTC.B6=0;                 //PortC.B6= output
TRISC.B6=0;

PORTC.B5=0;                 //PortC.B5= output for continuous wave
TRISC.B5=0;

 PORTC.B4=0;                 //PortC.B5= output for continuous wave
TRISC.B4=0;

PORTB=0;                 //PortB= input for interrupt
TRISB=255;

RCON =0x80;
INTCON.GIEH=1;
INTCON.GIEL=1;


    // Settings For INT1
INT1IE_bit=1;                // enable interrupt1
INT1IP_bit=1;                //Enable priority  high
INT1IF_bit=0;                // clear flag bit
INTEDG1_bit=1;       // interrupt on rising edge

// settings for Timer1
           INTCON =0xC0;   // since PEIE and GIE are already enabled.
           TMR1H= 0x0B;                // set timer values for 100ms
           TMR1L= 0xDC;
           TMR1IF_bit=0;             // clear flag bit
           TMR1IE_bit=0;              // disable timer1 interrupt
           TMR1IP_bit=0;         //priority  as low
           T1CON= 0x30;             // 16bit mode with pre-scalar of 1:8
                                // timer 1 not started*/*/
         // settings for Timer3
           INTCON =0xC0;   // since PEIE and GIE are already enabled.
           TMR0H= 0x0B;                // set timer values for 100ms
           TMR0L= 0xDC;
           TMR0IF_bit=0;             // clear flag bit
           TMR0IE_bit=1;              // enable timer3 interrupt
           TMR0IP_bit=0;         //priority  as low
           T0CON= 0x82;             // 16bit mode with pre-scalar of 1:8
                                // timer 0 started

        do
        {
LATC.B6 = PORTC.B7;
        }  while(1)  ;
      }
 

I'm not sure if the Edaboard software is changing your writing style but I would suggest this to make life easier in the future:

1. Be careful with the indentation you use. Try to indent each block of nested code (using the tab key is easiest) so the structure of the program is more obvious. Exactly how it looks is your choice and the compiler doesn't care at all but to make it easier to follow, try to keep to the same style. For example
Code:
        do
        {
LATC.B6 = PORTC.B7;
        }  while(1)  ;

looks a bit strange, if you format it like this
Code:
        do
        {
            LATC.B6 = PORTC.B7;
        }while(1);

the algnment of the { and } make it clearer that the lines between them are grouped together.

2. Try to use aliases. For example, instead of
Code:
if(cnt>=5)
{
cnt=0;
LATC.B4= ~LATC.B4;                 // toggle PORTC.B5
}

use
Code:
if(cnt>=5)
{
    cnt=0;
    OutputPin = ~OutputPin;                 // toggle output pin
}

which makes it clearer what the instruction is doing. You can use a #define to associate PORTC.B4 (or is it B5 ??) with the 'friendly' name.

From a functional aspect, I'm not sure what your program actually does but if you are trying to keep the square wave constant throughout the interrupt, consider using the PWM output instead. It will produce a signal entirely from hardware so once set up it will continue running independently of the program code.

Brian.
 
Thanks a lot.
It is helpful for me. I'll try to incorporate in my style and also next time you don't need to mug-up deeply with my code/doubts.
 
Last edited:

hello,


it is better to arm (autorisation) interrupts AFTER all init are made..
so just before the infinite loop in main program

Code:
.....
INTCON.GIEH=1;
INTCON.GIEL=1;
do
{
   LATC.B6 = PORTC.B7;
} 
while(1) *;
}

and , like betwixt allready tell you, improve the code presentation by using indentation
wich render more readeable the code.
You have to reinit timer1 value inside each loop if you want to keep the initial 100mS time
else you will count the maximum 65535 cycles when the first timer1 out arrive..

place also, the RAZ of interrupt flag INTIF at the end of interrupt treatment, otherwise
you can get another interrupt during the interrupt treatment..

Code:
void interrupt (void)                        //hardware interrupt ISR
{
 if(INT1IF_bit==1)
 {                                          //for high priority interrupt
  for(i=0;i<10;i++)                  //this loop provides 5 cycles
  {
    LATC.B5= ~LATC.B5;          // toggle the output  
    for(j=0;j<5;j++)                 // Loop to provide 500ms delay;
    {
        TMR1ON_bit= 0;
        TMR1H= 0x0B;                // set timer values for 100ms
        TMR1L= 0xDC;
        TMR1ON_bit= 1;            //  start the timer1
        while(TMR1IF_bit==0);
        TMR1IF_bit=0;
    }
  }
  INT1IF_bit=0;
 } 
}
 

Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top