Go Down

Topic: PWM phase-correct and analogRead DUE (Read 300 times) previous topic - next topic

Tyarel

Hi,

I have an Arduino DUe and I have to generate a centereed (phase-correct PWM) PWM with 50% duty cycle at a specific frequency (525Hz). I found how to change PWM frequency modifying timers but only for fast PWM method, i didn't find anyhng for phase-correct one, anyone knows?

I have another question, I have to acquire data from 2 signals using analogRead 12-bit at 10ksps and be sure that the period of acquisition doesn't change, how can I do that?

Thanks a lot.
Luigi

ard_newbie

There are lots of example sketches of PWM direct register programming in the DUE sub forum, or use the PWM library from antodom.


For ADC conversions at a precise sample rate, there is, for example, this thread (reply #1):
https://forum.arduino.cc/index.php?topic=589213.msg4008020#msg4008020

Tyarel

Thank you for the reply, but I have to send data to the serial port, and I have seen that it takes much time, how can I save or reduce that time?


ard_newbie


Serial write rather than Serial print if you don't need a human readeable output, use the maximum baud rate (250000) with the Serial Monitor, switch to SerialUSB (Naztive USB Port) if necessary. 

Anyway, write your code step by step and post your code if you have issues with it.

Tyarel

Thank you so much for your suggestions, I followed your suggestion but now I have a problem for the  ADC because I would like to have 12-bit resolution and I can't understand which resolution I will have from the code you have shown (I can't test it now).

Which resolution I have with that code?
If I have 10-bit resolution, can I switch to 12 with just the command analogReadResolution(12)?
If not, how can I achieve 12-bit resolution?

Here the code you pointed out

Code: [Select]

/*******************************************************************************/
/*                     10 Hz ADC conversions for nchannels                     */
/*******************************************************************************/
#define SampleRate  (10)
uint8_t channels[] = {7,6,5};
#define nchannels (sizeof(channels))
uint16_t Buf[nchannels];

volatile boolean FlagConversion;

void setup()
{
  Serial.begin(250000);
  pinMode(LED_BUILTIN, OUTPUT);
  adc_setup();
  tc_setup();
}

void loop()
{

  if (FlagConversion == true)
  {
    FlagConversion = false;
    for (int i = 0; i < nchannels; i++)
    {
      printf(" ADC[%d] = %d\n", channels[i], Buf[i]);
    }
  }
}

/*************  Configure ADC 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_PRESCAL(1) |
                  ADC_MR_TRGSEL_ADC_TRIG3;               // Trigger by TIOA2 Rising edge

  ADC->ADC_IDR = ~(0ul);
  ADC->ADC_CHDR = ~(0ul);
  for (int i = 0; i < nchannels; i++)
  {
    ADC->ADC_CHER |= ADC_CHER_CH0 << channels[i];
  }
  ADC->ADC_IER |= ADC_IER_EOC0 << channels[nchannels - 1];
  //ADC->ADC_ACR = ADC_ACR_IBCTL(0b01);                    // For frequencies > 500 KHz
  ADC->ADC_PTCR |= ADC_PTCR_RXTDIS | ADC_PTCR_TXTDIS;    // Disable PDC DMA
  NVIC_EnableIRQ(ADC_IRQn);                              // Enable ADC interrupt
}

/*************  Timer Counter 0 Channel 2 to generate PWM pulses thru TIOA2  ************/
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 = F_CPU / 8 / SampleRate; //<*********************  Frequency = (Mck/8)/TC_RC  Hz = 10 Hz
  TC0->TC_CHANNEL[2].TC_RA = 50;  //<********************   Any Duty cycle in between 1 and (TC_RC - 1)

  TC0->TC_CHANNEL[2].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN; // Software trigger TC2 counter and enable
}

void ADC_Handler() {

  static uint32_t LedBlink;

  for (int i = 0; i < nchannels; i++)
  {
      Buf[i] = (uint16_t) * (ADC->ADC_CDR + channels[i]);
  }
   FlagConversion = true;
 
  // For debugging only : Blink every 1 Hz
 
  if (LedBlink++ == SampleRate)
  {
    LedBlink = 0;
    PIOB->PIO_ODSR ^= PIO_ODSR_P27;
  }
 
}


Actually, I do not understand the function ADC_Handler what is used for, because it is never called in the code, which is its utility?

My code:

Code: [Select]
#define SampleRate  (10000)
uint8_t channels[] = {1,2};
#define nchannels (sizeof(channels))
uint16_t Buf[nchannels];
float F_Conv=3.3/4096.0;

volatile boolean FlagConversion;

void setup(){
 
  SerialUSB.begin(250000);
  while(!SerialUSB){}
  pwmD4_setup();
  adc_setup();
  tc_setup();
 
}

void loop(){
 
  SerialUSB.print(millis(), 3); SerialUSB.print(',');
  if (FlagConversion == true){
   
    FlagConversion = false;
    /*for (int i = 0; i < nchannels-1; i++)
    {
      SerialUSB.print(F_Conv*Buf[i],4); SerialUSB.print(',');
    }*/
    SerialUSB.println(F_Conv*Buf[nchannels-1],4);
   
  }
 
}

// Output 50% duty cycle PWM at 525kHz on digital pins D4 and D5 using TC6
void pwmD4_setup() {
 
  REG_PMC_PCER1 |= PMC_PCER1_PID33;                 // Enable peripheral TC6 (TC2 Channel 0)
  REG_PIOC_ABSR |= PIO_ABSR_P26 | PIO_ABSR_P25;     // Switch the multiplexer to peripheral B for TIOA6 and TIOB6
  REG_PIOC_PDR |= PIO_PDR_P26 | PIO_PDR_P25;        // Disable the GPIO on the corresponding pins

  REG_TC2_CMR0 = TC_CMR_BCPC_SET |                  // Set TIOB on counter match with RC0
                 TC_CMR_ACPC_SET |                  // Set TIOA on counter match with RC0
                 TC_CMR_BCPB_CLEAR |                // Clear TIOB on counter match with RB0
                 TC_CMR_ACPA_CLEAR |                // Clear TIOA on counter match with RA0
                 TC_CMR_WAVE |                      // Enable wave mode
                 TC_CMR_WAVSEL_UP_RC |              // Count up with automatic trigger on RC compare
                 TC_CMR_EEVT_XC0 |                  // Set event selection to XC0 to make TIOB an output
                 TC_CMR_TCCLKS_TIMER_CLOCK4;        // Set the timer clock to TCLK4: /128 (MCK/128 = 84MHz/128 = 656250Hz)

  REG_TC2_RA0 = 625;                               // Load the RA0 register as DT 50%
  REG_TC2_RB0 = 625;                               // Load the RB0 register
  REG_TC2_RC0 = 1250;                              // Load the RC0 register as 656250/1250=525Hz

  REG_TC2_CCR0 = TC_CCR_SWTRG | TC_CCR_CLKEN;       // Enable the timer TC6

}

/*************  Configure ADC 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_PRESCAL(1) |
                  ADC_MR_TRGSEL_ADC_TRIG3;               // Trigger by TIOA2 Rising edge

  ADC->ADC_IDR = ~(0ul);
  ADC->ADC_CHDR = ~(0ul);
  for (int i = 0; i < nchannels; i++){
   
    ADC->ADC_CHER |= ADC_CHER_CH0 << channels[i];
   
  }
 
  ADC->ADC_IER |= ADC_IER_EOC0 << channels[nchannels - 1];
  //ADC->ADC_ACR = ADC_ACR_IBCTL(0b01);                   // For frequencies > 500 KHz
  ADC->ADC_PTCR |= ADC_PTCR_RXTDIS | ADC_PTCR_TXTDIS;    // Disable PDC DMA
  NVIC_EnableIRQ(ADC_IRQn);                              // Enable ADC interrupt

}

/*************  Timer Counter 0 Channel 2 to generate PWM pulses thru TIOA2  ************/
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 = F_CPU / 8 / SampleRate; //<*********************  Frequency = (Mck/8)/TC_RC  Hz = 10000 Hz
  TC0->TC_CHANNEL[2].TC_RA = 50;  //<********************   Any Duty cycle in between 1 and (TC_RC - 1)

  TC0->TC_CHANNEL[2].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN; // Software trigger TC2 counter and enable

}

ard_newbie

It's about time you read the ADC section of Sam3x datasheet  :)

Read page 1333 for ADC_MR register details.

Tyarel

Perfect, I have solved that problem, thank you very much for your time.

I have just another question about the code in the function ADC_Handler.
I can't  understand in a clear manner what does " (uint16_t)* ..."  stand for: is it a cast and does "*" stand for a pointer? And is "ADC_CDR + channels" like a concatenation?

Code: [Select]
void ADC_Handler() {

  for (int i = 0; i < nchannels; i++)
  {
      Buf[i] = (uint16_t) * (ADC->ADC_CDR + channels[i]);
  }
}



ard_newbie


You could as well write :


Buf = ADC->ADC_CDR[channels];

Go Up