I am trying to measure current through a BLDC motor via a high side current sense resistor. To do this, I have to synchronize the ADC measurement of the current sense hardware to the section of the PWM period where the three MOSFET bridges are activated (otherwise the current sense resistor is isolated from the motor because the MOSFETs are all the same state). This active region is, by design, always centered about the 50% duty cycle state change of the PWM waveform.
As I have already prototyped the hardware, some simpler methods are not possible (such as triggering via a digital input tied directly to one of the PWM outputs for synchronization). So, what am intending to do is:
1) Have FTM0 trigger the PDB at the start of each PWM waveform
2) PDB delays for 1/2*PWM_period then triggers the ADC to measure.
3) ADC uses DMA to record the value to memory.
My issue:
I have pieced together a prototype (a lot from different posts on here, some custom) program that does 1 & 2 successfully, but the ADC interrupt is refusing to trigger the DMA to store the recorded values. I can read the ADC values via the ADC interrupt, verified by my scope, so I know the program is working up to the DMA request. Can anyone offer advice on getting the DMA to acknowledge the ADC correctly?
Thanks.
As I have already prototyped the hardware, some simpler methods are not possible (such as triggering via a digital input tied directly to one of the PWM outputs for synchronization). So, what am intending to do is:
1) Have FTM0 trigger the PDB at the start of each PWM waveform
2) PDB delays for 1/2*PWM_period then triggers the ADC to measure.
3) ADC uses DMA to record the value to memory.
My issue:
I have pieced together a prototype (a lot from different posts on here, some custom) program that does 1 & 2 successfully, but the ADC interrupt is refusing to trigger the DMA to store the recorded values. I can read the ADC values via the ADC interrupt, verified by my scope, so I know the program is working up to the DMA request. Can anyone offer advice on getting the DMA to acknowledge the ADC correctly?
Thanks.
Code:
int PIN_MA = 21; //BLDC phase 1
int PIN_MB = 22; //BLDC phase 2
int PIN_MC = 23; //BLDC phase 3
float Offset = 1024; //11bit PWM centerpoint
long CNT = 0;
int C_Sense = A2;
long dc_average = 0;
uint16_t samples[16];
uint32_t tmp = 0;
//-----------------------------------------------------------------------------------------------------------------------
void dmaInit() {
// Enable DMA, DMAMUX clocks
SIM_SCGC7 |= SIM_SCGC7_DMA;
SIM_SCGC6 |= SIM_SCGC6_DMAMUX;
// Use default configuration
//DMA_CR = 0;
// Source address
DMA_TCD1_SADDR = &ADC0_RA;
// Don't change source address
DMA_TCD1_SOFF = 0;
DMA_TCD1_SLAST = 0;
// Destination address
DMA_TCD1_DADDR = samples;
// Destination offset (2 byte)
DMA_TCD1_DOFF = 2;
// Restore destination address after major loop
DMA_TCD1_DLASTSGA = -sizeof(samples);
// Source and destination size 16 bit
DMA_TCD1_ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
// Number of bytes to transfer (in each service request)
DMA_TCD1_NBYTES_MLNO = 2;
// Set loop counts
DMA_TCD1_CITER_ELINKNO = sizeof(samples) / 2;
DMA_TCD1_BITER_ELINKNO = sizeof(samples) / 2;
// Enable interrupt (end-of-major loop)
DMA_TCD1_CSR = DMA_TCD_CSR_INTMAJOR;
// Set ADC as source (CH 1), enable DMA MUX
DMAMUX0_CHCFG1 = DMAMUX_DISABLE;
DMAMUX0_CHCFG1 = DMAMUX_SOURCE_ADC0 | DMAMUX_ENABLE;
// Enable request input signal for channel 1
DMA_SERQ = 1;
// Enable interrupt request
NVIC_ENABLE_IRQ(IRQ_DMA_CH1);
}
//-------------------------------------------------------------------------------------------------------------------------
//static const uint8_t channel2sc1a[] = {
// 5, 14, 8, 9, 13, 12, 6, 7, 15, 4,
// 0, 19, 3, 21, 26, 22
//};
#define ADC_CONFIG1 (ADC_CFG1_ADIV(0) | ADC_CFG1_MODE(3))
#define ADC_CONFIG2 (ADC_CFG2_ADHSC)
void adcInit() {
ADC0_CFG1 = ADC_CONFIG1;
ADC0_CFG2 = ADC_CONFIG2;
// Voltage ref vcc, hardware trigger, DMA
ADC0_SC2 = ADC_SC2_REFSEL(0) | ADC_SC2_ADTRG; // | ADC_SC2_DMAEN;
// Enable averaging, 4 samples
ADC0_SC3 = ADC_SC3_AVGE | ADC_SC3_AVGS(1);
adcCalibrate();
// Enable ADC interrupt, configure pin
ADC0_SC1A = ADC_SC1_AIEN | 0x8;
NVIC_ENABLE_IRQ(IRQ_ADC0);
}
void adcCalibrate() {
uint16_t sum;
// Begin calibration
ADC0_SC3 = ADC_SC3_CAL;
// Wait for calibration
while (ADC0_SC3 & ADC_SC3_CAL);
// Plus side gain
sum = ADC0_CLPS + ADC0_CLP4 + ADC0_CLP3 + ADC0_CLP2 + ADC0_CLP1 + ADC0_CLP0;
sum = (sum / 2) | 0x8000;
ADC0_PG = sum;
// Minus side gain (not used in single-ended mode)
sum = ADC0_CLMS + ADC0_CLM4 + ADC0_CLM3 + ADC0_CLM2 + ADC0_CLM1 + ADC0_CLM0;
sum = (sum / 2) | 0x8000;
ADC0_MG = sum;
}
//---------------------------------------------------------------------------------------------------------------------------
/* Note: >>> denotes old code*/
void setup(void)
{
Serial.begin(9200);
pinMode(33,OUTPUT);
adcInit();
dmaInit();
//Initialize "0" PWM at desired frequency
analogWriteFrequency(PIN_MA,40000);
analogWriteFrequency(PIN_MB,40000);
analogWriteFrequency(PIN_MC,40000);
analogWriteResolution(11);
analogWrite(PIN_MA,Offset/2);
analogWrite(PIN_MB,Offset/2);
analogWrite(PIN_MC,Offset/2);
//Initialize FTM interrupts for PDB
//NVIC_SET_PRIORITY(IRQ_FTM0, 62);
//NVIC_ENABLE_IRQ(IRQ_FTM0);
FTM0_EXTTRIG = FTM_EXTTRIG_INITTRIGEN; //Enable external trigger on FTM0 Channel 0 for PDB -> corresponds to pin 22 on 3.6
FTM0_SC = FTM_SC_CLKS(1) | FTM_SC_PS(0);// | FTM_SC_TOIE; //Uncomment the last item to enable FTM ISR. Leave commented to only enable EXT PDB interrupt. // update status register > sys clock | div by 1 | enable TOF interrupt
//PDB initialization
uint16_t FTM_Period = FTM0_MOD/2 - 0.0144*FTM0_MOD/2; //Calibrated by scope.
SIM_SCGC6 |= SIM_SCGC6_PDB;
PDB0_IDLY = FTM_Period; //Delay. Corresponds to 1/2 PWM period. For triggering testing interrupt
PDB0_MOD = FTM0_MOD/2;// FTM_Period; //Timer period, defaults to 65536 (max for 16bit int
PDB0_CH0DLY0 = FTM_Period;
PDB0_CH0C1 = 0x0101;
PDB0_SC |= PDB_SC_PRESCALER(0x0) | PDB_SC_TRGSEL(0x8) | PDB_SC_PDBEN | PDB_SC_PDBIE | PDB_SC_MULT(0x0) | PDB_SC_LDOK;
//For testing PDB trigger timing
NVIC_ENABLE_IRQ(IRQ_PDB);
NVIC_SET_PRIORITY(IRQ_PDB, 64);
}
void loop(){
//Write a new pwm value to see if interrupts continue functioning
analogWrite(PIN_MA,Offset + 0);
analogWrite(PIN_MB,Offset + 10);
analogWrite(PIN_MC,Offset - 10);
// long PDB_temp = PDB0_CNT;
// if(PDB_temp > CNT){
// CNT = PDB_temp;
// //Serial.println(PDB_PERIOD);
// //Serial.println(F_BUS / 128 / 10 / 1);
// Serial.print(CNT);
// Serial.print(" | ");
// Serial.println(FTM0_MOD/2);
// delay(100);
// }
Serial.print(samples[1]);
Serial.print(" | ");
Serial.print(FTM0_MOD/2);
Serial.print(" | ");
Serial.println(tmp);
delay(100);
}
void dma_ch1_isr() {
// Serial.print("dma isr: ");
// Serial.println(millis());
// Clear interrupt request for channel 1
DMA_CINT = 1;
GPIOE_PTOR = 1UL << 24; //flip pin 33
}
//void ftm0_isr(void) //Not used here, just for learning purposes
//{
//FTM0_SC &= ~FTM_SC_TOF; // clear the interrupt overflow flag
//GPIOE_PTOR = 1UL << 24; //flip pin 33
//}
void adc0_isr() {
tmp = ADC0_RA; // read ADC result to clear interrupt
GPIOE_PTOR = 1UL << 24; //flip pin 33
}
void pdb_isr() {
PDB0_SC &= ~PDB_SC_PDBIF; // clear interrupt flag
//FTM0_SC &= ~FTM_SC_TOF;
GPIOE_PTOR = 1UL << 24; //flip pin 33
}