Go Down

Topic: Please give advice. A problem occurred when setting the ADC sample point. (Read 1 time) previous topic - next topic

chaiwon89

Hello, I'm interested in sensor using Arduino Due ADC.

I used other people's ADC and FFT codes.
https://forum.arduino.cc/index.php?topic=137635.0
http://forum.arduino.cc/index.php?topic=225204.0

So, this is my code. (Sample rate : 1M, sample points : 2048 or 2000)
Quote
Code: [Select]

#define   SMP_RATE          1000000UL
#define   CLK_MAIN       84000000UL
#define   TMR_CNTR       CLK_MAIN / (2 *SMP_RATE)

#define   INP_BUFF  2048

volatile   uint16_t   sptr              =   0 ;
volatile    int16_t   flag              =   0 ;

           uint16_t  inp[2][INP_BUFF]   = { 0};
volatile   uint16_t   i = 1;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  adc_setup ();         
  tmr_setup ();
  pinMode(2,INPUT);
 
  while(1)
  {
    // put your main code here, to run repeatedly:
    Serial.print(inp[0][i]);
    Serial.println();
   
    if(i==INP_BUFF)
    {
      break;
    }
    else
    {
      i++;
    }
  }
  //Serial.print(FFT_SIZE);  //2048
}

void loop() {

}


void 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 tmr_setup ()
{
  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 = TMR_CNTR;              // counter resets on RC, so sets period in terms of 42MHz clock
  t->TC_RA = TMR_CNTR /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 ()
{
  pmc_enable_periph_clk(ID_ADC);  // pmc : power management controller
  adc_init(ADC, SystemCoreClock, 42000000UL, ADC_STARTUP_FAST);
  NVIC_EnableIRQ (ADC_IRQn);               // enable ADC interrupt vector      IRQ : Interrupt ReQuest 164page

  adc_disable_all_channel(ADC);
  adc_enable_interrupt(ADC, ADC_IER_RXBUFF);    //ADC_IER : (write only) ADC interrupt enable register, RXBUFF : receive buffer full interrupt enable

  ADC->ADC_RPR  =  (uint32_t)  inp[0];      // DMA buffer
  ADC->ADC_RCR  =  INP_BUFF;
  ADC->ADC_RNPR =  (uint32_t)  inp[1];      // next DMA buffer
  ADC->ADC_RNCR =  INP_BUFF;
  ADC->ADC_PTCR =  1;

  adc_set_bias_current(ADC, 0x01);
  //  adc_enable_tag(ADC);
  adc_enable_channel(ADC, ADC_CHANNEL_7);  // AN0
  adc_configure_trigger(ADC, ADC_TRIG_TIO_CH_0, 0);
  adc_start(ADC);
}

void ADC_Handler (void)
{
  if((adc_get_status(ADC) & ADC_ISR_RXBUFF) == ADC_ISR_RXBUFF) {
    flag = ++sptr;
    sptr &=  0x01;
    ADC->ADC_RNPR  =  (uint32_t)  inp[sptr];
    ADC->ADC_RNCR  =  INP_BUFF;
    }
}

I tested ADC by applying a signal with Signal Generator(20kHZ 300mVpp sine wave).

When I set sample points 2000(or 100, 500, 1000), there was not problem.
Quote
#define   INP_BUFF  2000
After data acquisition and plotting in MATLAB, I can get clear sine wave. [Figure 1]

Quote
#define   INP_BUFF  2048
However, when I set sample points 2048(or 512, 1024) and use ADC, the plotted sine wave was very distorted. [Figure 2]

In my project, I should use FFT function. So, I should use not 2000(or 100, 500, 1000) points but 2048(or 1024) points.

Is there any problem in my code and ADC setting? I don't know how to solve it.
Please give me your advice. I will listen sincerely.

MasterT

I can see a few issues with adaptation of the code:
1. You can't transfer data with rate 1Msps x 16 = 16 000 000 (!!!) bit/sec over serial link with baud rate 115 200 bit/sec.
2. ADC is overclocked more than twice.
3. Original code used "ping-pong" two buffers configuration,  to run sampling and conversion simultaneously real-time.
   That was make sense for lower sampling rate, limited by arduino DUE capability to process data. In your case, speed would be  limited by serial link, see 1
4. Arduino DUE ADC is not capable to sample 1 Msps sharp in not free-running mode, upper limit about 950 ksps.

ard_newbie

Hello chaiwon89,

I enclosed a few thoughts about your code, although It will not be sufficient for a reliable sketch... :

Code: [Select]

/***********  Comment this line when not using Serial USB   **************/
//#define Serial SerialUSB
/*************************************************************************/

// *************** There is a bug in ADC_STARTUP_FAST init, it should not be 768 periods of ADC CLOCK see page 1334. I set 8 periods instead
#define   ADC_STARTUP_FAST (ADC_MR_STARTUP_SUT8)

// ************* Always use braces for #define
#define   SMP_RATE       (1000000UL)
#define   CLK_MAIN       (84000000UL)
#define   TMR_CNTR       (CLK_MAIN / (2 *SMP_RATE))

#define   INP_BUFF      (2048)

volatile   uint16_t   sptr; // ******=   0 ;
//************* volatile    int16_t   flag              =   0 ;

uint16_t  inp[2][INP_BUFF]   = { 0};
volatile   uint16_t   i = 1;

void setup() {
  // put your setup code here, to run once:
  // **********Serial.begin(115200);
  Serial.begin(250000);
  while (!Serial); // ************** Irrelevant for Serial but necessary for SerialUSB
  adc_setup ();
  tmr_setup ();
  pinMode(2, INPUT);

// ***  shouldn't it be in loop to run repeatedly with plotter serial ???
  while (1)
  {
    
    Serial.println(inp[0][i]);
    // ************Serial.println();

    if (i == INP_BUFF)
    {
      break;
    }
    else
    {
      i++;
    }
  }
  //Serial.print(FFT_SIZE);  //2048
}

void loop() {

}


void pio_TIOA0 ()  // Configure Ard pin 2 as output from TC0 channel *****  0  ****** (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 tmr_setup ()
{
  pmc_enable_periph_clk(TC_INTERFACE_ID + 0 * 3 + 0); // clock  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

  //****** First init of TC0->TC_CHANNEL[0].TC_CMR
  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_EEVTEDG_RISING | //******** Set external event Edge selection, e.g. on Rising
              //****** what is output on TIOA after this ???
              TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_CLEAR |
              TC_CMR_BCPB_CLEAR | TC_CMR_BCPC_CLEAR ;

  t->TC_RC = TMR_CNTR;              // counter resets on RC, so sets period in terms of 42MHz clock
  t->TC_RA = TMR_CNTR / 2;          // roughly square wave

  //******** You are doing a new initialization of TC0->TC_CHANNEL[0].TC_CMR ??
  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 ;  // ****  Enable local clocking and switch to hardware trigger source.
}

void adc_setup ()
{
  pmc_enable_periph_clk(ID_ADC);  // pmc : power management controller

  //*********************** adc_init(ADC, SystemCoreClock, 42000000UL, ADC_STARTUP_FAST);
  // ***** ADC_FREQ_MAX = 20 MHz
  adc_init(ADC, SystemCoreClock, ADC_FREQ_MAX, ADC_STARTUP_FAST);
  NVIC_EnableIRQ (ADC_IRQn);               // enable ADC interrupt vector      IRQ : Interrupt ReQuest 164page

  adc_disable_all_channel(ADC);
  adc_enable_interrupt(ADC, ADC_IER_RXBUFF);    //ADC_IER : (write only) ADC interrupt enable register, RXBUFF : receive buffer full interrupt enable


  ADC->ADC_RPR  =  (uint32_t)  inp[0];      // DMA buffer
  ADC->ADC_RCR  =  INP_BUFF;
  ADC->ADC_RNPR =  (uint32_t)  inp[1];      // next DMA buffer
  ADC->ADC_RNCR =  INP_BUFF;
  sptr = 1;                         //*********************************
  ADC->ADC_PTCR |= ADC_PTCR_RXTEN;        // ****** Enable PDC receiver channel request  *********************

  adc_set_bias_current(ADC, 0x01);
  //  adc_enable_tag(ADC);
  //*************adc_enable_channel(ADC, ADC_CHANNEL_7);  // AN0
  ADC->ADC_CHER = ADC_CHER_CH7;             // Enable Channel 7 = A0
  //**********  Trigger selection on TIOA Output of the Timer Counter Channel 0
  ADC->ADC_MR |=  ADC_MR_TRGEN | ADC_MR_TRGSEL_ADC_TRIG1 | ADC_MR_FREERUN_OFF;
  // *************** adc_configure_trigger(ADC, ADC_TRIG_TIO_CH_0, 0);
  adc_start(ADC);
}

void ADC_Handler (void)
{
  if ((adc_get_status(ADC) & ADC_ISR_RXBUFF) ==  ADC_ISR_RXBUFF) {
    //***************************  flag = ++sptr;
    sptr =  (sptr + 1) % 2; // *************************** sptr = 0 or 1
    ADC->ADC_RNPR  =  (uint32_t)  inp[sptr];
    ADC->ADC_RNCR  =  INP_BUFF;
  }
}





chaiwon89

Thank you MasterT.
I understand your comment.
I understand that I have to reduce the sample rate to match the maximum speed of the Serial.
Then, how much should we reduce the sample rate?

chaiwon89

Thank you ard_newbie.
I checked your code.
Code: [Select]

void setup() {
  // put your setup code here, to run once:
  // **********Serial.begin(115200);
  Serial.begin(250000);

Your code use 250000 bit/sec serial communication, but as you know, it exceed the limitation of maximum speed of sketch...
How can I see the result of your code operation?

MasterT

I understand that I have to reduce the sample rate to match the maximum speed of the Serial.
Then, how much should we reduce the sample rate?

Depends on receiving computer, what software application you have at the other end. I read somewhere on this forum , that arduino DUE was able to stream data about 1Mbits/s over serialUSB. 
Basic serial link with speed 115200 'd provide no more than 115200/ 16 (two bytes based on 12-bits ADC result) = 7200 kHz, it's quite rough approximation, realistically value will be lower

Go Up