Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 2 of 2

Thread: PDB ADC 'bursting'?

  1. #1

    PDB ADC 'bursting'?

    ... 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?

  2. #2
    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;
    }

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •