chipKIT® Development Platform

Inspired by Arduino™

MPIDE TimerB ISR

Created Fri, 15 May 2015 05:01:21 +0000 by Suwandy


Suwandy

Fri, 15 May 2015 05:01:21 +0000

Hi everyone, I'm trying to create simple stopwatch that tick every 1 second using timer B (32-bit mode). I've been searching around on how define the ISR routine around google but find no single clue for MPIDE usage. I'm using ChipKit Uno32 PIC32MX and MPIDE as editor. Here is my code:

void setup(){
T2CON=0; T3CON=0; IEC0=0; TMR4=0;

IEC0 |= (1<<12); //Timer3 Interrupt Enabled

PR2 = 0xFFFFFFFF;  //Timer23 Top FFFF FFFF

T2CON |= (1<<3);  //T32 mode enabled
T2CON &= (0<<4);  //0
T2CON |= (1<<5);  //1    110 ->  64 prescaller
T2CON |= (1<<6);  //1
T2CON |= (1<<15); //Timer enabled

}

void __ISR(_TIMER_3_VECTOR,IPL2) Timer23Handler(void){
IFS0 &= (0<<12);  // clear the interrupt flag
}

void loop(){

So far, i got this error saying "Variable or field '__ISR' declared void" and "IPL2 was not declared in this scope". I tried typing ISR with arduino style:

ISR(TIMER3_OVF_vect){
}

But also got an error: "Expected constructor, destructor, or type conversion before '(' token"

How to properly write ISR for timer??


majenko

Fri, 15 May 2015 09:34:07 +0000

The second post on the front page of chipKIT.net is what you're after.

[url]http://chipkit.net/interrupts-made-easy-with-chipkit/[/url]

All you could ever need to know about working with chipKIT Interrupts - even an example using a timer.


Suwandy

Sun, 17 May 2015 01:51:55 +0000

Dear Majenko, Thank you for replying. I tried the suggestion form attached .pdf in that post. From the code, i still got error saying: expected initializer before 'myISR'

void __USER_ISR myISR(){
  clearIntFlag(_TIMER_3_IRQ);
}

Also error for below code: expected constructor, destructor, or type conversion before '(' token

setIntVector(_TIMER_3_VECTOR,myISR);
setIntPriority(_TIMER_3_VECTOR, 4, 0);

What gives?


majenko

Sun, 17 May 2015 08:51:28 +0000

It sounds like you're using an old version of MPIDE with the old compiler. You should upgrade.


Suwandy

Sun, 17 May 2015 11:03:55 +0000

I have mpide-0023-windows-20140821. this link [url]http://chipkit.net/started/install-chipkit-software/[/url] lead me to the same version so i think it is already up to date. I attach my current version screenshot.


majenko

Sun, 17 May 2015 16:44:39 +0000

Do you have the Wi-FIRE board in that version of MPIDE?


Suwandy

Tue, 19 May 2015 00:16:33 +0000

You mean this? it was there


majenko

Tue, 19 May 2015 09:48:08 +0000

Ok, it looks like that macro was actually added in November - so it's not in your version. You could grab the latest test release of MPIDE, or you could use the beta version of UECIDE, or you could just add the macro to your own program:

#define __USER_ISR __attribute__((nomips16, interrupt()))

guymc

Tue, 19 May 2015 16:25:50 +0000

On the "Install chipKIT Software" page, there are instructions for finding more recent test versions of MPIDE. Scroll down a little, and look for a heading that says "Having Trouble Porting Code?" In that section, you will find links to all of our software builds.

The "official" release of MPIDE is somewhat behind the times. The test build dated 20150204 works great for me. You will also find a link to UECIDE, a much improved IDE that supports chipKIT boards, in addition to several other embedded systems.

Cheers


atriaharsha

Tue, 02 Jun 2015 14:30:07 +0000

Here is the time delay function you might get some hwlp from that..

/******************************************************************************

File Name: TimeDelay.c Dependencies: None Processor: PIC10/PIC12/PIC16/PIC18/PIC24/dsPIC30/dsPIC33/PIC32 Compiler: PICC, C18, C30, C32 Company: Microchip Technology, Inc.

Copyright (C) 2011 Microchip Technology Inc. All rights reserved.

Microchip licenses to you the right to use, modify, copy and distribute Software only when embedded on a Microchip microcontroller or digital signal controller that is integrated into your product or third party product (pursuant to the sublicense terms in the accompanying license agreement).

You should refer to the license agreement accompanying this Software for additional information regarding your rights and obligations.

SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY, TITLE, NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MICROCHIP OR ITS LICENSORS BE LIABLE OR OBLIGATED UNDER CONTRACT, NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, BREACH OF WARRANTY, OR OTHER LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT DAMAGES OR EXPENSES INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT, PUNITIVE OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY CLAIMS BY THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER SIMILAR COSTS.

Date Comments

2009.10.14 File created 2009.10.15 CHANGED C18 DELAY ROUTINE TO DECREMENT ENTIRE NUMBER OF CYCLES 2009.10.19 CHANGED C30 DELAY ROUTINE TO MATCH C18 IMPLEMENTATION 2009.10.26 ADDED C32 DELAY ROUTINE TO MATCH C18 IMPLEMENTATION 2009.10.27 CONSOLIDATED C30 AND C32 IMPLEMENTATIONS, ADDED PADDING TO MAKE C30 DELAYS MORE ACCURATE 2010.01.26 Converted locals to volatile 2010.03.07 Added include "Compiler.h" 2010.10.13 Added PICC support *******************************************************************************/ #if defined(PIC32MX) #include <plib.h> #endif

// My Definitions #define GetSystemClock() 80000000L #define GetInstructionClock() GetSystemClock() #define GetPeripheralClock() GetSystemClock()

//#include "Include/Compiler.h" #include "Include/Compiler.h" //#include "HardwareProfile.h" #include "TimeDelay.h"

/**************************************************************************** Function: void Delay10us( UINT32 tenMicroSecondCounter )

Description: This routine performs a software delay in intervals of 10 microseconds.

Precondition: None

Parameters: UINT32 tenMicroSecondCounter - number of ten microsecond delays to perform at once.

Returns: None

Remarks: None ***************************************************************************/ void Delay10us( UINT32 tenMicroSecondCounter ) { volatile INT32 cyclesRequiredForEntireDelay;

#if defined(__18CXX) || defined (COMPILER_HITECH_PICC)

    if (GetInstructionClock() &lt;= 2500000) //for all FCY speeds under 2MHz (FOSC &lt;= 10MHz)
    {
        //26 cycles burned through this path (includes return to caller).
        //For FOSC == 1MHZ, it takes 104us.
        //For FOSC == 4MHZ, it takes 26us
        //For FOSC == 8MHZ, it takes 13us.
        //For FOSC == 10MHZ, it takes 10.5us.
    }
    else
    {
        //14 cycles burned to this point.
        
        //We want to pre-calculate number of cycles required to delay 10us * tenMicroSecondCounter using a 1 cycle granule.
        cyclesRequiredForEntireDelay = (INT32)(GetInstructionClock()/100000) * tenMicroSecondCounter;
        
        //We subtract all the cycles used up until we reach the while loop below, where each loop cycle count is subtracted.
        //Also we subtract the 22 cycle function return.
        cyclesRequiredForEntireDelay -= (153 + 22);
        
        if (cyclesRequiredForEntireDelay &lt;= 45)
        {
            // If we have exceeded the cycle count already, bail! Best compromise between FOSC == 12MHz and FOSC == 24MHz.
        }    
        else
        {
            //Try as you may, you can't get out of this heavier-duty case under 30us. ;]
            
            while (cyclesRequiredForEntireDelay&gt;0) //153 cycles used to this point.
            {
                Nop(); //Delay one instruction cycle at a time, not absolutely necessary.
                cyclesRequiredForEntireDelay -= 42; //Subtract cycles burned while doing each delay stage, 42 in this case.
            }
        }
    }

#elif defined(__C30__) || defined(__PIC32MX__)

    if(GetInstructionClock() &lt;= 500000) //for all FCY speeds under 500KHz (FOSC &lt;= 1MHz)
    {
        //10 cycles burned through this path (includes return to caller).
        //For FOSC == 1MHZ, it takes 5us.
        //For FOSC == 4MHZ, it takes 0.5us
        //For FOSC == 8MHZ, it takes 0.25us.
        //For FOSC == 10MHZ, it takes 0.2us.
    }    
    else
    {
        //7 cycles burned to this point.
        
        //We want to pre-calculate number of cycles required to delay 10us * tenMicroSecondCounter using a 1 cycle granule.
        cyclesRequiredForEntireDelay = (INT32)(GetInstructionClock()/100000)*tenMicroSecondCounter;
        
        #if defined(__C30__)
            //We subtract all the cycles used up until we reach the while loop below, where each loop cycle count is subtracted.
            //Also we subtract the 5 cycle function return.
            cyclesRequiredForEntireDelay -= 44; //(29 + 5) + 10 cycles padding
        #elif defined(__PIC32MX__)
            //We subtract all the cycles used up until we reach the while loop below, where each loop cycle count is subtracted.
            //Also we subtract the 5 cycle function return.
            cyclesRequiredForEntireDelay -= 24; //(19 + 5)
        #endif
        
        if(cyclesRequiredForEntireDelay &lt;= 0)
        {
            // If we have exceeded the cycle count already, bail!
        }
        else
        {   
            while(cyclesRequiredForEntireDelay&gt;0) //19 cycles used to this point.
            {
                #if defined(__C30__)
                    cyclesRequiredForEntireDelay -= 11; //Subtract cycles burned while doing each delay stage, 12 in this case. Add one cycle as padding.
                #elif defined(__PIC32MX__)
                    cyclesRequiredForEntireDelay -= 8; //Subtract cycles burned while doing each delay stage, 8 in this case.
                #endif
            }
        }
    }
#endif

}

/**************************************************************************** Function: void DelayMs( UINT16 ms )

Description: This routine performs a software delay in intervals of 1 millisecond.

Precondition: None

Parameters: UINT16 ms - number of one millisecond delays to perform at once.

Returns: None

Remarks: None ***************************************************************************/ void DelayMs( UINT16 ms ) { #if defined(__18CXX) || defined (COMPILER_HITECH_PICC)

    INT32 cyclesRequiredForEntireDelay;
    
    // We want to pre-calculate number of cycles required to delay 1ms, using a 1 cycle granule.
    cyclesRequiredForEntireDelay = (signed long)(GetInstructionClock()/1000) * ms;
    
    // We subtract all the cycles used up until we reach the while loop below, where each loop cycle count is subtracted.
    // Also we subtract the 22 cycle function return.
    cyclesRequiredForEntireDelay -= (148 + 22);

    if (cyclesRequiredForEntireDelay &lt;= (170+25)) 
    {
        return;     // If we have exceeded the cycle count already, bail!
    }    
    else
    {
        while (cyclesRequiredForEntireDelay &gt; 0) //148 cycles used to this point.
        {
            Nop();                              // Delay one instruction cycle at a time, not absolutely necessary.
            cyclesRequiredForEntireDelay -= 39; // Subtract cycles burned while doing each delay stage, 39 in this case.
        }
    }
    
#elif defined(__C30__) || defined(__PIC32MX__)

    volatile UINT8 i;
    
    while (ms--)
    {
        i = 4;
        while (i--)
        {
            Delay10us( 25 );
        }
    }
#endif

}


Suwandy

Thu, 04 Jun 2015 03:58:31 +0000

Sorry for the late reply! my sweet time with ChipKIT been interrupted by other projects :D finally, can focus again on this project.

Thank you so much for majenko, guymc, and atriaharsha for the replies!

i have tested again the code in the link ([url]http://chipkit.net/interrupts-made-easy-with-chipkit/[/url]) provided by majenko and it works fine! Apparently i'm missing one bracket that made the ISR routine not wrapped in my function. sorry for my silly human-error! :roll:

That code successfully produce a PWM signal which i observed with oscilloscope. The part that i don't get is which "TICKS_PER_SECOND" refer to? was it refer to the internal clock? (80MHz for Uno32) or refer to frequency that i wish to generate? with same start_timer_3(8000) and TICKS_PER_SECOND 40000000, i got 8KHz signal on the oscilloscope. if i change TICKS_PER_SECOND to 80000000, i got 4KHz on the oscilloscope

Also, i tried to generate the same thing but with manual approach (bit operation)

void setup(){
pinMode(7,OUTPUT);

T2CON=0; IEC0=0; TMR2=0;			//Tried to clear all register beforehand

IEC0 |= (1&lt;&lt;8);						//Timer2 Interrupt Enabled
PR2 = 0xFFFF;

T2CON &amp;= ~(1&lt;&lt;4);					//0
T2CON |= (1&lt;&lt;5);					//1    110 -&gt;  64 prescaller
T2CON |= (1&lt;&lt;6);					//1
T2CON |= (1&lt;&lt;15);					//Timer2 enabled

TMR2=0;
PR2=0xFFFF;

setIntVector(_TIMER_3_VECTOR, myISR);
setIntPriority(_TIMER_3_VECTOR, 4, 0);
clearIntFlag(_TIMER_3_IRQ);
setIntEnable(_TIMER_3_IRQ);
}

I ended up with flat line on the oscilloscope (failed!)

i wonder why? do i have to use the pre-defined method for SET/CLR the bits? (like TxCONSET/CLR) Or did i do the step in wrong order? Do i really _have to turn off_the timer before setting the prescaller (TxCON) and period (PRx) ? maybe i'm missing out some part of the algorithm.


Suwandy

Tue, 09 Jun 2015 09:28:37 +0000

After going and testing some stuffs, i think:

Here's my code:

#define T3_ON 0x8000
bool state=LOW;
 
/* Define the Interrupt Service Routine (ISR) */
void __attribute__((interrupt)) myISR() {
  //toggling the PIN state every overflow interruption
  state = !state;
  digitalWrite(7,state);
  clearIntFlag(_TIMER_3_IRQ);
}
 
void start_timer_3() {
  T3CON &amp;= ~(1&lt;&lt;15);	/* Turn the timer off */
  T3CON = 0;			/* Set the prescaler 1:1 */
  TMR3 = 0;			/* Clear the counter  */
  PR3 = 597;			/* Set the period for 67KHz  */
  T3CON |= (1&lt;&lt;15);		/* Turn the timer on  */
} /* start_timer_3 */
 
void setup() { 
  pinMode(7,OUTPUT);	/* PIN that i observed using oscilloscope */
  start_timer_3();
  setIntVector(_TIMER_3_VECTOR, myISR);
  setIntPriority(_TIMER_3_VECTOR, 4, 0);
  clearIntFlag(_TIMER_3_IRQ);
  setIntEnable(_TIMER_3_IRQ);
} 

void loop() {
  /*			I tested producing signal with frequency changing every 3s
  PR3 = 597;      //67KHz
  delay(3000);
  PR3 = 634;      //63KHz
  delay(3000);
  PR3 = 571;      //70KHz
  delay(3000);
  			Turns out to work just fine. already observed with oscilloscope
  */
}

I tried several timer formula i found on google:

All of them incorrect against what i observed with oscilloscope (maybe because i'm using PIC32 ??) Turns out, i found a formula that works correctly according to Oscilloscope:

With 80MHz system clock, 1:1 pre-scaller, and PRx=5000, i got: Fout = (80MHz / 2) / 1 * 5000 Fout = 40MHz / 5000 = 8KHz And it was indeed correct, by setting the PRx to 5000 i got 8KHz signal on the oscilloscope.

Alright, now i'm trying to produce high frequency signal (somewhere around 500KHz). I didn't know what exactly happening, but whenever i tried to produce frequency like 200KHz, 500KHz, or greater the signal frequency was stuck at 180KHz (Observed with oscilloscope). Does it because the PRx value too small? in this case the PRx i'm using was smaller than 100 (PRx=80 for 500KHz, PRx=40 for 1MHz). Was this small value affecting the result?? Previously, i successfully producing 2KHz, 4KHz, 12KHz, 67KHz, 150KHz correctly by using the bolded formula. Low frequency seems fine. But i can't get it right for frequency 200KHz and above.

With that formula, increasing the prescaller won't give me high frequency (it will yield lower frequency!). Already tried using 1:8 and the timer frequency does become lower. So does that mean i have to increase the system clock?


majenko

Tue, 09 Jun 2015 10:02:40 +0000

Timer frequency does take a little getting your head round.

The basic way it works is:

  • The incoming clock (peripheral clock, FPB) is divided by the prescaler.
  • The pre-scaled clock increments the timer TMR count.
  • The count is compared to the PR register.
  • If they are equal an interrupt is triggered and TMR is reset.

Under normal circumstances you don't need to ever touch TMR. It is mainly used to read from when you want to know how long has passed between two events.

So the formula for the tick period is ((FPB / PS) / PR).

So for an 80MHz system clock, with a 1:1 peripheral bus, and a 1:1 prescaler, that is:

f = ((80000000 / 1) / PR)

If you set PR to 1000, say, then you get:

((80000000 / 1) / 1000) = 80000Hz.

To calculate a specific PR for a desired frequency you can rotate the formula around:

PR = ((FPB / PR) / f)

So for an 80KHz tick, that would be:

PR = ((80000000 / 1) / 80000) = 1000

And of course, with this being a 16-bit timer by default, the maximum PR value is 65535 (or 0xFFFF). So with both 1:1 prescaler and peripheral bus clock, the lowest frequency that can be created is about 1220Hz.


Suwandy

Wed, 10 Jun 2015 02:41:45 +0000

Thank you Majenko for your reply.

I think this piece of code cause my calculation become halved

void __attribute__((interrupt)) myISR() {
  state = !state;
  digitalWrite(7,state);
  clearIntFlag(_TIMER_3_IRQ);
}

Every Overflow interrupt, i change the output state of PIN 7. The idea is to produce square wave with specified frequency. I suppose that made my frequency become halved, because producing one pulse (HIGH and LOW) will need two overflow interrupt.

That's why i got 40KHz tick with PRx = 1000, instead of 80KHz.

is there any other way to produce square wave with high frequency?? i don't understand why the frequency would stuck at 180KHz. does PRx value below 100 (or any small value) cause this problem?


majenko

Wed, 10 Jun 2015 09:20:07 +0000

Having the interrupt happen too fast can be an issue, yes. You have quite a lot of clock cycles where you are entering and leaving the interrupt routine, and that limits how rapidly you can trigger the interrupt.

For high frequency square waves you're better off using the OCx module to basically generate a 50% duty cycle PWM. No interrupts needed then.


Suwandy

Tue, 16 Jun 2015 07:40:03 +0000

You have quite a lot of clock cycles where you are entering and leaving the interrupt routine

Ask: does interrupt by Overflow and interrupt Output Compare different??


majenko

Tue, 16 Jun 2015 09:14:28 +0000

When using Output Compare you don't (usually) use an interrupt. You just set the parameters and let it run - it does its own thing in the background. It's how analogWrite() works.