PTG, Peripheral Trigger Generator, a useful module of DSPIC33EP series MCU. Just like PDB (Programmable Delay Blocks) module of S32K144 series MCU, it can be used to trigger ADC sample when carry out motor control applications. Sometimes you want to sample multi times during one PWM period. It was difficult if only use PWM event, here I give two code examples to show how to use PTG achieve multi sample.
1. Sequence Sampling
If you want to get a high resolution (12-bit) of ADC result, only sequence configuration can be used, and there is only one S&H amplifier in the 12-bit configuration. The PTG and ADC initial code as below:
extern void Init_ADC_SEQ(void)
{
AD1CON1 = 0; //Clear
AD1CON1bits.ADON = 0; //ADC is off
AD1CON1bits.ADSIDL = 0; //0 = Continues module operation in Idle mode
AD1CON1bits.AD12B = 1; //12 bits, 1 channel S/H
AD1CON1bits.FORM = 2; //10 = Fractional (DOUT = dddd dddd dddd 0000)
AD1CON1bits.SSRC = 3; //011 = PTGO12 primary trigger compare ends sampling and starts conversion
AD1CON1bits.SSRCG = 1; //1 = Sample Trigger Source Group bit = 1
AD1CON1bits.SAMP = 0; //1 = ADC Sample-and-Hold amplifiers are sampling
AD1CON1bits.ASAM = 1; //1 = Sampling begins immediately after the last conversion; SAMP bit is auto-set
AD1CON1bits.SIMSAM = 0; //0 = Samples multiple channels individually in sequence
AD1CON1bits.DONE = 0; //ADC conversion has not started
AD1CON2 = 0;
AD1CON2bits.CHPS = 0x00; //1x = Converts CH0
AD1CON2bits.VCFG = 0x00; //ADC Voltage Reference VREFH = AVDD; VREFL = AVSS
AD1CON2bits.CSCNA = 1; //Scan Inputs
AD1CON2bits.SMPI = 5; //Generates interrupt after completion of every 6th sample/conversion operation
AD1CON2bits.ALTS = 0; //0 = Always uses channel input selects for Sample MUXA
AD1CON2bits.BUFM = 0; //0 = Starts filling buffer from the Start address
//ADC Clock Period (TAD) = TCY*(ADCS<7:0>+1),TCONV = 14*TAD for 12bits
AD1CON3 = 0;
AD1CON3bits.SAMC = 2; //Auto-Sample Time bits
AD1CON3bits.ADRC = 0; //0 = Clock derived from system clock
AD1CON3bits.ADCS = 3; //ADC1 Conversion Clock Select bits
AD1CON4 = 0; //DMA will not be used
//config MUXA and MUXB,no use here
AD1CHS0bits.CH0SA = 0; //00001 = Channel 0 positive input is AN4
AD1CHS0bits.CH0NA = 0; //0 = CH0 negative input is VREFL
AD1CHS123bits.CH123SA = 0; //CH1,2,3 positive input is AN0,1,2
AD1CHS123bits.CH123NA = 0; //0 = CH1,2,3 negative input is VREFL
AD1CHS0bits.CH0SB = 0; //00100 = Channel 0 positive input is AN5
AD1CHS0bits.CH0NB = 0; //0 = CH0 negative input is VREFL
AD1CHS123bits.CH123SB = 0; //CH1,2,3 positive input is AN0,1,2
AD1CHS123bits.CH123NB = 0; //0 = CH1,2,3 negative input is VREFL
//Selects AN0-AN5 for input scan
AD1CSSL = 0x003F;
// Enabling ADC1 interrupt.
IPC3bits.AD1IP = 5;
IFS0bits.AD1IF = 0;
IEC0bits.AD1IE = 1;
AD1CON1bits.ADON = 1; //Enable ADC
}
extern void Init_PTG_ADC_SEQ(void)
{
PTGCST = (0x8000 & 0x77FF); // disabling PTGEN bit and PTGSTRT bit
// Clock Frequency = 15.0 MHz; PTGWDT disabled; PTGDIV 1; PTGCLK TAD; PTGPWD 1;
PTGCON = 0x4000;
PTGBTE = 0x00;
PTGHOLD = 0;
// Timer0 delay = 0.0 ns; PTGT0LIM 0;
PTGT0LIM = 14;//seq
// Timer1 delay = 0.0 ns; PTGT1LIM 0;
PTGT1LIM = 0;
PTGSDLIM = 0x00;
PTGC0LIM = 5;//seq
PTGC1LIM = 0;
PTGADJ = 0;
PTGL0 = 0x00;
PTGQPTR = 0x00;
/* Step Commands,one loop */
_STEP0 = PTGWHI | 0x2; // Wait for PWM1 low-to-high edge interrupt
_STEP1 = PTGTRIG | 0xc; // Sample Trigger for ADC(PTGO12)
_STEP2 = PTGCTRL | 0x8; // Start and wait for the PTG Timer0 to match PTGT0LIM
#ifndef PTG_INT
_STEP3 = PTGJMPC0 | 0x1; // Jump to STEP1 if PTGC0!= PTGC0LIM, increment PTGC0
_STEP4 = PTGJMP | 0x0; // Jump to STEP0
#else
_STEP3 = PTGIRQ | 0x0; // generate interrupt
_STEP4 = PTGJMPC0 | 0x1; // Jump to STEP1 if PTGC0!= PTGC0LIM, increment PTGC0
_STEP5 = PTGJMP | 0x0; // Jump to STEP0
//PTG interrupt Enable
IEC9bits.PTG0IE = 1;
#endif
//PTG Enable
PTGCSTbits.PTGEN = 1;
//Start Step Sequence
PTGCSTbits.PTGSTRT = 1;
}
The effect is as follows:
2. Simultaneous Sampling
The phase current of motor shall be sampled at the same time, so mostly we use simultaneous sampling configuration. The PTG and ADC initial code as below:
extern void Init_ADC_SIM(void)
{
AD1CON1 = 0; //Clear
AD1CON1bits.ADON = 0; //ADC is off
AD1CON1bits.ADSIDL = 0; //0 = Continues module operation in Idle mode
AD1CON1bits.AD12B = 0; //10 bits, 4 channel S/H
AD1CON1bits.FORM = 2; //11 = Signed fractional (DOUT = sddd dddd dd00 0000, where s = .NOT.d[11])
//10 = Fractional (DOUT = dddd dddd dd00 0000)
AD1CON1bits.SSRC = 3; //011 = PTGO12 primary trigger compare ends sampling and starts conversion
AD1CON1bits.SSRCG = 1; //1 = Sample Trigger Source Group bit = 1
AD1CON1bits.SAMP = 1; //Samples CH0, CH1, CH2, CH3 simultaneously
AD1CON1bits.ASAM = 1; //Sampling begins immediately after the last conversion; SAMP bit is auto-set
AD1CON1bits.SIMSAM = 1; //Simultaneously
AD1CON1bits.DONE = 0; //ADC conversion has not started
AD1CON2 = 0;
AD1CON2bits.CHPS = 0x03; //1x = Converts CH0, CH1, CH2 and CH3
AD1CON2bits.VCFG = 0x00; //ADC Voltage Reference VREFH = AVDD; VREFL = AVSS
AD1CON2bits.CSCNA = 0; //0 = Does not scan inputs
AD1CON2bits.SMPI = 1; //Generates interrupt after completion of every 2 sample/conversion operation
AD1CON2bits.ALTS = 1; //Alternate Input(MUXA/MUXB)
AD1CON2bits.BUFM = 0; //0 = Starts filling buffer from the Start address
//ADC Clock Period (TAD) = TCY*(ADCS<7:0>+1),TCONV = 12*TAD for 10bits
AD1CON3 = 0;
AD1CON3bits.SAMC = 2; //Auto-Sample Time bits
AD1CON3bits.ADRC = 0; //0 = Clock derived from system clock
AD1CON3bits.ADCS = 2; //ADC1 Conversion Clock Select bits
AD1CON4 = 0; //DMA will not be used
//config MUXA and MUXB
AD1CHS0bits.CH0SA = 4; //Channel 0 positive input is AN4
AD1CHS0bits.CH0NA = 0; //0 = CH0 negative input is VREFL
AD1CHS123bits.CH123SA = 0; //CH1,2,3 positive input is AN0,1,2
AD1CHS123bits.CH123NA = 0; //0 = CH1,2,3 negative input is VREFL
AD1CHS0bits.CH0SB = 5; //Channel 0 positive input is AN5
AD1CHS0bits.CH0NB = 0; //0 = CH0 negative input is VREFL
AD1CHS123bits.CH123SB = 1; //CH1,2,3 positive input is AN3,0,6
AD1CHS123bits.CH123NB = 0; //0 = CH1,2,3 negative input is VREFL
//Selects AN0-AN5 for input scan,no use here
AD1CSSL = 0x0000;
// Enabling ADC1 interrupt.
IPC3bits.AD1IP = 5;
IFS0bits.AD1IF = 0;
IEC0bits.AD1IE = 1;
AD1CON1bits.ADON = 1; //Enable ADC
}
extern void Init_PTG_ADC_SIM(void)
{
PTGCST = (0x8000 & 0x77FF); // disabling PTGEN bit and PTGSTRT bit
// Clock Frequency = 15.0 MHz; PTGWDT disabled; PTGDIV 1; PTGCLK TAD; PTGPWD 1;
PTGCON = 0x4000;
PTGBTE = 0x00;
PTGHOLD = 0;
// Timer0 delay = 0.0 ns; PTGT0LIM 0;
PTGT0LIM = 48;//sim
// Timer1 delay = 0.0 ns; PTGT1LIM 0;
PTGT1LIM = 0;
PTGSDLIM = 0x00;
PTGC0LIM = 1;//sim
PTGC1LIM = 0;
PTGADJ = 0;
PTGL0 = 0x00;
PTGQPTR = 0x00;
/* Step Commands,one loop */
_STEP0 = PTGWHI | 0x2; // Wait for PWM1 low-to-high edge interrupt
_STEP1 = PTGTRIG | 0xc; // Sample Trigger for ADC(PTGO12)
_STEP2 = PTGCTRL | 0x8; // Start and wait for the PTG Timer0 to match PTGT0LIM
#ifndef PTG_INT
_STEP3 = PTGJMPC0 | 0x1; // Jump to STEP1 if PTGC0!= PTGC0LIM, increment PTGC0
_STEP4 = PTGJMP | 0x0; // Jump to STEP0
#else
_STEP3 = PTGIRQ | 0x0; // generate interrupt
_STEP4 = PTGJMPC0 | 0x1; // Jump to STEP1 if PTGC0!= PTGC0LIM, increment PTGC0
_STEP5 = PTGJMP | 0x0; // Jump to STEP0
//PTG interrupt Enable
IEC9bits.PTG0IE = 1;
#endif
//PTG Enable
PTGCSTbits.PTGEN = 1;
//Start Step Sequence
PTGCSTbits.PTGSTRT = 1;
}
The effect is as follows:
3. PTG interrupt
Sometimes, it is necessary to know the specific trigger moment, so you can enable the PTG interrupt, add the pulse of GPIO to observe.
void __attribute__ ( ( interrupt, no_auto_psv ) ) _PTG0Interrupt ( void )
{
// clear the PTG Trigger0 interrupt flag
IFS9bits.PTG0IF = 0;
// add gpio puls here
...
}