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...
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:
/***********************************************************************************/
/* 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() {
}
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()