PDB ADC 'bursting'?

Status
Not open for further replies.

kam42

Active member
... for lack of a better term.

I've been playing around with the programmable delay block, coupled with DMA and the ADC to read 6 channels, switching out the ADC0_SC1A for each sample to enable converting different pins. I'd like to sample those 6 channels as fast as possible, considering them as one 'timestep'. Does anyone have some advice on how I would go about timing that timestep?

Basically, I want to run conversions on those 6 pins as fast as possible, with a set delay between timesteps (100uS). I can get the PDB running at that speed, but it would result in spacing out each conversion evenly throughout (one every ~16 uS). As far as I can understand it, there's one PDB for each ADC. I've been considering just sampling at a faster speed using the PDB and discarding that I don't need, but I'd like a more elegant solution.

I've written it with the standard analogRead, but I'm at a loss on how to do it at a lower level.

Would the flextimer be able to start the PDB for a one-shot 6 conversion cycle repeatedly?
 
It strikes me that I should post some code. It will run consecutive conversions on the 6 channels listed in channels[]. It's not robust, so be careful if you decide to modify it for your own use.

Code:
//From http://cache.freescale.com/files/32bit/doc/quick_ref_guide/KQRUG.pdf
// Page 174
// Various PJRC forum posts that I've proceeded to butcher

#define PDB_CH0C1_TOS 0x0100
#define PDB_CH0C1_EN 0x01

uint8_t channels[] = {1,2,3,4,5,6}; 

volatile uint16_t samples[] = {0,0,0,0,0,0};
uint16_t sample_buffer[6];
volatile boolean samples_flag;

void setup(){
  
  while(!Serial){}
   
  channelSetup(channels, 6); 
   
  for(int i = 0; i < 6; i++){
    Serial.println(channels[i]);
  }
  
  INIT_ADC();
  INIT_PDB();
  INIT_DMA_CH1();
  INIT_DMA_CH0();
  
  
  // Start PDB
  Serial.println("Starting PDB SWTRIG");
  PDB0_SC |= PDB_SC_SWTRIG;
}

void loop(){
  if(samples_flag){
      Serial.print("Samples present: ");
      for(int i = 0; i < 6; i++){
        noInterrupts();
        sample_buffer[i] = samples[i];
        interrupts();
      }
      for(int i = 0; i < 6; i++){
        Serial.print(" ");
        Serial.print(samples[i]);
        Serial.print(" ");
      }
      Serial.println("");
      noInterrupts();
      samples_flag = false;
      interrupts();
  }
}

void pdb_isr(){
  PDB0_SC &= ~PDB_SC_PDBIF;
}

void pdb_error_isr(){
  PDB0_SC &= ~PDB_SC_PDBEN; // disable PDB
  PDB0_CH0S &= ~1; // reset error CH0
  
  PDB0_SC |= PDB_SC_PDBEN; // Enable PDB
  Serial.println("PDB ERROR! RESTARTING!"); 
}

void adc0_isr(){
  int res = ADC0_RA;
  Serial.print(ADC0_SC1A & ~ADC_SC1_AIEN);
  Serial.print(":");
  Serial.print("ADC ISR: ");
  Serial.print(res);
  Serial.println("");   
}

void dma_ch1_isr(){
  // set samples flag for further processing
  samples_flag = true;
  
  // Clear interrupt request for channel 1
  DMA_CINT = 1;  
}

void dma_ch0_isr(){
  // Clear interrupt request for channel 0
  DMA_CINT = 0;
}

// Slow down PDB so we can see it.
#define PDB_PERIOD (F_BUS)
#define PDB_CONFIG (PDB_SC_TRGSEL(15) | PDB_SC_PDBEN | PDB_SC_PDBIE \
	| PDB_SC_CONT | PDB_SC_PRESCALER(6) | PDB_SC_MULT(1))

void INIT_PDB(){
  SIM_SCGC6 |= SIM_SCGC6_PDB;                                       
  PDB0_MOD = PDB_PERIOD;                                             
  PDB0_IDLY = 0;                                                    
  PDB0_CH0C1 = PDB_CH0C1_TOS | PDB_CH0C1_EN;                       
  PDB0_SC = PDB_CONFIG | PDB_SC_LDOK | PDB_SC_PDBEIE;                                
  NVIC_ENABLE_IRQ(IRQ_PDB);                                         
}

void INIT_ADC(){
 analogReadRes(12);
 analogReadAveraging(1);
 analogReference('DEFAULT'); 
 
 // run a few conversions
 int sum = 0;
 for(int i = 0; i <1024; i++){
  sum += analogRead(A0); 
  sum += analogRead(A1);
  sum += analogRead(A2);
  sum += analogRead(A3);
  sum += analogRead(A4);
  sum += analogRead(A5);
 }
 
 // Enable ADC conversion complete interrupt, configure the pin to read.
 ADC0_SC1A = ADC_SC1_AIEN | channels[0];
 int res = ADC0_RA;
 
 // Enable ADC conversion complete flag to request DMA transfer
 ADC0_SC2 |= ADC_SC2_DMAEN;
 
 // Enable hardware interrupt, need this enabled to allow for PDB to trigger ADC.
 ADC0_SC2 |=  ADC_SC2_ADTRG;
 
 // Enable interrupts for ADC
 NVIC_ENABLE_IRQ(IRQ_ADC0);
}

void  INIT_DMA_CH1(){
 // Enable DMA, DMAMUX clocks
 SIM_SCGC7 |= SIM_SCGC7_DMA;
 SIM_SCGC6 |= SIM_SCGC6_DMAMUX;
 
 // Source address
 DMA_TCD1_SADDR = &ADC0_RA;
 // Increment source address by 2 bytes per iteration.
 DMA_TCD1_SOFF = 0;
 // After loop complete, return to start. 
 DMA_TCD1_SLAST = 0;
  
 DMA_TCD1_DADDR = &samples[0];
 DMA_TCD1_DOFF = 0x02;
 DMA_TCD1_DLASTSGA = -0xC;
 
 // Both Source and Dest size are 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_MLOFFNO = DMA_TCD_NBYTES_MLOFFNO_NBYTES(2);
 
 // Set loop counts
 DMA_TCD1_CITER_ELINKYES = 6 | DMA_TCD_CITER_ELINK;
 DMA_TCD1_BITER_ELINKYES = 6 | DMA_TCD_BITER_ELINK;
  
 // Enable interrupt (end-of-major loop)
 DMA_TCD1_CSR = (DMA_TCD_CSR_MAJORLINKCH(0)|DMA_TCD_CSR_MAJORELINK) 
                 | DMA_TCD_CSR_INTMAJOR;
 
 // Set ADC0 as source of transfer request, turn mux on.
 DMAMUX0_CHCFG1 = DMAMUX_SOURCE_ADC0 | DMAMUX_ENABLE;
 
 // Enable request input signal for channel 1
 DMA_SERQ = 1;
 DMA_ERQ |= DMA_ERQ_ERQ1;
 // Enable interrupt request
 NVIC_ENABLE_IRQ(IRQ_DMA_CH1);
}

void INIT_DMA_CH0(){
 // Transfer request sent by DMA CH 1 to eventually move new setup data into ADC0_SC1A
 // To change the sampled pin.
 
 // Enable CH0, make it always requestor.
 DMAMUX0_CHCFG0 = DMAMUX_ENABLE
    | DMAMUX_SOURCE_ALWAYS0;  
  
 DMA_TCD0_SADDR = &channels[0];
 DMA_TCD0_SOFF = 0x01;
 DMA_TCD0_SLAST = -0x06;

 DMA_TCD0_DADDR = &ADC0_SC1A;
 DMA_TCD0_DOFF = 0x00;
 DMA_TCD0_DLASTSGA = 0x00;
 
 DMA_TCD0_NBYTES_MLNO = 0x01;
 DMA_TCD0_BITER_ELINKNO = 0x6;
 DMA_TCD0_CITER_ELINKNO = 0x6;
 
 DMA_TCD0_ATTR = DMA_TCD_ATTR_SSIZE(0) | DMA_TCD_ATTR_DSIZE(0);

 DMA_TCD0_CSR = DMA_TCD_CSR_INTMAJOR;

 NVIC_ENABLE_IRQ(IRQ_DMA_CH0);
}

void channelSetup(uint8_t input_channels[], int num_channels){
  uint8_t channel2sc1a[] = {
	5, 14, 8, 9, 13, 12, 6, 7, 15, 4,
	0, 19, 3, 21, 26, 22};
  
  short temp = input_channels[0];
  for(int i = 1; i < num_channels; i++){
    input_channels[i-1] = channel2sc1a[input_channels[i]] | ADC_SC1_AIEN;
  }
  input_channels[num_channels-1] = channel2sc1a[temp] | ADC_SC1_AIEN;
}
 
Status
Not open for further replies.
Back
Top