Go Down

Topic: Arduino Due - DAC constant Sampling rate (Read 3276 times) previous topic - next topic

matanh

Hi,

I build simple code when first the user send command via serial port to the board which select different vectors (12 bit vectors with different length) and then when the user push external interrupt button the DAC port write the selected vector.

The purpose of this project is to "PLAY" different vectors with minimal latency as possible from the Due board through simple headphone amplifier to earphones.

I try to set the DAC sampling rate to be constant (44.1KHz or even 92kHz) using dacc_set_timing() but without success...

any ideas?

Matan

ard_newbie

It would be better to post your code between code tags </>


When you want to output audio thru DAC0/DAC1, forget analogwrite() function.

DACC direct register programing plus a PDC DMA is a much better solution. A timer Counter triggers DAC conversions at a precise pace (a PWM Event Line 0 or 1 could trigger DAC conversions as well).

An example sketch to output a sine wave on both DACs:

Code: [Select]

/***********************************************************************************/
/*   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 on DAC1 !!
   sinus[0][i]  = 2047*sinf(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() {

}







matanh

#2
Dec 13, 2019, 07:32 pm Last Edit: Dec 13, 2019, 07:57 pm by matanh
Thanks ard_newbie!

The timer counter is a software trigger?

If I need to "PLAY" more arbitrary 1D array, what adjustments should I do referring to:

1. Array length?
2. Array values?

Thanks again,

Matan

ard_newbie

The timer counter is a software trigger?
Yes

While the DMA outputs DAC conversions from Buffer[0] you have plenty of time to modify Buffer[1] and vice versa---> synchronise Buffer filling/modifications with DACC_Handler()

matanh

Thanks again,

How can I implement this solution to an arbitrary waveform? for example chirp.

Matan

matanh

Hi,

why the size of buffer should be a power of 2?

I need to keep a constant vector length/

thanks again,

Matan

Go Up