## Simple PI control using the dsPIC30F4011

This is an initial sketch of a simple PI controller using the dsPIC30F4011.

I hope to add in quite a bit more detail and explanation to accompany this in the coming days, but I thought I’d post the current code for the time being since someone is currently waiting to use it in a project.

What I’ve done so far is very closely modeled on this excellent example on Wikipedia. However, I removed the derivative term since I’m just creating a PI controller.

```//
// main.c - PI control with a dsPIC30F4011
// Written by Ted Burke
// Last updated 11-6-2013
//
// This example program shows a simple way to perform PI control
// on a system such as a DC voltage converter. The controller
// output takes the form of two complementary PWM channels (with
// 2us dead time between them). The PWM outputs are OC1 (pin 23)
// and OC2 (pin 18). I'm currently just driving an RC low pass
// filter (R = 220 ohm, C = 1000 uF) with PWM from pin 23 and
// feeding the filtered voltage back to an analog input (AN1).
// The setpoint is determined by the voltage on another analog
// input (AN0). Bascially, the PI controller adjusts the PWM
// duty cycle to make AN1 match AN0. The PWM duty cycle is
// clamped to the range 0.2 to 0.8.
//
// The PWM period is 50us, so the PWM frequency is 20kHz.
// The dead time is set to 2us which is in the right ballpark
// for IGBT devices, assuming some kind of push-pull system.
// The coefficients Kp and Ki are currently just a complete
// guess - these should be tuned for the actual system.
// dt is the sample time, which is 50us since the controller
// calculation is repeated at the start of each PWM cycle.
//

#include <xc.h>
#include <libpic30.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

unsigned int read_analog_channel(int channel);

int main()
{
// Define time values
int Tpwm = 1500; // PWM period = 1500 * Tcy = 50us (i.e. 20kHz)
int dead_time = 60; // 2us deadtime is in the right ballpark for IGBTs
int Ton = Tpwm - dead_time - dead_time; // total on time during each cycle
double duty_cycle = 0.5;

double setpoint, measured_value, error, output;
double previous_error = 0, integral = 0 ;
double dt = 0.00005, Kp = 0.001, Ki = 0.001;

// Make all port D pins outputs
TRISD = 0;

// Configure AN0-AN8 as analog inputs
ADCON3bits.ADCS = 15;  // Tad = 266ns, conversion time is 12*Tad
ADCON1bits.ADON = 1;   // Turn ADC ON

// Configure timer 2 (default timer for output compare)
PR2 = Tpwm;         // 20kHz PWM frequency
T2CONbits.TON = 1;  // Enable timer 2

// Initialise OC channel 1 & 2 start and stop times
OC1R = 0;
OC1RS = (int)(duty_cycle * Ton);
OC2R = OC1RS + dead_time;
OC2RS = PR2 - dead_time;

// Set output compare mode for continuous pulses
OC1CONbits.OCM = 0b101;
OC2CONbits.OCM = 0b101;

while(1)
{
while(_T2IF == 0); // wait for start of next cycle
_T2IF = 0;

setpoint = read_analog_channel(0);
measured_value = read_analog_channel(1);

error = setpoint - measured_value;
integral = integral + error*dt;
output = Kp*error + Ki*integral;
previous_error = error;

duty_cycle = output;
if (duty_cycle > 0.8) duty_cycle = 0.8;
if (duty_cycle < 0.2) duty_cycle = 0.2;

OC1RS = (int)(duty_cycle * Ton);
OC2R = OC1RS + dead_time;
}

return 0;
}

// This function reads a single sample from the specified
// analog input. It should take less than 5us when the
// microcontroller is running at 30 MIPS.
// The dsPIC30F4011 has a 10-bit ADC, so the value
// returned is between 0 and 1023 inclusive.
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 = 3.2us
return ADCBUF0;
}
```
Advertisements
This entry was posted in Uncategorized. Bookmark the permalink.

### 33 Responses to Simple PI control using the dsPIC30F4011

1. zah says:

hi ted,
I’m inspired by your source code, but I want to use pwm port. here is my code. I do not know if good or not because I can not simulate it. CAN you please correct me?
code:
/*
* File: newmain.c
* Author: zah
*
* Created on 13 juin 2013, 16:48
*/

#include
#include
#include
#include

// Configuration settings
_FOSC(CSW_FSCM_OFF & FRC_PLL16); // Fosc=16×7.5MHz, i.e. 30 MIPS
_FWDT(WDT_OFF); // Watchdog timer off
_FBORPOR(MCLR_DIS); // Disable reset pin

unsigned int read_analog_channel(int channel);

int main()
{

double cons, measured_value, measured_value_c , error, output,error_c, output_c, multip, sinus;
double integral = 0 ;
double dt = 0.00005, Kp = 1.9, Ki = 0.1,kpc = -9 ;

// Configure AN0-AN8 as analog inputs
ADCON3bits.ADCS = 15; // Tad = 266ns,
ADCON1bits.ADON = 1; // Turn ADC ON
while(1)
{

cons = 400;
measured_value = read_analog_channel(2);

error = cons – measured_value;
integral = integral + error*dt;
output = Kp*error + Ki*integral;

measured_value_c = read_analog_channel(1);
sinus = read_analog_channel(3);
multip= output * sinus;
error_c =measured_value_c – multip;
output_c =kpc* error_c;

PDC1 = output_c;
PDC2 = output_c;

// Configure PWM for free running mode
// PWM period = Tcy * prescale * PTPER = 33.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
}
return 0;
}

// This function reads a single sample from the specified
// analog input. It should take less than 5us when the
// microcontroller is running at 30 MIPS.
// The dsPIC30F4011 has a 10-bit ADC, so the value
// returned is between 0 and 1023 inclusive.
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 = 3.2us
return ADCBUF0;
}

thank you

• batchloaf says:

Hi zah,

I’m afraid I can’t completely understand the maths of your controller, but I’ve made some changes to the layout of the main function which I think make sense (code is below). The while loop is the bit that repeats over and over updating the output. You have the PWM setup code inside the while loop, but it only needs to run once, so I’ve moved it to a point before the while loop.

Also, I can’t see anything inside the while loop which will control the sample rate. Maybe you need to include some kind of delay or something. For example, placing the following 1ms delay inside the while loop would give you a sampling rate of about 1kHz:

```__delay32(30000);
```

Ok, that’s it. Modified main function is below.

Ted

—————–

```int main()
{
double cons, measured_value, measured_value_c , error, output,error_c, output_c, multip, sinus;
double integral = 0 ;
double dt = 0.00005, Kp = 1.9, Ki = 0.1,kpc = -9 ;

// Configure PWM for free running mode
// PWM period = Tcy * prescale * PTPER = 33.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; PDC2 = 0; PDC3 = 0; // 0% duty cycle on channels 1,2,3 (max is 65536)
PTMR = 0; // Clear 15-bit PWM timer counter
_PTEN = 1; // Enable PWM time base

// Configure AN0-AN8 as analog inputs
ADCON3bits.ADCS = 15; // Tad = 266ns,
ADCON1bits.ADON = 1; // Turn ADC ON

while(1)
{
cons = 400;
measured_value = read_analog_channel(2);

error = cons – measured_value;
integral = integral + error*dt;
output = Kp*error + Ki*integral;

measured_value_c = read_analog_channel(1);
sinus = read_analog_channel(3);
multip = output * sinus;
error_c = measured_value_c – multip;
output_c = kpc * error_c;

PDC1 = output_c;
PDC2 = output_c;
}

return 0;
}
```
• madi says:

Hi, i need to use this code to program PIC18F4431 but it don’t work, do i have change some parametters? please need me?

• batchloaf says:

Hi Madi,

I haven’t used that exact chip before, but the PIC18F use a different C compiler from the dsPIC30F chips, so you would be better starting with an 18F example and modifying that. There is information for the PIC18F4620 on this page:

http://robosumo.wordpress.com/pic18f4620/

It’s part of the documentation for an undergraduate module I teach and contains several examples.

Ted

2. zah says:

hi ted
thank you for your help, in your code you used while (_T2IF == 0) / / wait for start of next cycle
_T2IF = 0;
but I use the pwm ports??
for the controller, I used two. the first is a PI to the voltage loop which gives a reference to the current, and the second one is proportional to the inner current loop

3. batchloaf says:

I think if you’re using the PWM generator instead of Timer 2, you should change those two lines to this:

```while(_PWMIF == 0); // Wait for PWM interrupt flag to be set indicating the end of a PWM cycle.
_PWMIF = 0;         // Reset the PWM interrupt flag
```
4. zah says:

hi ted,
I’ll try to implement this code this week, I’ll tell you the result.
thank you Mr.ted

• batchloaf says:

Best of luck with it. Do please let me know how you get on.

Ted

5. Silvio Cruz says:

Oi Ted… admiro muito seu blog… parabéns !
Eu notei que você ão utiliza a variável “previous_error”… algum motivo para isto ?

• batchloaf says:

Hi Silvio,

Thanks, that’s a good question. Basically, I initially wrote the code as if it was a PID controller (proportional, integral and derivative terms), so I included that variable to calculate the derivative term (to estimate the rate of change of the error by comparing its value between two successive samples). However, because it’s actually only a PI controller (no derivative term), I don’t think that variable is used at all. You can remove it if you like. Does that answer your question?

Ted

• Silvio Cruz says:

Perfect… thanks.
Excuse me for writing in Portuguese … is only now that I realized !

• batchloaf says:

No problem at all – I enjoy a few multilingual comments!

6. Ambitious says:

Dear batchloaf,
I am writing a code wherein the output of a PI controller is used to set the duty cycle of two switches of a pushpull type of converter.I am facing two problems in it and i would be very grateful to you if you help me to sort it out! The problems are-
1. The output pulses for the two switches are not complimentary,rather,they overlap.And the minimum duty cycle of each pulse is 50%.
To explain it to you in a better way,assume that initially the two pulses are at 50% duty cycle separated by a T/2 period apart(and no overlap).
Now,if the duty cycle increases,each pulse will ‘expand’ in both directions and there will be a overlap between the two pulses(two regions of overlap in a time period T,i.e. on both sides of T/2 and on both sides of T or 0).
So how should i write the code for this?Perhaps i will have to use the centre aligned mode and independent operation modes of the PWM module, or can i do it using OC?Please help!
2. What should be the relation between PI controller outputs and the duty cycle.
Should i put the PI output directly in the PDC registers or something else? It would be helpful if you illustrate the solution to this with an example!
Thanks a lot 🙂

7. MaXi says:

hello Sir,
i didn’t get …what is “dt”?? & why it is used ??what is significance of it

• batchloaf says:

Hi MaXi,

In this example, the variable dt is basically just the sampling period, which is 50us because the frequency of the PWM timer (Timer 2) is 20kHz.

In other words, the main control loop iterates once every 50us (equivalent to 20,000 iterations per second). At the beginning of each iteration, it waits for the Timer 2 interrupt flag to be set, which occurs every 50us – this is what controls the rate of iteration in the loop.

Every time the Timer 2 interrupt flag is set, the program samples the setpoint and measured_value analog inputs and then recalculates the controller output. The following line integrates the error (between measured_value and setpoint):

```integral = integral + error*dt;
```

What is added onto the integral variable during each iteration is the product of the current error and the sampling period.

Hopefully that explains it?

Ted

8. sanju says:

hai,

i wanted to controll the amplitude of sine wave using dspic, i made the wave with open loop using the methode here in this post ie,
duty_cycle = sinetable[sine_table_index];
duty_cycle1 = ((unsigned long)duty_cycle * (unsigned long)amplitude)>>15;
duty_cycle2 = ((unsigned long)duty_cycle * (unsigned long)MAXPWM) >> 15;

if(++sine_table_index > MAXVALUES)
{
sine_table_index = 0;
}
PDC1 =duty_cycle2;
PDC2= (2*PTPER)-PDC1;

here my sine is ok, but how can i club with this with amplitude?
i want to controll the amplitude of output by turning a pot value of 0-1023=0-250v.
iam getting only the higher value of 267 volt at out put.
help me….
(my ptper=1379)

• batchloaf says:

Hi Sanju,

There are several things in your code I don’t understand.

Firstly, in these lines…

```duty_cycle1 = ((unsigned long)duty_cycle * (unsigned long)amplitude)>>15;
duty_cycle2 = ((unsigned long)duty_cycle * (unsigned long)MAXPWM) >> 15;
```

…what is the purpose of bit-shifting down by 15 bits (“>> 15”) ? Won’t that make the values duty_cycle1 and duty_cycle2 very small indeed?

Also, the output will naturally depend on the circuit which the dsPIC is attached to. When you say you’re getting only the higher value of 267 V, do you mean that the voltage is constant at 267 V or that it’s a sine wave with it’s maximum at 267 V?

Ted

9. sanju says:

hai ted,
this is my simple code for h bridge sine with an amplitude of 267v(battery=12.6v).
i am not able to do the voltage correction for varying output loads, how can i club my outvolt adc
to control the open width of pwm? ..i tried your code (which having floats such as v_min,v_max,duty,), but that is having a problem that in my 56us pwm isr , the term

duty = sine_term * v_min + (2 – sine_term) * v_max;
PDC1 = duty * PTPER;
PDC2= (2*PTPER)-PDC1;

eats around 35-40 us inside the isr,so i cant get quick adc result to modify next duty by adjusting

if(upsvoltoutput)
{
v_min+=.00001;
v_max-=.00001;
}

so i decided to remove floatmath in code, now the modified code iam able to produce 267 volt in openloop.no ant controll against battery volt/output load..
how can i achive this? pls read the code and advice me..

new code:(i got little bit from EDABOARD faq..but not complete)
#include
#include
_FOSC(CSW_ON_FSCM_OFF & XT_PLL16);
_FWDT(WDT_OFF);
_FBORPOR(MCLR_EN & PBOR_ON & BORV_42 & RST_PWMPIN & PWMxH_ACT_LO & PWMxL_ACT_LO);
_FGS (CODE_PROT_OFF);

#define MAXPWM 1379 /* Whatever your max pwm is? */
#define MAXVALUES 359 /* Size of look up table */

#define MAXVOLTS 0 /* Maximum pwm amplitude, choose to suit application */
#define MINVOLTS 5000U /* Minimum pwm amplitude */
unsigned int duty_cycle;
unsigned int amplitude=0;
unsigned int sine_table_index = 0;

/*— Set duty cycle using a timer interrupt, change period register for frequency —*/

const unsigned int sinetable[] =
{
32500,33067,33634,34200,34767,35332,35897,36460,37023,37584,
38143,38701,39257,39810,40362,40911,41458,42002,42543,43080,
43615,44146,44674,45198,45718,46235,46747,47254,47757,48256,
48750,49238,49722,50200,50673,51141,51603,52058,52508,52952,
53390,53821,54246,54664,55076,55480,55878,56268,56652,57028,
57396,57757,58110,58455,58793,59122,59443,59756,60061,60357,
60645,60925,61195,61457,61710,61955,62190,62416,62633,62841,
63040,63229,63409,63579,63741,63892,64034,64167,64289,64402,
64506,64599,64683,64757,64821,64876,64920,64955,64980,64995,
65000,64995,64980,64955,64920,64876,64821,64757,64683,64599,
64506,64402,64289,64167,64034,63892,63741,63579,63409,63229,
63040,62841,62633,62416,62190,61955,61710,61457,61195,60925,
60645,60357,60061,59756,59443,59122,58793,58455,58110,57757,
57396,57028,56652,56268,55878,55480,55076,54664,54246,53821,
53390,52952,52508,52058,51603,51141,50673,50200,49722,49238,
48750,48256,47757,47254,46747,46235,45718,45198,44674,44146,
43615,43080,42543,42002,41458,40911,40362,39810,39257,38701,
38143,37584,37023,36460,35897,35332,34767,34200,33634,33067,
32500,31932,31365,30799,30232,29667,29102,28539,27976,27415,
26856,26298,25742,25189,24637,24088,23541,22997,22456,21919,
21384,20853,20325,19801,19281,18764,18252,17745,17242,16743,
16250,15761,15277,14799,14326,13858,13396,12941,12491,12047,
11609,11178,10753,10335,9923,9519,9121,8731,8347,7971,
7603,7242,6889,6544,6206,5877,5556,5243,4938,4642,
4354,4074,3804,3542,3289,3044,2809,2583,2366,2158,
1959,1770,1590,1420,1258,1107,965,832,710,597,
493,400,316,242,178,123,79,44,19,4,
0,4,19,44,79,123,178,242,316,400,
493,597,710,832,965,1107,1258,1420,1590,1770,
1959,2158,2366,2583,2809,3044,3289,3542,3804,4074,
4354,4642,4938,5243,5556,5877,6206,6544,6889,7242,
7603,7971,8347,8731,9121,9519,9923,10335,10753,11178,
11609,12047,12491,12941,13396,13858,14326,14799,15277,15761,
16250,16743,17242,17745,18252,18764,19281,19801,20325,20853,
21384,21919,22456,22997,23541,24088,24637,25189,25742,26298,
26856,27415,27976,28539,29102,29667,30232,30799,31365,31932
};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void __attribute__((__interrupt__, __auto_psv__)) _T1Interrupt(void)
{
duty_cycle = sinetable[sine_table_index];
duty_cycle = ((unsigned long)duty_cycle * (unsigned long)amplitude)>>15;
duty_cycle = ((unsigned long)duty_cycle * (unsigned long)MAXPWM) >> 15;
if(++sine_table_index > MAXVALUES)
{
sine_table_index = 0;
}
PDC1 =duty_cycle;
PDC2= (2*PTPER)-PDC1;

}// THIS FUNCTION TAKES ONLY 4 MICRO SECOND TO MAKE THE SINE WAVE!..
//////////////////////////////////////////////////////////////////////////////
void init_PWM()
{
PTCON=0XE002;
PTMR = 0x0000;
PTPER = MAXPWM; (8 KHZ…..BUT THE OVERALL EFFECT IS 16 KHZ IN BRIDGE SIDE);
SEVTCMP = 0x0000;
PWMCON1 = 0x0033;
PWMCON2 = 0x0000;
DTCON1=0X0059;
FLTACON = 0x0000;
OVDCON=0X0000;
PDC1=PDC2=PTPER;
T1CON = 0XE000;
TMR1 = 0;
PR1 = 1363;
_T1IF = 0;
_T1IE = 1;
}
///////////////////////////////////////////////////////////////////////////
int main()
{
TRISF=0X0000;
TRISE=0X000F;
TRISD=0X0000;
TRISC=0X0000;
TRISB=0XFFFF;
delay_ms(500);
init_PWM();
InitADC1();
while(1)
{

/
}
}////////////////end of main.

////adc result is variable from 2.5 volt offset. i can calculate output volt pre-determination
// using exel table sin(1 degree voltage)= sin(1* setvolt), sin(2degree voltage)= sin(2* setvolt)

and so …on,ie;

sin radiance setvolt sin(n) required outvolt/degree
1 0.017453293 230 0.017452406 4.014053481
2 0.034906585 230 0.034899497 8.026884242
3 0.052359878 230 0.052335956 12.03726994
4 0.06981317 230 0.069756474 16.04398896
5 0.087266463 230 0.087155743 20.04582083
6 0.104719755 230 0.104528463 24.04154655
7 0.122173048 230 0.121869343 28.02994898
8 0.13962634 230 0.139173101 32.00981322
9 0.157079633 230 0.156434465 35.97992696
10 0.174532925 230 0.173648178 39.93908086
/////
if this value is not the same throughout my 360 updations, i want to increase/ decreace the width od duty cycle…

help me for this pls…

10. sanju says:

hai ted,
i forget to mention that this code will work only if i comment out the second line like
duty_cycle = sinetable[sine_table_index];
///////duty_cycle = ((unsigned long)duty_cycle * (unsigned long)amplitude)>>15;
duty_cycle = ((unsigned long)duty_cycle * (unsigned long)MAXPWM) >> 15;

if i release the comment block, mosfet will take high current and burn(may be the over flow of PDC registers beyond the PTPER value)…

thanks..

11. sanju says:

hai ted..pls reply me…with an idea to use ampiltude controll….

12. Payrav says:

Hi Mr. Ted. Please replay me… with an idea of PID realization in dspic30f4011 using PWM for motor controlling. It is necessary for my project. Thank you for attention.

• batchloaf says:

Hi Payrav,

When you say “using PWM for motor controlling”, do you mean that you’re trying to control the speed of a DC motor? If so, how are you sensing the motor speed? The dsPIC implementation will depend on the method you use to sense the speed of the motor.

Ted

13. Payrav says:

Thank you Mr Ted for your replay. I use the feedback sensor to speed sensing.
I understood your PI algorithm code but I need your help. The working principle of my project : I measure the speed of motor using feedback sensor and it is compared with reference speed that I supply myself. And the result of comparing enters to the PID regulator. After to the PWM modulator.

14. gapguillen says:

Hola Ted, a mi me gusta tu proyecto y yo lo voy a hacer porque es una buena referencia para otro proyecto que yo quiero hacer.

Pregunta: Cual es la frecuencia central o de resonancia de tu filtro pasa bajas?… fo= 1/(2*pi*RC)

El proyecto que yo quiero hacer es el control de velocidad de un motor de corriente directa por medio de PWM … Yo ya hice un tacometro usando una placa ARDUINO y me mide la velocidad en rpm … esta velocidad yo quiero enviarla al puerto de comunicacion UART del dspic30f4011 … el dspic30f4011 controlara la velocidad del motor de corriente directa tomando como referencia la velocidad v1 y que yo la propongo … el video de mi tacometro lo voy a cargar en youtube en pocos dias

Pregunta: Puede tu codigo servirme para controlar la velocidad de referencia v1?

Si no me entiendes porfavor avisame a mi para que yo escriba todo esto en el idioma ingles …saludos

15. Ravindra says:

Hello sir, How to find Kp and Ki value?

• batchloaf says:

Hi Ravindra,

To find the values of Kp and Ki, you need to use some kind of PI controller tuning method. That’s a huge topic in itself and goes way beyond the scope of this article, but you should be able to find lots of good information about it online. You could start by looking at the loop tuning section of the Wikipedia article on PID controllers.

Ted

16. Hafiz says:

Hi ted,
If I want use this code for dsPIC33f what should I edit ? I’m very new this kind of programing , I will be grateful if you can help me. Thank you

• batchloaf says:

Hi Hafiz,

I’m not too sure, to be honest. It’s some time since I’ve used a dsPIC33F and I can’t remember whether they include the same hardware modules. Which exact chip are you using? Did you try compiling the code exactly as it is?

I expect the clock speed will probably work out differently and you may need to modify the three lines of configuration settings at the top, but I guess it’s possible it might compile for dsPIC33F apart from that?

Ted

• Hafiz says:

Thanks for reply Sir Ted ,
Actually I’m trying to simulate pid code in proteus 8, but in proteus there are no dspic30f library . That’s why I’m trying to used your code to dspic33f but It’s cant compile at all.
If you can suggest me what software available to simulate dspic30f , I will such a grateful

• Hafiz says:

HI again Mr Ted,
so how to modify this 3 lines of configuration settings in order to compile it with dspic33f?
And did I need to change #include to #include ?

• batchloaf says:

Hi Hafiz,

Sorry, I can’t offer any advice about a simulator because I don’t use one at all. I just try everything directly on the hardware. I guess there’s some kind of simulation possible in MPLABX, which is free to download from microchip.com, but like I say I haven’t used ti myself so i don’t know for sure.

Regarding the header files, I think the two that are there might be ok with the dsPIC33F. I certainly expect that “xc.h” will be included. I’m not quite so sure about the other one, but unfortunately I can’t test it here because I’m working on a Linux machine so I don’t have any dsPIC compiler tools available.

Regarding the configuration settings, again I can’t really offer much useful advice. I would try compiling as is and see if they produce any errors. Since I don’t have a dsPIC33F here and I don’t have the compiler installed on this machine, it’s hard for me to check.

Ted

17. Rohan says:

Hi Sir, your PI control algorithm worked very well for me. Thank you so much. Now, I want to run two PI controllers simultaneously. Please guide me in this case.

18. Akim Amzah says:

hi ted,

what i need to do is to control using PID using dsPIC30f4011. I also add on push button to my project to increase or decrease duty cycle. can you correct me or give some advice for my coding?

#include
#include

// Configuration settings
_FOSC(CSW_FSCM_OFF & FRC_PLL16); // Fosc=16×7.5MHz, i.e. 30 MIPS
_FWDT(WDT_OFF); // Watchdog timer off
_FBORPOR(MCLR_DIS); // Disable reset pin

unsigned int read_analog_channel(int channel);
#define on PORTFbits.RF0
#define off PORTFbits.RF1

int main()
{

double setpoint, measured_value, error, output;
double previous_error = 0, integral = 0, derivative = 0;
double dt = 0.00002, Kp = 1, Ki = 1, Kd = 1;

// Make all port D pins outputs
TRISFbits.TRISF0 = 1;
TRISFbits.TRISF1 = 1;
TRISE = 0x00;
PORTE = 0x00;

// Configure PWM for free running mode
// PWM period = Tcy * prescale * PTPER = 33.33ns * 64 * 9470 = 20ms
PWMCON1bits.PEN1H = 1; // PWM High pin is enabled
PTCONbits.PTOPS = 1; // PWM timer post-scale
PTCONbits.PTCKPS = 0; // PWM timer pre-scale
PTCONbits.PTMOD = 2; // PWM operates in Up-down Mode continuously
DTCON1bits.DTAPS = 0; //DeadTime pre-scaler
DTCON1bits.DTA = 2; //DeadTime value for 4 us.
_PTCKPS = 3; // prescale=1:64 (0=1:1, 1=1:4, 2=1:16, 3=1:64)
PTPER = 18; // 20us PWM period (15-bit period value)
PDC1 = 18; // 0% duty cycle on channels 1,2,3 (max is 65536)
_IUE = 1; // Enable immediate duty cycle update
PTMR = 0; // Clear 15-bit PWM timer counter
_PTEN = 1; // Enable PWM time base

// Configure AN0-AN8 as analog inputs
ADCON3bits.ADCS = 15; // Tad = 266ns, conversion time is 12*Tad
ADCON1bits.ADON = 1; // Turn ADC ON

while(1)
{

while(_PWMIF == 0); // Wait for PWM interrupt flag to be set indicating the end of a PWM cycle.
_PWMIF = 0; // Reset the PWM interrupt flag

setpoint = 1023;
measured_value = read_analog_channel(1);

error = setpoint – measured_value;
integral = integral + error*dt;
derivative = (error – previous_error) / dt;
output = Kp*error + Ki*integral + Kd*derivative;
previous_error = error;

PDC1 = output;
__delay32(30000);
if (PDC1 > 28) PDC1 = 28;
__delay32(30000);
if (PDC1 < 8) PDC1 = 8;

// If the switch is pressed
if (0==on)
{
while(!on);
PDC1 = PDC1 + 1;
if(36=PDC1)
{
PDC1=4;
}
}
}
return 0;
}

// This function reads a single sample from the specified
// analog input. It should take less than 5us when the
// microcontroller is running at 30 MIPS.
// The dsPIC30F4011 has a 10-bit ADC, so the value
// returned is between 0 and 1023 inclusive.
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 = 3.2us
return ADCBUF0;
}