chipKIT® Development Platform

Inspired by Arduino™

Maximum pin frequency

Created Tue, 05 Jul 2011 14:35:15 +0000 by tanvach


tanvach

Tue, 05 Jul 2011 14:35:15 +0000

I would like to use the UNO32 as a high frequency squarewave generator, and since the internal/core frequency is set to 80MHz, would that mean the maximum frequency output is 40MHz if I were to use a timer interrupt to toggle a digital pin on and off?

I'm not too familiar with PIC32, more from an AVR background.


rtestardi

Tue, 05 Jul 2011 18:39:11 +0000

You can use a timer in conjunction with an output compare module to toggle a pin every time the timer expires (no interrupts). Or you can use programmatic pin control to toggle the pin. The program below shows a 20MHz square wave on rd0 (pin 3) and a 5MHz square wave on rd1 (pin 5). I thought the pic32 could go faster, so the config bits or SYSTEMConfigPerformance() in the bootloader may be wrong. You can go a bit faster with the programmatic pin control by unrolling the loop, but it introduces jitter. I thought the output compare module could go faster, too -- maybe with some magic and a compare value of 0 or OCM value 5?

#define VALUE  1

void setup() {
  // set up timer 3 for fast period
  T3CONCLR = _T3CON_ON_MASK;
  TMR3 = 0;
  PR3 = VALUE;
  T3CON = _T3CON_ON_MASK;  
  
  // set up output compare 1 to track timer 3 on rd0 (pin 3)
  OC1CONCLR = _OC1CON_ON_MASK;
  OC1R = 0;
  OC1RS = VALUE;
  OC1CON = _OC1CON_ON_MASK|_OC1CON_OCTSEL_MASK|(3<<_OC1CON_OCM0_POSITION);
  
  // set rd1 for digital output
  TRISDCLR = 0x2;
}

void loop() {
  // invert rd1 (pin 5)
  LATDINV = 0x2;
}

Or maybe I have aliasing going on in my (40Ms/s) bitscope???


hairymnstr

Wed, 06 Jul 2011 10:21:57 +0000

I'm pretty sure I got 40MHz 50% duty out of an output compare peripheral when I was experimenting.

Nyquist sampling theory states that to be able to see a repetitive signal you must sample at least twice as fast as the signal so for 40MHz you need at least 80MS/s. For non repetitive signals (like duty ratio other than 50%) you need 5 times faster or more usually.


rtestardi

Wed, 06 Jul 2011 21:33:35 +0000

Yeah, I'd buy the Nyquist explanation, except I also measured the following frequencies for differing values of VALUE:

VALUE = 1; frequency = 20MHz VALUE = 3; frequency = 10MHz VALUE = 7; frequency = 5MHz

So I'm pretty sure what I measured was correct since it follows the pattern established at the lower frequencies where Nyquist can't be an issue. (That and even with the bitscope sampling at 40Ms/s, the result was really consistent and predictable over many samples -- and it has an option to show raw data...)

With that said, I could not get VALUE = 0 to work... And I did not try any of the other Output Compare modes (like 5?).

Curiously, folks on the web claim 72MHz pin toggles using LATxINV... But the code we compile can't do anything like that:

00000060 <loop>:
  60:   24030002        li      v1,2
  64:   3c020000        lui     v0,0x0
  68:   ac430000        sw      v1,0(v0)
  6c:   03e00008        jr      ra
  70:   00000000        nop

svofski

Sat, 09 Jul 2011 00:43:38 +0000

Curiously, folks on the web claim 72MHz pin toggles using LATxINV... But the code we compile can't do anything like that:

00000060 <loop>:
60:   24030002        li      v1,2
64:   3c020000        lui     v0,0x0
68:   ac430000        sw      v1,0(v0)
6c:   03e00008        jr      ra
70:   00000000        nop

Maybe with an unrolled loop? Only useful for short bursts, but it should work, no?


tanvach

Wed, 13 Jul 2011 09:16:58 +0000

Thank you, I'm curious why the programatic pin toggle is so slow, is this because of arduino compatibility layer?

You can use a timer in conjunction with an output compare module to toggle a pin every time the timer expires (no interrupts). Or you can use programmatic pin control to toggle the pin. The program below shows a 20MHz square wave on rd0 (pin 3) and a 5MHz square wave on rd1 (pin 5). I thought the pic32 could go faster, so the config bits or SYSTEMConfigPerformance() in the bootloader may be wrong. You can go a bit faster with the programmatic pin control by unrolling the loop, but it introduces jitter. I thought the output compare module could go faster, too -- maybe with some magic and a compare value of 0 or OCM value 5?

#define VALUE  1
void setup() {
// set up timer 3 for fast period
T3CONCLR = _T3CON_ON_MASK;
TMR3 = 0;
PR3 = VALUE;
T3CON = _T3CON_ON_MASK;  
// set up output compare 1 to track timer 3 on rd0 (pin 3)
OC1CONCLR = _OC1CON_ON_MASK;
OC1R = 0;
OC1RS = VALUE;
OC1CON = _OC1CON_ON_MASK|_OC1CON_OCTSEL_MASK|(3<<_OC1CON_OCM0_POSITION);
// set rd1 for digital output
TRISDCLR = 0x2;
}
void loop() {
// invert rd1 (pin 5)
LATDINV = 0x2;
}

Or maybe I have aliasing going on in my (40Ms/s) bitscope???


svofski

Wed, 13 Jul 2011 10:48:37 +0000

There are also 2 SPI modules, each of which can be used in frame mode. It's a mode in which the clock is running continuously and can be "misused" for other things as well.


rtestardi

Wed, 13 Jul 2011 13:58:36 +0000

Thank you, I'm curious why the programatic pin toggle is so slow, is this because of arduino compatibility layer?

No, there is no compatibility layer there -- we're playing directly with MCU registers.

The main problem is probably just compiler options -- the compiler is generating un-optimized code (note the delay slot is completely unused, and you can't tell from the snip, but "ra" actually has the address of "loop", so the actual loop has five instructions in it, when it only needs two -- the jr and the sw, with the sw being in the delay slot of the jr).

There might be a secondary problem related to MCU setup (wait states, cache, SYSTEMConfigPerformance(), etc.), but I think that is actually right...

-- Rich


hairymnstr

Thu, 14 Jul 2011 12:13:11 +0000

There is some Arduino overhead there. The loop() function is called from the main so the main is something more like:

void main(void) {
  init();

 while(1) {
  loop();
 }
}

So there's the overhead of the while operation and the call/return overhead.


rtestardi

Thu, 14 Jul 2011 15:35:30 +0000

Good point.

I didn't see any function call in the disassembly (it must have been automatically inlined), but this definitely helps:

void loop() {
  for (;;) {
    // invert rd1 (pin 5)
    LATDINV = 0x2;
  }
}

And generates slightly better code:

00000000 <loop>:
   0:   3c030000        lui     v1,0x0
   4:   24020002        li      v0,2
   8:   ac620000        sw      v0,0(v1)
   c:   ac620000        sw      v0,0(v1)
  10:   08000002        j       8 <loop+0x8>
  14:   00000000        nop

The delay slot is still unused (wasting an instruction), but at least the loop is shorter... And the compiler unrolled the loop once... So the pin toggle rate jumps to 20MHz (and I'm definitely seeing aliasing on my 40Ms/s bitscope now!).