3rd year levitator
Dependencies: MovingAverage NOKIA_5110 PinDetect mbed
main.cpp
- Committer:
- Generic
- Date:
- 2018-09-09
- Revision:
- 0:d7cc9bad311b
File content as of revision 0:d7cc9bad311b:
#include "mbed.h" #include "NOKIA_5110.h" #include "PinDetect.h" #include "Pattern.h" #include "MovingAverage.h" #define PWM_PERIOD_US 100 //10kHz #define MAX_PWM 1.0f //Maximum duty cycle 100% #define MIN_PWM 0.0f //Minimum duty cycle 0% #define OPEN 17080.0f //No object sensor value #define CLOSED 0.1f //Fully obstructed sensor value #define REFERENCE 1150.0f //Setpoint (desired) sensor value #define MAX_POWER 40.8f //Maximum power watts of coil #define MAX_CURRENT 3.5f //Maximum current amps through coil (Used to calculate instantaneous current) #define CONST_POWER 4.78f //Const power drawn by fans and other hardware #define COIL_VOLTAGE 12.0f //Voltage of the coil //-- State variables #define REF_PARAM 0 #define KP_PARAM 1 #define KD_PARAM 2 #define TEST 1 #define IDLE 2 #define LEVITATE 3 #define ERROR 4 #define CALIBRATION 5 //-- Function prototypes void Initialise(); void Idle(); void Calibrate(); void Test(); void Error(); void SetRGB(bool,bool,bool); void SetState(char); void SelectButtonPress(); void UpButtonPress(); void DownButtonPress(); void SelectButtonHeld(); void UpButtonHeld(); void DownButtonHeld(); void UpdateScreen(); void PDController(); void Levitate(); void SetErrorLine(int,char*); float RoundDigits(float,int); void ResetTime(); void UpdateTime(); //-- Mbed objects AnalogIn PosSensor(PC_0); AnalogIn AmbSensor(PC_1); NokiaLcd *Display; LcdPins DisplayPins; PinDetect SelectButton(PB_3); PinDetect UpButton(PA_10); PinDetect DownButton(PB_5); Ticker ScreenUpdateTimer; Timer CycleTimer; DigitalOut RedLed(PA_8); DigitalOut GreenLed(PA_9); DigitalOut BlueLed(PB_10); MovingAverage <float>AveragePower(10,0.0f); MovingAverage <float>AverageCurrent(10,0.0f); PwmOut CoilPWM(PWM_OUT); //Variable decleration and initialisation float ref = 0.0f; //Reference value float pos = 0.0f; //Position value float amb = 0.0f; //Ambient sensor value float err = 0.0f; //Difference between setpoint and measured value float prev_err = 0.0f; //Previous error float d_err = 0.0f; //Rate of error change float duty = 0.0f; //Current duty cycle float current = 0.0f; float power_consumed = 0.0f; char line_buffer[14]; char error_line_1[14]; char error_line_2[14]; char error_line_3[14]; char error_line_4[14]; char error_line_5[14]; int state = 0; //State variable float T = 0.000016129f; //Cycle time (measured) float cycle_t = 0.0f; float open_val = 0.0f; int seconds = 0; int minutes = 0; int hours = 0; int delay = 260; int prev_seconds = 0; int prev_minutes = 0; int prev_hours = 0; bool levitating = false; volatile float Kp = 11000.0; //Proportional value of PD controller volatile float Kd = 65.0f; //Derivative value of PD controller volatile float offset = 20.0f; //Control bias volatile int selected_parameter = 0; volatile bool select_button_flag = false; volatile bool down_button_flag = false; volatile bool up_button_flag = false; volatile bool select_button_held_flag = false; volatile bool down_button_held_flag = false; volatile bool up_button_held_flag = false; int main() { Initialise(); while(1) { switch(state) { case IDLE: Idle(); break; case CALIBRATION: Calibrate(); break; case TEST: Test(); break; case LEVITATE: Levitate(); break; case ERROR: Error(); break; } } } void Initialise() { DisplayPins.sce = PB_8; DisplayPins.rst = PB_9; DisplayPins.dc = PB_6; DisplayPins.mosi = PA_7; DisplayPins.miso = NC; DisplayPins.sclk = PA_5; Display = new NokiaLcd(DisplayPins); Display->InitLcd(); Display->ClearLcdMem(); Display->SetXY(0,0); wait(0.1); CoilPWM.period_us(PWM_PERIOD_US); ref = REFERENCE; CoilPWM.period_us(PWM_PERIOD_US); CoilPWM = 0.0f; SelectButton.mode(PullDown); SelectButton.attach_asserted(&SelectButtonPress); SelectButton.attach_asserted_held(&SelectButtonHeld); SelectButton.setSampleFrequency(); UpButton.mode(PullDown); UpButton.attach_asserted(&UpButtonPress); UpButton.attach_asserted_held(&UpButtonHeld); UpButton.setSampleFrequency(); DownButton.mode(PullDown); DownButton.attach_asserted(&DownButtonPress); DownButton.attach_asserted_held(&DownButtonHeld); DownButton.setSampleFrequency(); DisplayPattern(Display,1); SetRGB(true,false,false); wait(0.5); SetRGB(false,true,false); wait(0.5); SetRGB(false,false,true); wait(0.5); SetRGB(true,true,true); wait(2); Display->ClearLcdMem(); SetRGB(false,false,false); ScreenUpdateTimer.attach(&UpdateScreen,1); SetState(TEST); } void Levitate() { PDController(); if( down_button_held_flag && up_button_held_flag && select_button_held_flag ) { down_button_held_flag = false; up_button_held_flag = false; select_button_held_flag = false; SetState(CALIBRATION); } if( select_button_held_flag ) { select_button_held_flag = false; select_button_flag = false; SetState(IDLE); } } void PDController() { pos = PosSensor.read(); pos *= 3461.5f; err = pos - ref; d_err = (err-prev_err)/T; duty = 17*(Kp*err+Kd*d_err)+10000.0f; prev_err = err; if( pos < CLOSED || pos > OPEN ) { CoilPWM.pulsewidth(0.0f); AveragePower.Insert(0.0f); AverageCurrent.Insert(0.0f); } else { if( duty > PWM_PERIOD_US ) { CoilPWM.pulsewidth_us(MAX_PWM*PWM_PERIOD_US); //AveragePower.Insert() AverageCurrent.Insert(MAX_PWM*MAX_CURRENT); } else if( duty < 0.0f ) { CoilPWM.pulsewidth_us(0.0f); AveragePower.Insert(0.0f); AverageCurrent.Insert(0.0f); } } wait_us(260); //Delay longer than PWM period needed to ensure the set duty cycle completes atleast one cycle } void Error() { CoilPWM = 0.0f; if( select_button_held_flag ) { select_button_held_flag = false; select_button_flag = false; SetState(TEST); } } void Test() { bool test_passed = false; open_val = 3461.5f*PosSensor.read(); amb = 3461.5f*AmbSensor.read(); test_passed = ( open_val > amb ); Display->ClearLcdMem(); Display->SetXY(0,0); if( test_passed ) { Display->DrawString("Test Passed"); } else { Display->DrawString("Test Failed"); } wait(2); if( test_passed ) { SetState(IDLE); } else { SetErrorLine(1,"Error"); SetErrorLine(2,"!!!!!!!!!!!!!!"); SetErrorLine(3,"Sensor Blocked"); SetErrorLine(4,"or"); SetErrorLine(5,"Disconnected"); SetState(ERROR); } } void Idle() { if( select_button_held_flag && down_button_held_flag && up_button_held_flag ) { select_button_held_flag = false; down_button_held_flag = false; up_button_held_flag = false; SetState(CALIBRATION); } if( select_button_flag ) { select_button_flag = false; SetState(LEVITATE); } CoilPWM = 0.0f; } void Calibrate() { CycleTimer.start(); if( select_button_held_flag ) { select_button_held_flag = false; select_button_flag = false; SetState(IDLE); } if( up_button_held_flag ) { up_button_held_flag = false; if( selected_parameter == KP_PARAM ) Kp += 10.0f; else if( selected_parameter == KD_PARAM ) Kd += 10.0f; else if( selected_parameter == REF_PARAM ) ref += 10.0f; } if( down_button_held_flag ) { down_button_held_flag = false; if( selected_parameter == KP_PARAM ) Kp -= 10.0f; else if( selected_parameter == KD_PARAM ) Kd -= 10.0f; else if( selected_parameter == REF_PARAM ) ref -= 10.0f; } if( select_button_flag ) { select_button_flag = false; if( selected_parameter < 2 ) selected_parameter++; else selected_parameter = 0; } if( up_button_flag ) { up_button_flag = false; if( selected_parameter == KP_PARAM ) Kp += 5.0f; else if( selected_parameter == KD_PARAM ) Kd += 5.0f; else if( selected_parameter == REF_PARAM ) ref += 5.0f; } if( down_button_flag ) { down_button_flag = false; if( selected_parameter == KP_PARAM ) Kp -= 5.0f; else if( selected_parameter == KD_PARAM ) Kd -= 5.0f; else if( selected_parameter == REF_PARAM ) ref -= 5.0f; } PDController(); CycleTimer.stop(); cycle_t = CycleTimer.read()*1000000.0f; CycleTimer.reset(); } void SetRGB(bool r, bool g, bool b) { RedLed = (r ? 1 : 0 ); GreenLed = (g ? 1 : 0 ); BlueLed = (b ? 1 : 0 ); } float RoundDigits(float x, int numdigits) { return ceil(x * pow(10.0f,numdigits))/pow(10.0f,numdigits); } void SetState(char to_state) { Display->ClearLcdMem(); Display->SetXY(0,0); CoilPWM = 0.0f; if( to_state == IDLE ) { state = IDLE; SetRGB(false,true,false); DisplayPattern(Display,2); } else if( to_state == CALIBRATION) { state = CALIBRATION; SetRGB(true,true,false); } else if( to_state == TEST ) { state = TEST; SetRGB(false,true,true); Display->DrawString("Testing"); } else if( to_state == ERROR ) { SetRGB(true,false,false); Display->DrawString(error_line_1); Display->SetXY(0,1); Display->DrawString(error_line_2); Display->SetXY(0,2); Display->DrawString(error_line_3); Display->SetXY(0,3); Display->DrawString(error_line_4); Display->SetXY(0,4); Display->DrawString(error_line_5); state = ERROR; } else if( to_state == LEVITATE ) { Display->DrawString("Levitate"); SetRGB(false,false,true); state = LEVITATE; } } void SelectButtonPress() { select_button_flag = true; } void UpButtonPress() { up_button_flag = true; } void DownButtonPress() { down_button_flag = true; } void SelectButtonHeld() { select_button_held_flag = true; } void UpButtonHeld() { up_button_held_flag = true; } void DownButtonHeld() { down_button_held_flag = true; } void UpdateScreen() { power_consumed += (CONST_POWER + AverageCurrent.GetAverage()*COIL_VOLTAGE)/1000.0f; if( state == CALIBRATION ) { Display->ClearLcdMem(); Display->SetXY(0,0); Display->DrawString("Calibrate"); Display->SetXY(0,1); sprintf(line_buffer,"Kp=%.2f",Kp); if( selected_parameter == KP_PARAM ) Display->DrawChar('>'); Display->DrawString(line_buffer); Display->SetXY(0,2); sprintf(line_buffer,"Kd=%.2f",Kd); if( selected_parameter == KD_PARAM ) Display->DrawChar('>'); Display->DrawString(line_buffer); Display->SetXY(0,3); sprintf(line_buffer,"T=%.2f",ref); if( selected_parameter == REF_PARAM ) Display->DrawChar('>'); Display->DrawString(line_buffer); Display->SetXY(0,4); sprintf(line_buffer,"T=%.2fus",cycle_t); Display->DrawString(line_buffer); Display->SetXY(0,5); sprintf(line_buffer,"Pos=%.2f",pos); Display->DrawString(line_buffer); } if( state == LEVITATE ) { if( pos < open_val-100) { UpdateTime(); levitating = true; } else { prev_seconds = seconds; prev_minutes = minutes; prev_hours = hours; levitating = false; ResetTime(); } Display->ClearLcdMem(); Display->SetXY(0,0); Display->DrawString("Current:"); //Display->SetXY(0,1); current = AverageCurrent.GetAverage(); if( current < 0.0f ) current = 0.0f; sprintf(line_buffer,"%.2fA",current); Display->DrawString(line_buffer); sprintf(line_buffer,"%.2fkWs",power_consumed); Display->SetXY(0,1); Display->DrawString("Consumption"); Display->SetXY(0,2); Display->DrawString(line_buffer); if( levitating ) { SetRGB(false,false,true); sprintf(line_buffer,"%dh%dm%ds",hours,minutes,seconds); } else { SetRGB(true,false,true); sprintf(line_buffer,"%dh%dm%ds",prev_hours,prev_minutes,prev_seconds); } Display->SetXY(0,3); Display->DrawString("Time:"); //Display->SetXY(0,5); Display->DrawString(line_buffer); Display->SetXY(0,4); Display->DrawString("Airgap:15mm"); } } void SetErrorLine(int line,char *message) { char *temp; switch(line) { case 1: temp = error_line_1; break; case 2: temp = error_line_2; break; case 3: temp = error_line_3; break; case 4: temp = error_line_4; break; case 5: temp = error_line_5; break; } for(int i = 0; i < 14 ; i++ ) { *temp = message[i]; temp++; } } void ResetTime() { seconds = 0; minutes = 0; hours = 0; } void UpdateTime() { if( seconds < 60 ) seconds++; else { seconds = 0; if( minutes < 60 ) minutes++; else { minutes = 0; hours++; } } }