dsPIC30F4011

The dsPIC Microntroller

This section contains several useful pieces of sample code for different tasks on the dsPIC. There is just one example question (at the end), which provides a reasonably detailed description of an imagined control scenario for which you are asked to write a dsPIC program in C.

Introduction

The microcontroller that we use in the Robotics module lab sessions and mini-project belongs to the PIC range, which is produced by Microchip Inc. (see http://www.microchip.com/). This range of microcontrollers contains hundred of different chips, grouped into several product families. Microcontrollers with features similar to those in the PIC range are available from many other manufacturers (e.g. Texas Instruments, Intel, Atmel, etc.), but to keep things simple in this module we always use the same microcontroller – Microchip’s dsPIC30F4011.

Like all chips in the 30F family, the dsPIC30F4011 is a 16-bit microcontroller. What that means is that each of its registers (aka memory locations) stores one 16-bit binary number. The dsPIC chips we use come in a 40-pin dual in-line package (DIP). In this context, the word ‘package’ simply refers to the actual body of the chip – the block of dark plastic in which the tiny silicon integrated circuit is permanently encased (rather than the wrapping the chip is sold in). DIP chips have a standard pin size and spacing that makes them suitable for plugging straight into a breadboard. It is possible to buy the exact same dsPIC in a much smaller surface-mount package, but it cannot then be used easily in a breadboard. The DIP package is therefore much more convenient for the ad hoc experimental work we do in the Robotics lab.

The following are some of the main reasons why the dsPIC30F4011 was chosen as the microcontroller for this module:

  • It is available in a DIP form factor, which makes it convenient for prototyping work on a breadboard.
  • It has a wide supply voltage range (2.5-5.5V). This means that it can be used with a variety of different power supplies or battery packs and that it can be interfaced easily to other chips which may be less accommodating about supply voltage.
  • Electrically speaking, it is quite robust. Of course a dsPIC can be damaged by applying excessive voltage to the wrong pin. However, in my own experience, when something isn’t working in the Robotics lab, it very rarely turns out to be a faulty dsPIC chip that’s causing the problem.
  • It provides up to 9 10-bit analog input channels.
  • Because it has 40 pins, there are plenty of available pins for digital I/O (input and output).
  • Because the 30F4011 is specifically targetted at motor control applications, it provides three dedicated pairs of PWM outputs. These can be used for lots of interesting things, but they provide a particularly convenient way of controlling up to three servos.
  • The dsPIC is cheap and are not hard to buy at short notice. The exact price varies depending on when you buy it, who you buy it from and how many you buy, but a rough guideline is between €4 and €5 per chip in small quantities. There are other microcontrollers (including some PICs) that can be bought for a fraction of this cost, but for the broad range of applications we are interested in the dsPIC provides excellent flexibility for its price.
  • Development software (MPLAB and the C30 compiler among others) is available free of charge.
  • The dsPIC can be programmed using the PICkit2 USB programmer, which only costs about €30 and provides some other useful features (basic logic analyser, 5V serial input).

Please take a few minutes to read the full list of features on pages 1 and 2 of the dsPIC30F4011 datasheet. Of the dsPIC-related documents supplied by the manufacturer, those which are most useful to have at your side while programming are the following:

From here on, I will mostly refer to the dsPIC30F4011 simply as the ‘dsPIC’. However, do please bear in mind that there are many many other models in this family, each with a different combination of features and/or shape and size. Nevertheless, the basic principles of operation are identical across the 30F range of microcontrollers and once you have learned to program one you should be able to transfer that knowedge to other chips in the same family without much difficulty.

Analog input

The following example program demonstrates basic analog input on the dsPIC30F4011. Only one channel is read (AN0), although the ‘read_analog_channel()’ function should work without modification for the other analog inputs on the chip. The sampling time is controlled manually and is currently set to 1µs, which should be adequate for any signal source with a source impedance below the maximum value recommended by the manufacturer of the dsPIC (5kΩ). The total time per measurement (including sampling time and conversion time) should be less than 2.5µs.

//
// This is a simple analog input example for the dsPIC30F4011
// It configures the lowest 8 PORTB pins as analog inputs, then reads
// the voltage on AN0 repeatedly. The input voltage determines
// the brightness of an LED on RD0 by varying its duty cycle
// in proportion to the analog input reading.
//
// Written by Ted Burke - last updated 5-12-2010
//

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

_FOSC(CSW_FSCM_OFF & FRC_PLL16); // Clock speed = 7.5MHz x 16, i.e. 30 MIPS
_FWDT(WDT_OFF); // Watchdog timer off

void configure_pins();
unsigned int read_analog_channel(int n);

int main()
{
	long int n;

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

	while(1)
	{
		// Read the analog channel. The result is an
		// integer between 0 and 1023 inclusive.
		n = read_analog_channel(0);

		// LED on
		_LATD0 = 1;
		__delay32(30*n); // m us

		// LED off
		_LATD0 = 0;
		__delay32(30*(1023 - n)); // (1023-m) us
	}

	return 0;
}

void configure_pins()
{
	// Configure digital I/O
	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
}

// 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;
}

PWM – Pulse Width Modulation

One feature of the dsPIC30F4011 that is very useful in control, automation and robotics is that it provides three dedicated pairs of PWM output pins, each of which can be used to control a servo, to vary the speed of a DC motor, or for many other purposes.

The PWM signal generated on each of the outputs depends on a number of factors:

  • The instruction cycle, T_{cy} . In the robotics lab, we almost always run the dsPIC at 30 MIPS, which gives a value of T_{cy}=33\mbox{ns} .
  • The prescale value. I have set this equal to 64 in this example (see the ‘configure_pins’ function). Basically, this means that the PWM timebase (which is the PTMR register) increments just once every 64 instruction cycles.
  • The PTPER register stores an unsigned 15-bit number that sets the value up to which the timebase has to count before it resets to zero (thus initiating a new PWM pulse). The value in this register determines the PWM period.
  • The registers PDC1, PDC2 and PDC3 each store an unsigned 16-bit number that determines the duty cycles for the corresponding PWM channel (1, 2 or 3).

The PWM period, T_{pwm} , is calculated as follows:

T_{pwm} = T_{cy} \times \mbox{prescale} \times \mbox{PTPER}

T_{pwm} = 33\times10^{-9} \times 64 \times \mbox{PTPER}

In the example below, I have set PTPER equal to 9470 so that the PWM period will be 20ms (suitable for controlling the servos).

The pulse width (aka duty cycle) on PWM channel 1 is controlled by the unsigned 16-bit integer stored in register PDC1. The formula for calculating the pulse width is as follows:

\mbox{PWM channel 1 pulse width in seconds} = \mbox{PDC1} \times \mbox{prescale} \times \frac{T_{cy}}{2}

\mbox{PWM channel 1 pulse width in seconds} = \mbox{PDC1} \times 64 \times 16.5 \times 10^{-9}

Of course, the same formula can be used with PDC2 and PDC3 to control the pulse width on PWM channels 2 and 3 respectively.

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 should be 20ms.

//
// 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;
}

Timer interrupts – repeating an action at regular intervals

Sometimes, we want to make the dsPIC perform the same action over and over again at a regular time interval. For example, we might want to flash an LED by toggling a digital output pin once every 500ms. Or we might want to read an analog input once every 10ms. The dsPIC provides a very convenient way of designating a function in our C program that it will automatically call at a regular interval that we specify. The dsPIC uses one of its timers to tell it when to call the function. When the timer reaches a particular value (which we have specified in our program) the dsPIC interrupts whatever it is doing and jumps to the designated function, which we call the interrupt service routine (ISR).

Here’s a quick example that used a timer interrupt to flash an LED.

//
// This is a Timer 1 interrupt example for the dsPIC30F4011.
// Timer 1 is a 16-bit Type A timer.
// It toggles an LED on pin RD0 8 times a second.
//
// Written by Ted Burke - last updated 5-12-2010
//

#include <p30f4011.h>

// Clock speed = 7.5MHz x 16, i.e. 30 MIPS
_FOSC(CSW_FSCM_OFF & FRC_PLL16);

// Watchdog timer off
_FWDT(WDT_OFF);

// Function prototype for Timer 1 interrupt service routine
void __attribute__((__interrupt__, __auto_psv__)) _T1Interrupt(void);

int main()
{
    // Configure RD0 (pin 23) as digital output for the LED
    LATD = 0;
    TRISD = 0b11111110;

    // Configure Timer 1
    // In this example, I'm setting PR1 and TCKPS for 8Hz
    T1CON = 0;            // Clear the Timer 1 configuration
    TMR1 = 0;             // Reset Timer 1 counter
    PR1 = 14648;          // Set the Timer 1 period (max 65535)
    T1CONbits.TCS = 0;    // Select internal clock (Fosc/4)
    T1CONbits.TCKPS = 3;  // Prescaler (0=1:1, 1=1:8, 2=1:64, 3=1:256)
    _T1IP = 1;            // Set the Timer 1 interrupt priority
    _T1IF = 0;            // Clear the Timer 1 interrupt flag
    _T1IE = 1;            // Enable Timer 1 interrupt
    T1CONbits.TON = 1;    // Turn on Timer 1

    // Lock the main function here in an empty loop and let
    // the Timer 1 interrupt service routine do all the work.
    while(1);

    return 0;
}

// Timer 1 interrupt service routine
void __attribute__((__interrupt__, __auto_psv__)) _T1Interrupt(void)
{
    // Clear Timer 1 interrupt flag
    _T1IF = 0;

    // Toggle LED
    if (_LATD0) _LATD0 = 0; else _LATD0 = 1;
}

Sending text from the dsPIC to the PC via the UART

When we write a C program to run on a PC, we often use the printf function to display text messages on the screen. In general, the dsPIC has no screen attached to it, but we can still use printf to display messages via a serial connection to a PC. The dsPIC contains a subsystem called a UART (universal asynchronous receiver and transmitter) which allows data to be transmitted and received serially (i.e. as a sequence of ones and zeros, represented by high and low voltages) via a pair of pins.

The following program is an example of serial data transmission from the dsPIC30F4011 UART. This program repeatedly reads 10-bit analog readings from AN0-AN7 and then prints the results to the serial output. The baud rate is set to 38,400. However, occasional glitches may be observed in the incoming data because I’ve used the internal RC oscillator to clock the PIC, so it’s probable that Fcy (and therefore the baud rate) is somewhat inaccurate.

//
// This program is a UART transmission example for the dsPIC30F4011.
//
// To compile this, a heap size must be specified because
// printf is used. I set my heap size to 1024 in MPLAB
// as follows:
//
//	Project->Build Options...->Project->MPLAB LINK30->Heap size = 1024
//
// Apparently, a heap size of 0 might be ok for printf, but
// for scanf a bigger number may be required (e.g. 1024).
//
// Written by Ted Burke - last updated 4-12-2010
//

#include <libpic30.h>
#include <p30f4011.h>
#include <stdio.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()
{
	// Variables to store analog input voltages
	int v0, v1, v2, v3, v4, v5, v6, v7;

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

	while(1)
	{
		// Read AN0-AN7
		v0 = read_analog_channel(0);
		v1 = read_analog_channel(1);
		v2 = read_analog_channel(2);
		v3 = read_analog_channel(3);
		v4 = read_analog_channel(4);
		v5 = read_analog_channel(5);
		v6 = read_analog_channel(6);
		v7 = read_analog_channel(7);

		// Print the measured voltages on the serial o/p
		printf("AN0=%04d, AN1=%04d, AN2=%04d, AN3=%04d, ", v0, v1, v2, v3);
		printf("AN4=%04d, AN5=%04d, AN6=%04d, AN7=%04d\r\n", v4, v5, v6, v7);
	}

	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
	ADCON1bits.ADON = 1; // Turn ADC ON

	// Set up UART
	// Default is 8 data bits, 1 stop bit, no parity bit
	U1BRG = 48;            // 38400 baud @ 30 MIPS
	U1MODEbits.UARTEN = 1; // Enable UART
	U1STAbits.UTXISEL = 1; // interrupt when TX buffer is empty
	U1STAbits.UTXEN = 1;   // Enable TX
}

// 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;
}

I recorded the serial output of this program in the PICkit2 UART tool. Here’s a few lines of what it produced:

AN0=0116, AN1=0079, AN2=0000, AN3=0666, AN4=0466, AN5=0328, AN6=0230, AN7=0162
AN0=0116, AN1=0079, AN2=0000, AN3=0666, AN4=0472, AN5=0330, AN6=0232, AN7=0164
AN0=0114, AN1=0079, AN2=0000, AN3=0666, AN4=0472, AN5=0332, AN6=0234, AN7=0165
AN0=0116, AN1=0079, AN2=0000, AN3=0666, AN4=0472, AN5=0334, AN6=0236, AN7=0168
AN0=0115, AN1=0078, AN2=0000, AN3=0661, AN4=0469, AN5=0333, AN6=0233, AN7=0165
AN0=0117, AN1=0079, AN2=0000, AN3=0661, AN4=0469, AN5=0333, AN6=0237, AN7=0167
AN0=0119, AN1=0081, AN2=0000, AN3=0661, AN4=0469, AN5=0333, AN6=0237, AN7=0167
AN0=0121, AN1=0083, AN2=0000, AN3=0661, AN4=0469, AN5=0333, AN6=0240, AN7=0169

Example Question

Question:

You are writing a C program for the dsPIC controller in a service elevator, which is used to transport one trolley at a time carrying materials from the ground floor delivery area of a factory to the production line on the first floor. The elevator should behave as follows:

  • The empty elevator should wait on the ground floor for a trolley to be placed in it.
  • When a trolley is detected by the weight sensor, the elevator should wait for two seconds then begins ascending to the first floor.
  • When the elevator reaches the first floor, it should stop and wait for the trolley to be removed.
  • When the trolley is removed, the elevator should wait for two seconds then begin descending to the ground floor.
  • When the elevator reaches the ground floor, it should stop and wait for the next trolley to arrive.

The elevator is driven up and down by a DC motor, which is controlled by two of the dsPIC’s digital output pins (RD0 and Rd1) as follows:

  • When both pins are low (or both pins are high) the elevator is stationary.
  • When RD0 = 0 and RD1 = 1 the elevator is ascending.
  • When RD0 = 1 and RD1 = 0 the elevator is descending.

The position of the elevator is sensed using a pair of contact switches that are connected to two of the dsPIC digital input pins (RD2 and RD3) as follows:

  • RD2 = 1 when the elevator is at the ground floor; RD2 = 0 otherwise.
  • RD3 = 1 when the elevator is at the first floor; RD3 = 0 otherwise.

The elevator is fitted with an analog weight sensor that outputs a voltage between 0V and 5V. The weight sensor output is connected to pin AN0 on the dsPIC.

  • When a full trolley is in the elevator, the output voltage of the weight sensor is guaranteed to be greater than 3V.
  • When no trolley is in the elevator, the output voltage of the weight sensor is guaranteed to be less than 2V.

Write a C program to control the elevator. You can assume that the appropriate pins have already been designated as digital inputs, digital outputs, analog inputs, etc. Assume also that a function “read_analog_channel (int x)” has been provided, which returns a 10-bit unsigned integer representing the analog voltage on analog input channel x (i.e. pin ANx). Assume also that a function “delay_ms(int n)” has been provided which delays for the specified number of milliseconds.

Before writing the program, we just need to know what values the ADC will read at the two critical threshold voltages (2V and 3V). The full input voltage range of the ADC is 0V-5V and because it is a 10-bit device, there are 2^{10} voltage levels over that range (i.e. from 0 to 1023). The ADC output at the lower threshold (2V) is calculated as follows:

\mbox{low-weight-threshold} = 1024 * 2 / 5 = 409.6

Similarly, the ADC output at the upper threshold voltage (3V) is calculated as follows:

\mbox{high-weight-threshold} = 1024 * 3 / 5 = 614.4

So, when the ADC reads the voltage on pin AN0 and the result is less than 410, there is definitely no trolley in the elevator. Conversely, when the ADC reads AN0 and the result is greater than 614, there is definitely a full trolley in the elevator.

int main()
{
	int weight;

	configure_pins();

	// Keep going up and down as described in
	// the question.
	while(1)
	{
		// Elevator is at ground floor.
		// Wait for trolley to arrive.
		weight = read_analog_channel(0); // read the weight sensor
		while(weight <= 614)
		{
			weight = read_analog_channel(0); // read the weight sensor
		}

		// A full trolley has arrived
		// Wait 2 seconds
		delay_ms(2000);

		// Send elevator upwards
		_LATD0 = 0;
		_LATD1 = 1;

		// Keep going until we reach the first floor.
		// RD3 will go high only when we are at the first floor.
		// Keep busy doing nothing until then.
		while(_RD3 == 0) {}

		// Stop the elevator
		_LATD0 = 0;
		_LATD1 = 0;

		// Elevator is at first floor.
		// Wait for trolley to leave.
		weight = read_analog_channel(0); // read the weight sensor
		while(weight >= 410)
		{
			weight = read_analog_channel(0); // read the weight sensor
		}

		// The trolley is gone
		// Wait 2 seconds
		delay_ms(2000);

		// Send elevator downwards
		_LATD0 = 1;
		_LATD1 = 0;

		// Keep going until we reach the first floor
		while(_RD2 == 0) {}

		// Stop the elevator
		_LATD0 = 0;
		_LATD1 = 0;
	}

	return 0;
}
Advertisements

75 Responses to dsPIC30F4011

  1. Bianca says:

    This is great.I’m using also an dsPic30f4011 in a school project and you post helps me a lot in understanding how this uC works…… In my project i have to drive an H bridge inverter in complementary mode.
    thank you and keep up the good work

  2. Bianca says:

    I have a few questions, if you don’t mind.
    1. Were did you get this formula: Tpwm= TcyX prescale X PTPER? because in the dspic30f datasheet is other formula for pwm period and now i dont know which one is best…
    2. PDC1 = (int)((voltage / 1023.0) * 2 * PTPER); i don’t understand why you say that the duty cycle equals 2 times PTPER.
    3.pw1 = PDC1 x prescale x Tcy/2 ; what is pw1? you say it’s pulse width(duty cycle) , but PDC1 is dutycycle…i’m confused.

    if you can give some examples it will be wonderful.
    Thank you very much,

    • batchloaf says:

      Hi Bianca,

      I haven’t got all of the documentation here in front of me, but I’ll do my best to answer your questions.

      1. One possible (but not very likely) reason that you found a different formula for the PWM period in the datasheet is that I think there are actually two different ways of generating a PWM signal on the 30f4011. The formula you found might be for PWM via the “Output Compare” pins (see section 14 of the dsPIC 30F Family Reference Manual). However, the formula I have used is for the dedicated PWM pins (as described in section 15 of the dsPIC30F4011 data sheet). The most likely reason that the equation you are looking at is different from the one I have shown is that you are looking at equation 15-1 on page 94 of the dsPIC30F4011 data sheet. To be honest, I’m finding it difficult to reconcile that equation (15-1) with what I know happens on the chip in real life. The only explanation I can offer for what they have written in the their equation is that they are considering the “prescaler value” to be 1, 1/4, 1/16 or 1/64, rather than 1, 4, 16, 64 as I have assumed it to be. To my mind, the “bigger” the prescaler value, the longer it takes PTMR to count up, and therefore the longer the period is. Anyway, the formula I have shown corresponds with what happens in practice, provided that you consider the prescale value to be 1, 4, 16 or 64, as I have done. Either way, it’s only a matter of notation.
      2. I was confused about this at first too. When you set the PWM period (by writing a value into PTPER), the period length is calculated in multiples of the instruction cycle, Tcy. However, the duty cycle is specified as a multiple of half the instruction cycle. This may seem strange, but in order to give higher resolution for the duty cycle, the PWM output has been designed to be able to switch half way through an instruction cycle! Because the PWM period and duty cycle are specified in different “units” (Tcy and Tcy/2 respectively), a factor of two is required in the equation you mentioned. For example, the PDC value for a 100% duty cycle will be twice the PTPER value. Very confusing – I know!
      3. The last one is probably my fault for not explaining clearly when I wrote it. I’m really just using “pw1” as an ordinary mathematical variable (in units of seconds). In other words, it’s not a computer variable or a register name or anything like that. I probably should have just written “Pulse width for PWM channel 1 in seconds” instead of “pw1”. Sorry about that – I’ll edit it when I have a minute to spare.

      Sorry that these answers are a bit rushed – I’m just running out the door! Anyway, I hope they help to clear thing up a bit, but please let me know if you’re still having trouble making sense of it.

  3. Bianca says:

    First I have to thank you. It’s more then enough. These day I will see if it works phisically.

  4. sir I didnt get it how u calculate Tad and Tcy in this step
    ADCON3 = 0x0005; // Manual Sample, ADCS=5 -> Tad = 3*Tcy = 0.1us
    TRISB = 0x01FF; // Port B all inputs (would it make a difference if I wote TRISB = 0x1FF )
    ADPCFG = 0xFF00 // (Lowest 8 PORTB pins are analog inputs )
    and Can I change ADPCFG = 0xFC ?
    are Lowest 8 portb Pins RB0 Abd RB1 ?

    Help !!:)

    • batchloaf says:

      It won’t make any difference if you write “TRISB = 0x1FF” – the computer will interpret it as exactly the same value. The reason I wrote it as a four digit hex value is that I’m in the habit of writing values explicitly as 16-bit numbers if I’m storing them into a 16-bit register. Since each hex digit represents four binary digits (i.e. bits), a four digit hex value is equivalent to a 16 digit binary value. Writing it this way helps me to remember that I’m writing all 16 bits into the register – some of them most significant bits just happen to be zeros!

      You can change ADPCFG to 0xFC if you like. The effect will be that only RB0, RB1 and RB8 will be analog inputs (because 0xFC written as a 16-bit binary value is 0b0000000011111100. The three zeros I’ve highlighted there are the bits that select pins RB8, RB1 and RB0 as analog pins. The other 6 pins in PORTB will be configured as digital I/O pins (that’s what the six ones in the ADPCFG value do). To find out more details about ADPCFG and the other special function registers involved in using the analog-to-digital converter, refer to section 17 of the dsPIC30F Family Reference Manual, available from here. In particular, look are section 17.8.1 where ADPCFG is explained and the table off special function registers in section 17.26.

      The “lowest 8 PORTB pins” are RB0, RB1, RB2, RB3, RB4, RB5, RB6 and RB7.

      I’ll have to explain how Tad is calculated later because I have to go now. The info is basically given in section 17.7 of the dsPIC30F Family Reference Manual.

    • batchloaf says:

      Ok, here’s a quick summary of what’s going on with TAD:

      This line from the example program sets the dsPIC’s clock frequency to 120MHz:

      _FOSC(CSW_FSCM_OFF & FRC_PLL16); // Fosc=16x7.5MHz, Fcy=30MHz

      So the clock frequency of the chip is 120MHz. That means the clock signal inside the chip repeats 120,000,000 times per second. However, the dsPIC performs only one machine instruction for every four clock cycles. Therefore, the number of machine instructions performed each second is 30,000,000. “30 MIPS” simply means “30 million instructions per second”. The time taken to perform a single machine instruction is what we call the instruction cycle, Tcy.

      Tcy = 1 / 30000000 = 33.33ns

      That’s equivalent to three machine instructions per microsecond. When programming the dsPIC, Tcy is the most important time to be aware of since every time value specified in your program will be specified as a multiple of it. If you select a different clock speed for your chip, the value of Tcy will change. I almost always run the dsPIC at 30 MIPS, giving an instruction cycle of 33.33ns.

      Ok, so in the example program, the value I write into ADCON3 is hexadecimal 5 (written as 0x0005 in the code). We can write this same value as the following 16-bit binary value:

      ADCON3 = 0000000000000101

      As explained in section 17.26 of the dsPIC30F Family Reference Manual, the six bits I have marked in red specify the value ADCS, the single bit I have marked in green specifies the value ADRC, and the five bits I have marked in blue specify the value SAMC.

      So, the value stored in ADCON3 in the example program specifies the following values:

      SAMC = 0
      ADRC = 0
      ADCS = 5

      According to equation 17-1 from the Family Reference Manual, the ADC clock period, TAD, is determined as follows:

      TAD = (TCY(ADCS + 1)) / 2

      Since ADCS was set to 5,

      TAD = 3 * TCY

      Analog-to-digital conversion of a single sample takes 12 * TAD. Also, according to section 17.7 of the Family Reference Manual, TAD must be at least 83.33ns. Therefore, since TCY = 33.33ns, ADCS cannot have any value less than 5.

      For more details, please refer to Section 17 of the dsPIC30F Family Reference Manual. It’s complicated to read, but it will help you to decipher the exact meaning of any line in the example program.

  5. sir I tired the analog input code and its working fine :i connected the led at RD0 and potentiometer to AN0 and I could controll the brightness of the led.
    today I m goin to try the PWM code ..but before doing that I wanted to ask you if i could put the PWM loop in the timer Interrupt.. would that control the PWM duty cycle .. and could I just use the Free running mode PWM .?

    Can I make a library with ADC.c and then Just Init ADC.?and even for Interrput and PWM ?

    Thank you very much Sir ,

    • batchloaf says:

      When you say the PWM loop, If you mean this bit from the example:

      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);
      }
      

      then yes, you can put the contents of the loop (i.e. everything inside the curly braces) into an interrupt service routine instead.

      It is possible to move the ADC and interrupt code to another C file, which will be accessed via a single function call from main.c. However, unless your program is getting very unwieldy, I wouldn’t bother doing that. For short to medium length dsPIC programs I just try to keep all my code in a single C file.

      One small point that you’re probably already aware of: Once you switch to using the actual PWM code, you’re PWM output will be through one of the dsPIC’s dedicated PWM pins. i.e. You won’t be able to route the PWM signal out through RD0.

      • sir u have made the RD0 as a digital output,how should I enable the Pwm1L/RE0 to be my output Pin..

        should I configure Pwm1L/RE0
        same as
        // Configure RD0 as a digital output
        LATD = 0;
        TRISD = 0b11111110;
        is there any particular way i can do that ?
        Thank You Sir ….

      • batchloaf says:

        “how should I enable the Pwm1L/RE0 to be my output Pin..”
        I’m not exactly sure what you mean here. If you run the PWM example exactly as it is shown above, the PWM signals will come out of the dsPIC’s dedicated PWM pins. Those are the only pins that the dsPIC can use for PWM, so if you’re doing PWM at all, those are the pins where the signal will come out.
        The following lines from the example program are the ones that enable PWM output on all three channels:

        // 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
        

        Once PWM output is enabled, the period is controlled by changing the value of the PTPER register and the duty cycle for each channel is controlled by changing the value of the PDC1, PDC2 or PDC3 registers (each of those controls the duty cycle on an individual channel). For example, these are the lines that repeatedly update the duty cycle on PWM channel 1 to reflect changes in the analog voltage on AN0:

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

        You should begin by running the PWM example from this page without modifying it in any way. Then just check with an oscilloscope or logic analyzer to see that the PWM signal is coming out as expected. You do not need to do anything else to enable the PWM outputs. if you use the code above, they should already be working.

  6. Sorry Sir ,I have to ask you one more question Sir
    The project which I m working on ..
    I have got the first part of it
    so if i sense the volatge from the analog input it will covert the volatge to Digital as the Duty Cycle will be the Function of the Input Volatge and voltage reference(ie 12 V battery). then I have to set this Duty Cycle Value to the PWM pin..
    How can this be done Can u please explain me .. I will update my code and Can u please go through it and help my mistakes ..

    Thank you very much Sir ….

  7. thanks sir now its clear .. was kinda confused a bit .. not anymore 🙂

    • batchloaf says:

      Great. Best of luck with your project!

      • today I got a chance to test the above code with an oscilloscope and it works pefeectly I also noted down changes when I changed the PTPER value and also tried so tricks on the PC1 register…
        Apart from my programing i wanted

        i wanted to ask why do we need and how do we calculate the Rgs for the Power Mosfet ?
        that would be my last question to u hopefull 🙂
        thank u very much
        Honestly grateful to you
        Xavier

  8. Pingback: Robotics Component Kit – First RoboLab 3.1 « robomark

  9. Pingback: Robotics Component Kit « robopaddy

  10. nor says:

    Dear sir,
    my name is nor. first of all, sorry for my broken english.
    i’m new with dspic30F4011. i have tried your c code for “Sending text from the dsPIC to the PC via the UART” and i also have changed the heap size to 1024.

    however, i got this error “heap: Link Error: Could not allocate section .heap, size = 1024 bytes, attributes = heap
    Link Error: Could not allocate data memory”.

    even if i change the heap size to other value, it still gives the same result.
    i feel so stupid. i could not run the program even though you have told the technique. =(

    so, i would like to have your suggestion to fix this problem.
    thank you very much sir.

    • batchloaf says:

      Hi Nor,
      I’m sorry to hear it’s not working for you. Are you using the C30 compiler? (That’s the one I use.) Also, have you tried setting the heap size to something smaller than 1024? As far as I recall, there’s only 2KB RAM on the dsPIC30F4011, and this space has to accommodate the heap as well as any arrays and variables you’ve declared, so you could easily find yourself short of space. I think this example will work ok with a smaller heap size (e.g. 512 or even 256), so that’s what I’d try first. If that doesn’t work, feel free to send me your code and I’ll investigate further.
      Regards,
      Ted

  11. nor says:

    i’m using mplab c30 compiler v3.25 and also using your code (just copy and paste).

    i have tried changing the heap size to smaller size, but i got another error:
    for heap size = 512,
    heap: Link Error: Could not allocate section .heap, size = 512 bytes, attributes = heap
    Link Error: Could not allocate data memory

    for heap size = 256,
    Error: Not enough memory for stack (528 bytes needed, 150 bytes available)
    Link step failed.

    sir, this question might be silly. but i really want to know, do i need to do other setting rather than just run code in the mplab?

    • batchloaf says:

      Hi Nor,
      I’m using v3.23 of the C30 compiler. However, I don’t think it’s the compiler version that is causing your problem. It looks like you’re just using too much data memory in your program. Based on the error you’re getting,

      “Not enough memory for stack (528 bytes needed, 150 bytes available)”

      It looks like you wouldn’t have enough RAM memory even with a heap size of zero.

      • Can you send me a zip file of your complete project folder so that I can check your project settings?
      • Did you use the Project Wizard to create your project in MPLAB?
      • Assuming you did, is it possible that you selected the wrong type of chip when you created your project?

      Here’s a summary of the complete process from scratch to create and compile the project:

      1. In MPLAB, click “Project” on the menu bar.
      2. Click “Project Wizard…” – the project wizard dialog box should appear.
      3. On the welcome screen of the wizard, click “Next”.
      4. Steo One: Select the chip as “dsPIC30F4011” and click next.
      5. Step Two: Set the Active Toolsuite to “Microchip C30 Toolsuite” and click next.
      6. Step Three: Using the “Browse” button, select a project folder and type a name for your project (for example “test”), then click next.
      7. Step four: Just click next.
      8. On the “Summary” page, just click “Finish”.
      9. On the MPLAB menu bar, click “File” and then “New” to create a new file.
      10. Paste in the complete example code from my blog and save it to your project folder as “main.c”.
      11. On the MPLAB menu bar, click “Project” and then “Add Files to Project…”. Select the new file “main.c” and add it to your project.
      12. On the MPLAB menu bar, click “Project -> Build Options… -> Project”. Select the tab called “MPLAB LINK30”.
      13. Set the heap size to 256 bytes and press “OK”.
      14. Finally, on the MPLAB menu bar, click “Project” and then “Build All”. Hopefully the project should build without errors.
  12. ANCHAL says:

    Hello,

    Sir/Madam I am using dsPIC30F4011 for controlling the BLDC motor.What i expect from the code given here in the PWM module is according to the change in the analog input channel AN0 from 0Volts to 5Volts the duty cycle must vary from 0% to 100%.Firstly i used this C code then i converted this C code to Hex code by MPLAB although the build is successful I am not able to get the desired output from port PWM1L that is from pin no. 38.I am using a 6.144 MHZ external oscillator. Please Respond Soon.

    • batchloaf says:

      Hi Anchal,
      Ok, let’s see if we can work this out.
      1.) Are you using the C30 compiler? Are you using MPLAB? Is the program downloading to the chip without errors?
      2.) Have you tried running the simpler examples to check that they are working for you? (e.g. flashing LED example)
      3.) Have you got at least the minimum required wiring done – i.e. positive and negative power connections (e.g. pins 11 and 12) and VADD and VACC (pins 39 and 40) connected to 5V and 0V respectively?
      4.) What output are you seeing on pin 38? Is the signal constantly low or is it just that the duty cycle / period is wrong? Since you’re running the chip at a different speed (with the external oscillator), the PWM period will be different.
      5.) Have you modified the code to select an external oscillator? (Specifically, you’ll at least need to change the value “FRC_PLL16” at approximately line 12).
      6.) Did you check pin 37 (PWM1H)? That’s actually the output I used. I would have expected PWM1L to be changing too, but I can’t remember if I actually checked it.
      7.) Did you make any other changes to the code?
      Let me know when you’ve checked all that.

      • ANCHAL says:

        Thank you so much for the quick response Sir.I did all those things what you have told to do and got that whatever I want.Sir your program is working very nicely once again I am very thankful to you.Just one more question Sir how can I vary the frequency of the PWM signal according to me through the code and can i use the differential equations in the C code.

        Thank You

      • batchloaf says:

        Hi Anchal,

        Great, I’m glad that worked for you.

        Once the PWM signal is present at the output pin 37 (PWM1H), you can change its frequency at any time by setting the value of the PTPER register. For example, the following line from the configure_pins function in the sample program sets the initial PWM frequency:

            PTPER = 9470; // 20ms PWM period (15-bit period value)

        That gives a period of 20ms when the clock frequency is 120MHz (equivalent to 30MIPS or Tcy=33.3ns). If your clock speed is different, the number above will produce a different frequency. Anyway, you can change the frequency at any point in your program by changing the value of PTPER. If I wanted to double the frequency (i.e. halve the period) I would just write:

            PTPER = 4735;

        The PWM frequency and pulse width are controlled independently, so if I wanted to keep the duty cycle at 50%, I would also need to set the PDC1 register to the same value:

            PDC1 = 4735;

        The PTPER register controls the period for all three PWM channels, but the pulse width of each channel is controlled by an individual register (PDC1, PDC2, PDC3).

        Hopefully, that’s everything you need!

        Regards,
        Ted

  13. ANCHAL says:

    Hello Sir,

    Thank you so much for responding soon and helping me.Now I am able to control PWM signal from your post.
    Sir now I want to implement the PI controller in dsPIC30F4011 through C code.What all will be required for this i want to know.Waiting for your reply.Thank you again Sir.

    Regards,
    Anchal

  14. shahed says:

    thanks a lot guys. you made me sure about my code style in dsPICs

  15. Mo says:

    Hi Batchloaf
    I have gone through your tutorial for dspic30f4011 and I have found it very useful and well organised notes.

    At the moment I am working on my final year project and I have generated two PWM with the same duty ratio which I can control those pulses by a potentiometer. I just need to make a phase shift of 180 degrees between them.
    Phase shift is very important and crucial for my project and I dont know how to make 180degrees shifted between them.

    Can you please help me to do that.
    I appreciate in advance for your help buddy

    Regards
    Mo

    • batchloaf says:

      Hi Mo. I think what you want is “complementary mode” where the two output pins of a single PWM channel are always at opposite logic levels – is that right? In other words, when the first pin is high the second is low and vice versa.

      If this is what you want, then it’s easy to do. You just need to set the PTMOD1, PTMOD2 and/or PTMOD3 bits of the PWMCON1 register to zero to put whichever PWM channels you want into complementary mode. You can enable all three channels in complementary mode using this line of C:

      PWMCON1 = 0x00FF;

      The two pins in each channel (e.g. pins PWM1H and PWM1L for channel 1) will then always be the opposite of each other.

      If this isn’t what you’re trying to do, perhaps you can explain in a bit more detail exactly what you need. There will almost certainly be a way of doing it.

      • Mo says:

        Hi again Batchloaf

        I have a complementary mode pwm code as I copied my code below. it is only gives me PWM1L in 180degrees phase shifted in regard to PWM1H in the case when i set the duty cycle to be 50%.

        I need two exactly similar pulse with a 180 degrees phase shifted which means
        for example when the pwm1 has 75% duty cycle, the pwm2 must have a 75% duty cycle with 180degrees phase shifted.

        in the below code I have set my pulse to be 25KHz and I have selected PWMCON1 =0x00FF; which is complementary mode and if i set PWM1H to 75% it gives me PWM1L 25% and it gives me 270degrees phase shifted.

        If I set PWMCON1=0b0000111111111111; it gives me all the 6 pulses exactly similar which start&stop in the same time and in this case i just need to make 180degrees phase shift between on of them.

        To be more clear I will put a pic of the pulsed that I need in3 different duty cycle (25% 50% and 75%).

        ……………………………………………………………………………………………………………………….
        #define __dsPIC30F4011__
        #include
        #include
        #include
        #include

        // Configuration settings
        _FOSC(CSW_FSCM_OFF & FRC); // Fosc=16×7.5MHz, Fcy=30MHz
        _FWDT(WDT_OFF); // Watchdog timer off
        _FBORPOR(MCLR_EN); // 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)
        {

        //if switch on port RD1(pin18) is ON(state1) we can controll PWM manually)
        // 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()
        {

        LATD = 0; // Configure all four port D pins (RD0, RD1, RD2, RD3)as digital outputs
        TRISD = 0b1111111111110000;

        // 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 = 1.6281us
        ADCON1bits.ADON = 1; // Turn ADC ON

        // Configure PWM for free running mode
        // FOSC=7.37M….FCY=FOSC/4=1.8425M….TCY=1/FCY=0.5427uSec….Tpwm=TCY*Prescaler*PTPER
        //Tpwm=0.533USec*4*18=39.077USec…..Fpwm=1/Tpwm=25.59KHz
        PWMCON1 =0x00FF; //0b0000111111111111;//0x00FF=255=0b11111111;// Enable all PWM pairs in complementary mode
        PTCON =0; //0b1000000000000000; //0x8000=32768=1,000,000,000,000,000 bit15(PTEN)=1 which Enables the PWM module
        _PTCKPS = 1; // prescale=1:4 (0=1:1, 1=1:4, 2=1:16, 3=1:64)
        PTPER = 18; // TCY=1/FCY=0.5427uSec 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;
        }
        ……………………………………………………………………………………………………………………….

  16. Mo says:

    I have draw the phase 180degrees shifted pulse in 3 differentes situation (25%, 50% and 75%).
    the below pulses might not drawn 100% correct but it should be 180degrees phase shifted.
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    . . . . . . . . . . . . . . .
    . . . . . 50%PWM1H . .
    . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
    . . . . . . . .
    . . . .
    . . . . . . . . . . . . . . . . . . . . . . . . 50%PWM1L

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    . . . . . . . . . . . . . . . . . . . . . . .
    . . . . . .
    . . . . . . . . . . . . . . . . . .50%PWM1H
    . . . . . . . . . . . . . . . . . .
    . . . . . .50%PWM1L
    . . . . . . . . . . . . . . . . . . . . . . .
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    75%PWM1H
    . . . . . . . . . . . . . . . .. . . . . . . . . .
    . . . . . .
    . . . . . . . . . . . .
    . . . . . . . . . . . . . . . . . . .. . . . . . . . . .
    . . . . . . .
    . . . . . . . . . . . . . . . .
    75%PWM1L

    I appreciate your help

    Thanks

    • batchloaf says:

      Hi Mo. Unfortunately, your picture has become mangled once you posted it. However, I think I understand now what you’re trying to do. I haven’t done anything quite like this before, but I think I have an idea how to do it:

      1. Select center-aligned PWM (Section 15.4 of data sheet) by setting the PWM module to “up/down counting mode”. In C, something like “_PTMOD = 0b10;”

      2. Use two separate PWM channels (e.g. 1 and 2) for your two outputs, both in complementary mode.

      3. Set PTPER to whatever value you want for your PWM period.

      4. Set PDC1 to the desired pulse width – let’s call it “pw”.

      5. Now set PDC2 to the period minus pw. In your C program, that will probably be “PDC2 = (2 * PTPER) – PDC1;”.

      6. Now your two outputs are the high pin from channel 1 and the low pin from channel 2, i.e. PWM1H and PWM2L.

      If I get a minute later on, I’ll try this myself to make sure it works.

      • Mo says:

        Sorry for the mangled pic. I tried to upload the real photo but i couldn’t.

        I will give it a try and if i could get the result will let you know.
        meanwhile if you can try it i will appreciate your time and effort.

        Thanks a lot batchloaf. 🙂

  17. Mo Hsen says:

    I have tried the method that you suggested me.
    I have set:
    _PTMOD = 0b10; and PDC2 = (int)((voltage / 1023.0) * ((2 * PTPER)-PDC1));

    the output pulses from pin 37/RE1 and 36/RE2, (PWM1H and PWM2L) are shifted BUT they have different duty ratio and they are not 180degrees phase shifted.

    can you please tell me how can I upload the pulse’s pictures, I think with picture the problem would be more clearer for you in order to guide me to tackel it.

    thanks
    MO

  18. Mo Hsen says:

    Oh thats a great job Batchloaf, appreciate it

    I have tried your code it works 100% correct.
    But I faced a problem when I have increased the frequency to 25KHz I got the different duty cycle and phase shift.
    The problem is because when I increase the signal more than 2.5KHz the signal will alias and i can not get the proper output. (you can note this “Signal greater than 2500Hz will alias” in the screen shot of the pulses that you have uploaded under sample rate column.

    By the way your method is a really good method for phase shifting the pulses and I am sure there is a way to do it for signals greater than 2.5KHz.

    I will upload my code in below in case that might help you to see the problem.

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    #define __dsPIC30F4011__
    #include
    #include
    #include
    #include

    // Configuration settings
    _FOSC(CSW_FSCM_OFF & FRC); // Fosc=1×7.5MHz, Fcy=1.875MHz
    _FWDT(WDT_OFF); // Watchdog timer off
    _FBORPOR(MCLR_EN); // Enable 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)((1-(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()
    {

    LATD = 0; // Configure all four port D pins (RD0, RD1, RD2, RD3)as digital outputs
    TRISD = 0b1111111111110000;

    // 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 = 1.6281us
    ADCON1bits.ADON = 1; // Turn ADC ON

    // Configure PWM for free running mode
    //FOSC=7.37M….FCY=FOSC/4=1.8425M….TCY=1/FCY=0.5427uSec….Tpwm=TCY*Prescaler*///PTPER Tpwm=0.533USec*4*18=39.077USec…..Fpwm=1/Tpwm=25.59KHz

    PWMCON1 = 0b00110011;

    PTCON =0;
    _PTCKPS = 1; // prescale=1:1 (0=1:1, 1=1:4, 2=1:16, 3=1:64)
    //Select center-aligned PWM (Section 15.4 of data sheet) by setting the PWM module to
    //“up/down counting mode”. In C, something like “_PTMOD = 0b10;”
    _PTMOD = 0b10;
    PTPER = 8; // TCY=1/FCY=0.5427uSec 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;
    }

    • batchloaf says:

      Hi Mo. The 2500Hz limit shown in the screen shot is only to do with how the signal was recorded and displayed using the PICkit2 Logic Tool. It’s not a limit of the PWM method used on the PIC. Even if the waveform appears aliased wrong in the PICkit2 Logic Tool, that doesn’t mean it’s not coming out of the dsPIC correctly.

      If you’re having trouble getting 25kHz PWM, you may need to increase your clock speed. You’ll see that in my code, I use “_FOSC(CSW_FSCM_OFF & FRC_PLL16);” to enable the 16x PLL multiplier, making Fosc=120MHz and Fcy=30MHz.

      I suggest that you change your _FOSC line to this:

      _FOSC(CSW_FSCM_OFF & FRC_PLL16);

      and set _PTCKPS and PTPER as follows:

      _PTCKPS = 0; // PWM clock prescaler 1:1
      PTPER = 600; // PWM frequency = 25kHz @ Fosc = 120MHz

      If you’re using the PICkit2 Logic Tool to check the waveforms, make sure you increase the sampling frequency a lot higher than I did (about 500 times faster if possible). Alternatively, if you have access to an oscilloscope, use that.

      Let me know if that works for you.

    • Mo Hsen says:

      1_If I decrease the pwm frequency less than 2.5KHz I am able to get the exact waveform with 180 degrees phase shift but by increasing the frequency the pwm behave differently in compare to previous situation.

      2_I also tried to change the FRC to FRC_PLL16 and check the output but my code only works when I define FOSC as below:
      _FOSC(CSW_FSCM_OFF & FRC); // Fosc=1×7.5MHz, Fcy=1.875MHz

      I dont know why when I change FRC to FRC_PLL I can not observe any pulses in output.

  19. Mo Hsen says:

    Hi batchloaf
    I have changed my code as you advised me but unfortunately it doesn’t work when I change my clock frequency to _FOSC(CSW_FSCM_OFF & FRC_PLL16); .

    do you think I have to change my PIC or anything else might be wrong?

    • batchloaf says:

      You’re using the dsPIC30F4011, right? If so, then no, I don’t think you need to change to a different chip. However, I’ll try generating 25kHz PWM myself to make sure it’s possible and I’ll reply here. I probably won’t get a chance to do it today though, since my diary is chock full for the day.

      One quick question: When you set FRC_PLL16, does the chip stop running altogether (i.e. can’t flash an LED or anything) or is it just that the PWM outputs stop working?

      • Mo Hsen says:

        Yeah I am using dspPIC30F4011.
        I have tried to blink two LED on the RD0 and RD1 pins with below code but I couldn’t as the oscillator’s frequency is set to _FOSC(CSW_FSCM_OFF & FRC_PLL16);
        But when I change the _FOSC to _FOSC(CSW_FSCM_OFF & FRC); it will work.

        Do you have any Idea why “FRC_PLL16″,”FRC_PLL8”, and “FRC_PLL4” doesn’t work for me?

        this the simple blink two LEDs on pin 18 and 23 of dsPIC30F4011:

        #define __dsPIC30F4011__
        #include
        // Configuration settings
        _FOSC(CSW_FSCM_OFF & FRC_PLL16); // Fosc=16×7.5MHz, Fcy=30MHz
        _FWDT(WDT_OFF); // Watchdog timer off
        _FBORPOR(MCLR_EN); // Enable reset pin

        int main()
        {
        LATD = 0;
        TRISD = 0b1111111111110000;

        // Flash LEDs on RD0 and RD1 at 1Hz for 4 seconds
        while(1)
        {
        _LATD0 = 1; //RD0(pin23)
        _LATD1=1; //RD1(pin18)
        __delay32(150);
        _LATD0 = 0; //RD0(pin23)
        _LATD1=0; //RD1(pin18)
        __delay32(150);
        }

        return 0;
        }

    • batchloaf says:

      Hi Mo. I’ve just updated my blog post about your PWM problem. The code now produces 25kHz waveforms. It’s working fine for me, as you can see from the example waveforms shown in the blog post. I’ve also included pictures of my breadboard circuit so that you can see if anything is different for you. Pins 39 and 40 really should be connected to 5V and 0V too, but I ran out of wire and I got away with it on this occasion.

      Updated blog post on 25kHz PWM with 180 degree phase shift

      • Mo Hsen says:

        Hi Batchload

        I appreciate your time and effort to help me in this problem.
        I have discussed the problem in above.

        your code works perfectly when I set _FOSC(CSW_FSCM_OFF & FRC); but it doesnt work when I set _FOSC(CSW_FSCM_OFF & FRC_PLL16);

        I again thank you for your great help and advice.

        Regards
        Mo Hsen

  20. Mo Hsen says:

    I forgot to mention that all my connection is correct and I am using “MPLAB X IDE Beta7.02” With “MAC OS X”, I am sure nothing wrong with the MPLAB version or my MAC but I wanted to cite it just in case.

    • batchloaf says:

      Hi Mo. Are you using the C30 compiler with MPLAB?

      I’m using C30 v3.23 – I think I’m using the version that’s free for academic use.

      Here’s a link to my compiled hex file:

      http://dl.dropbox.com/u/2482633/pwm_phase.hex

      You can try downloading that hex file to your dsPIC and see if it works. If it does, then maybe the FRC_PLL16 problem is something at the compiler end, or a problem with the configuration bits.

      One other thing I should mention: Under the “Configure” menu in MPLAB, select “Configuration bits…” to open the Configuration Bits window. Make sure that the box called “Configuration Bits set in code” is ticked.

  21. jx says:

    thanks for the help !! can u share some basic idea about the timer interrupt that using ( no_auto_psv)? i have no idea how to setting up the things.

    • batchloaf says:

      Hi JX. I posted a very minimalistic example of a Timer 1 interrupt (for dsPIC30F4011) a while ago on my other blog. Here’s the link:

      http://batchloaf.wordpress.com/2012/04/12/simple-timer-1-interrupt-example-for-the-dspic30f4011/

      To be honest, I actually can’t remember what the no_auto_psv bit was for, but I always have it in there. I always use my dsPIC30F4011 Super Example as the starting point for each of my dsPIC programs and it already contains a simple Timer 1 interrupt which I just adapt if I need it. The only parts I usually need to change are the clock prescaler, which affects how fast the timer counts up, and the timer period which affects how often the clock counter resets to zero and triggers an interrupt. For example, the following line set’s the prescaler ratio to 1:256 which means that Timer 1 will increase its value by one every 256 instruction cycles (equivalent to 1024 clock cycles on the dsPIC):

      T1CONbits.TCKPS = 3;
      

      The other possible values for the prescaler are: 0=1:1, 1=1:8, 2=1:64, 3=1:256

      The following line sets the Timer 1 period to 10000.

      PR1 = 10000;
      

      This means that the Timer 1 counter will reset to zero and trigger an interrupt every time it reaches the value 10000. If the clock frequency was 7.5MHz (i.e. using the internal fast RC oscillator) and the PLL multiplier was enabled with a scaling factor of 16, the oscillator frequency would be 120MHz. That would mean that the dsPIC is performing 30000000 machine code instructions per second. In other words, the instruction cycle, Tcy (the time taken to do one machine code instruction) would be equal to 33.33ns.

      The Timer 1 counter will increment once every 256 instruction cycles (if configured as shown above) which means that it increases its value by one every 256 x 33.33ns.

      When the Timer 1 counter reaches 10000, it resets to zero and triggers an interrupt. This will happen every 10000 x 256 x 33.33ns = 85.32ms. So, that’s how ofter the timer would trigger the interrupt. You can check the value of the Timer 1 counter at any time just by reading the value in TMR1.

      • jx says:

        Thanks, it’s a great help. and i found out the the
        void __attribute__((__interrupt__,no_auto_psv)) _T3Interrupt(void);
        no_auto_psv is for the purpose of control more than one interrupt by the own user which u can set the interrupt priority on your own.
        below are some code that add only
        SRbits.IPL=7; //user interrupts disable
        INTCON1=0;
        INTCON2=0;
        IFS0=IFS1=IFS2=0;
        IEC0=IEC1=IEC2=0;
        _T3IP = 4; // Set the Timer 3 interrupt priority 4
        _T3IF = 0; // Clear the Timer 3 interrupt flag
        _T3IE = 1; // Enable Timer 3 interrupt
        _ADIP = 5; //set the ADC interrupt priority 5
        _ADIF = 0;
        _ADIE = 1;
        SRbits.IPL =0;
        T3CONbits.TON = 1; // Turn on Timer 3
        Thanks to your example , i success modify and use it..^^

      • batchloaf says:

        Ah, that’s interesting information about the interrupts. Thanks for the example. Also, I’m glad you were able to adapt my code. Best of luck with whatever you’re working on!

  22. abdu says:

    i’ve a question what makrs dspic better than pic, a comparison table ?

    • batchloaf says:

      Hi Abdu,

      Well, I don’t think there’s a black-and-white answer to your question, but there are some significant differences that spring to mind. For the purpose of this comparison, I’m going to compare the PIC and dsPIC that I’ve used most which are the PIC18F4620 and the dsPIC30F4011, but I think the most of the following points apply to PIC18F and dsPIC microcontrollers in general:

      • The maximum clock speed for the dsPIC30F4011 (Fosc=120MHz, corresponding to 30 MIPS) is about 4 times that of the PIC18F4620.
      • The dsPIC microcontrollers are all 16-bit devices, meaning that each memory location stores a 16-bit number, whereas the PIC18F family are all 8-bit devices. If your program manipulates a lot of 16-bit values (e.g. regular ints are 16-bit values in C programs for PIC and dsPIC), these operations will be carried out more efficiently in a 16-bit device.
      • The microprocessor architecture of the dsPIC microcontrollers features extra hardware provision for performing certain maths operations (e.g. vector multiply-accumulate) much faster than is possible on PIC18F devices. Many practical applications in DSP (e.g. digital filtering) require this efficiency, and would not be possible on a PIC18F processor due to the lower clock rate and the less efficient way in which calculations of this type are performed.
      • The 16-bit architecture of the dsPIC simplifies the structure of certain special function registers. For example, when an analog-to-digital conversion is performed on the PIC18F4620, the 10-bit result has to be split between two registers, ADRESH and ADRESL, since each register can only store 8 bits. An additional operation is therefore required to combine the result into a single value. By contrast, the 10-bit result in a dsPIC can be stored in a single register with room to spare.

      For a more detailed comparison, you’ll need to go to microchip.com, where they have an automatic tool for selecting and comparing different microcontroller models.

      To summarise my own feelings on PIC versus dsPIC, I generally prefer to use the dsPIC since I’m not aware of any particular downsides to using it, apart perhaps from a slight price difference. However, we do still use the PIC18F4620 in our RoboSumo module for undergraduate engineers here in the Dublin Institute of Technology. From a teaching and learning point of view, I think there is still something very appealing about the simplicity of the 8-bit architecture, and for applications that are not time critical or computationally intensive you probably won’t notice much of a difference between PIC and dsPIC.

      Ted

  23. Saneesh says:

    Hi,

    Could you please help me with C code for Space Vector to control PMSM motor ,also using Hall senssor and QEI for speed control of the same. I have seen some reference with dsPIC 30F2010, but i am not clear with QEI and hall sensor using for PMSM.

    Thanks
    Saneesh

    • batchloaf says:

      Hi Saneesh,

      Sorry for the delay in responding.

      I’m afraid I know next to nothing about Space Vector control of PMSM motor and QEI, so I doubt I can help much. If you can explain a bit about what you need the dsPIC to do in the system, I might be able to help with that, but no guarantees!

      Ted

      • Saneesh says:

        Sorry I missed to respond to your earlier answer.
        I am trying to Spin a PMSM motor , where a sine current is driving the motor based on the Motor rotor position , which is sensed by hall sensor . I have a optical encoder which will be used for position control .I need to use both hall sensor and Encoder in different time frames.

        Thanks for your response ,
        Saneesh

  24. Shuan says:

    I really like this site. I have a question about the dsPIC30F4013. the 12 bit ADC of it has only one sample and hold meaning that i cant perform a simultaneous sampling of two channels.. now the question is how do i perform the sequential sampling? i can post my code for what i think might work but i”m not 100% happy with it…see the code below

    #include
    #include
    #include
    #include

    //****** Device configuration register macros for building the hex file ******//
    _FOSC(CSW_FSCM_OFF & FRC);
    _FWDT(WDT_OFF); /* Watchdog timer disabled */
    _FBORPOR(PBOR_OFF & MCLR_EN); /*Brown-out reset disabled, MCLR reset enabled*/
    _FGS(CODE_PROT_OFF);

    unsigned int ADC_voltage();
    unsigned int ADC_current();
    int Voltage_process();
    int Current_process();
    int Conversion();
    double V1_rms,I1_rms;

    void PORT_INITIAL (void);

    int main()
    {
    long int V;
    long int I;

    V = ADC_voltage(0);
    I = ADC_current(0);

    PORT_INITIAL ();
    ADC_voltage();
    ADC_current();
    //—-configure Oscillator———–//
    OSCCONbits.COSC0 =1; //internal fast oscillator
    OSCCONbits.COSC1 =0;

    }
    //***************************PORT INITIALISATION******************************//
    void PORT_INITIAL (void)
    {
    LATB = 0xFFFF;
    ADPCFG = 0xFFCF; // RB4 & RB5 pins are analog
    TRISB = 0x00C0; //ONLY RB4,5 pins are inputs

    }
    //***************************CONVERTING VOLTAGE*******************************//
    unsigned int ADC_voltage()
    {
    ADCHSbits.CH0SA = 0x0004; // Connect RB4/AN4 as CH0 input for MUXA
    ADCSSL = 0;
    ADCON1 = 0x00E0; // SSRC bit = 111 implies internal
    // buffer results are integer FORM=00

    ADCON2 = 0x0001; // Select AVdd and AVss as reference voltage
    // Interrupts at the completion of conversion for each sample/convert sequence
    // alternates between MUXA and MUXB

    ADCON3 = 0x0113; // Sample time = 1Tad, Tad = 333.33 ns @ 30 MIPS
    // which will give 1 / (15 * 333.33 ns) = 200 ksps

    ADCON1bits.ADON = 1; // turn ADC ON

    while (1) // repeat continuously
    {

    IFS0bits.ADIF = 0; // clear ADC interrupt flag
    ADCON1bits.ASAM = 1; // auto start sampling for 31Tad then go to conversion

    while (!IFS0bits.ADIF); // conversion done?
    ADCON1bits.ASAM = 0; // yes then stop sample/convert
    return ADCBUF4; // yes then get ADC voltage value from buffer4
    }
    }
    //***************************CONVERTING CURRENT*******************************//
    unsigned int ADC_current()
    {
    ADCHSbits.CH0SB = 0x0005; // Connect RB5/AN5 as CH0 input for MUXB
    ADCSSL = 0;
    ADCON1 = 0x00E0; // SSRC bit = 111 implies internal
    // buffer results are integer FORM=00

    ADCON2 = 0x0001; // Select AVdd and AVss as reference voltage
    // Interrupts at the completion of conversion for each sample/convert sequence
    // alternates between MUXA and MUXB

    ADCON3 = 0x0113; // Sample time = 1Tad, Tad = 333.33 ns @ 30 MIPS
    // which will give 1 / (15 * 333.33 ns) = 200 ksps

    ADCON1bits.ADON = 1; // turn ADC ON

    while (1) // repeat continuously
    {
    IFS0bits.ADIF = 0; // clear ADC interrupt flag
    ADCON1bits.ASAM = 1; // auto start sampling for 31Tad then go to conversion
    while (!IFS0bits.ADIF); // conversion done?
    ADCON1bits.ASAM = 0; // yes then stop sample/convert
    return ADCBUF5; // yes then get ADC current value from buffer5
    }
    }

  25. B.Joseph kiran says:

    Hello sir,
    I have a quick question.I want to get a sine wave by creating a look up table, where i update my PDC value every time when PTMR = PTPER. So i have to use a Interrupt service routine which u have explained in one example.So in that Timer period is defined by some value, how can i assure that i get interrupt only when PTMR = PTPER..
    Please help!
    Thanks

    • batchloaf says:

      Hi Joseph,

      My interrupt example above uses Timer 1, but if you want the interrupt to happen strictly each time PTMR reaches PTPER, you should use a different interrupt specifically triggered by that condition (rather than Timer 1). The dsPIC provides an interrupt facility specifically for this, but I don’t have an example program to hand which illustrates its use. I suppose you could use my interrupt example as a starting point, but you’ll need to change at least a couple of things:

      • You need to configure and enable the PWM period match interrupt rather than the Timer 1 interrupt (see below).
      • You need to implement the PWM period match ISR (the function “_PWMInterrupt” rather than the Timer 1 ISR (the function “_T1interrupt”).

      From your question, I’m guessing you’ve already configured and enabled the PWM output. My guess is that you’ll need something like the following in your main function to configure and enable the PWM period match interrupt, which will be used to update the duty cycle each time PTMR reaches PTPER.

      // Configure PWM period match interrupt
      _PWMIP = 1; // Set PWM interrupt priority
      _PWMIF = 0; // Reset PWM interrupt flag
      _PWMIE = 1; // Enable PWM interrupt
      

      I’m actually not sure how necessary the first two lines are, but the last one is certainly required.
      Then, I suppose your PWM interrupt service routine will look something like this…

      // PWM period match ISR
      void __attribute__((__interrupt__)) _PWMInterrupt(void)
      {
      	// Reset PWM interrupt flag
      	_PWMIF = 0;
      	
      	// Update your PDC value(s) here
      	
      }
      

      Obviously, you need to put something in there to actually update the duty cycle.

      I hope that helps. Please let me know how you get on.

      Ted

  26. Hussein says:

    Hi all
    I hope if i can join you, any way i want to ask you if u can help me in my project where i want to play sound wave (.wav) throw output compare OC1 as PWM from SDCard. I had the SDCard working but i am fail to get signal at the speaker
    Thank you
    Hussein BANJAK

    • batchloaf says:

      Hi Hussein,

      What you want to do is certainly possible. I don’t have any example code to do exactly what you’re proposing, and unfortunately I’m too busy to write any just now, but I do have a couple of related programs that my colleague Richard Hayes and I were working on a while ago and may return to again. They use the PWM module rather than output compare to perform realtime filtering of an audio signal. The signal is recorded from an analog input, filtered using DSP, and the outputted to a speaker using PWM.

      Here’s a link to download one of our simpler examples (it’s a moving average filter):

      Real-time moving average filter for audio using dsPIC

      The Timer 1 interrupt service routine does all the sample input and output. Each time it runs, one sample is recorded, one output sample is calculated, and the PWM duty cycle is updated.

      When pin RD2 (a digital input) is high, the moving average filter is applied to the signal; otherwise it passes through unfiltered.

      It’s not exactly what you’re looking for, but hopefully it might help a bit?

      Ted

  27. muthumurugan says:

    It was very useful to me. Thanks to publish your idea.

  28. Alonso says:

    Hello, First it is an amazing article. I have a question, if I want to use 3 PWM at the same time but with different frequencies, it is possible to use DSPIC30F4011 or should I need another one.
    Best

    • batchloaf says:

      Hi Alonso,

      Short answer: yes, it should be possible to create three PWM outputs with different frequencies. However, it will be a little bit complicated than doing three at the same frequency, which is easy to do. Basically, the dsPIC30F011 provides 5 16-bit timers, so you could almost certainly arrange three of those (making careful use of interrupts) to create three PWM outputs at different frequencies. Using three timers in that way could probably facilitate PWM up to quite high frequency and with good duty cycle resolution. However, if your PWM frequencies are all relatively low, there may be a simpler solution. Could you tell me a bit more about your application, so that I can provide a more detailed description of an appropriate approach.

      Specifically,

      • What are the required PWM frequencies?
      • Are the three frequencies constant or changing over time?
      • What duty cycle resolution is required?
      • Are the three duty cycles constant or changing over time?

      Regards,
      Ted

      • Alonso says:

        Hello, Thank you for the reply;
        Well I am doing a buck converter, but with 9 IGBTs for manage a lot of current, and to avoid harmonic I am using 3 different frequencies, 17000, 19000, 23000 hertz. As you can see there are 3 slow frequencies. My idea it is to put the igbts on groups of 3 and each group with each frequency.

        The frequencies are constant in time, but the duty cycle change, the resolution of the duty cycle with two decimal it would work well.

        I ask these because on the data sheet of the DSPIC30F4011 said that has only two dedicate timer for the PWM.
        best

      • batchloaf says:

        Hi again Alonso,

        Sorry for the delay responding. I had to think about this a bit!

        I’ve posted my draft solution as a new blog post. Here’s the link:

        Three PWM outputs with three different frequencies using the dsPIC30F4011 microcontroller

        The solution is a bit of a mish-mash because I needed to use both the PWM module and the two Output Compare channels in order to use three different timers to generate the three different frequencies. I haven’t been able to test the code on a dsPIC yet, because I don’t have one here at home, but I’ll check it when I’m back in the office.

        Ted

  29. Alonso says:

    Thank you very much for your time, and the example code. I am going to try it, and let you know if it works. Thank you again.

    best

  30. jose says:

    how to written a program in “Dspic30f” for closed loop boost converter, by using 2 ADC(Vref, Vout) for error calculation and produce pwm..
    please help me….

  31. Hi Ted
    Will you share an example of the QEI module in dspic30f4011?
    Thanks in advance.

    • batchloaf says:

      Hi ekitapucretsiz,

      I’ll try to post a bit more detail on this in the coming days, hopefully including a video, but in the meantime here’s a quick example program that I’ve just tested:

      //
      // Quadrature Encoder Interface (QEI) example for dsPIC30F4011
      // Written by Ted Burke, Last updated 11-4-2017
      //
      
      #include <xc.h>
      #include <libpic30.h>
      #include <stdio.h>
       
      // Configuration settings
      _FOSC(CSW_FSCM_OFF & FRC_PLL16); // Fosc=16x7.5MHz, i.e. 30 MIPS
      _FWDT(WDT_OFF);                  // Watchdog timer off
      _FBORPOR(MCLR_DIS);              // Disable reset pin
       
      void main()
      {
          // Use RD0 to blink an LED so that we can see program in running
          _TRISD0 = 0;
           
          // Setup UART so that we can monitor POSCNT value
          U1BRG = 48;            // 38400 baud @ 30 MIPS
          U1MODEbits.UARTEN = 1; // Enable UART
          
          // To use pins 6 and 7 as QEA and QEB (quadrature encoding inputs)
          // analog input must be disabled on both pins by setting the bits
          // for AN4 and AN5 in the ADPCFG register.
          _PCFG4 = 1;
          _PCFG5 = 1;
          
          // Enable the QEI module (x4 mode with POSCNT reset by MAXCNT match)
          _QEIM = 0b111;
      
          while(1)
          {
              // Print current value of position counter register
              printf("POSCNT = %d\n", POSCNT);
              
              _LATD0 = 1;         // LED on
              __delay32(3000000); // 100 ms
              _LATD0 = 0;         // LED off
              __delay32(3000000); // 100 ms
          }
      }
      

      Regards,
      Ted

    • batchloaf says:

      Hi again ekitapucretsiz,

      FYI. I posted more detail (including a video explanation) of my QEI example for dsPIC30F here:

      https://batchloaf.wordpress.com/2017/04/13/dspic30f-quadrature-encoder-interface-qei-basic-example/

      Ted

  32. ogun2016 says:

    Thanks TED
    It was very beneficial

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 )

Google photo

You are commenting using your Google 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 )

Connecting to %s