AnsweredAssumed Answered

need help with K20 DMA ADC config emulating flexible scan mode

Question asked by Joel Brinton on Oct 21, 2013
Latest reply on Oct 31, 2013 by Kan_Li

I'm referencing Application Note AN4590 to add high speed sampling and hardware channel scan on a K20 (specifically MK20DX128VLH5 on the FRDM-K20D50M dev board). However, I also need PDB hardware triggering of the ADC for my low-jitter DSP application.

 

I can get the PDB to trigger the ADC and, provided I disable ADC DMA and enable ADC INT, I can read analog data. But when I disable ADC INT and enable ADC DMA I get a PDB sequence error interrupt (PDB0_CH0S = 0x10001). The reference manual says:

When one conversion, which is triggered by one of the pre-triggers from PDB channel n, is in progress, new trigger from PDB channel's corresponding pre-trigger m cannot be accepted by ADCn, and ERR[m] is set.

 

So, it sounds like the COCO flag is not getting cleared. In my DMA configuration, I cannot see why DMA is not clearing the ADC after copy. Any help is appreciated!

 

DMA Initialization:

 

void dma_adc_init(uint8_t *chan_list, uint32_t num_chan, uint16_t *results, uint32_t results_size)

{

  printf("Initializing DMA\r\n");

 

  /* Enable Error Interrupts on Ch0 and Ch1. */

  DMA_CERR = 0x40;

  DMA_EEI |= DMA_EEI_EEI0_MASK | DMA_EEI_EEI1_MASK;

 

  /****************************************************************************

  * Setup Transfer Control Descriptor #0 (Channel List to ADC)

  ***************************************************************************/

  

  DMAMUX0_CHCFG0 = DMAMUX_CHCFG_ENBL_MASK | DMAMUX_CHCFG_SOURCE(54); // DMA

 

  /* Source and data destination both 8-bits long. */

  DMA_TCD0_ATTR = DMA_ATTR_SSIZE(0x00) | DMA_ATTR_DSIZE(0x00);

 

  /* DMA_TCD0_SADDR = chan_list */

  DMA_TCD0_SADDR = (uint32_t)&chan_list[0];                            

  DMA_TCD0_NBYTES_MLNO = 1;

  DMA_TCD0_SOFF = 1;

  DMA_TCD0_SLAST = (uint32_t)-((int32_t)(num_chan));

 

  /* DMA_TCD0_DADDR = adc_buffer */

  DMA_TCD0_DADDR = (uint32_t) &ADC0_SC1A;

  DMA_TCD0_DOFF = 0;

  DMA_TCD0_DLASTSGA = 0;

 

  /* Ch0 Major iteration count. */

  DMA_TCD0_BITER_ELINKNO = DMA_BITER_ELINKNO_BITER(num_chan);

  DMA_TCD0_CITER_ELINKNO = DMA_CITER_ELINKNO_CITER(num_chan);

 

  /* Don't link to another channel. Don't generate interrupt. This DMA gets

    called when Ch1 finishes indicating that the ADC results have been

  copied to memory. This DMA copies the next ADC settings to the ADC

  status-control register. */

  DMA_TCD0_CSR = 0;

  

  /****************************************************************************

  * Setup Transfer Control Descriptor #1 (ADC -> Memory)

  ***************************************************************************/

  

  DMAMUX0_CHCFG1 = DMAMUX_CHCFG_ENBL_MASK | DMAMUX_CHCFG_SOURCE(40); // ADC0

 

  /* Source and data destination both 16-bits long. */

  DMA_TCD1_ATTR = DMA_ATTR_SSIZE(0x01) |

                  DMA_ATTR_DSIZE(0x01);

 

  /* DMA_TCD0_SADDR = adc_buffer2 */

  DMA_TCD1_SADDR = (uint32_t) &ADC0_RA;

  DMA_TCD1_NBYTES_MLNO = 2;

  DMA_TCD1_SOFF = 0;

  DMA_TCD1_SLAST = 0;

 

  /* DMA_TCD0_DADDR = adc_buffer */

  DMA_TCD1_DADDR = (uint32_t) &results[0];

  DMA_TCD1_DOFF = 2;

  DMA_TCD1_DLASTSGA = (uint32_t)-((int32_t)(results_size));

 

  /* Link to Ch0 on major iteration. */

  DMA_TCD1_BITER_ELINKYES = DMA_BITER_ELINKYES_ELINK_MASK |

                           DMA_BITER_ELINKYES_LINKCH(0x0) |

  DMA_BITER_ELINKNO_BITER(results_size/sizeof(uint16_t));

  DMA_TCD1_CITER_ELINKYES = DMA_CITER_ELINKYES_ELINK_MASK |

   DMA_CITER_ELINKNO_CITER(results_size/sizeof(uint16_t));

 

  /* Don't link to another channel. Don't generate interrupt. This DMA gets

    called when Ch1 finishes indicating that the ADC results have been

  copied to memory. This DMA copies the next ADC settings to the ADC

  status-control register. */

  DMA_TCD1_CSR = DMA_CSR_MAJORELINK_MASK |

  DMA_CSR_MAJORLINKCH(0) |

  DMA_CSR_INTHALF_MASK |

  DMA_CSR_INTMAJOR_MASK;

 

  /* Enable DMA Ch1. */

  DMA_ERQ |= DMA_ERQ_ERQ1_MASK;

}

 

 

and the main() function:

 

int main(void)

{

  cpu_init();

  pta_init();

  ptc_init();

  ptd_init();

  uart0_init();

  printf("\r\n\n\n" "Digital Signal Processor Firmware Version " FW_VERSION_ "\r\n");

  print_uuid();

  print_reset_status();

  SysTick_Init();

  ftfl_init();

  ftm0_init();

  pit_init();

  vref_init();

  pdb_init(50000); /// totally slow for debugging

 

  printf("Initializing RAM...\r\n");

  memset(adc_buffer, 0, sizeof(adc_buffer));

  memset(res_buffer, 0, sizeof(res_buffer));

  memset(out_buffer, 0, sizeof(out_buffer));

 

  adc_init();

  dma_adc_init(uc_adc_mux, sizeof(uc_adc_mux)/sizeof(uc_adc_mux[0]), adc_buffer, sizeof(adc_buffer));

 

  printf("Starting ADC sampling (sample rate 100kHz)\r\n");

  pdb_trigger();

 

  while(1) { delay_ms(1000); print_time(); }

}

 

 

ADC Initialization:

 

void adc_init(void)

{

  printf("Initializing ADC\r\n");

 

  /* Use PDB for hardware trigger. */

  SIM_SOPT7 = 0;

 

  /* Set ADC clocks. */

  ADC0_CFG1 = ADC_CFG1_ADIV(0x01) | ADC_CFG1_MODE(0x03) | ADC_CFG1_ADICLK(0x02);

  ADC0_CFG2 = ADC_CFG2_ADHSC_MASK | ADC_CFG2_ADLSTS(0x03);                      

 

  /* No compare nor offset values. */

  ADC0_CV1 = ADC_CV1_CV(0x00);                                  

  ADC0_CV2 = ADC_CV2_CV(0x00);                                  

  ADC0_OFS = ADC_OFS_OFS(0x00);

 

  /* Set to software trigger for now. After calibration we'll change it back

    to hardware and trigger on PDB. Enable DMA. */

  ADC0_SC2 = ADC_SC2_DMAEN_MASK | ADC_SC2_REFSEL(0x00);

  ADC0_SC2 = ADC_SC2_REFSEL(0x00);

 

  /* Disable hardware averaging. Disable ADC continuous conversion - multiple

    triggers from PDB. */

  ADC0_SC3 = ADC_SC3_AVGS(0x00);

 

  // Start Calibration

  printf("Calibrating ADC...\r\n");

  ADC0_SC3 |= ADC_SC3_CAL_MASK;

 

  // Wait for calibration to finish

  while( (ADC0_SC1A & ADC_SC1_COCO_MASK ) == ADC_SC1_COCO_MASK );

  

  if ((ADC0_SC3 & ADC_SC3_CALF_MASK) == ADC_SC3_CALF_MASK )

  {

  printf("Calibration failed!\r\n");

  }

 

  // switch to hardware trigger

  ADC0_SC2 |= ADC_SC2_ADTRG_MASK;

 

  /* Set the channel mux for A trigger. */

  ADC0_SC1A = ADC_SC1_ADCH(0x00);

}

 

 

Programmable Delay Block Initialization:

 

void pdb_init(uint16_t period)

{

  printf("Initializing Programmable Delay Block (PDB)\r\n");

 

  /* Software trigger, error interrupts enabled, continuous mode. */

  PDB0_SC = PDB_SC_LDMOD(0x00) |

           PDB_SC_PDBEIE_MASK |

            PDB_SC_PRESCALER(0x8) |

            PDB_SC_TRGSEL(0xF) |

           PDB_SC_PDBIE_MASK |

            PDB_SC_MULT(0x3) |

           PDB_SC_CONT_MASK;

 

  /* Set the PDB modulus. */

  PDB0_MOD = period;

 

  /* Disable PDB Interrupt Delay (i.e. don't generate interrupt). */

  PDB0_IDLY = PDB_IDLY_IDLY(0xFFFF);

 

  /* Pretriggers 0 and 1 are both enabled. */

  PDB0_CH0C1 = PDB_C1_BB(0x00) | PDB_C1_TOS(0x03) | PDB_C1_EN(0x03);             

 

  /* Clear channel flags. Clear channel sequence error flags. */

  PDB0_CH0S = PDB_S_CF(0x00) | PDB_S_ERR(0xFF);  

 

  /* Channel 0 delay. */

  PDB0_CH0DLY0 = PDB_DLY_DLY(period/2);

  PDB0_CH0DLY1 = PDB_DLY_DLY(0xFFFF);

 

  /* Start module. */

  PDB0_SC |= PDB_SC_PDBEN_MASK | PDB_SC_LDOK_MASK;

 

  // NOTE: We're not using the Pulse-Out module.

}

Outcomes