Other Parts Discussed in Thread: MSP-IQMATHLIB
Tool/software: Code Composer Studio
I have written a code for implementing PID control on a motor. The code is as follows:
#include <msp430g2553.h>
/**
* main.c
*/
int main(void) {
WDTCTL = WDTPW + WDTHOLD; //disabling the watch-dog timer
P2DIR = BIT2; //selecting and setting the output bits for timer module
P2SEL = BIT2;
BCSCTL1 = CALBC1_16MHZ; //setting the clock frequency to 16MHz
DCOCTL = CALDCO_16MHZ;
//Timer module
TA1CCR0 = 1000; //giving a count for total time of the PWM to be generated
TA1CCTL1 = OUTMOD_7; //setting the timer mode to count in set-reset mode
TA1CTL = MC_1 + ID_0 + TASSEL_2 + TACLR; //MC_1 is mode control 1 it implies that timer counts in up-mode
//ID_0 is clock divider by 1, TASSEL_2 selects SMCLK and TACLR clears the timer(this makes the timer count from 0)
//ADC module
ADC10CTL0 = ADC10ON + ADC10IE + SREF_1 + REFON + REF2_5V + MSC; //enabling interrupts, setting reference to be 2.5V
ADC10CTL1 = ADC10DIV_0 + CONSEQ_2; //clock division and CONSEQ_2 is for single channel convert repeatedly
ADC10AE0 = INCH_0; //input available on channel 0 i.e pin A0
ADC10CTL0 |= ENC + ADC10SC; //start conversion
_BIS_SR(GIE); //Global interrupt enable
while(1);
}
#pragma vector = ADC10_VECTOR
__interrupt void adc10_interrupt(void) {
P2DIR |= 0x01; //Set P2.0
P2OUT |= 0x01; //Set P2.0 as high(for checking time taken for loop to run)
float set_point = 2.24, t_on; //set point is 2.24V
float kp = 1, ki = 1, p_term, imax = 2.24, imin = 0, pi_term, pi_min = 0, pi_max = 2;
static float i_term = 0;
int adc_val = ADC10MEM; //assigning value of ADC to a variable
float adc_out = (float) (adc_val * 2.5/1024); //scaling the hex value to voltage according to reference given
float error = set_point - adc_out; //getting error from ADC and set-point
//proportional term
p_term = kp * error;
//integral term
static float i_temp = 0;
i_temp = i_temp + error; //calculating the integral term
if(i_temp > imax) //integral anti-windup
i_temp = imax;
else if(i_temp < imin) //integral anti-windup
i_temp = imin;
i_term = ki * i_temp; //final i_term
//derivative term
/*float d_temp = error; //check this for static
d_term = kd * (d_temp-error); //check the calculations*/
//pi term
pi_term = p_term + i_term;
//clipping the output pi_term
if(pi_term > pi_max)
pi_term = pi_max;
else if(pi_term < pi_min)
pi_term = pi_min;
t_on = (pi_term * 1000)/pi_max;
TA1CCR1 = t_on; //setting the ON time according to pi_term
P2OUT ^= 0x01; //setting P2.0 to low (for checking time taken for loop to run)
_BIS_SR(GIE); //global interrupt enable
}
The problems I am facing are:
1) I have to calculate the time taken for the PI loop to run. To do so, I have made a pin high at the beginning of the control loop and toggled it at the end. I have checked the output of this pin on a DSO. Surprisingly, the interrupt is being called only once. So, I see only one spike on the oscilloscope as soon as I run the code.
2) I have hence tried putting the control loop in a while(1){} loop. Now, when I repeat the same procedure here, to toggle a pin, I observe a completely different and unexpected behavior. When I keep the pin high in the beginning, and toggle it at the end, the DSO shows me a timing diagram where a high pulse sustains for a long time of 280 microseconds and the low pulse for 0.5 micro. This seems to be correct. But when I do it the other way round, i.e at the beginning of the loop, the pin is set to low. The DSO shows me a pulse with a duty cycle of 50% and the time period is (2*280) microsec.
#include <msp430g2553.h>
/**
* main.c
*/
int main(void) {
WDTCTL = WDTPW + WDTHOLD; //disabling the watch-dog timer
P2DIR = BIT2; //selecting and setting the output bits for timer module
P2SEL = BIT2;
BCSCTL1 = CALBC1_16MHZ; //setting the clock frequency to 16MHz
DCOCTL = CALDCO_16MHZ;
//Timer module
TA1CCR0 = 1000; //giving a count for total time of the PWM to be generated
TA1CCTL1 = OUTMOD_7; //setting the timer mode to count in set-reset mode
TA1CTL = MC_1 + ID_0 + TASSEL_2 + TACLR; //MC_1 is mode control 1 it implies that timer counts in up-mode
//ID_0 is clock divider by 1, TASSEL_2 selects SMCLK and TACLR clears the timer(this makes the timer count from 0)
//ADC module
ADC10CTL0 = ADC10ON + ADC10IE + SREF_1 + REFON + REF2_5V + MSC; //enabling interrupts, setting reference to be 2.5V
ADC10CTL1 = ADC10DIV_0 + CONSEQ_2; //clock division and CONSEQ_2 is for single channel convert repeatedly
ADC10AE0 = INCH_0; //input available on channel 0 i.e pin A0
ADC10CTL0 |= ENC + ADC10SC; //start conversion
while(1)
{
P2DIR |= 0x01; //Set P2.0
P2OUT |= 0x00; //Set P2.0 as low(for checking time taken for loop to run)
float set_point = 2.24, t_on; //set point is 2.24V
float kp = 1, ki = 1, p_term, imax = 2.24, imin = 0, pi_term, pi_min = 0, pi_max = 2;
static float i_term = 0;
int adc_val = ADC10MEM; //assigning value of ADC to a variable
float adc_out = (float) (adc_val * 2.5/1024); //scaling the hex value to voltage according to reference given
float error = set_point - adc_out; //getting error from ADC and set-point
//proportional term
p_term = kp * error;
//integral term
static float i_temp = 0;
i_temp = i_temp + error; //calculating the integral term
if(i_temp > imax) //integral anti-windup
i_temp = imax;
else if(i_temp < imin) //integral anti-windup
i_temp = imin;
i_term = ki * i_temp; //final i_term
//derivative term
/*float d_temp = error; //check this for static
d_term = kd * (d_temp-error); //check the calculations*/
//pi term
pi_term = p_term + i_term;
//clipping the output pi_term
if(pi_term > pi_max)
pi_term = pi_max;
else if(pi_term < pi_min)
pi_term = pi_min;
t_on = (pi_term * 1000)/pi_max;
TA1CCR1 = t_on; //setting the ON time according to pi_term
P2OUT ^= 0x01; //setting P2.0 flip (for checking time taken for loop to run)
// _BIS_SR(GIE); //Global interrupt enable
}
}