Created Tue, 05 Jul 2011 14:35:15 +0000 by 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.
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???
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.
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
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?
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???
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.
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
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.
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!).