DUE PWM controller COMPARISON UNIT based ADC and DAC synchronization.

Hi everyone.

I have read the PWM controller chapter in ATSAM3X8E MCU datasheet. After reading the PWM comparison unit of the chapter. I thought of sensing an analog signal through ADC and output the value through DAC of MCU for every 20 microsecond.

Is it possible ?

If it is, How to configure the COMPARISON unit in PWM controller for such an event based execution.

Thanks in advance.

ADC and DAC conversions can be triggered by a PWM pulse generated with a Timer Counter, or triggered by a PWM Event Line 0 or 1.

You will find both example sketches in these threads:

https://forum.arduino.cc/index.php?topic=605842.0

https://forum.arduino.cc/index.php?topic=332795.0

A 20 us period equals a 50 KHz frequency so it should be doable, but use a DMA so that interrupts frequency will be dramatically reduced.

Ard_Newbie

Thanks for the help.

I will try.

  1. I am trying to initialize the DACC module. But the DACC chapter in datasheet is not clear. The difference between the free running mode and triggered mode is not mentioned.

  2. In DACC mode register, three bits are allocated for Trigger selection. For a combination of 001, TIO Output of the Timer Counter Channel 0 is the selected trigger.

  3. I have seen the Timer Counter channel-0 block diagram for waveform mode. There the TIOA and TIOB are Output lines. I don't know which line triggers the DAC core. Should I initialize the Timer counter for the triggering of DACC ?

  4. How to set the DACC in TAG mode.

chamarthi:

  1. I am trying to initialize the DACC module. But the DACC chapter in datasheet is not clear. The difference between the free running mode and triggered mode is not mentioned.

See page 1364 of Sam3x datasheet

An example sketch to trigger the DAC peripheral (DAC0 and DAC1) from a timer counter with a PDC DMA:

/***********************************************************************************/
/*   DAC0 and DAC1 output of a sin wave - Frequency of sin = 44.1 KHz / sinsize    */
/***********************************************************************************/

const uint32_t sinsize  = 256 ;   // Size of buffer must be a power of 2
uint32_t sinus[2][sinsize];
  
volatile uint32_t bufn;

void dac_setup () {

  PMC->PMC_PCER1 = PMC_PCER1_PID38;     // DACC power ON
  DACC->DACC_CR = DACC_CR_SWRST ;       // Reset DACC

  DACC->DACC_MR = DACC_MR_TRGEN_EN                   // Hardware trigger select
                  | DACC_MR_TRGSEL(0b011)            // Trigger by TIOA2
                  | DACC_MR_TAG_EN                   // enable TAG to set channel in CDR
                  | DACC_MR_WORD_WORD                // write to both channels
                  | DACC_MR_REFRESH (1)
                  | DACC_MR_STARTUP_8
                  | DACC_MR_MAXS;

  DACC->DACC_IER |= DACC_IER_TXBUFE;                 // Interrupt used by PDC DMA
                
  DACC->DACC_ACR = DACC_ACR_IBCTLCH0(0b10)
                   | DACC_ACR_IBCTLCH1(0b10)
                   | DACC_ACR_IBCTLDACCORE(0b01);

  NVIC_EnableIRQ(DACC_IRQn);                         // Enable DACC interrupt

  DACC->DACC_CHER = DACC_CHER_CH0                    // enable channel 0 = DAC0
                    | DACC_CHER_CH1;                 // enable channel 1 = DAC1

  /*************   configure PDC/DMA  for DAC *******************/

  DACC->DACC_TPR  = (uint32_t)sinus[0];         // DMA buffer
  DACC->DACC_TCR  = sinsize;
  DACC->DACC_TNPR = (uint32_t)sinus[1];         // next DMA buffer (circular buffer)
  DACC->DACC_TNCR = sinsize;
  bufn = 1;
  DACC->DACC_PTCR = DACC_PTCR_TXTEN;            // Enable PDC Transmit channel request

}

void DACC_Handler() {
  
  uint32_t status = DACC->DACC_ISR;   // Read and save DAC status register
  if (status & DACC_ISR_TXBUFE) {     // move DMA pointer to next buffer
    bufn = (bufn + 1) & 1;
    DACC->DACC_TNPR = (uint32_t)sinus[bufn];
    DACC->DACC_TNCR = sinsize;
  }
}

void tc_setup() {

  PMC->PMC_PCER0 |= PMC_PCER0_PID29;                      // TC2 power ON : Timer Counter 0 channel 2 IS TC2
  TC0->TC_CHANNEL[2].TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK2  // MCK/8, clk on rising edge
                              | TC_CMR_WAVE               // Waveform mode
                              | TC_CMR_WAVSEL_UP_RC        // UP mode with automatic trigger on RC Compare
                              | TC_CMR_ACPA_CLEAR          // Clear TIOA2 on RA compare match
                              | TC_CMR_ACPC_SET;           // Set TIOA2 on RC compare match


  TC0->TC_CHANNEL[2].TC_RC = 238;  //<*********************  Frequency = (Mck/8)/TC_RC = 44.1 MHz
  TC0->TC_CHANNEL[2].TC_RA = 40;  //<********************   Any Duty cycle in between 1 and TC_RC

  TC0->TC_CHANNEL[2].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN; // Software trigger TC2 counter and enable
}

void setup() {

  for(int i = 0; i < sinsize; i++) 
    {
   uint32_t chsel = (0<<12) | (1<<28);                      // LSB on DAC0, MSB DAC1 !!
   sinus[0][i]  = 2047*sin(i * 2 * PI/sinsize) + 2047;      //  0 < sinus [i] < 4096
   sinus[1][i] = sinus[0][i] |= sinus[0][i] <<16 | chsel;   // two buffers formated
                                                            // MSB [31:16]on channel 1
                                                            // LSB [15:0] on chanel 0
    }
  tc_setup();
  dac_setup();
}

void loop() {

}

Thanks for the help.

I made my own code file. I want to use the DACC in tag mode for the present code example. What are the modification should I make.

[
void setup() 
{
  ADC_setup();
  DAC_setup();
  TC_setup();
}

void loop() 
{
  // Do nothing.
}

void ADC_setup(void)
{
  pmc_set_writeprotect(false);            // PMC controller write protection disable.
  PMC->PMC_PCER1 |= (PMC_PCER1_PID37);    // power on of the ADC module
  ADC->ADC_CR     = (ADC_CR_SWRST);       // Reset of ADC.
  ADC->ADC_IDR    = (0xFFFFFFFF) ;        // disabling the interrupts.
  ADC->ADC_IER    = (ADC_IER_EOC7);       // enabling the EOC interrupt of the channel-6.
  ADC->ADC_MR    |= (ADC_MR_TRGEN_EN)|
                    (ADC_MR_TRGSEL_ADC_TRIG1);  // ADC Trigger setting TIOA of TIMER/COUNTER channel-0.
  ADC->ADC_CHER   = (ADC_CHER_CH6)|       // Selecting the Channel-6
                    (ADC_CHER_CH7);       // Selecting the channel-7 for the conversion.
  ADC->ADC_CR     = (ADC_CR_START);       // Starting the ADC module
  NVIC_EnableIRQ(ADC_IRQn);               // Enabling the ADC interrut handler in NVIC. 
}

void TC_setup(void)
{
  pmc_enable_periph_clk(ID_TC0);             // Power on the TC0 peripheral.
  TC0->TC_CHANNEL[0].TC_CCR = TC_CCR_CLKDIS; // disabling the internal clock while setting the registers.
  TC0->TC_CHANNEL[0].TC_IDR = 0xFFFFFFFF;    // disabling the interrupts (Actually there are eight different interrupts available for each channel).
  TC0->TC_CHANNEL[0].TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1 |
                              TC_CMR_WAVE |
                              TC_CMR_WAVSEL_UP_RC |
                              TC_CMR_ACPA_CLEAR | 
                              TC_CMR_ACPC_CLEAR ;
  TC0->TC_CHANNEL[0].TC_RC = 830;            // The Triggering frequency selected is 50KHz for ADC and DACC modules.
  TC0->TC_CHANNEL[0].TC_RA = 300;
  TC0->TC_CHANNEL[0].TC_CMR = ((TC0->TC_CHANNEL[0].TC_CMR) & 0xFFF0FFFF) | TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_SET ;
  TC0->TC_CHANNEL[0].TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG ;
  
  // This part of the code is to check the sampling frequency in Arduino DUE pin.
  PIOB->PIO_WPMR   = 0x50494F00;                // disabling the write protect registers for PIOB Peripheral controller.
  PIOB->PIO_PDR    = PIO_PB25B_TIOA0 ;          // disable PIO control.
  PIOB->PIO_IDR    = PIO_PB25B_TIOA0 ;          // disable PIO interrupts.
  PIOB->PIO_ABSR  |= PIO_PB25B_TIOA0 ;          // switch to B peripheral.
}

void DAC_setup(void)
{
  PMC->PMC_PCER1  |= (PMC_PCER1_PID38);          // Power on for the DACC peripheral.
  DACC->DACC_CR    = (DACC_CR_SWRST);            // Resetting the DAC.
  DACC->DACC_MR   |= (DACC_MR_TRGEN_EN)|
                     (DACC_MR_TRGSEL(1))|        // Selecting TIOA as the trigger.
                     (DACC_MR_WORD_HALF)|        // Enabling Halg word transfer.
                     (DACC_MR_USER_SEL_CHANNEL0)|
                     (DACC_MR_REFRESH(5))|
                     (DACC_MR_STARTUP_16);
  DACC->DACC_IDR   = 0xFFFFFFFF ;
  DACC->DACC_CHER |= DACC_CHER_CH0;
}

void dac_write (int val)
{
  DACC->DACC_CDR = val & 0xFFF ;
}

void ADC_Handler (void)
{
  if (ADC->ADC_ISR & ADC_ISR_EOC7)   // ensure there was an End-of-Conversion and we read the ISR reg.
  {
    int V = *(ADC->ADC_CDR+7) ;
    int U = *(ADC->ADC_CDR+6) ;    
    dac_write (0xFFF & (V - U)) ;       // copy inverted to DAC output FIFO.
  }
}

//]