Go Down

Topic: Sinewave 100k generator, DAC + PDC + Timer. (Read 5329 times) previous topic - next topic

Magician

Mar 09, 2014, 11:07 pm Last Edit: Mar 26, 2014, 01:09 am by Magician Reason: 1
Atmel has an example of sinewave source , integrating DAC and Timer, max. freq. 3 kHz with LUT 100 samples. It doesn't use PDC.
So here is my research, build with different snippets of the code posted on this forum and some atmel's example code.
Max freq. 10k if "high fidelity" LUT in use , 160 samples per period of sine. To get freq. up to 100k, code is switching for another, "low resolution" LUT, only 16 samples per one period. CLI expecting nnnnnf command line, default 1k.

Code: [Select]

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


jt6245

Thanks for your example. It is hard to find PDC + DAC example for SAM in internet. XD

Magician

Than find and show for everyone.
The problem, that not every example you find would compile with arduino IDE.  Example:
Code: [Select]
dacc_disable_interrupt(DACC, DACC_IER_ENDTX);
pdc_enable_transfer(PDC_DACC, PERIPH_PTCR_TXTEN);

First line is o'key, but second generate an error message, because for some reason IDE doesn't include PDC support.
In this topic I demonstrate workaround, (but of course it's not missile technology) to get direct access to a few registers:
Code: [Select]
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.

I also get my DUE 2 weeks ago, and already "impressed" by informativity of the atmel data sheet, where registers TCR and TNPR listed in PDC section, and you have to guess that DACC->DACC_TCR would address to right place!

xubuli

#3
Mar 26, 2014, 12:13 am Last Edit: Mar 26, 2014, 12:26 am by xubuli Reason: 1
Any particular reason to use a 2D array for a LUT? 1D array LUT will just work, right?

Also, shouldn't it be
if((dacc_get_interrupt_status(DACC_BASE) & DACC_ISR_ENDTX) ==  DACC_ISR_ENDTX)
instead of using "DACC_IER_ENDTX"

Please check!

Magician

#4
Mar 26, 2014, 01:18 am Last Edit: Mar 26, 2014, 07:10 am by Magician Reason: 1
Quote
Any particular reason to use a 2D array for a LUT? 1D array LUT will just work, right?

No, plz. read comments in OP. There are 2 array, for freq. <10k, and >10k. DAC sampling conversion limited to ~1.6 MHz, so only 16 values is used to compose sine wave at freq. 10 - 100 k. Of course, distortion level is quite high, I have no means to measure it's directly, but math estimation tells ~1 % THD.

Correction:   I just realized, that you mean something else, why each array is split in 2-subarrays?  This is specific of the DMA (PDC). To make it run continuously, I have to supply two pointers to the data, current one and NEXT. If there is only one, than DMA stops after transmission completed , and I have to jump inside ISR to restart all process again, and there is no warranty that this could be done in time, especially if slot is 84/1.6 = 52 clock cycles CPU. This is why 160-points sinewave array divided in two half. Time slot extended more than 80x, reload pointers timeframe equals all the time till one half is feed-ed to DAC

Quote
Also, shouldn't it be
if((dacc_get_interrupt_status(DACC_BASE) & DACC_ISR_ENDTX) ==  DACC_ISR_ENDTX)
instead of using "DACC_IER_ENDTX"
Here you are right, of course ISR_ENDTX. I changed code to fix a bug, thanks.

MarkT

If you use DDS you use a full size sine-table whatever frequency since the phase value is
always in the correct range.

In short:
Code: [Select]

  analogWrite (sine_table [(phase += freq) >> shift]) ;
[ I will NOT respond to personal messages, I WILL delete them, use the forum please ]

Magician

Quote
If you use DDS you use a full size sine-table whatever frequency since the phase value is
always in the correct range.

Correct if there is no PDC. DMA controller always address memory sequentially, so phase accumulator technics, that is exploiting jumping along sinewave table is not possible. Don't forget, there is  1.6 MHz transfer rate, and is only way to get 100k sine
Simple DDS, is exactly what Atmel already did, works great,  below 3k...
 

CARTU

Hi All

To do same  100k  generator job as for one DAC channel how to set up for both DAC0 and DAC1 channels working
together as stereo mode , must I have double LUT size and what about trigger and other
Thanks
C.

bolandross

Thanks for the great example, this helped me a lot.

Eirikr88

Thanks for the example.

I'm wondering how to enable both DAC channels on the Arduino DUE to output sine waves on both channels at the same time. Simply enabling the second channel does not work, any ideas?

Thanks

ard_newbie

#10
Jun 07, 2018, 11:08 am Last Edit: Jun 07, 2018, 11:08 am by ard_newbie
Several registers are concerned when it comes to output to both DAC0 and DAC1 (DACC_MR and DACC_CDR).

Here is an example sketch to output alternatively on DAC0 and DAC1:

Code: [Select]

/*********************************************************************************************************/
/*  44.1 KHz ADC conversions of A0 , idem for A1, triggered by Timer Counter 0 channel 1 TIOA1           */
/*  44.1 KHz DAC output on DAC0, idem for DAC1, triggered by Timer Counter 0 channel 2 TIOA2             */
/*********************************************************************************************************/

volatile uint8_t bufn, Oldbufn, bufn_dac;
const uint16_t bufsize = 128;            
const uint8_t bufnumber = 4;            
const uint8_t _bufnumber = bufnumber - 1;
volatile uint16_t buf[bufnumber][bufsize];        
void setup()
{

  pinMode(LED_BUILTIN, OUTPUT);  // For ADC debugging
  pinMode(12, OUTPUT);           // For DACC debugging

  adc_setup();
  dac_setup();
  tc_adc_setup();
  tc_dac_setup();
}

void loop()
{

}

/*************  Configure adc_setup function  *******************/
void adc_setup() {

  PMC->PMC_PCER1 |= PMC_PCER1_PID37;                    // ADC power ON

  ADC->ADC_CR = ADC_CR_SWRST;                           // Reset ADC
  ADC->ADC_MR |=  ADC_MR_TRGEN_EN                       // Hardware trigger select
                  | ADC_MR_TRGSEL_ADC_TRIG2             // Trigger by TIOA1
                  | ADC_MR_PRESCAL(1);

  // ADC->ADC_ACR = ADC_ACR_IBCTL(0b01);                 // For frequencies > 500 KHz

  ADC->ADC_IDR = ~ADC_IDR_ENDRX;
  ADC->ADC_IER = ADC_IER_ENDRX;                         // End Of Conversion interrupt enable for channel 7
  //NVIC_SetPriority(ADC_IRQn, 0xFF);
  NVIC_EnableIRQ(ADC_IRQn);                             // Enable ADC interrupt
  ADC->ADC_CHER = ADC_CHER_CH6 | ADC_CHER_CH7;          // Enable Channels 7 = A0 and 6 = A1; Trigger frequency is multiplied by 2
                                                        // The sampling frequency for 1 channel times the number of channels !!

  /*********  PDC/DMA  buffer filling sequence **********/
  ADC->ADC_RPR = (uint32_t)buf[2];                      // DMA buffer - First one will be buf[1]
  ADC->ADC_RCR = bufsize;
  ADC->ADC_RNPR = (uint32_t)buf[3];                     // next DMA buffer
  ADC->ADC_RNCR = bufsize;
  bufn = 3;
  ADC->ADC_PTCR |= ADC_PTCR_RXTEN;                      // Enable PDC Receiver channel request
  ADC->ADC_CR = ADC_CR_START;
}

/*********  Call back function for ADC PDC/DMA **************/
void ADC_Handler () {

  //if ( ADC->ADC_ISR & ADC_ISR_ENDRX) {               // Useless because the only one
  Oldbufn = bufn;

  bufn = (bufn + 1) & _bufnumber;
  ADC->ADC_RNPR = (uint32_t)buf[bufn];
  ADC->ADC_RNCR = bufsize;

  /*******************************************/
  boolean chsel;
  for (uint8_t i = 0; i < bufsize; i++) {
    buf[Oldbufn][i] |= chsel << 12;                    // Select alternatively DAC0 and DAC1
    chsel = !chsel;
  }

  // Todo : digital filtering before DAC output
  
 /*************************************************/
  // For debugging only
  static uint32_t Count;
  if (Count++ == 689) { //84000000/8/238/2/128  = ~689
    Count = 0;
    PIOB->PIO_ODSR ^= PIO_ODSR_P27;  // Toggle LED_BUILTIN every 1 Hz
  }
  /*************************************************/
  // }
}

/*************  Configure adc_setup function  *******************/
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                    // Output on DAC0 and DAC1
                  | DACC_MR_WORD_HALF                
                  | DACC_MR_REFRESH (1)
                  | DACC_MR_STARTUP_8
                  | DACC_MR_MAXS;

  DACC->DACC_ACR = DACC_ACR_IBCTLCH0(0b11) //0b10
                   | DACC_ACR_IBCTLCH1(0b11) // 0b10
                   | DACC_ACR_IBCTLDACCORE(0b01);

  DACC->DACC_IDR = ~DACC_IDR_ENDTX;
  DACC->DACC_IER = DACC_IER_ENDTX;                    // TXBUFE works too !!!
  //NVIC_SetPriority(DACC_IRQn, 0xFF);
  NVIC_EnableIRQ(DACC_IRQn);
  DACC->DACC_CHER = DACC_CHER_CH0 | DACC_CHER_CH1;    // enable channels 1 = DAC1 and 0 = DAC0

  /*************   configure PDC/DMA  for DAC *******************/
  DACC->DACC_TPR  = (uint32_t)buf[0];                 // DMA buffer
  DACC->DACC_TCR  = bufsize;
  DACC->DACC_TNPR = (uint32_t)buf[1];                 // next DMA buffer
  DACC->DACC_TNCR =  bufsize;
  bufn_dac = 1;
  DACC->DACC_PTCR = DACC_PTCR_TXTEN;                  // Enable PDC Transmit channel request

}

/*********  Call back function for DAC PDC/DMA **************/
void DACC_Handler() {   // DACC_ISR_HANDLER()         // move Sinus/PDC/DMA pointers to next buffer

  //if ( DACC->DACC_ISR & DACC_ISR_ENDTX) {           // Useless because the only one

  bufn_dac = (bufn_dac + 1) & _bufnumber;
  DACC->DACC_TNPR = (uint32_t)buf[bufn_dac];
  DACC->DACC_TNCR = bufsize;
  
  /*****************************************/
  // For debugging only
  static uint32_t Count;
  if (Count++ == 689) { //84000000/8/119/128 = ~689
    Count = 0;
    PIOD->PIO_ODSR ^= PIO_ODSR_P8;  // Toggle LED_BUILTIN every 1 Hz
  }
  /****************************************/
  //}
}

/*******  Timer Counter 0 Channel 1 to generate PWM pulses thru TIOA1  for ADC ********/
void tc_adc_setup() {

  PMC->PMC_PCER0 |= PMC_PCER0_PID28;                      // TC1 power ON : Timer Counter 0 channel 1 IS TC1
  TC0->TC_CHANNEL[1].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 TIOA1 on RA compare match
                              | TC_CMR_ACPC_SET;          // Set TIOA1 on RC compare match

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

  TC0->TC_CHANNEL[1].TC_CCR = TC_CCR_CLKEN;               // TC1 enable

}

/*******  Timer Counter 0 Channel 2 to generate PWM pulses thru TIOA2  for DACC ********/
void tc_dac_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  Hz = 44.1 Hz
  TC0->TC_CHANNEL[2].TC_RA = 20;  //<********************   Any Duty cycle in between 1 and TC_RC

  TC0->TC_CHANNEL[2].TC_CCR = TC_CCR_CLKEN;               // TC2 enable
  TC0->TC_BCR = TC_BCR_SYNC;                              // Synchro TC1 and TC2
}



newbie-arduino

Hi,

 I know this is an old topic, but have a noob question. Working on a sine generator with variable input from LCD for the two frequencies that can be independently turned on and off. I am trying to get the two variable  independent sine waves on each DACC output using 2 TC's  TIOA0 and TIOA7 for each frequency  and 2 adc channels, how would I go about assigning the one ADC channel  to one channel of the dacc and the other ADC channel to the other dacc channel? if using the DACC Tag or ADC channel in the buffer dma, anyone have any examples?

 I have looked at several examples for sine generator but that only produce 1 sinewave. Have also seen using both DACC outputs but not independent signals.

Additionally where would I use the lookup tables for the sine wave?  at the adc channel dma level before sending to DACC ?

Thanks in advance for any guidance.

ard_newbie

#12
Sep 27, 2018, 06:05 am Last Edit: Sep 27, 2018, 06:06 am by ard_newbie
In the example sketch below, a sine wave is output on both DACs. As you can see, bits 12 and 28 of chsel variable select a different DAC output. If you want a different wave for each DAC, modify sinus buffer:

Code: [Select]

/***********************************************************************************/
/*  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
                  | 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 = 238;  //<*********************  Frequency = (Mck/8)/TC_RC = 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() {

}





newbie-arduino

#13
Sep 27, 2018, 09:15 am Last Edit: Sep 28, 2018, 12:30 am by newbie-arduino
thanks for the reply. just to make sure I'm straight on this. Based off your example I could use 1 tc and have it running on clock0 or clock1 for a range of 20-100khz output and adjust the sinesize accordingly of the sinus separately  for ch1 data and ch2 data of the DACC DMA buffer for the different freq and not use the adc's at all?




Go Up