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
}