¿Cómo verificar la velocidad de reloj del sistema PIC MCU?

3

Todo el proyecto depende de esto, así que solo quiero asegurarme de que lo tengo bien. Estoy usando un resonador externo de 20MHz y lo estoy poniendo a través del PLL con la esperanza de obtener un F_SYS = 120MHz. Sin tener que entrar en las matemáticas en un temporizador, pensé que la forma más sencilla de verificar la frecuencia del sistema sería alternar un pin GPIO cada 1000 iteraciones aproximadamente, pero la frecuencia del interruptor en un o-scope no tiene sentido. Veo 175Hz para dos alternaciones cada ~ 10000 instrucciones (o 7MHz?). ¿Cuál es la forma real de validar lo que sale del 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);
}

Bits de configuración generados, si te interesa mirar:

// 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)
    
pregunta tarabyte

4 respuestas

5

Felicitaciones por hacer el esfuerzo de asegurarse de que su reloj esté configurado correctamente. Es muy importante en dspics, que buscará relojes alternativos si no ve el reloj que espera.

Estás muy cerca. En lugar de tratar de validar su reloj utilizando un bucle compilado en el que cree saber cuántos tics usa, simplemente alterne un poco en la interrupción del temporizador y pase el tiempo que necesita para comprender cómo configurar el temporizador correctamente. Aunque no quieras hacerlo, creo que es mucho más fácil a corto plazo. La alternativa será mirar el código de ensamblaje que genera el compilador para asegurarse de que comprende lo que el compilador está haciendo con sus bucles, o simplemente escribir el bucle en ensamblado.

    
respondido por el Scott Seidman
4

Es cierto que no necesita configurar IESO si no va a cambiar la fuente del reloj físico. Aquí están los bits de configuración que utilizo para un proyecto dsPIC33FJ16GS504:

// 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)

Además, su operación C:

_RB5 ^= 1;

se menciona como posiblemente el uso menos eficiente del compilador conocido por la comunidad. Incluso Microchip cita esto como "no hacer" en sus cursos de capacitación. Esto va a explotar hasta 11 operaciones en lenguaje ensamblador porque GCC no sabe y no le importan las optimizaciones de operación SFR de un solo bit en el hardware.

(Además, deberías alternar el pestillo, no el puerto, pero ese es un tema para otro día).

_LATA0 ^= 1;

se convierte en

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

Si observa los documentos del compilador, encontrará una función llamada _builtin_btg que hará el trabajo de manera mucho más eficiente.

__builtin_btg(&LATA, 0);

se convierte en

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

que es mejor. La solución óptima es:

asm(“btg LATA, #0”);

que te da

btg _LATA, #0;

Moraleja: no espere averiguar el tiempo según el código de lenguaje C. El ensamblado generado para cada línea puede variar dramáticamente.

    
respondido por el Adam Lawrence
2

Dependiendo del PIC real que esté utilizando, puede dirigir el reloj (directamente o dividido) a una salida. En la hoja de datos que vinculó anteriormente, consulte el capítulo 9 (Configuración del oscilador) y busque "REFCLKO". Puede enrutar el oscilador primario directamente al pin REFCLKO o Fosc (que sería la frecuencia después del PLL en su caso).

Según la hoja de datos, debería estar disponible en su dsPIC. Allí también puede enrutar REFCLKO a cualquier pin (utilizando la función de reasignación, consulte el capítulo 11.4.4.2)

    
respondido por el hli
1

¡Validado con el timer1! Yo alterno un GPIO en la interrupción del timer1. Todo esto se basa en mi suposición de que el tiempo entre los disparos de interrupción del temporizador1 viene dado por:

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

donde el prescaler se elige a través de T1CONbits.TCKPS y el timer_period se elige a través de PR1 . Tenga en cuenta que f_osc es la salida del PLL si tiene uno configurado.

    
respondido por el tarabyte

Lea otras preguntas en las etiquetas