DMA - ADC measurement sychronized via FTM -> PDB

Status
Not open for further replies.

RRdae

Active member
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.

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
  
}
 
Status
Not open for further replies.
Back
Top