Quantcast
Channel: ad hocumentation • n. fast documentation of ideas and solutions.
Viewing all articles
Browse latest Browse all 72

Simple PI control using the dsPIC30F4011

$
0
0

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


Viewing all articles
Browse latest Browse all 72

Trending Articles