Arduino Forum

Products => Arduino Due => Topic started by: MarkT on Dec 16, 2013, 08:23 pm

Title: Example of driving ADC and DAC from timer for regular sampling
Post by: MarkT on Dec 16, 2013, 08:23 pm
Hopefully this may save some people time configuring the Due chip for timer-driven
ADC and DAC conversion.  Took a while to read through the timer-counter section
of the datasheeet!

The hypothetical setup is sampling audio at 48kHz and 12 bit, mono.  Timer counter
TC0 channel 0 is initialized to run in waveform mode at 48kHz (and also to generate
PWM on Arduino pin 2 as it happens, but that's optional).

The ADC and DAC are both set to trigger on the TIO event from the timer, and an EOC
ADC interrupt handler is used to capture the input samples into a circular buffer and also
copied inverted straight to the DAC output FIFO.

So when run this reads pin A0 (which is actually AD7 on the chip, note) and outputs
to DAC0.  Pin D2 also gets the 48kHz clock (but can be disabled by commenting out
the call to setup_pio_TIOA0)


Code: [Select]
void setup()
{
  Serial.begin (57600) ; // was for debugging
  adc_setup () ;         // setup ADC
 
  pmc_enable_periph_clk (TC_INTERFACE_ID + 0*3+0) ;  // clock the TC0 channel 0

  TcChannel * t = &(TC0->TC_CHANNEL)[0] ;    // pointer to TC0 registers for its channel 0
  t->TC_CCR = TC_CCR_CLKDIS ;  // disable internal clocking while setup regs
  t->TC_IDR = 0xFFFFFFFF ;     // disable interrupts
  t->TC_SR ;                   // read int status reg to clear pending
  t->TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1 |   // use TCLK1 (prescale by 2, = 42MHz)
              TC_CMR_WAVE |                  // waveform mode
              TC_CMR_WAVSEL_UP_RC |          // count-up PWM using RC as threshold
              TC_CMR_EEVT_XC0 |     // Set external events from XC0 (this setup TIOB as output)
              TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_CLEAR |
              TC_CMR_BCPB_CLEAR | TC_CMR_BCPC_CLEAR ;
 
  t->TC_RC =  875 ;     // counter resets on RC, so sets period in terms of 42MHz clock
  t->TC_RA =  440 ;     // roughly square wave
  t->TC_CMR = (t->TC_CMR & 0xFFF0FFFF) | TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_SET ;  // set clear and set from RA and RC compares
 
  t->TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG ;  // re-enable local clocking and switch to hardware trigger source.

  setup_pio_TIOA0 () ;  // drive Arduino pin 2 at 48kHz to bring clock out
  dac_setup () ;        // setup up DAC auto-triggered at 48kHz
}

void setup_pio_TIOA0 ()  // Configure Ard pin 2 as output from TC0 channel A (copy of trigger event)
{
  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 ()
{
  pmc_enable_periph_clk (DACC_INTERFACE_ID) ; // start clocking DAC
  DACC->DACC_CR = DACC_CR_SWRST ;  // reset DAC

  DACC->DACC_MR =
    DACC_MR_TRGEN_EN | DACC_MR_TRGSEL (1) |  // trigger 1 = TIO output of TC0
    (0 << DACC_MR_USER_SEL_Pos) |  // select channel 0
    DACC_MR_REFRESH (0x0F) |       // bit of a guess... I'm assuming refresh not needed at 48kHz
    (24 << DACC_MR_STARTUP_Pos) ;  // 24 = 1536 cycles which I think is in range 23..45us since DAC clock = 42MHz

  DACC->DACC_IDR = 0xFFFFFFFF ; // no interrupts
  DACC->DACC_CHER = DACC_CHER_CH0 << 0 ; // enable chan0
}

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



void adc_setup ()
{
  NVIC_EnableIRQ (ADC_IRQn) ;   // enable ADC interrupt vector
  ADC->ADC_IDR = 0xFFFFFFFF ;   // disable interrupts
  ADC->ADC_IER = 0x80 ;         // enable AD7 End-Of-Conv interrupt (Arduino pin A0)
  ADC->ADC_CHDR = 0xFFFF ;      // disable all channels
  ADC->ADC_CHER = 0x80 ;        // enable just A0
  ADC->ADC_CGR = 0x15555555 ;   // All gains set to x1
  ADC->ADC_COR = 0x00000000 ;   // All offsets off
 
  ADC->ADC_MR = (ADC->ADC_MR & 0xFFFFFFF0) | (1 << 1) | ADC_MR_TRGEN ;  // 1 = trig source TIO from TC0
}

// Circular buffer, power of two.
#define BUFSIZE 0x400
#define BUFMASK 0x3FF
volatile int samples [BUFSIZE] ;
volatile int sptr = 0 ;
volatile int isr_count = 0 ;   // this was for debugging


#ifdef __cplusplus
extern "C"
{
#endif

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 val = *(ADC->ADC_CDR+7) ;    // get conversion result
    samples [sptr] = val ;           // stick in circular buffer
    sptr = (sptr+1) & BUFMASK ;      // move pointer
    dac_write (0xFFF & ~val) ;       // copy inverted to DAC output FIFO
  }
  isr_count ++ ;
}

#ifdef __cplusplus
}
#endif


void loop()
{
}
Title: Re: Example of driving ADC and DAC from timer for regular sampling
Post by: MarkT on Dec 19, 2013, 12:10 am
Thought I'd add some 'scope shots - I removed the inversion and looked at input
v. output, there's clearly a 2 sample delay (probably because I fill the DAC FIFO
before starting up triggers to the DAC)

(http://www.mythic-beasts.com/~markt/IMAG1843.jpg)

(http://www.mythic-beasts.com/~markt/IMAG1847.jpg)
Title: Re: Example of driving ADC and DAC from timer for regular sampling
Post by: greeTufty on Jan 15, 2014, 12:36 pm
This is pretty neat.

Is there more code, where you define constants, the ADC class etc, or does it all get defined in the depths of the Arduino source code?
Title: Re: Example of driving ADC and DAC from timer for regular sampling
Post by: MarkT on Jan 15, 2014, 02:22 pm
That's the complete sketch.  You need to look at the sources for libsam, all there
in the release, plain to see.
Title: Re: Example of driving ADC and DAC from timer for regular sampling
Post by: bbrriiaann on Feb 07, 2014, 01:32 am
MarkT,
Nice example, thanks.  I noticed that you did not disable ADC Write Protect Mode Register (ADC_WPMR) before changing the ADC parameters.  Is ADC_WPMR automatically disabled, do you know?
Title: Re: Example of driving ADC and DAC from timer for regular sampling
Post by: kbrown on Feb 15, 2014, 09:34 pm
I seem to have a fried DAC0. What do I need to change to make this work with DAC1 instead?

Cheers,
Kari
Title: Re: Example of driving ADC and DAC from timer for regular sampling
Post by: kbrown on Feb 15, 2014, 10:03 pm
Think I got it. Changed the dac_setup() to:

Code: [Select]

void dac_setup()
{
  pmc_enable_periph_clk(DACC_INTERFACE_ID);            // start clocking DAC
  DACC->DACC_CR = DACC_CR_SWRST;                       // reset DAC

  DACC->DACC_MR =
    DACC_MR_TRGEN_EN | DACC_MR_TRGSEL(1) |             // trigger 1 = TIO output of TC0
    //(0 << DACC_MR_USER_SEL_Pos) |                      // select channel 0
    (1 << DACC_MR_USER_SEL_Pos) |                      // select channel 1
    DACC_MR_REFRESH (0x0F) |                           // bit of a guess... I'm assuming refresh not needed at 48kHz
    (24 << DACC_MR_STARTUP_Pos);                       // 24 = 1536 cycles which I think is in range 23..45us since DAC clock = 42MHz

  DACC->DACC_IDR = 0xFFFFFFFF;                         // no interrupts
  //DACC->DACC_CHER = DACC_CHER_CH0 << 0;                // enable chan0
  DACC->DACC_CHER = DACC_CHER_CH1 << 0;                // enable chan1
}
Title: Re: Example of driving ADC and DAC from timer for regular sampling
Post by: rutkowsk14 on Mar 18, 2014, 03:11 am
Great thanks sooooo much. I was only using one ADC for my project so I used your code to trigger the ADCs for A0-A8, and averaged all of their values. Attached is a testing sketch I made for doing this! I later kept a running average using these values to get even more accuracy.

Code: [Select]
const int ADC_FREQ = 100000;
void setup()
{
 Serial.begin(115200);
 adc_setup () ;         // setup ADC
 
 pmc_enable_periph_clk (TC_INTERFACE_ID + 0*3+0) ;  // clock the TC0 channel 0

 TcChannel * t = &(TC0->TC_CHANNEL)[0] ;    // pointer to TC0 registers for its channel 0
 t->TC_CCR = TC_CCR_CLKDIS ;  // disable internal clocking while setup regs
 t->TC_IDR = 0xFFFFFFFF ;     // disable interrupts
 t->TC_SR ;                   // read int status reg to clear pending
 t->TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1 |   // use TCLK1 (prescale by 2, = 42MHz)
             TC_CMR_WAVE |                  // waveform mode
             TC_CMR_WAVSEL_UP_RC |          // count-up PWM using RC as threshold
             TC_CMR_EEVT_XC0 |     // Set external events from XC0 (this setup TIOB as output)
             TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_CLEAR |
             TC_CMR_BCPB_CLEAR | TC_CMR_BCPC_CLEAR ;
 
 t->TC_RC =  VARIANT_MCK/2/ADC_FREQ;     // counter resets on RC, so sets period in terms of 42MHz clock
 t->TC_RA =  VARIANT_MCK/2/ADC_FREQ/2 ;     // roughly square wave
 t->TC_CMR = (t->TC_CMR & 0xFFF0FFFF) | TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_SET ;  // set clear and set from RA and RC compares
 
 t->TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG ;  // re-enable local clocking and switch to hardware trigger source.

}

void adc_setup ()
{
 NVIC_EnableIRQ (ADC_IRQn) ;   // enable ADC interrupt vector
 ADC->ADC_IDR = 0xFFFFFFFF ;   // disable interrupts
 ADC->ADC_IER = 0x80 ;         // enable AD7 End-Of-Conv interrupt (Arduino pin A0)
 ADC->ADC_CHDR = 0xFFFF ;      // disable all channels
 ADC->ADC_CHER = 0xFF ;        // ch7:A0 ch6:A1 ch5:A2 ch4:A3 ch3:A4 ch2:A5 ch1:A6 ch0:A7
 ADC->ADC_CGR = 0x15555555 ;   // All gains set to x1
 ADC->ADC_COR = 0x00000000 ;   // All offsets off
 
 ADC->ADC_MR = (ADC->ADC_MR & 0xFFFFFFF0) | (1 << 1) | ADC_MR_TRGEN ;  // 1 = trig source TIO from TC0
}


volatile int isr_count = 0 ;   // this was for debugging
volatile unsigned int last;


#ifdef __cplusplus
extern "C"
{
#endif

void ADC_Handler (void)
{
 //wait untill all 8 ADCs have finished thier converstion.
 while(!(((ADC->ADC_ISR & ADC_ISR_EOC7) && (ADC->ADC_ISR & ADC_ISR_EOC6) && (ADC->ADC_ISR & ADC_ISR_EOC5) && (ADC->ADC_ISR & ADC_ISR_EOC4)
  && (ADC->ADC_ISR & ADC_ISR_EOC3) && (ADC->ADC_ISR & ADC_ISR_EOC2) && (ADC->ADC_ISR & ADC_ISR_EOC1) && (ADC->ADC_ISR & ADC_ISR_EOC0))));
 
 //add all of adc values up and bit shit right three times to devide by 8
 //typcast to unsigned int so leading bits are shifted in as 0s
 int avj = ((unsigned int)(*(ADC->ADC_CDR+7)+*(ADC->ADC_CDR+6)+*(ADC->ADC_CDR+5)+*(ADC->ADC_CDR+4)+*(ADC->ADC_CDR+3)+*(ADC->ADC_CDR+2)+
 *(ADC->ADC_CDR+1)+*(ADC->ADC_CDR+0)) >>3;
 //Serial.println("  avj  "+ String(avj));
 isr_count ++;
}

#ifdef __cplusplus
}
#endif


void loop()
{
 }

}
Title: Re: Example of driving ADC and DAC from timer for regular sampling
Post by: limzz on Mar 19, 2014, 09:35 pm
I was pointed to this thread, and am wondering if this would be applicable to what I'm working on. I need to sample three analog inputs, each at about 34kHz, so I need ~100k samples/second(or as fast as I can get). I'm basically listening to three mics and trying to record the time the sound arrived at each, and analogRead isn't fast enough. I've starting messing with modifying this code and will continue to do so, but I think I need some help. I made some changes to rutkowsk14's code which I added below, it's an attempt to just read and spit out values, but clearly I'm missing something.

Code: [Select]

void adc_setup ()
{
  NVIC_EnableIRQ (ADC_IRQn) ;   // enable ADC interrupt vector
  ADC->ADC_IDR = 0xFFFFFFFF ;   // disable interrupts
  ADC->ADC_IER = 0x80 ;         // enable AD7 End-Of-Conv interrupt (Arduino pin A0)
  ADC->ADC_CHDR = 0xFFFF ;      // disable all channels
  ADC->ADC_CHER = 0xE0 ;        // enable A0, A1, A2?
  ADC->ADC_CGR = 0x15555555 ;   // All gains set to x1
  ADC->ADC_COR = 0x00000000 ;   // All offsets off
 
  ADC->ADC_MR = (ADC->ADC_MR & 0xFFFFFFF0) | (1 << 1) | ADC_MR_TRGEN ;  // 1 = trig source TIO from TC0
}


volatile unsigned int last;


#ifdef __cplusplus
extern "C"
{
#endif

void ADC_Handler (void)
{
  //wait untill all 8 ADCs have finished thier converstion.
  while(!(((ADC->ADC_ISR & ADC_ISR_EOC7) && (ADC->ADC_ISR & ADC_ISR_EOC6) && (ADC->ADC_ISR & ADC_ISR_EOC5))));
 
  int val0 = *(ADC->ADC_CDR+7) ;
  int val1 = *(ADC->ADC_CDR+6) ;
  int val2 = *(ADC->ADC_CDR+5) ;

  Serial.println(val0);
  Serial.println(val1);
  Serial.println(val2);
  delay(1000);
}

#ifdef __cplusplus
}
#endif
Title: Re: Example of driving ADC and DAC from timer for regular sampling
Post by: ecousin on Aug 06, 2014, 03:01 pm
I don't really understand where you configure which clock is used and at which frequency.
Indeed you consider you're working at 84 MHz (or at the end 42 Mhz because of one prescaler set to 2), and the following calculations are made from that.
But the uC has the MainCLK, the PLLACLK, the SlowCLK, and it does not seem to be initialized.

Has someone the answer to these questions?
Thanks in advance.
Title: Re: Example of driving ADC and DAC from timer for regular sampling
Post by: MarkT on Aug 07, 2014, 09:58 pm

MarkT,
Nice example, thanks.  I noticed that you did not disable ADC Write Protect Mode Register (ADC_WPMR) before changing the ADC parameters.  Is ADC_WPMR automatically disabled, do you know?


All fields in all control registers default to zeroes at reset to my knowledge, including
the "enable write-protect" bits.
Title: Re: Example of driving ADC and DAC from timer for regular sampling
Post by: MarkT on Aug 07, 2014, 10:06 pm

I don't really understand where you configure which clock is used and at which frequency.
Indeed you consider you're working at 84 MHz (or at the end 42 Mhz because of one prescaler set to 2), and the following calculations are made from that.
But the uC has the MainCLK, the PLLACLK, the SlowCLK, and it does not seem to be initialized.

Has someone the answer to these questions?
Thanks in advance.


All the main clocking is setup in SystemInit() in CMSIS, which for the SAM3X
processors defaults to 84MHz.  These code examples are run from setup() so the
system is already up and running.

See:
<distrib>/hardware/arduino/sam/system/CMSIS/Device/ATMEL/sam3xa/source/system_sam3xa.c
Title: Re: Example of driving ADC and DAC from timer for regular sampling
Post by: Steve_Jacobs on Oct 16, 2015, 10:03 pm
I realize this is an old thread, but I'll take a shot at someone seeing this...

Thank you MarkT for your example.  I made use of it, and another example I found elsewhere, to develop my own sketch for the Due that copies an analog signal from input to output.  The goal is to set the sampling rate as high as possible, and eventually to explore digital filtering with this I/O as the starting point.

My sketch is below.  I have it working, and the sampling rate is a little over 400 kHz.  Sine wave in, sine wave out, no problem.  My question is...why does it work?  In particular, I borrowed from your sketch the line that enables analog input pin A0:

ADC->ADC_CHER = 0x00000001 << 7 ;

or, equivalently

ADC->ADC_CHER = 0x00000080 ;

What I'm wondering is, why doesn't this enable channel 7 instead of channel 0?  From the SAM3X datasheet, I expect that the correct syntax to enable channel 0 would be:

ADC->ADC_CHER = 0x00000001 ;

I am stumped.  Any help or redirection you can provide is greatly appreciated.

Thanks,
Steve
Title: Re: Example of driving ADC and DAC from timer for regular sampling
Post by: Steve_Jacobs on Oct 16, 2015, 11:23 pm
Nevermind...I figured it out.  Pin A0 is ADC channel 7.  In fact, for some reason pins A0 - A7 are adc channels AD7 - AD0, in reverse order.  This is shown on the left side of this pinout…

http://forum.arduino.cc/index.php?topic=132130.0

…although I have no idea why they would do this.

The lesson learned?  Start with the Due pinout next time...


Title: Re: Example of driving ADC and DAC from timer for regular sampling
Post by: MarkT on Oct 17, 2015, 08:42 pm
I know exactly why they did it that way, because it made the PCB layout simpler. 
Title: Re: Example of driving ADC and DAC from timer for regular sampling
Post by: JulianLewis on Jan 06, 2016, 05:39 pm
I am working with the DUE timers, could you please tell me where you found the definitions you used in your code, the TcChannel structure, where is this structure defined ? Where is it documented ? Where are the hardware definitions coming from like PIOB and functions like pmc_enable_periph_clk . I am very sorry to ask such a dumb question but I simply can't seem to find the hardware description anywhere ? The code is a great example for understanding the timers, I just need the definitions to make it do what I want.
Title: Re: Example of driving ADC and DAC from timer for regular sampling
Post by: alnoor45 on Feb 19, 2017, 10:04 am
So what the wrong her :
error: base operand of '->' is not a pointer
Title: Re: Example of driving ADC and DAC from timer for regular sampling
Post by: alnoor45 on Feb 19, 2017, 10:17 am
 So what the meaning of this ''error: base operand of '->' is not a pointer'' and how I can fix it . :(  :( 
Title: Re: Example of driving ADC and DAC from timer for regular sampling
Post by: chemsou on Apr 22, 2017, 01:00 am
hi
i would like to realise acquisition of 3 analog signals at 1KHz ,with arduino DUE ,and I dont now how to do it .
thanks for advance
Title: Re: Example of driving ADC and DAC from timer for regular sampling
Post by: ard_newbie on Apr 22, 2017, 07:51 am
To understand the examples of this thread, I suggest that you start by reading the Analog to Digital Converter and Timer Counters sections of Sam3x datasheet.

There are many ways to trigger the ADC controller at a 3 KHz frequency for 3 analog inputs resulting in a 1 KHz sampling of each analog input. One of them is a hardware trigger, as listed in table TRGSEL section 43.7.2 page 1333. Note that there is a typo in this table because ADC_TRIG5 equals to PWM Event Line 1.

Edit : If a TIOA is used to trigger ADC conversions, it is interesting to use TIOA2 (TC0 channel 2 = TC2) since TIOA2 is output on pin PA5 which is not broken out. So you keep broken out pins TIOA0 (PB25) and TIOA1 (PA2)  usable for other purposes.
Title: Re: Example of driving ADC and DAC from timer for regular sampling
Post by: adeel on Jul 08, 2017, 08:32 am
Hi,


I have used this code to successfully acquire the audio from a 2-wire intercom system and played it back using a audio amplifier.

Now on the same 2-wire system there is data present too, which again acquired( on A0 using ADC) using this code and shown on the oscilloscope at DAC output.


Now I would like to show the received data onto serial port ?
Title: Re: Example of driving ADC and DAC from timer for regular sampling
Post by: EpicFoodCartDestroyer on Jun 29, 2018, 09:49 pm
Code: [Select]


void dac_setup ()
{
  pmc_enable_periph_clk (DACC_INTERFACE_ID) ; // start clocking DAC
  DACC->DACC_CR = DACC_CR_SWRST ;  // reset DAC
 
  DACC->DACC_MR =
    DACC_MR_TRGEN_EN | DACC_MR_TRGSEL (1) |  // trigger 1 = TIO output of TC0
    (0 << DACC_MR_USER_SEL_Pos) |  // select channel 0
    DACC_MR_REFRESH (0x0F) |       // bit of a guess... I'm assuming refresh not needed at 48kHz
    (24 << DACC_MR_STARTUP_Pos) ;  // 24 = 1536 cycles which I think is in range 23..45us since DAC clock = 42MHz

  DACC->DACC_IDR = 0xFFFFFFFF ; // no interrupts
  DACC->DACC_CHER = DACC_CHER_CH0 << 0 ; // enable chan0
}

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


Hi, This might be long shot since this hasn't been posted on in a while, but I had a question regarding the "dac_write" function:

Section 44.6.4 of the SAM3A-Atmel documentation says: "Warning: Writing in the DACC_CDR register while TXRDY flag is inactive will corrupt FIFO data."

I was wondering why it's not necessary to implement some sort of check that prevents writing to the CDR when the the TXRDY flag is inactive.

Regards,
-H

Title: Re: Example of driving ADC and DAC from timer for regular sampling
Post by: ard_newbie on Jun 30, 2018, 06:04 am
The DAC peripheral is much faster than the ADC peripheral:

Sam3x datasheet, Page 1403: Max ADC sampling frequency = 1 MHz

Sam3x datasheet, Page 1411: Max DAC sampling frequency = 2 MHz


BTW, to sample from one of the builtin ADC channels and output from one of the builtin DAC channels, the best option is to make use of the PDC DMA to divide by the buffer size the total number of interruptions  :)
Title: Re: Example of driving ADC and DAC from timer for regular sampling
Post by: EpicFoodCartDestroyer on Jul 18, 2018, 06:47 pm
Code: [Select]

pmc_enable_periph_clk (TC_INTERFACE_ID + 0*3+0) ;  // clock the TC0 channel 0

  TcChannel * t = &(TC0->TC_CHANNEL)[0] ;                // pointer to TC0 registers for its channel 0
  t->TC_CCR = TC_CCR_CLKDIS ;                                // disable internal clocking while setup regs
  t->TC_IDR = 0xFFFFFFFF ;                                        // disable interrupts
  t->TC_SR ;                                                              // read int status reg to clear pending
  t->TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1 |      // use TCLK1 (prescale by 2, = 42MHz)
              TC_CMR_WAVE |                                         // waveform mode
              TC_CMR_WAVSEL_UP_RC |                         // count-up PWM using RC as threshold
              TC_CMR_EEVT_XC0 |                                // Set external events from XC0 (this setup TIOB as output)
              TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_CLEAR |
              TC_CMR_BCPB_CLEAR | TC_CMR_BCPC_CLEAR ;
 
  t->TC_RC =  875 ;                                                // counter resets on RC, so sets period in terms of 42MHz clock
  t->TC_RA =  440 ;                                             //why 440???
  t->TC_CMR = (t->TC_CMR & 0xFFF0FFFF) | TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_SET ;  // set clear and set from RA and RC compares
 
  t->TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG ;  // re-enable local clocking and switch to hardware trigger source.



I am trying to output a 60Hz sinewave via a DMA. Would anyone be able to explain why TC_RA is set to 440?


In the attached code I have a 2000 point sinewave (I know it could have less points) I set TC_RC to 350 because the dac should output at a frequency of n*"desired hz"

However, when I ran the code with RA at 440 the dac did not output anything, when I  changed RA to 110 the DAC began to output.

Title: Re: Example of driving ADC and DAC from timer for regular sampling
Post by: ard_newbie on Jul 18, 2018, 08:44 pm

TC_RA = TC_RC/2 to output a 50% duty cycle. And TC_RA < TC_RC  !!
Title: Re: Example of driving ADC and DAC from timer for regular sampling
Post by: TUCTUC77 on May 15, 2019, 04:34 pm
I am trying to use the sketch number 1, but actually I cannot have what there is on the pictures of the oscilloscope, can someone help me ?
Title: Re: Example of driving ADC and DAC from timer for regular sampling
Post by: ard_newbie on May 15, 2019, 04:54 pm

Post the sketch you are actually using (between code tags).
Title: Re: Example of driving ADC and DAC from timer for regular sampling
Post by: TUCTUC77 on May 16, 2019, 06:48 pm
Thank you for your answer, actually I can have it but the signals seems to be in opposite phase
this is my code
Code: [Select]

void setup()

//generation du Timer sampling a 1kHz
{
  adc_setup () ;         // setup ADC
 
  pmc_enable_periph_clk (TC_INTERFACE_ID + 0*3+0) ;  // clock the TC0 channel 0

  TcChannel * t = &(TC0->TC_CHANNEL)[0] ;    // pointer to TC0 registers for its channel 0
  t->TC_CCR = TC_CCR_CLKDIS ;  // disable internal clocking while setup regs
  t->TC_IDR = 0xFFFFFFFF ;     // disable interrupts
  t->TC_SR ;                   // read int status reg to clear pending
  t->TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1 |   // use TCLK1 (prescale by 2, = 42MHz)
              TC_CMR_WAVE |                  // waveform mode
              TC_CMR_WAVSEL_UP_RC |          // count-up PWM using RC as threshold
              TC_CMR_EEVT_XC0 |     // Set external events from XC0 (this setup TIOB as output)
              TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_CLEAR |
              TC_CMR_BCPB_CLEAR | TC_CMR_BCPC_CLEAR ;
 
  t->TC_RC =  42000 ;     // counter resets on RC, so sets period in terms of 42MHz clock
  t->TC_RA =  21000 ;     // roughly square wave
  t->TC_CMR = (t->TC_CMR & 0xFFF0FFFF) | TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_SET ;  // set clear and set from RA and RC compares
 
  t->TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG ;  // re-enable local clocking and switch to hardware trigger source.

  setup_pio_TIOA0 () ;  // drive Arduino pin 2 at 48kHz to bring clock out
  dac_setup () ;        // setup up DAC auto-triggered at 48kHz
}

void setup_pio_TIOA0 ()  // Configure Ard pin 2 as output from TC0 channel A (copy of trigger event)
{
  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_write (int val)
{
  DACC->DACC_CDR = val & 0xFFF ;
}


void dac_setup()
{
  pmc_enable_periph_clk(DACC_INTERFACE_ID);            // start clocking DAC
  DACC->DACC_CR = DACC_CR_SWRST;                       // reset DAC

  DACC->DACC_MR =
    DACC_MR_TRGEN_EN | DACC_MR_TRGSEL(1) |             // trigger 1 = TIO output of TC0
    (0 << DACC_MR_USER_SEL_Pos) |                      // select channel 0
    DACC_MR_REFRESH (0x0F) |                           // bit of a guess... I'm assuming refresh not needed at 48kHz
    (24 << DACC_MR_STARTUP_Pos);                       // 24 = 1536 cycles which I think is in range 23..45us since DAC clock = 42MHz
  DACC->DACC_IDR = 0xFFFFFFFF;                         // no interrupts
  DACC->DACC_CHER = DACC_CHER_CH0 << 0;                // enable chan0
 
}

void adc_setup ()
{
  NVIC_EnableIRQ (ADC_IRQn) ;   // enable ADC interrupt vector
  ADC->ADC_IDR = 0xFFFFFFFF ;   // disable interrupts
  ADC->ADC_IER = 0x80 ;         // enable AD7 End-Of-Conv interrupt (Arduino pin A0)
  ADC->ADC_CHDR = 0xFFFF ;      // disable all channels
  ADC->ADC_CHER = 0x80 ;        // enable just A0
  ADC->ADC_CGR = 0x15555555 ;   // All gains set to x1
  ADC->ADC_COR = 0x00000000 ;   // All offsets off
 
 
  ADC->ADC_MR = (ADC->ADC_MR & 0xFFFFFFF0)| (1 << 1) | ADC_MR_TRGEN;   // 1 = trig source TIO from TC0\
 
}

// Circular buffer, power of two.
#define BUFSIZE 0x400
#define BUFMASK 0x3FF
volatile int samples [BUFSIZE] ;
volatile int sptr = 0 ;
volatile int isr_count = 0 ;   // this was for debugging


#ifdef __cplusplus
extern "C"
{
#endif

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 val = *(ADC->ADC_CDR+7) ;    // get conversion result
    samples [sptr] = val ;           // stick in circular buffer
    sptr = (sptr+1) & BUFMASK ;      // move pointer
    dac_write (0xFFF & ~val) ;       // copy inverted to DAC output FIFO
  }
  isr_count ++ ;
}

#ifdef __cplusplus
}
#endif


void loop()
{
}


this is exactly the same, just modifying the sampling rate...
Title: Re: Example of driving ADC and DAC from timer for regular sampling
Post by: ard_newbie on May 16, 2019, 08:10 pm


I can have it but the signals seems to be in opposite phase

dac_write (0xFFF & ~val) ;       // copy inverted to DAC output FIFO


BTW, there is an interrupt after each and every conversion ( 1000 times per second). A more rational sampling method would be to fill a sampling buffer (e.g. 128 values), output this buffer thru a DAC while continuously sampling to fill another buffer with a PDC DMA thus dividing interrupts by a 128 scale.

https://forum.arduino.cc/index.php?topic=224672.0 (https://forum.arduino.cc/index.php?topic=224672.0)
reply 10
Title: Re: Example of driving ADC and DAC from timer for regular sampling
Post by: TUCTUC77 on May 17, 2019, 11:28 am
thank you for your reply, i did not saw the inversion, think i was tired.

About the other sketch, do you think it is really better, because this one works good and i only need a sampling frequency of 1k and the input is a sinewave of 100hz, i have the feeling that i can works with that sketch, no ?
Title: Re: Example of driving ADC and DAC from timer for regular sampling
Post by: ard_newbie on May 17, 2019, 12:32 pm

Sampling a 100 Hz sinwave at a frequency of 1000 Hz is not a good idea (10 points per period is a bit weak). You won't see a nice sinewave output from the DAC.

I would go for at least 60 points per period.
Title: Re: Example of driving ADC and DAC from timer for regular sampling
Post by: TUCTUC77 on May 17, 2019, 02:38 pm
Thank you for your reply, actually this is for an experiment so i need to see the steps ! but for sure this is not a good sinewave fron the DAC
Title: Re: Example of driving ADC and DAC from timer for regular sampling
Post by: TUCTUC77 on May 17, 2019, 03:37 pm
I have one additional question, in that case (the timer drives the ADC), does it mean that the ADC_clock = sampling frequency ?

If not how can i know the ADC_clock ?


Thank you