29.5.3.1 Ratioed Sampling Step Command Program

This section describes the step command programming for implementing the timing sequence shown in Figure 29-12.

The following assumptions are made:
  • Trigger Input 15 is connected to the zero-crossing detect. The rising edge of the Zero-Crossing Detect signal starts the sequence.
  • Trigger Output 24 enables the SCR in the application circuit.
  • The trigger delay from Trigger Input 15 to the generation of Trigger Output 24 is 2 ms.
  • Trigger Output 26 is connected to PWM1’s synchronization signal.
  • PWM1 is configured to trigger the ADC to sample at 1/2x rate, and it is configured to trigger PWM2 which will sample the ADC at 1x rate.
  • Trigger Output 12 is connected to the ADC to sample other data values (channels 0, 1, 2, and 3) once per cycle.
  • The PTG clock is 8 MHz.

Ratioed Sampling Step Command Program

/*
 * File:   main.c
 * Author: C68555
 *
 * Created on August 26, 2022, 10:00 AM
 */

#include "xc.h"
#include "ptg.h"                 //Contains Examples 2-1, 2-2, and 2-3
void clocks_initialize() {
    
    //Configure CLKGEN5 to provide an 8MHz clock for the PWM
    CLK5CONbits.ON = 1; //Enable CLKGEN5, if not already enabled
    
    //Reset CLKGEN5 dividers for 1:1 ratio
    CLK5DIVbits.INTDIV = 0;
    CLK5DIVbits.FRACDIV = 0;
    CLK5CONbits.DIVSWEN = 1;
    //Wait for divider switch to complete
    while(CLK5CONbits.DIVSWEN);

    CLK5CONbits.NOSC = 1;      //Select FRC, 8MHz
    CLK5CONbits.OSWEN = 1;     //Request clock switch
    while (CLK5CONbits.OSWEN); //Wait for switch to complete
    
    PCLKCONbits.MCLKSEL = 1; //Use CLKGEN5 for PWM clock
    
    
    PLL1CONbits.ON = 1; //Enable PLL generator 1, if not already enabled

    //Set up PLL1
    PLL1DIVbits.PLLPRE = 1;       //Reference input will be 8MHz, no division
    PLL1DIVbits.PLLFBDIV = 125;   //Fvco = 8MHz * 125 = 1000MHz
    PLL1DIVbits.POSTDIV1 = 5;     //Divide Fcvo by 5
    PLL1DIVbits.POSTDIV2 = 1;     //Fpllo = Fvco / 5 / 1 = 200 MHz

    //The PLLSWEN bit controls changes to the PLL feedback divider.
    //Request PLL1 feedback divider switch
    PLL1CONbits.PLLSWEN = 1;
    //Wait for PLL1 feedback divider switch to complete
    while(PLL1CONbits.PLLSWEN);

    //The FOUTSWEN bit controls changes to the PLL output dividers.
    //Request PLL1 output divider switch
    PLL1CONbits.FOUTSWEN = 1;
    //Wait for PLL1 output divider switch to complete
    while(PLL1CONbits.FOUTSWEN);

    VCO1DIVbits.INTDIV = 2; //Divide Fvco by 4
    //The DIVSWEN bit controls changes to the VCO divider.
    //Request PLL1 VCO divider switch
    PLL1CONbits.DIVSWEN = 1;
    //Wait for PLL1 VCO divider switch to complete
    while(PLL1CONbits.DIVSWEN);
    
    //Reset CLKGEN1 dividers for 1:1 ratio
    CLK1DIVbits.INTDIV = 0;
    CLK1DIVbits.FRACDIV = 0;
    CLK1CONbits.DIVSWEN = 1;
    //Wait for divider switch to complete
    while(CLK1CONbits.DIVSWEN);
    
    CLK1CONbits.NOSC = 1;     //Set FRC as CPU clock source
    CLK1CONbits.OSWEN = 1;     //Request clock switch
    while (CLK1CONbits.OSWEN); //Wait for switch to complete

    //Configure CLKGEN6 to provide a 250MHz input clock to the ADC.
    
    CLK6CONbits.ON = 1; //Enable CLKGEN6, if not already enabled
    
    //Reset CLKGEN6 dividers for 1:1 ratio
    CLK6DIVbits.INTDIV = 0;
    CLK6DIVbits.FRACDIV = 0;
    CLK6CONbits.DIVSWEN = 1;
    //Wait for divider switch to complete
    while(CLK6CONbits.DIVSWEN);
    
    CLK6CONbits.NOSC = 7;     //Set PLL1 VCODIV as ADC clock source
    CLK6CONbits.OSWEN = 1;    //Request clock switch
    while (CLK6CONbits.OSWEN); //Wait for switch to complete

    //Configure CLKGEN10 to provide an 8MHz clock for the PTG
    
    CLK10CONbits.ON = 1; //Enable CLKGEN10, if not already enabled
    
    //Reset CLKGEN10 dividers for 1:1 ratio
    CLK10DIVbits.INTDIV = 0;
    CLK10DIVbits.FRACDIV = 0;
    //Request CLKGEN10 divider switch
    CLK10CONbits.DIVSWEN = 1;
    //Wait for divider switch to complete
    while(CLK10CONbits.DIVSWEN);

    CLK10CONbits.NOSC = 1;   //Select FRC, 8MHz
    CLK10CONbits.OSWEN = 1;  //Request clock switch
    while (CLK10CONbits.OSWEN); //Wait for switch to complete
}

void PTG_initialize() {
 
    PTGT0LIM = 16000;         // 2 ms T0 delay
    PTGT1LIM = 8000;          // 1 ms T1 delay
    PTGC0LIM = 2;             // 3 iterations of the C0 loop

    PTGQPTR = 0; //Initialize step queue pointer
    
    //Initialize Step registers
    PTGQUE0bits.STEP0 = PTGWHI(15);        // Wait for trigger from INT2 (zero crossing detect)
    PTGQUE0bits.STEP1 = PTGTRIG(12);       // Take 1/8 rate samples using ADC trigger 30
    PTGQUE0bits.STEP2 = PTGCTRL(t0Wait);   // Wait 2ms
    PTGQUE0bits.STEP3 = PTGTRIG(24);       // Trigger PPS output 55 (SCR)
    PTGQUE1bits.STEP4 = PTGCTRL(t1Wait);   // Wait1ms before starting the 1x and 1/2x triggers
    PTGQUE1bits.STEP5 = PTGTRIG(26);       // Trigger PWM1 to trigger conversions at 1x and 1/2x rates
    //Start main loop
    PTGQUE1bits.STEP6 = PTGCTRL(t0Wait);   // Wait2ms (pre-trigger delay) for subsequent triggers
    PTGQUE1bits.STEP7 = PTGTRIG(26);       // Trigger PWM1 to trigger ADC conversions at 1x and 1/2x rates
    PTGQUE2bits.STEP8 = PTGJMPC0(6);       // Jump to step 6 (twice, for 3 iterations total)
    //End main loop
    PTGQUE2bits.STEP9 = PTGJMP(0);

    //Start the PTG
    PTGCONbits.ON = 1;
    PTGCONbits.PTGSTRT = 1;
}

void ADC_initialize() {
 
    //Enable analog inputs AD1AN0 - AD1AN5
    _ANSELA2 = 1;
    _ANSELA4 = 1;

    _ANSELA6 = 1;
    _ANSELA5 = 1;
    _ANSELB1 = 1;
    _ANSELB3 = 1;

    //Enable ADC
    AD1CONbits.ON = 1;
    while(!AD1CONbits.ADRDY);

    //Assign ADC inputs to core 1 channels 0 - 5
    AD1CH0CONbits.MODE = 0; //Single-sample mode
    AD1CH0CONbits.PINSEL = 0; //Positive input is AD1AN0/RA2
    AD1CH0CONbits.NINSEL = 0; //Single-ended mode

    AD1CH1CONbits.MODE = 0; //Single-sample mode
    AD1CH1CONbits.PINSEL = 1; //Positive input is AD1AN1/RA4
    AD1CH1CONbits.NINSEL = 0; //Single-ended mode

    AD1CH2CONbits.MODE = 0; //Single-sample mode
    AD1CH2CONbits.PINSEL = 2; //Positive input is AD1AN2/RA6
    AD1CH2CONbits.NINSEL = 0; //Single-ended mode

    AD1CH3CONbits.MODE = 0; //Single-sample mode
    AD1CH3CONbits.PINSEL = 3; //Positive input is AD1AN3/RA5
    AD1CH3CONbits.NINSEL = 0; //Single-ended mode

    AD1CH4CONbits.MODE = 0; //Single-sample mode
    AD1CH4CONbits.PINSEL = 4; //Positive input is AD1AN4/RB1
    AD1CH4CONbits.NINSEL = 0; //Single-ended mode

    AD1CH5CONbits.MODE = 0; //Single-sample mode
    AD1CH5CONbits.PINSEL = 5; //Positive input is AD1AN5/RB3
    AD1CH5CONbits.NINSEL = 0; //Single-ended mode

    //Configure channel triggers
    AD1CH0CONbits.TRG1SRC = 30; //Channel 0 triggered by ADC trigger 30 (PTG trigger 12)
    AD1CH1CONbits.TRG1SRC = 30; //Channel 1 triggered by ADC trigger 30 (PTG trigger 12)
    AD1CH2CONbits.TRG1SRC = 30; //Channel 2 triggered by ADC trigger 30 (PTG trigger 12)
    AD1CH3CONbits.TRG1SRC = 30; //Channel 3 triggered by ADC trigger 30 (PTG trigger 12)
    AD1CH4CONbits.TRG1SRC = 5; //Channel 4 triggered by PWM1 ADC Trigger 2
    AD1CH5CONbits.TRG1SRC = 7; //Channel 5 triggered by PWM2 ADC Trigger 2

    //Enable interrupts for ADC core 1 channels 0 - 5
    _AD1CH0IE = 1;
    _AD1CH1IE = 1;
    _AD1CH2IE = 1;
    _AD1CH3IE = 1;
    _AD1CH4IE = 1;
    _AD1CH5IE = 1;
 
}
void PWM1_initialize() {
    
    PG1CONbits.CLKSEL = 1; //Main PWM clock (no dividing or scaling) used for PWM1
    PG1CONbits.SOCS = 0b1111; //PCI sync used for start of cycle
    PG1SPCIbits.PSS = 0b01100; //PCI12 as PWM1 sync source
    PG1CONbits.TRGMOD = 1; //PWM generator is re-triggerable
    PG1IOCONbits.PENH = 0; //PWM generator 1 does not control PWM1H pin
    PG1EVTbits.ADTR2EN1 = 1; //Enable PGA1TRIGA match as ADC trigger 2 source
    PG1EVTbits.ADTR1EN1 = 1; //Enable PGA1TRIGA match as ADC trigger 1 source
    PG1EVTbits.PGTRGSEL = 1; //Use TRIGA compare as PWM generator trigger

    PG1PER = 16000 << 4; //PWM1 period is 2ms
    PG1DC = 8000 << 4; //50% duty cycle
    PG1PHASE = 0; //0 phase offset

    PG1TRIGA = 0; //ADC trigger at 0

    PG1CONbits.ON = 1; //Enable PWM Generator 1
}

void PWM2_initialize() {
    
    PG2CONbits.CLKSEL = 1; //Main PWM clock (no dividing or scaling) used for PWM2
    PG2CONbits.TRGCNT = 1; //PWM2 completes two cycles once triggered
    PG2CONbits.TRGMOD = 1; //PWM2 is re-triggerable
    PG2CONbits.SOCS = 0b0001; //PWM start of cycle from PG1, selected by PGTRGSEL
    PG2IOCONbits.PENH = 0; //PWM generator 2 does not control PWM2H pin
    PG2EVTbits.ADTR2EN1 = 1; //Enable PGA1TRIGA match as ADC trigger 2 source
    
    PG2PER = 8000 << 4; //PWM2 period is 1ms
    PG2DC = 4000 << 4; //50% duty cycle
    PG2PHASE = 0; //0 phase offset

    PG1TRIGA = 0; //ADC trigger at 0

    PG2CONbits.ON = 1; //Enable PWM Generator 2
}

void IO_initialize() {

    //Configure INT2 to use RC2 (zero-crossing detect input)
    _TRISC2 = 1;
    _INT2R = 35;
    //Configure PTG trigger 24 to use RC3 (SCR output)
    _TRISC3 = 0;
    _RP36R = 55; 
    //Connect PCI12 to PTG Trigger 26, PPS input #167 (RPI166)
    _PCI12R = 167;

    //Output indicators for ADC interrupts
    _TRISD4 = 0;
    _TRISD5 = 0;
    _TRISD6 = 0;
    _TRISD10 = 0;
    _TRISD11 = 0;
    _TRISD12 = 0;
}

int main(void) {
   
    clocks_initialize();
    IO_initialize();
    ADC_initialize();
    PWM1_initialize();
    PWM2_initialize();
    PTG_initialize();

    while (1);

    return 0;
}

void __attribute__((__interrupt__)) _AD1CH0Interrupt() {
    unsigned int adc_result = AD1CH0DATA; //Reading the result is required to clear the interrupt
    _AD1CH0IF = 0; //Clear flag
    _LATD4 ^= 1; //Toggle I/O indicator
}

void __attribute__((__interrupt__)) _AD1CH1Interrupt() {
    unsigned int adc_result = AD1CH1DATA; //Reading the result is required to clear the interrupt
    _AD1CH1IF = 0; //Clear flag
    _LATD5 ^= 1; //Toggle I/O indicator
}

void __attribute__((__interrupt__)) _AD1CH2Interrupt() {
    unsigned int adc_result = AD1CH2DATA; //Reading the result is required to clear the interrupt
    _AD1CH2IF = 0; //Clear flag
    _LATD6 ^= 1; //Toggle I/O indicator
}

void __attribute__((__interrupt__)) _AD1CH3Interrupt() {
    unsigned int adc_result = AD1CH3DATA; //Reading the result is required to clear the interrupt
    _AD1CH3IF = 0; //Clear flag
    _LATD10 ^= 1; //Toggle I/O indicator
}

void __attribute__((__interrupt__)) _AD1CH4Interrupt() {
    unsigned int adc_result = AD1CH4DATA; //Reading the result is required to clear the interrupt
    _AD1CH4IF = 0; //Clear flag
    _LATD11 ^= 1; //Toggle I/O indicator
}

void __attribute__((__interrupt__)) _AD1CH5Interrupt() {
    unsigned int adc_result = AD1CH5DATA; //Reading the result is required to clear the interrupt
    _AD1CH5IF = 0; //Clear flag
    _LATD12 ^= 1; //Toggle I/O indicator
}