3
\$\begingroup\$

The entire project depends on this, so I just want to make sure I have it right. I'm using a 20MHz external resonator and putting that through the PLL in hopes of getting an F_SYS = 120MHz. Without having to get into the math on a timer, I thought the simplest way to check the system frequency would be to toggle a GPIO pin every 1000 iterations or so but the frequency of the toggle on an o-scope makes no sense. I see 175Hz for two toggles every ~10000 instructions (or 7MHz?). What is the real way to validate what comes out of the PLL?:

int16_t main(void) {
  uint32_t i = 0;
  ConfigureOscillator();
  _TRISB5 = 0; _RB5 = 0;

  while (1) {
    i++;
    if (i > 10000) {
      i = 0;
      _RB5 ^= 1;
    }
  }

  return 0;
}

void ConfigureOscillator(void) {
  // Disable the Watch Dog Timer
  RCONbits.SWDTEN = 0;

  // Assume the internal oscillator has come up

  // Configure PLL factors
  PLLFBDbits.PLLDIV = 70;
  CLKDIVbits.PLLPRE = 4;
  CLKDIVbits.PLLPOST = 0;

  // Initiate Clock Switch to Primary Oscillator with PLL
  __builtin_write_OSCCONH(0x03);  // Set OSCCONH for clock switch
  __builtin_write_OSCCONL(OSCCON | 0x01);  // Start clock switching

  // Block until the clock switch has completed
  while (0b011 != OSCCONbits.COSC);

  // Block until the PLL has locked
  while (1 != OSCCONbits.LOCK);
}

Generated configuration bits if you care to look:

// FICD
#pragma config ICS = PGD1               // ICD Communication Channel Select bits (Communicate on PGEC1 and PGED1)
#pragma config JTAGEN = OFF             // JTAG Enable bit (JTAG is disabled)

// FPOR
#pragma config ALTI2C1 = OFF            // Alternate I2C1 pins (I2C1 mapped to SDA1/SCL1 pins)
#pragma config ALTI2C2 = OFF            // Alternate I2C2 pins (I2C2 mapped to SDA2/SCL2 pins)
#pragma config WDTWIN = WIN25           // Watchdog Window Select bits (WDT Window is 25% of WDT period)

// FWDT
#pragma config WDTPOST = PS32768        // Watchdog Timer Postscaler bits (1:32,768)
#pragma config WDTPRE = PR128           // Watchdog Timer Prescaler bit (1:128)
#pragma config PLLKEN = OFF             // PLL Lock Enable bit (Clock switch will not wait for the PLL lock signal.)
#pragma config WINDIS = OFF             // Watchdog Timer Window Enable bit (Watchdog Timer in Non-Window mode)
#pragma config FWDTEN = OFF             // Watchdog Timer Enable bit (Watchdog timer enabled/disabled by user software)

// FOSC
#pragma config POSCMD = HS              // Primary Oscillator Mode Select bits (HS Crystal Oscillator Mode)
#pragma config OSCIOFNC = OFF           // OSC2 Pin Function bit (OSC2 is clock output)
#pragma config IOL1WAY = OFF            // Peripheral pin select configuration (Allow multiple reconfigurations)
#pragma config FCKSM = CSECMD           // Clock Switching Mode bits (Clock switching is enabled,Fail-safe Clock Monitor is disabled)

// FOSCSEL
#pragma config FNOSC = PRIPLL           // Oscillator Source Selection (Primary Oscillator with PLL module (XT + PLL, HS + PLL, EC + PLL))
#pragma config PWMLOCK = ON             // PWM Lock Enable bit (Certain PWM registers may only be written after key sequence)
#pragma config IESO = ON                // Two-speed Oscillator Start-up Enable bit (Start up device with FRC, then switch to user-selected oscillator source)

// FGS
#pragma config GWRP = OFF               // General Segment Write-Protect bit (General Segment may be written)
#pragma config GCP = OFF                // General Segment Code-Protect bit (General Segment Code protect is Disabled)
\$\endgroup\$
  • 1
    \$\begingroup\$ What PIC are you using? \$\endgroup\$ – K-Sid Nov 27 '13 at 4:40
  • \$\begingroup\$ a dsPIC33, microchip.com/wwwproducts/Devices.aspx?dDocName=en558753 \$\endgroup\$ – tarabyte Nov 27 '13 at 4:44
  • \$\begingroup\$ I also seem to have no configuration bit option than (startup with internal osc then automatically switch over to primary). I'm not sure if it's an issue that I'm manually switching? \$\endgroup\$ – tarabyte Nov 27 '13 at 4:45
  • 2
    \$\begingroup\$ You are not toggling "every ~10000 instructions". 10000 is your loop counter. Have you looked at a disassembly to see how many (and what) instructions are in your loop? \$\endgroup\$ – Tut Nov 27 '13 at 11:41
  • 1
    \$\begingroup\$ Can you use a 20meg crystal with the PLL? In some families, you're restricted to 10 \$\endgroup\$ – Scott Seidman Nov 27 '13 at 12:36
5
\$\begingroup\$

Congrats on making the effort to make sure your clock is set up correctly. Its super important on dspics, which will search for alternative clocks if it doesn't see the clock it expects.

You're very close. Instead of trying to validate you clock by using a compiled loop where you think you know how many ticks you use, just toggle a bit in a timer interrupt, and spend the time you need to understand how to set up the timer correctly. Even though yyou don't want to, I think its much easier in the short run. The alternative is going to be looking at your assembly code the compiler generates to make sure you understand what the compiler is doing to your loops, or just writing the loop in assembly.

\$\endgroup\$
4
\$\begingroup\$

You certainly don't need IESO set if you're not going to switch the physical clock source. Here are the config bits I use for a dsPIC33FJ16GS504 project:

// DSPIC33FJ16GS504 Configuration Bit Settings

// FBS
#pragma config BWRP = WRPROTECT_OFF     // Boot Segment Write Protect (Boot Segment may be written)
#pragma config BSS = NO_FLASH           // Boot Segment Program Flash Code Protection (No Boot program Flash segment)

// FGS
#pragma config GWRP = OFF               // General Code Segment Write Protect (General Segment may be written)
#pragma config GSS = OFF                // General Segment Code Protection (User program memory is not code-protected)

// FOSCSEL
#pragma config FNOSC = FRCPLL           // Oscillator Source Selection (Internal Fast RC (FRC) with PLL)
#pragma config IESO = OFF               // Internal External Switch Over Mode (Start up device with user-selected oscillator source)

// FOSC
#pragma config POSCMD = NONE            // Primary Oscillator Source (Primary oscillator disabled)
#pragma config OSCIOFNC = ON            // OSC2 Pin Function (OSC2 is general purpose digital I/O pin)
#pragma config IOL1WAY = OFF            // Peripheral Pin Select Configuration (Allow multiple reconfigurations)
#pragma config FCKSM = CSDCMD           // Clock Switching and Monitor (Clock switching and Fail-Safe Clock Monitor are disabled, Mon Disabled)

// FWDT
#pragma config WDTPOST = PS512          // Watchdog Timer Postscaler (1:512)
#pragma config WDTPRE = PR128           // WDT Prescaler (1:128)
#pragma config WINDIS = OFF             // Watchdog Timer Window (Watchdog Timer in Non-Window mode)
#pragma config FWDTEN = OFF             // Watchdog Timer Enable (Watchdog timer enabled/disabled by user software)

// FPOR
#pragma config FPWRT = PWR128           // POR Timer Value (128ms)

// FICD
#pragma config ICS = PGD3               // Comm Channel Select (Communicate on PGC3/EMUC3 and PGD3/EMUD3)
#pragma config JTAGEN = OFF             // JTAG Port Enable (JTAG is disabled)

Furthermore, your C operation:

_RB5 ^= 1;

is referenced as quite possibly the least efficient use of the compiler known to the community. Even Microchip cites this as 'not to do' in their training courses. This is going to blow up to 11 assembly language operations because GCC doesn't know and doesn't care about single-bit SFR operation optimizations in the hardware.

(Also, you should be toggling the latch, not the port, but that's a topic for another day.)

_LATA0 ^= 1;

becomes

mov.b _LATA,W0
and.b W0,#1,W0
btg W0,#0
and.b W0,#1,W0
and.b W0,#1,W2
mov.w #0x02c4,W1
mov.b [W1],W1
mov.b #0xfe,W0
and.b W1,W0,W0
ior.b W0,W2,W0
mov.b W0,0x02c4

If you look at the docs for the compiler you'll find a function called _builtin_btg which will do the job much more efficiently.

__builtin_btg(&LATA, 0);

becomes

mov.w #_LATA,W0
btg [W0], #0

which is better. The optimal solution is:

asm(“btg LATA, #0”);

which gives you

btg _LATA, #0;

Moral: don't expect to figure out timing based on C language code. The assembly generated for each line can vary dramatically.

\$\endgroup\$
  • \$\begingroup\$ thanks, didn't know about that one at all, looking it up now... \$\endgroup\$ – tarabyte Nov 27 '13 at 17:35
2
\$\begingroup\$

Depending on the actual PIC you are using, you can route the clock (directly or divided) to an output. In the data sheet you linked above, see chapter 9 (Oscillator configuration) and look for 'REFCLKO'. You can route either the primary oscillator directly to the REFCLKO pin, or Fosc (which would be the frequency after the PLL in your case).

According to the datasheet, it should be available on your dsPIC. There you can also route REFCLKO to any pin (using the remapping feature, see chapter 11.4.4.2)

\$\endgroup\$
1
\$\begingroup\$

Validated it with timer1! I toggle a GPIO in the timer1 interrupt. This all rests on my assumption that the time between timer1 interrupt fires is given by:

= (1 / (f_osc / 2) * prescaler) * timer_period
= (1 / (120MHz / 2) * 8) * 7500
= 1ms

where the prescaler is chosen through T1CONbits.TCKPS and the timer_period is chosen through PR1. Note that f_osc is the output of the PLL if you have one configured.

\$\endgroup\$
0
\$\begingroup\$

I use this configuration and a h + c files to setup the clock (Modtronics).

// PIC24FJ64GB002 Configuration Bit Settings

// 'C' source line config statements

// CONFIG4
#pragma config DSWDTPS = DSWDTPS3
#pragma config DSWDTOSC = LPRC
#pragma config RTCOSC = LPRC
#pragma config DSBOREN = OFF
#pragma config DSWDTEN = OFF

// CONFIG3
#pragma config WPFP = WPFP0
#pragma config SOSCSEL = IO
#pragma config WUTSEL = FST
#pragma config WPDIS = WPDIS
#pragma config WPCFG = WPCFGDIS
#pragma config WPEND = WPENDMEM

// CONFIG2
#pragma config POSCMOD = NONE
#pragma config I2C1SEL = PRI
#pragma config IOL1WAY = OFF
#pragma config OSCIOFNC = OFF
#pragma config FCKSM = CSECME
#pragma config FNOSC = FRCPLL
#pragma config PLL96MHZ = ON
#pragma config PLLDIV = DIV2
#pragma config IESO = OFF

// CONFIG1
#pragma config WDTPS = PS8192           
#pragma config FWPSA = PR32             
#pragma config WINDIS = OFF             
#pragma config FWDTEN = OFF             
#pragma config ICS = PGx1               
#pragma config GWRP = OFF               
#pragma config GCP = OFF                
#pragma config JTAGEN = OFF   

The main file:

int main(int argc, char** argv) {
    AD1PCFG = 0xFF; //+++ All PORTB as digital.
    INTCON1bits.NSTDIS = 1;     //+++ Interrupt nesting is disabled.
    SetupClock();
    TRISB = 0x00;
    TRISA = 0x00;       

    REFOCONbits.ROEN = 1;   //+++ Pin 24 REFO=Reference Clock Output=32MHz. Pin 10 //OSCIO/CLKO=Fosc/2=16MHz.

    while (1)
    {
//        LATBbits.LATB5 = ~LATBbits.LATB5; // 3 times slower.
        __builtin_btg(&LATB, 5);
    }

    return (EXIT_SUCCESS);
}

pic_182_1 is: yellow CLKO, cyan REFO

pic_182_2 is: yellow RB5, cyan REFO

pic_182_1 is: yellow CLKO, cyan REFO pic_182_2 is: yellow RB5, cyan REFO

I verified the warnings of Adam Lawrence: even if refo is 32 MHz and Clko is 16 MHhz, I don't get more than a square wave of 1.6 MHz.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.