Arduino Due DAC reset

I found this code on this page. It works. But it doesn't work anymore if I put an analogWrite in voidloop. How do I reset the DAC?

#define    NWAVE               80

uint16_t  Sinewave[2][NWAVE] = {
  {
   +4095,   +4093,   +4089,   +4081,   +4070,   +4056,   +4038,   +4018,   +3995,   +3968,   +3939,   +3907,   +3872,   +3834,   +3793,   +3750,
   +3704,   +3656,   +3605,   +3551,   +3495,   +3438,   +3377,   +3315,   +3251,   +3185,   +3118,   +3048,   +2977,   +2905,   +2831,   +2757,
   +2681,   +2604,   +2526,   +2447,   +2368,   +2289,   +2209,   +2128,   +2048,   +1968,   +1887,   +1807,   +1728,   +1649,   +1570,   +1492,
   +1415,   +1339,   +1265,   +1191,   +1119,   +1048,    +978,    +911,    +845,    +781,    +719,    +658,    +601,    +545,    +491,    +440,
    +392,    +346,    +303,    +262,    +224,    +189,    +157,    +128,    +101,     +78,     +58,     +40,     +26,     +15,      +7,      +3,
  },
  {
      +1,      +3,      +7,     +15,     +26,     +40,     +58,     +78,    +101,    +128,    +157,    +189,    +224,    +262,    +303,    +346,
    +392,    +440,    +491,    +545,    +601,    +658,    +719,    +781,    +845,    +911,    +978,   +1048,   +1119,   +1191,   +1265,   +1339,
   +1415,   +1492,   +1570,   +1649,   +1728,   +1807,   +1887,   +1968,   +2048,   +2128,   +2209,   +2289,   +2368,   +2447,   +2526,   +2604,
   +2681,   +2757,   +2831,   +2905,   +2977,   +3048,   +3118,   +3185,   +3251,   +3315,   +3377,   +3438,   +3495,   +3551,   +3605,   +3656,
   +3704,   +3750,   +3793,   +3834,   +3872,   +3907,   +3939,   +3968,   +3995,   +4018,   +4038,   +4056,   +4070,   +4081,   +4089,   +4093
  }
};

uint16_t  SineFast[2][NWAVE] = {
  {
   +4095,   +3939,   +3495,   +2831,   +2048,   +1265,    +601,    +157,      +1,    +157,    +601,   +1265,   +2048,   +2831,   +3495,   +3939,
   +4095,   +3939,   +3495,   +2831,   +2048,   +1265,    +601,    +157,      +1,    +157,    +601,   +1265,   +2048,   +2831,   +3495,   +3939,
   +4095,   +3939,   +3495,   +2831,   +2048,   +1265,    +601,    +157,      +1,    +157,    +601,   +1265,   +2048,   +2831,   +3495,   +3939,
   +4095,   +3939,   +3495,   +2831,   +2048,   +1265,    +601,    +157,      +1,    +157,    +601,   +1265,   +2048,   +2831,   +3495,   +3939,
   +4095,   +3939,   +3495,   +2831,   +2048,   +1265,    +601,    +157,      +1,    +157,    +601,   +1265,   +2048,   +2831,   +3495,   +3939
  },
  {
   +4095,   +3939,   +3495,   +2831,   +2048,   +1265,    +601,    +157,      +1,    +157,    +601,   +1265,   +2048,   +2831,   +3495,   +3939,
   +4095,   +3939,   +3495,   +2831,   +2048,   +1265,    +601,    +157,      +1,    +157,    +601,   +1265,   +2048,   +2831,   +3495,   +3939,
   +4095,   +3939,   +3495,   +2831,   +2048,   +1265,    +601,    +157,      +1,    +157,    +601,   +1265,   +2048,   +2831,   +3495,   +3939,
   +4095,   +3939,   +3495,   +2831,   +2048,   +1265,    +601,    +157,      +1,    +157,    +601,   +1265,   +2048,   +2831,   +3495,   +3939,
   +4095,   +3939,   +3495,   +2831,   +2048,   +1265,    +601,    +157,      +1,    +157,    +601,   +1265,   +2048,   +2831,   +3495,   +3939
  }
};

            int       fast_mode  =    0;
            int       freq_inhz  = 1000; // Default Hz
            int       freq_intc  =    0;
            int       user_intf  =    0;

volatile   uint16_t   sptr       =    0;

void setup()
{
  Serial.begin (115200);
  dac_setup();       
  freq_intc = freqToTc(freq_inhz);
  TC_setup();       
  setup_pio_TIOA0();
}

int tcToFreq( int tc_cntr)
{
  int freq_hz;     

  if( tc_cntr == 0 ) return 1000;
  if( fast_mode ) freq_hz = (420000000UL / tc_cntr) / (2 * NWAVE);
  else            freq_hz = ( 42000000UL / tc_cntr) / (2 * NWAVE);
  return freq_hz;   
}

int freqToTc( int freq_hz)
{
  int tc_cntr = 0;

  if( freq_hz == 0 ) return 25;
  if( fast_mode ) tc_cntr = (420000000UL / freq_hz) / (2 * NWAVE);
  else            tc_cntr = ( 42000000UL / freq_hz) / (2 * NWAVE);
  return tc_cntr;
}

void switch_mode( int mode)
{
  if( mode == 0 )
  {
    DACC->DACC_TPR  =  (uint32_t)  Sinewave[0];      // DMA buffer
    DACC->DACC_TCR  =  NWAVE;
    DACC->DACC_TNPR =  (uint32_t)  Sinewave[1];      // next DMA buffer
    DACC->DACC_TNCR =  NWAVE;
  }
  else
  {
    DACC->DACC_TPR  =  (uint32_t)  SineFast[0];      // DMA buffer
    DACC->DACC_TCR  =  NWAVE;
    DACC->DACC_TNPR =  (uint32_t)  SineFast[1];      // next DMA buffer
    DACC->DACC_TNCR =  NWAVE;
  }
}

void loop()
{
  char in_Byte;
  int     temp;
         
  if (Serial.available() > 0) {
    in_Byte = Serial.read();
    // Message Format To Set  Frequency:  1000f + "send".
    if((in_Byte >= '0') && (in_Byte <= '9'))
    {
      user_intf = (user_intf * 10) + (in_Byte - '0');
    }
    else
    {
      if (in_Byte == 'f') // end delimiter
      {
        if ((user_intf > 20) && (user_intf < 100000))
        {
          freq_inhz = user_intf;
            if( freq_inhz > 10000 ) temp = 1;
            else                    temp = 0;
            if (temp != fast_mode)
            {
              fast_mode = temp;
              switch_mode(fast_mode);
            }
         
          freq_intc = freqToTc(freq_inhz);
          TC_setup();       
          Serial.print("Fast Mode = ");
          Serial.println(fast_mode, DEC);     
          Serial.print("freq_inhz = ");
          Serial.println(freq_inhz, DEC);     
          Serial.print("freq_intc = ");
          Serial.println(freq_intc, DEC);     
          Serial.print("approximation = ");
          temp = tcToFreq(freq_intc);
          Serial.println(temp, DEC);     
        }
      user_intf = 0; // reset to 0 ready for the next sequence of digits
      }   
    }
  }
}

void DACC_Handler(void)
{
  if((dacc_get_interrupt_status(DACC) & DACC_ISR_ENDTX) == DACC_ISR_ENDTX) {
    ++sptr;
    sptr &=  0x01;
    if(fast_mode == 0)
    {
      DACC->DACC_TNPR =  (uint32_t)  Sinewave[sptr];      // next DMA buffer
      DACC->DACC_TNCR =  NWAVE;
    }
  else
    {
      DACC->DACC_TNPR =  (uint32_t)  SineFast[sptr];      // next DMA buffer
      DACC->DACC_TNCR =  NWAVE;
    }
  }
}

void setup_pio_TIOA0() 
{
  PIOB->PIO_PDR = PIO_PB25B_TIOA0; 
  PIOB->PIO_IDR = PIO_PB25B_TIOA0; 
  PIOB->PIO_ABSR |= PIO_PB25B_TIOA0;
}


void TC_setup ()
{
  pmc_enable_periph_clk(TC_INTERFACE_ID + 0 *3 + 0);

  TcChannel * t = &(TC0->TC_CHANNEL)[0];           
  t->TC_CCR = TC_CCR_CLKDIS;                       
  t->TC_IDR = 0xFFFFFFFF;                           
  t->TC_SR;                                         
  t->TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1 |         
              TC_CMR_WAVE |                         
              TC_CMR_WAVSEL_UP_RC |                 
              TC_CMR_EEVT_XC0 |     
              TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_CLEAR |
              TC_CMR_BCPB_CLEAR | TC_CMR_BCPC_CLEAR;
 
  t->TC_RC = freq_intc;
  t->TC_RA = freq_intc /2;       
  t->TC_CMR = (t->TC_CMR & 0xFFF0FFFF) | TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_SET;
  t->TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG;   
}

void dac_setup ()
{
  pmc_enable_periph_clk (DACC_INTERFACE_ID) ; // start clocking DAC
  dacc_reset(DACC);
  dacc_set_transfer_mode(DACC, 0);
  dacc_set_power_save(DACC, 0, 1);            // sleep = 0, fastwkup = 1
  dacc_set_analog_control(DACC, DACC_ACR_IBCTLCH0(0x02) | DACC_ACR_IBCTLCH1(0x02) | DACC_ACR_IBCTLDACCORE(0x01));
  dacc_set_trigger(DACC, 1);
 
//  dacc_set_channel_selection(DACC, 1);
  //dacc_enable_channel(DACC, 1);
  dacc_set_channel_selection(DACC, 0);
  dacc_enable_channel(DACC, 0);

  NVIC_DisableIRQ(DACC_IRQn);
  NVIC_ClearPendingIRQ(DACC_IRQn);
  NVIC_EnableIRQ(DACC_IRQn);
  dacc_enable_interrupt(DACC, DACC_IER_ENDTX);

  DACC->DACC_TPR  =  (uint32_t)  Sinewave[0];      // DMA buffer
  DACC->DACC_TCR  =  NWAVE;
  DACC->DACC_TNPR =  (uint32_t)  Sinewave[1];      // next DMA buffer
  DACC->DACC_TNCR =  NWAVE;
  DACC->DACC_PTCR =  0x00000100;  //TXTEN - 8, RXTEN - 1.
}

Maybe your DAC is burned.

See reply #1 of this topic:

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

Sorry. You misunderstood me. The DAC works. But it only gives out sine with this program. I can't modify the program to be able to give a constant voltage. So for example X time sine signal then Y time constant signals.

zagre:
I found this code on this page. It works. But it doesn't work anymore if I put an analogWrite in voidloop

If you add an analogWrite() in loop() on one of the DACs, you screw-up the previous DAC peripheral settings hence an unpredictable DAC behavior.

How can this be solved?
How do I reset the required settings?
Or what solution is there?

In DAC Handler...

void DACC_Handler(void)
{
  if((dacc_get_interrupt_status(DACC) & DACC_ISR_ENDTX) == DACC_ISR_ENDTX) {
    ++sptr;
    sptr &=  0x01;
    if(fast_mode == 0)
    {
      DACC->DACC_TNPR =  (uint32_t)  Sinewave[sptr];      // next DMA buffer
      DACC->DACC_TNCR =  NWAVE;
    }
  else
    {
      DACC->DACC_TNPR =  (uint32_t)  SineFast[sptr];      // next DMA buffer
      DACC->DACC_TNCR =  NWAVE;
    }
  }
}

...The selected buffer (sinewave or sineFast) is used by the DMA to output a specific sinewave (I guess a sinewave...). If you want to output a constant voltage, fill another array of 2 buffers (say Const[0] and Const[1] of NWAVE uint16_t values with a constant value ( e.g. 2048 to output ~ 1.65 V) and apply these buffers to the DMA inside the interrupt handler.

But firstly you want to understand how works this code before being able to add another type of "sinewave". Sinewave buffer selection should not be done in loop() but inside Dac Handler.

Note that DAC output is within 1/6 * 3.3V and 5/6 * 3.3V.

Could you send me a document for this?

1 Like

A document for what ?

I don’t understand what it does and how it does it:

DACC->DACC_TNPR =  (uint32_t)  Sinewave[sptr]; ....

Is there a library for this or something to understand how this code works?
Or just do it smoothly:

void switch_mode( int mode, int value)
{
  if( mode == 0 )  {...  }
  else if(mode ==1) {... }
  else if (mode ==2)
	{

    DACC->DACC_TPR  =  (uint32_t)  value;      // DMA buffer
    DACC->DACC_TCR  =  1;
    DACC->DACC_TNPR =  (uint32_t) value;      // next DMA buffer
    DACC->DACC_TNCR =  1;
	}

}

(I just threw this together quickly, I didn’t check it, I just did the illustration.)

1 Like

Chapter 26 of Sam3x datasheet details how PDC DMA works, nonetheless it's rather complex to understand the first time you read it.
Here is another example I wrote some time ago for DACs conversions at 44.1 KHz:

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

const uint32_t sinsize  = 256 ;   
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 - frequency divided by 2
                  | 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)           // For DAC frequency > 500 KHz
                   | 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 = 119;  //<*********************  Frequency = (Mck/8)/TC_RC = 2*44.1 KHz
  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*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() {

}

In this example code, DAC peripheral (DAC0 and DAC1) is triggered by a Timer Counter output (TIOA2) at 44.1 KHz. The connection of TIOA2 to the DAC is done internally.
Each sinewave buffer stores 256 elements. The DAC peripheral processes conversions of these elements at 44.1 KHz , one by one on both DACs. Once at the end of a buffer (in fact before the end…), an DAC interrupt is triggered to "tell" the DAC peripheral what is the new buffer for conversions and the size of this new buffer.
Each buffer contains a full sinewave period. If you need to output sometimes a constant value, IMO it's easier to keep the same size of buffer, and simply add a third buffer and select the one to be used after a DAC interrupt against mode value.

What does "DACC_Handler" do if it's not invited anywhere?

1 Like

If I call this command in the void loop:
dacc_reset (DACC);
will the program be restored? As if the void setup didn't run?

These apply to the program I first copied.

1 Like

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.