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:
- The dsPIC30F4011/4012 Data Sheet
- The dsPIC30F Family Reference Manual (available to download in sections from this link)
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, . In the robotics lab, we almost always run the dsPIC at 30 MIPS, which gives a value of .
- 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, , is calculated as follows:
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:
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
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 voltage levels over that range (i.e. from 0 to 1023). The ADC output at the lower threshold (2V) is calculated as follows:
Similarly, the ADC output at the upper threshold voltage (3V) is calculated as follows:
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; }
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
Thanks Bianca, I’m glad to hear you found it useful. Best of luck getting the H-bridge going!
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,
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.
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.
First I have to thank you. It’s more then enough. These day I will see if it works phisically.
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 !!:)
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.
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.
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 ,
When you say the PWM loop, If you mean this bit from the example:
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 ….
“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:
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:
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.
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 ….
thanks sir now its clear .. was kinda confused a bit .. not anymore 🙂
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
Pingback: Robotics Component Kit – First RoboLab 3.1 « robomark
Pingback: Robotics Component Kit « robopaddy
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.
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
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?
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,
It looks like you wouldn’t have enough RAM memory even with a heap size of zero.
Here’s a summary of the complete process from scratch to create and compile the project:
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.
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.
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
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:
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:
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:
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
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
thanks a lot guys. you made me sure about my code style in dsPICs
You’re welcome Shahed, I’m glad you found it useful.
Regards,
Ted
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
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.
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;
}
……………………………………………………………………………………………………………………….
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
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.
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. 🙂
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
Hi Mo. I’ve just posted a complete example of 180 degree phase shifted PWM output from two pins. Here’s the link:
http://batchloaf.wordpress.com/2012/04/05/pwm-output-on-2-channels-of-the-dspic30f4011-with-equal-duty-cycle-and-180-degrees-out-of-phase/
I hope that helps!
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;
}
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.
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.
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?
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?
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;
}
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
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
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.
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.
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.
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):
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.
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.
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..^^
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!
i’ve a question what makrs dspic better than pic, a comparison table ?
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:
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
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
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
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
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
}
}
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
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:
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.
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…
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
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
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
Thx for the fast repy i will take a look at richard work
Thanks again
It was very useful to me. Thanks to publish your idea.
You’re welcome! Glad you found it useful.
Ted
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
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,
Regards,
Ted
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
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
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
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….
Hi Ted
Will you share an example of the QEI module in dspic30f4011?
Thanks in advance.
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:
Regards,
Ted
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
Thanks TED
It was very beneficial
You’re welcome!
Ted