Motor PWM example for the dsPIC30F4011

The following program is an example of PWM output from the six motor PWM pins on the dsPIC30F4011. The duty cycle of each of the three PWM channels is controlled by an analog input. The duty cycle of each channel should vary between 0-100%. All three PWM channels are configured in complementary mode – i.e. the two pins in each channel are always at opposite logic levels. The PWM period is supposed to be 20ms, but I still have to confirm on a scope that this is actually the case.

//
// This program is an example of the motor PWM facility on the
// dsPIC30F4011.
//
// Written by Ted Burke - last updated 4-12-2010
//

#include <libpic30.h>
#include <p30f4011.h>

// Configuration settings
_FOSC(CSW_FSCM_OFF & FRC_PLL16);  // Fosc=16x7.5MHz, Fcy=30MHz
_FWDT(WDT_OFF);                   // Watchdog timer off
_FBORPOR(MCLR_DIS);               // Disable reset pin

// Function prototypes
void configure_pins();
unsigned int read_analog_channel(int n);

int main()
{
	int voltage;

	// Set up which pins are which
	configure_pins();

	while(1)
	{
		// Analog input 0 controls PWM 1 duty cycle.
		voltage = read_analog_channel(0);
		PDC1 = (int)((voltage / 1023.0) * 2 * PTPER);

		// Analog input 1 controls PWM 2 duty cycle.
		voltage = read_analog_channel(1);
		PDC2 = (int)((voltage / 1023.0) * 2 * PTPER);

		// Analog input 2 controls PWM 3 duty cycle.
		voltage = read_analog_channel(2);
		PDC3 = (int)((voltage / 1023.0) * 2 * PTPER);
	}

	return 0;
}

void configure_pins()
{
	// Configure RD0 as a digital output
	LATD = 0;
	TRISD = 0b11111110;

	// Configure analog inputs
	TRISB = 0x01FF;      // Port B all inputs
	ADPCFG = 0xFF00;     // Lowest 8 PORTB pins are analog inputs
	ADCON1 = 0;          // Manually clear SAMP to end sampling, start conversion
	ADCON2 = 0;          // Voltage reference from AVDD and AVSS
	ADCON3 = 0x0005;     // Manual Sample, ADCS=5 -> Tad = 3*Tcy = 0.1us
	ADCON1bits.ADON = 1; // Turn ADC ON

	// Configure PWM for free running mode
	// PWM period = Tcy * prescale * PTPER = 0.33ns * 64 * 9470 = 20ms
	PWMCON1 = 0x00FF; // Enable all PWM pairs in complementary mode
	PTCON = 0;
	_PTCKPS = 3;      // prescale=1:64 (0=1:1, 1=1:4, 2=1:16, 3=1:64)
	PTPER = 9470;     // 20ms PWM period (15-bit period value)
	PDC1 = 0;         // 0% duty cycle on channel 1 (max is 65536)
	PDC2 = 0;         // 0% duty cycle on channel 2 (max is 65536)
	PDC3 = 0;         // 0% duty cycle on channel 3 (max is 65536)
	PTMR = 0;         // Clear 15-bit PWM timer counter
	_PTEN = 1;        // Enable PWM time base
}

// This function reads a single sample from the specified
// analog input. It should take less than 2.5us if the chip
// is running at about 30 MIPS.
unsigned int read_analog_channel(int channel)
{
	ADCHS = channel;          // Select the requested channel
	ADCON1bits.SAMP = 1;      // start sampling
	__delay32(30);            // 1us delay @ 30 MIPS
	ADCON1bits.SAMP = 0;      // start Converting
	while (!ADCON1bits.DONE); // Should take 12 * Tad = 1.2us
	return ADCBUF0;
}
Advertisements
This entry was posted in Uncategorized. Bookmark the permalink.

9 Responses to Motor PWM example for the dsPIC30F4011

  1. Mo says:

    Hi
    Do you know how can I generate phase shifted PWM with dspic30f4011?

    thanks

  2. John says:

    Hi Ted,
    Can you help me with the conversion? I tried to display the ADC readings onto a LCD but I cant display the correct integer reading. I used the code from cytron’s website but at most I can only display BCD. Here’s cytron’s BCD conversion: lcd_putchar(ui_decimal[uc_digit – 1] + 0x30);

    • batchloaf says:

      Hi John,

      The reason they’re adding 0x30 (equal to 48 decimal) to every character is that if you look at the table of ascii character values, you’ll see that the characters “0” to “9” are represented by ascii values from 48 to 57. See here:

      http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters

      So all you need to do with your ADC value is break it down into individual digits values, add 48 to each one and send it to the LCD. I’m assuming you have the lcd_putchar function.

      Here’s a rough idea of how you might do it:

      int v; // analog voltage reading
      int d3, d2, d1, d0; // four digits
      
      // Read analog voltage
      v = read_analog_channel(0);
      
      // Calculate individual digits
      d3 = (v / 1000) % 10; // Thousands digit
      d2 = (v / 100) % 10;  // Hundreds digit
      d1 = (v / 10) % 10;   // Tens digit
      d0 = v % 10;          // Ones digit
      
      // Convert each character to ascii and display on LCD
      lcd_putchar(d3 + 48);
      lcd_putchar(d2 + 48);
      lcd_putchar(d1 + 48);
      lcd_putchar(d0 + 48);
      

      The “%” sign in those digit calculations is the modulo operator in C. It basically divides the number on the left by the number on the right, but gives you the remainder. If you haven’t come across it before, just google “C modulo operator examples”.

      Hope that helps.

      Ted

  3. a says:

    sir ,

    how i get pwm …….by using matlab ……which block set i have to use …in dspic 30f4011

  4. miss2802 says:

    Hi Sir,
    I want to generate 3 PWM signals using q dsPIC30f in Matlab/Simulink, please can you help me with some documents,
    Thanks in advance

  5. Hafiz Idris says:

    Hi Ted,
    I really love and inspire by your job, I need your help Ted. I am really new for this kind of programming and microcontroller and I need to made PWM by using DSPIC30F4011 with 0.4 duty cycle and 20KHz frequency , and I would appreciate and grateful if you can write me a very simple but complete code for that spec.
    Thank you Ted

    • batchloaf says:

      Hi Hafiz,

      Please try this out:

      //
      // This dsPIC30F4011 program generates 20kHz PWM with 40% duty cycle
      // Written by Ted Burke - last updated 19-4-2017
      //
      
      #include <xc.h>
      
      // Configuration settings
      _FOSC(CSW_FSCM_OFF & FRC_PLL16);  // Fosc=16x7.5MHz, Fcy=30MHz
      _FWDT(WDT_OFF);                   // Watchdog timer off
      _FBORPOR(MCLR_DIS);               // Disable reset pin
      
      int main()
      {
          // Configure PWM for free running mode
          PWMCON1 = 0x00FF; // Enable all PWM pairs in complementary mode
          _PTCKPS = 0;      // prescale=1:1 (0=1:1, 1=1:4, 2=1:16, 3=1:64)
          PTPER = 1500;     // 50us PWM period for 20kHz PWM
          PDC1 = 1200;      // 40% duty cycle on PWM channel 1 (pin 37)
          _PTEN = 1;        // Enable PWM time base
      
          while(1)
          {
              // Nothing here because PWM module does all the work!
          }
      }
      

      The output should be 20 kHz PWM with a 40% duty cycle on PWM1H (pin 37). Let me know if it works or not.

      Ted

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s