Configure PIC24E to run at 70 MIPS

Since Microchip introduced their PIC24EP family of products, it took a while before they became available from major vendors in Thailand. This family (and its close relatives dsPIC33EP) is attractive for its 70 MIPS performance. It could not run at that speed right out of the package, considering a user's choice of setup (such as oscillator type and frequency). So in this brief article, we show an example on how to configure PIC24EP to 70 MIPS. A PIC24EP256MC202 is used on our prototype, with 2 clock choices: the 7.37 MHz on-chip Internal Fast RC (FRC) oscillator, and an external 8 MHz crystal.

One might wonder how 70 MIPS performance could be obtained from such low oscillator circuits. The trick is to use a Phase Locked Loop (PLL) module inside the chip, which can multiply the clock frequency to a desired higher value. This is a preferred approach compared to using an external HF clock that could interfere with other components on the board. Along the oscillator path there are a couple of frequency adjustment units, whose parameters must be set properly in software. We will show you how in a minute.

Setup

We use a very simple circuit with only a push-button switch and an LED connected to the PIC24EP as shown in Figure 1.

Figure 1: simple PIC24EP circuit to test clock switching

After reset, the oscillator is initialized to its default value (equal the clock source, either FRC or external crystal). The resulting performance is by no mean close to the maximum performance. The execution speed is shown simply by blinking an LED in the main loop. Whenever the push-button switch is pressed,clock switching to 70 MIPS is performed in software. We then see the blinking speed significantly increases. That ends the demonstration.

Setting the configuration bits

Macro for setting configuration bits must be placed somewhere at the top of the source file, right after the #include lines. For portability sake, I recommend this method rather than setting them in IDE.

Regardless of which clock source you will be using afterwards, the system is configured to use FRC at startup.

_FOSCSEL(FNOSC_FRC);

Next, use this macro for FRC case

_FOSC(FCKSM_CSECMD & OSCIOFNC_ON & POSCMD_NONE);

and for the external crystal case

_FOSC(FCKSM_CSECMD & OSCIOFNC_OFF & POSCMD_XT);

The latter is used for both cases in Microchip document. Consult the PIC24EPdatasheet and family reference manual for the meaning of each definition.

I/O Port Definitions

As shown in figure 1 above, only one input port (RB2) is used for the push-button switch, and one output port (RB15) for LED. Define them accordingly

#define SW1 !_RB2      // switch is active-low
#define LEDB  _LATB15  // blue LED 

Then put these lines in main() before the infinite loop, or in an init( ) function called by main.

ANSELA=0x0000;  // use all ports RA and RB as digital
ANSELB=0x000;
_TRISB2 = 1;   // RB2 as input 
_CNPUB2 = 1;   // enable internal weak pull up
_TRISB15 = 0;   // RB15 as output

Configure the PLL

This is the essential part to make PIC fly. As shown in figure 2, there are 3 parameters that needs to be configured: PLLPRE, PLLDIV, and PLLPOST.

Figure 2: PLL block diagram (simplified from PIC24EP datasheet)

It also shows how the signals along the path are constrained. According to the datasheet, the relevant equations are

  • FCY = FOSC/2
  • FOSC = FIN x (M/(N1 x N2))
  • N1 = PLLPRE + 2
  • N2 = 2 x (PLLPOST + 1)
  • M = PLLDIV + 2

Let’s first consider the FRC case. The input clock frequency FIN = 7.37 MHz, and we want maximum output frequency FOSC = 140 MHz. There is some freedom in selecting 3 parameters to solve this problem. Our choice is to set PLLPRE = PLLPOST = 0, so N1 = N1 = 2. These values yields FPLLI = 3.685 MHz and FSYS = 280 MHz. From FSYS = FPLLI x M, we get M = 280/3.68 = 76. Hence PLLDIV = M - 2 = 76 - 2 = 74.

Now we just apply these 3 values to the chunk of code provided in Microchip document (which is executed after SW1 is pressed)

If (SW1)   {
       // Configure PLL prescaler, PLL postscaler, PLL divisor (FRC )
       PLLFBD = 74;   // M = 76
       CLKDIVbits.PLLPOST = 0;     // N2 = 2
       CLKDIVbits.PLLPRE=0;     // N1=2
       // initiate clock switch to FRC oscillator with PLL (NOSC=0b01)
       __builtin_write_OSCCONH(0x01);
       __builtin_write_OSCCONL(0x01);
       while (OSCCONbits.COSC != 0b001);
       while (OSCCONbits.LOCK != 1);
} while (SW1);

Again, consult the Microchip FRM for the explanation of these commands. In essence, we put in the 3 values to the fields of corresponding register CLKDIV (notice that in the command PLLDIV is called PLLFBD; I have no idea about the inconsistent names used). Then write to OSCCON using special macros __builtin_write_ to initiate clock switch. After that we must wait for the switch to complete before leaving.

It is left to the reader to verify for the external 8 MHz crystal case that N1 = 2, N2 = 2, M = 70 yield the desired 70 MIPS FCY. Replace the previous code by this one

If (SW1)   {
       // Configure PLL prescaler, PLL postscaler, PLL divisor (primary OSC )
       PLLFBD = 68;   // M = 70
       CLKDIVbits.PLLPOST = 0;     // N2 = 2
       CLKDIVbits.PLLPRE=0;     // N1 = 2
       // initiate clock switch to primary oscillator with PLL (NOSC=0b11)
       __builtin_write_OSCCONH(0x03);
       __builtin_write_OSCCONL(0x01);
       while (OSCCONbits.COSC != 0b011);
       while (OSCCONbits.LOCK != 1);
} while (SW1);

In addition to the difference in PLLFBD values, notice the values written to OSCCONH are 0x01 and 0x03 for the FRC and primary oscillator, respectively.

To blink the LED, the following lines are added at the beginning of infinite loop in main( )

LEDB = !LEDB;
for (i=0;i<200000l;i++);

A variable i of type long is used in for() loop as a simple delay. As demonstrated in this video, the LED blinks much faster after clock switching to 70 MIPS.

You can download complete C source file at the link below. It is written to cover both cases. For FRC, uncomment the #define FRC line at the top. For external crystal, uncomment the #define PRIOSC line.

main.c

Conclusion

In this article we give an example on how to configure a PIC24EP to run at its maximum performance of 70 MIPS, using internal FRC or external 8 MHz crystal. Using FRC is advantageous in case you want to minimize circuit components, or want to use the two pins RA2, RA3, which are consumed by the external crystal otherwise. It is not quite suitable when more precise timing is needed (though some tuning is possible) such as when using asynchronous communication. Using an external oscillator might be a better choice.

In this simple example, we verify the clock swtich visually via the LED blink speed. In case you are curious whether the PIC24EP is running at 70 MIPS as advertised, one way to verify is to set up a timer that sends out a toggle output. Then you can measure the period using an oscilloscope.

If a scope is not available, a crude way is to toggle an LED with timer output. You can set the blink speed that can be measured visually, say, 1 Hz. Then compare it with the second count on your watch. The code below shows how to set up timer1 for such purpose.

// global variables
volatile unsigned int msc;
// -------- timer functions ----------------
void initT1() // Initialize Timer 1 to interrupt every 1 ms
{
    T1CON = 0x8020 ;   // Internal Clock (FOSC/2)=70MHZ, 1:64 prescale
    PR1   = 1094;        //0.001/(tcy*64) where tcy = 1/70M
    TMR1  = 0x0000 ;
     msc = 0;
    _T1IF = 0;
    _T1IE = 1;  


}

void __attribute__((interrupt, auto_psv)) _T1Interrupt(void)
// Timer 1 interrupt every 1 mS
{
    msc++;
    if (msc>1000)   {  // blink LED each second
        LEDB = !LEDB;  
        msc = 0;
    }

    _T1IF = 0;
}

Note that we actually set interrupt period of timer1 to 1 milliseconds, and toggle LED every 1000 interrupts. Do not forget to put initT1() function into main().

Comments

Popular posts from this blog

An Introduction to Finite State Machine Design

PIC24E I2C communication with MCP4725