Go Down

Topic: 5 MHz signal from Due (Read 818 times) previous topic - next topic

paxtakor22

Mar 01, 2019, 04:33 pm Last Edit: Mar 01, 2019, 04:42 pm by paxtakor22
Hello,

I have a device that I'm trying to communicate with. I have to send 26 command bits along with a clock at 5 MHz to this device in order to get a response back. I wrote some code for the Due that will generate two signals that are close to 5 MHz (about 4.94 MHz) which i believe might be close enough. I am using PWM mode to drive these two signals. One signal is the clock the second is the data.

Here is what i want:

Image of what i want


Here is the code i have so far:

Code: [Select]


void setup ()

{

  PMC->PMC_PCER1 |= PMC_PCER1_PID36;

  REG_PWM_DIS = PWM_DIS_CHID0|PWM_DIS_CHID2; // disable both pins

  REG_PWM_CLK = PWM_CLK_PREA(0)|PWM_CLK_DIVA(1);

  REG_PIOC_PDR |= PIO_PDR_P3; // pin 35

  REG_PIOC_ABSR |= PIO_PC3B_PWMH0; // pin 35

  REG_PWM_CMR0 = PWM_CMR_CPRE_CLKA;    

  REG_PWM_CPRD0 = 1;                 //vs 16                      

  REG_PWM_CDTY0 = 0;//100%      //vs 8

  REG_PIOC_PDR |= PIO_PDR_P7; // pin 39

  REG_PIOC_ABSR |= PIO_PC7B_PWMH2; // pin 39  

  REG_PWM_CMR2 = PWM_CMR_CPRE_CLKA;    

  REG_PWM_CPRD2 = 16;                      //vs 1      

  REG_PWM_CDTY2 = 8; // 50%             //vs 0

  //REG_PWM_SCM = 5;

  //REG_PWM_ENA = PWM_ENA_CHID0|PWM_ENA_CHID2;

}


int cnt = 26;

void loop()
{
  while(1)
  {
    while(cnt)
    {
      PWM->PWM_ENA = 5;//PWM_ENA_CHID0|PWM_ENA_CHID2;
      PWM->PWM_DIS = 5;//PWM_DIS_CHID0|PWM_DIS_CHID2;
      --cnt;
    }
    delay(1000);
    cnt = 26;
  }
}





Questions:

1. Is there a better way to do this?

2. If i swap the two PWM pins,

ie

Code: [Select]


REG_PWM_CPRD2 = 1;
REG_PWM_CDTY2 = 0;

and

REG_PWM_CPRD0 = 16;
REG_PWM_CDTY0 = 8;



my frequency drops to ~3MHz, why does that happen?

3. Are there any more optimization techniques i can use to make this faster?


Thank You.


ard_newbie

#1
Mar 01, 2019, 05:25 pm Last Edit: Mar 01, 2019, 05:55 pm by ard_newbie
Without  altering the chip frequency (the default DUE frequency is 84 MHz), the closest square wave frequency to 5MHz you can generate is 4.94 MHz.

Question: is a 1.2 % error acceptable ?

paxtakor22

Without  altering the chip frequency (the default DUE frequency is 84 MHz), the closest square wave frequency to 5MHz you can generate is 4.94 MHz.

Question: is a 1.2 % error acceptable ?
Yes, it is acceptable. How can i alter the frequency though? Would i have to solder on a new oscillator?

ard_newbie

#3
Mar 01, 2019, 06:57 pm Last Edit: Mar 01, 2019, 07:02 pm by ard_newbie
There are on-chip RC oscillators (4,8,12 MHz). I didn't try out yet to switch from the Main 12 MHz oscillator to the 8 MHz on-chip RC oscillator, but this could (theoritecally) give a 80 Mhz frequency, and therefore you could output 5 MHz. Note that the 8 and 12 MHz on-chip RC oscillators are calibrated in factory (see page 546, Sam3x datasheet).

If I have some time this WE, I will try out to find a code for that.

Anyway, since 4.94MHz is acceptable, you can use a Timer Counter to output a square wave with this frequency, and trigger an interrupt at each end of a period to decide whether you set(1)or clear(0) a particuliar pin (see your drawing).

For the 5 MHz clock, do you need a nice square wave (PWM with a 50%duty cycle), or another constant duty cycle would fit too ?

paxtakor22

There are on-chip RC oscillators (4,8,12 MHz). I didn't try out yet to switch from the Main 12 MHz oscillator to the 8 MHz on-chip RC oscillator, but this could (theoritecally) give a 80 Mhz frequency, and therefore you could output 5 MHz. Note that the 8 and 12 MHz on-chip RC oscillators are calibrated in factory (see page 546, Sam3x datasheet).

If I have some time this WE, I will try out to find a code for that.

Anyway, since 4.94MHz is acceptable, you can use a Timer Counter to output a square wave with this frequency, and trigger an interrupt at each end of a period to decide whether you set(1)or clear(0) a particuliar pin (see your drawing).

For the 5 MHz clock, do you need a nice square wave (PWM with a 50%duty cycle), or another constant duty cycle would fit too ?
I'm hoping it is acceptable because i havent tried it out yet. Yes the clock signal has to be a 50% duty cycle square wave. I didnt know the Due had on-chip oscillators. Ill try to find more information about that.

Could you point me to any examples that use an interrupt in a similar manner?

Thank you!

ard_newbie


An example sketch to output a quasi square wave at 4.94 MHz and set or clear a pin every 4.94 MHz (not tested though):

Code: [Select]

/******************************************************************************/
/*                         PWMH0 ~ 5MHz                                        */
/******************************************************************************/

void setup() {

  PMC->PMC_PCER1 |= PMC_PCER1_PID36;                   // PWM power ON

  PWM->PWM_CLK = PWM_CLK_PREB(0) | PWM_CLK_DIVB(1);    // select Frequency for clock B: Mck = 84 MHz

  PWM->PWM_CH_NUM[0].PWM_CMR = PWM_CMR_CPRE_CLKB;      // The period is left aligned, clock source as CLKB on channel 0
  PWM->PWM_CH_NUM[0].PWM_CPRD = 17;                    // Set the PWM frequency 84 MHz/PWM_CPRD = 4941176 Hz

  PWM->PWM_CMP[0].PWM_CMPM = PWM_CMPM_CEN;
  PWM->PWM_CMP[0].PWM_CMPV = PWM_CMPV_CV(8);

  PWM->PWM_IER1 = PWM_IER1_CHID0;                      // Interrupt on PWM Channel 0 counter match (CPRD)
  PWM->PWM_IER2 = PWM_IER2_CMPM0;                      // Interrupt on CMPV match (CV)
  NVIC_EnableIRQ(PWM_IRQn);                            // Enable interrupt

  PWM->PWM_ENA = PWM_ENA_CHID0;                        // Enable PWM channel 0
}

void loop() {

}

// The Handler is triggered at 2*4.94 MHz
void PWM_Handler() {

  // End of the 4.94 Mhz period
  if (PWM->PWM_ISR1 & PWM_ISR1_CHID0)
  {
   
    // toggle the clock pin e.g.: PIOB->PIO_ODSR ^= PIO_ODSR_P27;
    // Set or clear the data pin with PIO_SODR/PIO_CODR

  }

  // End of the middle of the 4.94 MHz period
  if (PWM->PWM_ISR2 & PWM_ISR2_CMPM0)
  {
   
    // toggle the clock pin e.g.: PIOB->PIO_ODSR ^= PIO_ODSR_P27;
  }
}








weird_dave

Given that the data is clocked, does it really need to be 5MHz anyway, isn't that just the max for the device? What is the device?

paxtakor22

#7
Mar 05, 2019, 06:20 pm Last Edit: Mar 05, 2019, 06:34 pm by paxtakor22
An example sketch to output a quasi square wave at 4.94 MHz and set or clear a pin every 4.94 MHz (not tested though):

Code: [Select]

/******************************************************************************/
/*                         PWMH0 ~ 5MHz                                        */
/******************************************************************************/

void setup() {

  PMC->PMC_PCER1 |= PMC_PCER1_PID36;                   // PWM power ON

  PWM->PWM_CLK = PWM_CLK_PREB(0) | PWM_CLK_DIVB(1);    // select Frequency for clock B: Mck = 84 MHz

  PWM->PWM_CH_NUM[0].PWM_CMR = PWM_CMR_CPRE_CLKB;      // The period is left aligned, clock source as CLKB on channel 0
  PWM->PWM_CH_NUM[0].PWM_CPRD = 17;                    // Set the PWM frequency 84 MHz/PWM_CPRD = 4941176 Hz

  PWM->PWM_CMP[0].PWM_CMPM = PWM_CMPM_CEN;
  PWM->PWM_CMP[0].PWM_CMPV = PWM_CMPV_CV(8);

  PWM->PWM_IER1 = PWM_IER1_CHID0;                      // Interrupt on PWM Channel 0 counter match (CPRD)
  PWM->PWM_IER2 = PWM_IER2_CMPM0;                      // Interrupt on CMPV match (CV)
  NVIC_EnableIRQ(PWM_IRQn);                            // Enable interrupt

  PWM->PWM_ENA = PWM_ENA_CHID0;                        // Enable PWM channel 0
}

void loop() {

}

// The Handler is triggered at 2*4.94 MHz
void PWM_Handler() {

  // End of the 4.94 Mhz period
  if (PWM->PWM_ISR1 & PWM_ISR1_CHID0)
  {
    
    // toggle the clock pin e.g.: PIOB->PIO_ODSR ^= PIO_ODSR_P27;
    // Set or clear the data pin with PIO_SODR/PIO_CODR

  }

  // End of the middle of the 4.94 MHz period
  if (PWM->PWM_ISR2 & PWM_ISR2_CMPM0)
  {
    
    // toggle the clock pin e.g.: PIOB->PIO_ODSR ^= PIO_ODSR_P27;
  }
}





Why did you use clock B instead of A? Seems like the interrupts are slowing the signal down. I cant change the frequency even if i change the  PWM_CPRD = 20  and PWM_CMPV_CV(21) values.

I got a 1.333 MHz PWM signal, that is not at 50% duty cycle, off pin 13 with the following code:


Code: [Select]


/******************************************************************************/
/*                         PWMH0 ~ 5MHz                                        */
/******************************************************************************/

void setup() {
  
  pinMode(13, OUTPUT);

  PMC->PMC_PCER1 |= PMC_PCER1_PID36;                   // PWM power ON

  PWM->PWM_CLK = PWM_CLK_PREB(0) | PWM_CLK_DIVB(1);    // select Frequency for clock B: Mck = 84 MHz

  PWM->PWM_CH_NUM[0].PWM_CMR = PWM_CMR_CPRE_CLKB;      // The period is left aligned, clock source as CLKB on channel 0
  PWM->PWM_CH_NUM[0].PWM_CPRD = 17;                    // Set the PWM frequency 84 MHz/PWM_CPRD = 4941176 Hz

  PWM->PWM_CMP[0].PWM_CMPM = PWM_CMPM_CEN;
  PWM->PWM_CMP[0].PWM_CMPV = PWM_CMPV_CV(8);

  PWM->PWM_IER1 = PWM_IER1_CHID0;                      // Interrupt on PWM Channel 0 counter match (CPRD)
  PWM->PWM_IER2 = PWM_IER2_CMPM0;                      // Interrupt on CMPV match (CV)
  NVIC_EnableIRQ(PWM_IRQn);                            // Enable interrupt

  PWM->PWM_ENA = PWM_ENA_CHID0;                        // Enable PWM channel 0
}

void loop() {
  
  while(1)
  {
    PWM_Handler();
  }

}

// The Handler is triggered at 2*4.94 MHz
void PWM_Handler() {

  // End of the 4.94 Mhz period
  if (PWM->PWM_ISR1 & PWM_ISR1_CHID0)
  {
    
    // toggle the clock pin e.g.:
    PIOB->PIO_ODSR ^= PIO_ODSR_P27;
    // Set or clear the data pin with PIO_SODR/PIO_CODR

  }

  // End of the middle of the 4.94 MHz period
  if (PWM->PWM_ISR2 & PWM_ISR2_CMPM0)
  {
    
    // toggle the clock pin e.g.:
    PIOB->PIO_ODSR ^= PIO_ODSR_P27;
  }
}








Given that the data is clocked, does it really need to be 5MHz anyway, isn't that just the max for the device? What is the device?
I guess i could run it slower than 5MHz, i will confirm. The device is something our company makes, it is not commercially available to a regular consumer.

ard_newbie

#8
Mar 05, 2019, 06:41 pm Last Edit: Mar 05, 2019, 06:57 pm by ard_newbie
What frequency/duty cycle do you get for pin 35 (or pin 13):

Code: [Select]

/******************************************************************************/
/*                         PWMH0 5 MHz                                        */
/******************************************************************************/

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);

  PMC->PMC_PCER1 |= PMC_PCER1_PID36;                   // PWM power ON

  PIOC->PIO_PDR |= PIO_PDR_P3;                         // Set PWM pin to a peripheral
  PIOC->PIO_ABSR |= PIO_PC3B_PWMH0;                    // Set PWM pin peripheral type B for PWMH0 (Arduino pin 35)

  PWM->PWM_CLK = PWM_CLK_PREA(0) | PWM_CLK_DIVA(1);    // select Frequency for clock A: Mck = 84 MHz

  PWM->PWM_CH_NUM[0].PWM_CMR = PWM_CMR_CPRE_CLKA;      // The period is left aligned, clock source as CLKA on channel 0
  PWM->PWM_CH_NUM[0].PWM_CPRD = 17;                    // Set the PWM frequency 84 MHz/PWM_CPRD = 4941176 Hz
  PWM->PWM_CH_NUM[0].PWM_CDTY = 8;                     // Set the PWM duty cycle = (CPRD/CDTY) * 100 %

  PWM->PWM_IER1 = PWM_IER1_CHID0;                      // Interrupt on PWM Channel 0 counter
  NVIC_EnableIRQ(PWM_IRQn);                            // Enable interrupt

  PWM->PWM_ENA = PWM_ENA_CHID0;                        // Enable PWM channel 0
}

void loop() {
}

void PWM_Handler() {
  
  PWM->PWM_ISR1;      // Clear status register
  PIOB->PIO_ODSR ^= PIO_ODSR_P27;
 
}

paxtakor22

#9
Mar 05, 2019, 07:14 pm Last Edit: Mar 05, 2019, 07:20 pm by paxtakor22
I'm getting 4.94 MHz with 50% duty cycle on pin 35.
Getting 1.29 MHz on pin 13. Duty cycle is not at 50%

Thank You!!

The data signal (pin 13) lags behind the clock, is there a way to sync them or fire them off more closely to each other?

Also i believe i can use a lower frequency to do this after talking to another engineer. Would a lower frequency give me signals that are closer together? I know PWM signals can be synchronized. Was my idea to use two PWM signals (one at 50% duty cycle for clock and second at 100% duty cycle for data) by enabling and disabling the two PWM pins no good? I was able to get them pretty close to synchronized.

ard_newbie

#10
Mar 05, 2019, 08:40 pm Last Edit: Mar 05, 2019, 08:45 pm by ard_newbie
I don't understand the issue...maybe something weird with PB27, the builtin LED (Page 1413, Sam3x datasheet states that the maximum output frequency for PIOs is between 35 and 65 MHz).

Try to replace PB27 (pin 13) by PA15(pin 24).

And yes you can synchro several PWM channels with PWM channel 0(search in the DUE sub forum for example sketches for manual or automatic PWM synchro channels). Or more simply, enable 2 PWM channels at the same time : PWM->PWM_ENA = PWM_ENA_CHID0 | PWM_ENA_CHID1;

paxtakor22

Its even worse with pin24. The duty cycle doesnt stay constant.

I think i will go back to my original code in the first post. It works as desired except that i cannot get the 50% duty cycle pwm signal coming off of pin 35 to go down to 0 volts "quickly" (it takes about 400us) after the 26th "1"

Any idea how i can do this?

MartinL

#12
Mar 05, 2019, 11:03 pm Last Edit: Mar 05, 2019, 11:06 pm by MartinL
How about setting the clock and data as synchronous PWM channels and use the PDC (Peripheral DMA Controller) to transfer the 26 command bits from memory to the PWM Controller?

If say the clock is on channel 0 and the data on channel 1, the duty-cycle information for both channels can be interleaved in a single data array:

Code: [Select]
{ CLK, DATA, CLK, DATA, CLK, DATA ... }
This method would save having to call the interrupt service routine at 5MHz. (The ISR would only be called once the PDC reached the end of the array).

paxtakor22

#13
Mar 05, 2019, 11:55 pm Last Edit: Mar 05, 2019, 11:56 pm by paxtakor22
How about setting the clock and data as synchronous PWM channels and use the PDC (Peripheral DMA Controller) to transfer the 26 command bits from memory to the PWM Controller?

If say the clock is on channel 0 and the data on channel 1, the duty-cycle information for both channels can be interleaved in a single data array:

Code: [Select]
{ CLK, DATA, CLK, DATA, CLK, DATA ... }
This method would save having to call the interrupt service routine at 5MHz. (The ISR would only be called once the PDC reached the end of the array).
I dont quite follow. Could you give me an example or some more info?

At this point the signal need not be 5MHz. I just need two synchronized square waves that i could use for clock and data like i the "what i want" picture: What I want. I guess direct port manipulation would suffice.

ard_newbie

#14
Mar 06, 2019, 07:10 am Last Edit: Mar 06, 2019, 10:48 am by ard_newbie
If a PWM PDC DMA is used, the "clock pin" frequency will be 84 MHz/8/2 = 5250000 Hz. If a 5% error is still acceptable, that's OK.

Edit: I took some more time to explore a solution since a 5 MHz interrupt frequency is an issue.

1/ It's possible to output a 4.94 quasi square wave on a "clock pin", say PWM channel 1 PWMH1

2/ As MartinL suggested, use PWM channel 0 PWMH0 to output 26 signals on a "data pin" at a 4.94 MHz frequency by using this channel as a synchro channel but only with itself (only one interruption after 26 ouput signals), and enable PWM channel 0 and channel 1 at the same time to be synchro.

3/ Once the 26 signals have been sent by PWM channel 1, disable this channel inside the interrupt handler, but keep PWM channel 0 enabled.


I guess after that you will have to decipher a receiving message from the device and attachinterrupt() won't be fast enough to decipher 5 MHz  input signals....

Go Up