chipKIT® Development Platform

Inspired by Arduino™

32Bit Timer / PWM Output?

Created Wed, 07 Mar 2012 02:37:43 +0000 by Inspire


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.


Ryan K

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


Inspire

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.


dangeljs

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.


Inspire

Sun, 11 Mar 2012 16:28:25 +0000

I calculate it to be 80MHz


pietroFinland

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


dangeljs

Sat, 29 Sep 2012 23:23:44 +0000

Here is what I use for my servo:

#define TWENTY_MS  1600000  // 20ms/periphial clock(80MHz) --&gt; .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.

EmbeddedMan

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


pietroFinland

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.

  • got the following kits for testing:
  • [url]http://proto-pic.co.uk/mosfet-power-control-kit/[/url]
  • [url]http://www.pololu.com/catalog/product/713[/url]

pietroFinland

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 &lt;plib.h&gt;

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&amp;t=173&amp;p=1429
#ifdef __cplusplus
extern "C" {
#endif

  void __ISR(_TIMER_2_VECTOR, ipl7) T2Interrupt(void)
  {
    
    if ( Mode )
      {
      if ( Pwm &lt;= 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 &gt; 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

pietroFinland

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 &lt;p32xxxx.h&gt; /* this gives all the CPU/hardware definitions */
#include &lt;plib.h&gt; /* 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&amp;t=173&amp;p=1429
#ifdef __cplusplus
extern "C" {
#endif

  void __ISR(_TIMER_2_VECTOR, ipl7) T2Interrupt(void)
  {
       
    if(Serial.available() &gt;= 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


dangeljs

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() &gt;= 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.


pietroFinland

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.