Created Wed, 07 Mar 2012 02:37:43 +0000 by Inspire
Wed, 07 Mar 2012 02:37:43 +0000
Hi,
Where is the info on using the 32bit timer in the uno32? i want to use it to generate a 32bit PWM output. I haven't used the pic platform before and i realise that the arduino api is an abstraction layer on top of the real PIC code, so if you could point me to a PIC code example i could work with that.
With my application, the higher the frequency of the PWM the better, the actual frequency is not important but the resolution is.
EDIT: so 2 16bit timers become a single 32bit timer
Thanks I.
Thu, 08 Mar 2012 13:47:54 +0000
Hello,
The PIC32 timer registers can be found in this datasheet:
http://ww1.microchip.com/downloads/en/DeviceDoc/61132B.pdf
In the section timers on page 463 it tells you how to use 32-bit mode.
T2CONSET = (1 << 3); will set up timer2 and timer3 for 32-bit mode. The timer config registers are also defined in that section.
Best Regards, Ryan K
Fri, 09 Mar 2012 22:38:15 +0000
I got this so far:
#define period 0x100000; unsigned int dutyCycle = 0x010000;
void setup() { //Timer T2CON = 0x0; //turn the timer off T3CON = 0x0; T2CONSET = T2_PS_1_1 | T2_32BIT_MODE_ON; //set prescaler, 32bit operation PR2 = period; //count up to period T2CONSET = T2_ON;
OC1CON = 0x0; OC1R = dutyCycle; OC1RS = dutyCycle; OC1CON = OC_TIMER2_SRC | OC_PWM_FAULT_PIN_DISABLE | OC_TIMER_MODE32; OC1CONSET = OC_ON; }
Is there a way to increase the PWM frequency, its a bit too low at this point, i think i also have to figure out why both oc1rs and oc1r are required.
Sat, 10 Mar 2012 01:01:20 +0000
Out of curiosity, is your frequency 48 or 76 hz? I wasn't sure if the Periphial bus clock is running at 80 or 50 Mhz (as the datasheet references 50Mhz). Also, I believe the OC1RS register is used to safely and smoothly update the match register after the timer is reset. Not sure if the OC1R can be updated while the PWM is running but I guess changing in the middle of a compare could cause bad results for certain applications.
Sun, 11 Mar 2012 16:28:25 +0000
I calculate it to be 80MHz
Sat, 29 Sep 2012 00:14:59 +0000
I would be also interested in this what "Inspire" had started. Has anyone managed to maybe write some sort of high-level call for the 32-bit PWM generation?
I am totally new to the Uno32 platform and got the board mainly for the higher PWM resolution compared to the Arduino Uno with its 8-bit resolution.
So to start with I would be just happy to have something like the "3. Analog -> Fading" example to have for the 16 or 32-bit PWM.
Best, P
Sat, 29 Sep 2012 23:23:44 +0000
Here is what I use for my servo:
#define TWENTY_MS 1600000 // 20ms/periphial clock(80MHz) --> .02/(1/80,000,000)
#define MIN_MOTOR TWENTY_MS/20 //OFF But Armed 1ms pulse minimum to arm ESC
OC1CON = 0x0000;// Turn off the OC4 when performing the setup
OC1R = MIN_MOTOR;// Initialize primary Compare register
OC1RS = MIN_MOTOR;// Initialize secondary Compare register
OC1CON = 0x0006;// Configure for PWM mode without Fault pin enabled
T2CONSET = 0x0008;// Enable 32-bit Timer mode
PR2 = TWENTY_MS; // period of 20ms = 50Hz
T2CONSET = 0x8000;// Enable Timer2
OC1CONSET = 0x8020;// Enable OC1 in 32-bit mode.
Sun, 30 Sep 2012 02:52:07 +0000
Note that if you're just doing servo control, the built-in Servo library from MPIDE does a fantastic job, even at only 16 bits. (and you can run up to 24 servos at the same time) The 32-bit resolution is really lost on an RC servo - at least I've never run across a servo (even the really expensive ones) that can utilize that kind of resolution.
*Brian
Sun, 30 Sep 2012 12:17:05 +0000
Note that if you're just doing servo control, the built-in Servo library from MPIDE does a fantastic job, even at only 16 bits. (and you can run up to 24 servos at the same time) The 32-bit resolution is really lost on an RC servo - at least I've never run across a servo (even the really expensive ones) that can utilize that kind of resolution. *Brian
Thanks dangeljs for the code snippet :)
And personally I was looking just a way to switch current for power LEDs. I was planning to drive a MOSFET* with the PWM, trying to go around the problem that not many LED drivers respond to that kind of dynamic range.
Sun, 30 Sep 2012 17:51:29 +0000
And update to previous post.
I actually found a nice tutorial for the 32-bit PWM generation from "Northwestern University mechatronics design wiki" (http://hades.mech.northwestern.edu/index.php/PIC32MX:_PWM_Motor_Control)
They had an example of ramping the duty cycle up and down like for the FADING demo with an interrupt to change the duty cycle.
That is how my code looks for OC2 (Pin 5) then:
// Demo code from: http://hades.mech.northwestern.edu/index.php/PIC32MX:_PWM_Motor_Control
#define SYS_FREQ 80000000L // Give the system's clock frequency
#define MAX_DUTY 3999 // Max duty cycle
#include <plib.h>
unsigned int Pwm; // variable to store calculated PWM value
unsigned int Mode = 0; // variable to determine ramp up or ramp down
int main(void)
{
// Configure the proper PB frequency and the number of wait states
SYSTEMConfigPerformance(SYS_FREQ);
// Allow vector interrupts
INTEnableSystemMultiVectoredInt();
// init OC2 module
OpenOC2( OC_ON | OC_TIMER2_SRC | OC_PWM_FAULT_PIN_DISABLE, 0, 0);
// OC1 is mapped to Pin 3 on Uno32
// OC2 - Pin 5
// OC3 - Pin 6
// OC4 - Pin 9
// OC5 - Pin 10
// init Timer2 mode and period (PR2) (frequency of 1 / 20 kHz = (3999 + 1) / 80MHz * 1
OpenTimer2( T2_ON | T2_PS_1_1 | T2_SOURCE_INT, MAX_DUTY);
mT2SetIntPriority(7); // set Timer2 Interrupt Priority
mT2ClearIntFlag(); // clear interrupt flag
mT2IntEnable(1); // enable timer2 interrupts
while(1)
{}
CloseOC2();
} //end main
// fix from: http://www.chipkit.org/forum/viewtopic.php?f=13&t=173&p=1429
#ifdef __cplusplus
extern "C" {
#endif
void __ISR(_TIMER_2_VECTOR, ipl7) T2Interrupt(void)
{
if ( Mode )
{
if ( Pwm <= MAX_DUTY ) // ramp up mode
{
Pwm ++; // If the duty cycle is not at max, increase
SetDCOC2PWM(Pwm); // Write new duty cycle
}
else
{
Mode = 0; // PWM is at max, change mode to ramp down
}
} // end of ramp up
else
{
if ( Pwm > 0 ) // ramp down mode
{
Pwm --; // If the duty cycle is not at min, increase
SetDCOC2PWM(Pwm); // Write new duty cycle
}
else
{
Mode = 1; // PWM is at min, change mode to ramp up
}
} // end of ramp down
// clear interrupt flag and exit
mT2ClearIntFlag();
} // T2 Interrupt
#ifdef __cplusplus
}
#endif
Thu, 04 Oct 2012 06:33:52 +0000
Bumping this up again.
As being rather novice on directly writing to the registers and using the timer interrupts I was wondering if anyone had success with updating the timer parameters (such as PWM frequency and duty cycle) from some GUI front-end from the computer over the serial port.
I had a simple Python-based GUI for controlling the duty cycle of Arduino Uno's PWM output and the following code worked fine with the analogWrite() -function, but now it only responds to the very first change (so going from zero duty cycle to the first change I do), and then jams.
So probably I am doing something very inefficiently, and I would assume that the slow serial connection is causing the problems but I couldn't really figure out the specifics:
// Adopted the interrupt handling from:
// http://hades.mech.northwestern.edu/index.php/PIC32MX:_PWM_Motor_Control
#define SYS_FREQ 80000000L // Give the system's clock frequency
#if defined(__PIC32MX__)
#include <p32xxxx.h> /* this gives all the CPU/hardware definitions */
#include <plib.h> /* this gives the i/o definitions */
#endif
// INIT VARIABLES
unsigned int toControl;
// Bytes for PWM
unsigned int firstByte;
unsigned int secondByte;
unsigned int thirdByte;
unsigned int fourthByte;
// Combined intensity value from 1st, 2nd, 3rd, and 4th bytes
volatile unsigned int value32bit;
// PWM Frequency (frequency of 1 / pwmFrequency Hz = (maxDuty + 1) / 80MHz * 1
unsigned int pwmFrequency = 500; // Hz
unsigned int maxDuty = (SYS_FREQ / pwmFrequency) - 1;
unsigned int rangeMax = 65536;
// init the output channel values
unsigned int ledOut_ch1=0, ledOut_ch2=0, ledOut_ch3=0, ledOut_ch4=0;
void setup() {
// Configure the proper PB frequency and the number of wait states
SYSTEMConfigPerformance(SYS_FREQ);
// Allow vector interrupts
INTEnableSystemMultiVectoredInt();
// init OC2 module
OpenOC2( OC_ON | OC_TIMER2_SRC | OC_PWM_FAULT_PIN_DISABLE, 0, 0);
// OC1 is mapped to Pin 3 on Uno32
// OC2 - Pin 5
// OC3 - Pin 6
// OC4 - Pin 9
// OC5 - Pin 10
// init Timer2 mode and period (PR2) (frequency of 1 / pwmFrequency Hz = (maxDuty + 1) / 80MHz * 1
OpenTimer2( T2_ON | T2_PS_1_1 | T2_SOURCE_INT, maxDuty);
mT2SetIntPriority(7);// set Timer2 Interrupt Priority
mT2ClearIntFlag(); // clear interrupt flag
mT2IntEnable(1); // enable timer2 interrupts
// Set the baud rate
Serial.begin(57600);
}
void loop()
{
}
// CODE FOR INTERRUPT TO UPDATE DUTY CYCLE:
// fix from: http://www.chipkit.org/forum/viewtopic.php?f=13&t=173&p=1429
#ifdef __cplusplus
extern "C" {
#endif
void __ISR(_TIMER_2_VECTOR, ipl7) T2Interrupt(void)
{
if(Serial.available() >= 2){
// Checks the channel to be controlled
toControl = Serial.read();
toControl = byte(toControl);
// read the 8 bit ones
firstByte = Serial.read();
secondByte = Serial.read();
thirdByte = Serial.read();
fourthByte = Serial.read();
// combine to 32-bit value
value32bit = (firstByte * 16777216) + (secondByte * 65536) + (thirdByte * 256) + fourthByte;
// Serial.println(value32bit); // debug
// map the value based on the maximum duty cycle value
value32bit = map(value32bit, 0, rangeMax, 0, maxDuty);
// Serial.println(value32bit); // debug
// The cases are given from the .py file (or some other frontend)
switch(toControl) {
case 'r':
ledOut_ch1 = value32bit;
SetDCOC2PWM(ledOut_ch1);
break;
case 'g':
ledOut_ch2 = value32bit;
// SetDCOC3PWM(ledOut_ch2); // not configured yet, the timer
break;
}
mT2ClearIntFlag(); // clear interrupt flag
} // T2 Interrupt
#ifdef __cplusplus
}
#endif
so the toControl is just a char specifying what slider did I move in the GUI, and then the possible 32-bit duty cycle is sent as 4 x 8 byte words and re-combined in the sketch.
Thanks for any insights, P
Thu, 04 Oct 2012 21:35:40 +0000
Part of your problem may be that you are reading more bytes than you may really have:
if(Serial.available() >= 2){
// Checks the channel to be controlled
toControl = Serial.read();
toControl = byte(toControl);
// read the 8 bit ones
firstByte = Serial.read();
secondByte = Serial.read();
thirdByte = Serial.read();
fourthByte = Serial.read();
The above has 5 reads for potentially only 2 bytes in the buffer.
Also, when I update my duty cycle I just write a value to OCxRS register:
OC1RS = VALUE;
It will automatically change to this duty cycle on the next timer reset of the output compare.
Hope some of this is helpful.
Sun, 07 Oct 2012 18:40:01 +0000
Part of your problem may be that you are reading more bytes than you may really have: The above has 5 reads for potentially only 2 bytes in the buffer.
Thanks for the suggestion, indeed it was not very optimal and in the end I don't think I will be passing more than 3 x 8 bytes at once so got rid of the two extra calls.
And noticed that moving the "serial available" to the loop along with the delay() stabilized the behavior quite a lot.