Sven Kugathasan
/
SKAFMO_2
ES
main.cpp
- Committer:
- af2213
- Date:
- 2017-03-26
- Revision:
- 22:997c013e0f13
- Parent:
- 21:828582e4d4ef
File content as of revision 22:997c013e0f13:
/*_________________________________LIBRARIES__________________________________*/ #include "mbed.h" #include "rtos.h" #include "PID.h" #include "ctype.h" #include <string> #include "stdlib.h" #include "math.h" #include <limits> //#include <vector> /*_________________________________PIN SETUP__________________________________*/ //PhotoInterrupter Input Pins #define I1pin D2 #define I2pin D11 #define I3pin D12 //Incremental Encoder Input Pins #define CHA D7 #define CHB D8 //Motor Drive output pins //Mask in output byte #define L1Lpin D4 //0x01 #define L1Hpin D5 //0x02 #define L2Lpin D3 //0x04 #define L2Hpin D6 //0x08 #define L3Lpin D9 //0x10 #define L3Hpin D10 //0x20 //Photointerrupter Inputs as Interrupts InterruptIn InterruptI1(D2); InterruptIn InterruptI2(D11); InterruptIn InterruptI3(D12); //Incremental Encoder Inputs as Interrupts InterruptIn InterruptCHA(D7); DigitalIn InterruptCHB(D8); //Motor Drive Outputs in PWM PwmOut L1L(L1Lpin); PwmOut L1H(L1Hpin); PwmOut L2L(L2Lpin); PwmOut L2H(L2Hpin); PwmOut L3L(L3Lpin); PwmOut L3H(L3Hpin); PwmOut testOut(A5); //Status LED //DigitalOut led1(LED1); DigitalOut led2(LED2); DigitalOut led3(LED3); DigitalOut TIME(D13); //Toggle Digital Pin to measure Interrupt Times //Initialise the serial port Serial pc(SERIAL_TX, SERIAL_RX); //Timer Timer rps; // Measures Time for complete revolution Timer partial_rps; // Measures Time for partial revolutions Timer tmp; // Profiler Timer //PID Controller PID velocity_pid(0.75, 0.025, 0.2, 0.1); // (P, I, D, WAIT) PID dist_pid(2, 0.0, 0.01, 0.1); // (P, I, D, WAIT) //Initialize Threads //Thread pid_thread(osPriorityNormal, 512, NULL); Thread melody_thread(osPriorityNormal, 512, NULL); typedef struct{ char note; uint8_t sharp; uint8_t oct; float dur; }note_t; /*________________________Motor Drive States__________________________________*/ //Mapping from sequential drive states to motor phase outputs /* State L1 L2 L3 0 H - L 1 - H L 2 L H - 3 L - H 4 - L H 5 H L - 6 - - - 7 - - - */ //Drive state to output table const int8_t driveTable[] = {0x12,0x18,0x09,0x21,0x24,0x06,0x00,0x00}; //Mapping from interrupter inputs to sequential rotor states. 0x00 and 0x07 are not valid const int8_t stateMap[] = {0x07,0x05,0x03,0x04,0x01,0x00,0x02,0x07}; /*____________________Global Variable Initialization__________________________*/ //Rotor Direction Default const int8_t lead = -2; //Phase lead to make motor spin: 2 for forwards, -2 for backwards int8_t direction = 1; //+1: Backwards rotation; -1 for Forwards Rotation //Optical Disk States uint8_t orState=0; //Offset of Motor Field and Optical Disk uint8_t intState=0; //Current Optical Disk state const uint8_t num_states = 6; //Number of states in one rotation uint32_t count = 0; //Counts number of states traversed int8_t completed = 0; //Checks if rotation completed int8_t driveto = 0; //Holds value of new motor drive state //Angular Velocity Variables float PWM_freq = 0.00001f; //100kHz (> Motor LP cut-off frequency = 10Hz) float dutyout = 1.0f; //Initialized at 50% duty cycle float dutyout_max = 1.0f; //Maximum Duty Cycle will enable maximum speed float angular_vel = 0.0f; //Revolution per second (Measured over 360) float partial_vel = 0.0f; //Revolution per second (Measured over 360/117) float vel_target = 0.0f; //Target Speed float vel_max = 100; //Maximum Speed at 3.0V achievable is ~60 rps //Position Variables uint32_t revstates_count = 0; //Global Variable to pass into interrupt uint8_t pulse_count = 0; //Max.Pulse count = 117 float total_rev = 0.0f; float partial_rev = 0.0f; float rev_target = 0.0f; //Target Rotations float revstates_max = 0; //Debug Variables bool flag = false; float test_time = 0.0f; int8_t test = 0; int songLen = 0; // Timer interrupt Ticker calcPID_timer; /*_____Basic Functions (Motor Drive, Synchronization, Reading Rotor State)____*/ //Set a given drive state void motorOut(int8_t driveState){ //Lookup the output byte from the drive state. int8_t driveOut = driveTable[driveState & 0x07]; //Turn off first (PWM) if (~driveOut & 0x01) L1L = 0; if (~driveOut & 0x02) L1H.write(dutyout); L1H.period(PWM_freq); if (~driveOut & 0x04) L2L = 0; if (~driveOut & 0x08) L2H.write(dutyout); L2H.period(PWM_freq); if (~driveOut & 0x10) L3L = 0; if (~driveOut & 0x20) L3H.write(dutyout); L3H.period(PWM_freq); //Then turn on (PWM) if (driveOut & 0x01) L1L.write(dutyout); L1L.period(PWM_freq); if (driveOut & 0x02) L1H = 0; if (driveOut & 0x04) L2L.write(dutyout); L2L.period(PWM_freq); if (driveOut & 0x08) L2H = 0; if (driveOut & 0x10) L3L.write(dutyout); L3L.period(PWM_freq); if (driveOut & 0x20) L3H = 0; testOut.write(0.5f); testOut.period(PWM_freq); } //Convert photointerrupter inputs to a rotor state inline int8_t readRotorState(){ return stateMap[InterruptI1.read() + 2*InterruptI2.read() + 4*InterruptI3.read()]; } const float octaveMap[] = {27.50, 30.87, 16.35, 18.35, 20.60, 21.83, 24.50}; float returnNote(const note_t &n) { unsigned octMult = 1; octMult = octMult << n.oct; return 1.0f/( octMult*octaveMap[n.note - 'A'] ); } void playMelody(note_t* song){ for (int i=0; i<songLen; ++i){ //pc.printf("Playing note %c with duration %f and period %f\r\n", song[i].note, song[i].dur, returnNote(song[i])); PWM_freq = returnNote(song[i]); wait(song[i].dur); } PWM_freq = 0.00001f; delete[] song; } void calculatePID(){ //Calculate new PID Control Point if((total_rev/rev_target) > 0.75f){ dist_pid.setProcessValue(total_rev); dutyout = dist_pid.compute(); } else{ velocity_pid.setProcessValue(partial_vel); dutyout = velocity_pid.compute(); } } //Basic synchronisation routine int8_t motorHome() { //Put the motor in drive state X (e.g. 5) to avoid initial jitter //Set to maximum speed to get maximum momentum dutyout = 1.0f; motorOut(5); wait(1.0); //Put the motor in drive state 0 and wait for it to stabilise motorOut(0); wait(1.0); //Get the rotor state return readRotorState(); } /*________________Advanced Functions (Speed and Position Control)_____________*/ // Function involves PID void position_control(float rev, float vel){ completed = 0; dutyout = 1.0f; count = 0; rev_target = rev; vel_target = vel; //pc.printf("rev %f\r\n", rev); //pc.printf("vel %f\r\n", vel); //Reverses motor direction if forwards rotation requested if (rev_target < 0.0f || vel_target < 0.0f){ direction = -1; if (rev_target < 0.0f) rev_target = rev_target * -1; else vel_target = vel_target * -1; } else{ direction = 1; } //pc.printf("vel_target %f\r\n", vel_target); //pc.printf("dir %d\r\n", direction); pc.printf("Waiting for stabilize...\r\n"); dutyout = 0.0f; velocity_pid.reset(); dist_pid.reset(); wait(3); motorHome(); pc.printf("Restarting...\r\n"); velocity_pid.reset(); //velocity_pid.setInputLimits(0.0, 50); velocity_pid.setOutputLimits(0.0, 1.0); velocity_pid.setMode(1); velocity_pid.setSetPoint(vel_target); dist_pid.reset(); //dist_pid.setInputLimits(0.0, rev_target); dist_pid.setOutputLimits(0.0, 1.0); dist_pid.setMode(1); dist_pid.setSetPoint(rev_target); dutyout = 0.3f; intState = readRotorState(); driveto = (intState-orState+(direction*lead)+6)%6; motorOut(driveto); calcPID_timer.attach(&calculatePID, 0.05); } void changestate_isr(){ //TIME = 1; //led2 = !led2; // Profiling: Test time duration of ISR /*if(test == 0){ tmp.start(); test = 1; } else{ tmp.stop(); test_time = tmp.read(); tmp.reset(); test = 0; }*/ // Measure time for 360 Rotation if(driveto == 0x04){ //Next time drivestate=4, 360 degrees revolution pulse_count = 0; /*if(flag){ rps.stop(); angular_vel = 1/(rps.read()); rps.reset(); flag = 0; }*/ } /*if(driveto == 0x04){ //First time drivestate=4, Timer started at 0 degrees pulse_count = 0; //Synchronize Quadrature Encoder with PhotoInterrupter rps.start(); flag = 1; }*/ // Measure number of revolutions count++; if (rev_target == std::numeric_limits<float>::max()){ total_rev = 0.0f; } //Turn-off when target reached (if target is 0, never end) if(completed || total_rev >= rev_target){ completed = 1; total_rev = 0; count = 0; dutyout = 0; motorOut(0); led3 = 0; partial_rev = 0; calcPID_timer.detach(); } else{ intState = readRotorState(); driveto = (intState-orState+(direction*lead)+6)%6; motorOut(driveto); } //TIME = 0; } void pid_isr(){ //TIME = 1; //117 Pulses per revolution pulse_count++; //Measure Time to do 3 degrees of rotation if(test == 0){ partial_rps.start(); test = 1; } else{ partial_rps.stop(); partial_vel = 1/((117.0f * partial_rps.read())); partial_rps.reset(); test = 0; } //Partial Revolution Count partial_rev = pulse_count/117.0f; //Total Revolution Count total_rev = (count/6.0f) + partial_rev; //TIME = 0; } /*__________________________Main Function_____________________________________*/ void serial_com(){ pc.baud(9600); float r=0; float v=0; //velocity bool r_val=true; bool v_val=true; int16_t t_loc=0; int16_t r_loc=0; int16_t v_loc=0; char buf[20]; bool note_marker; bool dur_marker; bool accent_marker; string note=""; uint8_t duration=0; Timer t; t.start(); string input; while(1){ r=0; v=0; r_val=true; v_val=true; note_marker=false; dur_marker=false; accent_marker=false; t.stop(); pc.printf("Time taken was %f seconds. \r\n Please enter something\r\n", t.read()); pc.scanf("%s",&buf); t.reset(); t.start(); input=buf; pc.printf("The input string is %s\r\n",buf); t_loc=input.find('T'); r_loc=input.find('R'); v_loc=input.find('V'); //pc.printf("Location of T is %d\r\n",t_loc); //pc.printf("Location of R is %d\r\n",r_loc); //pc.printf("Location of V is %d\r\n",v_loc); if(t_loc==0 && input.length()>1){ //if melody marker present and there is something after it //printf("Note sequence detected\r\n"); //note_t* song = (note_t*)malloc(sizeof(note_t)*input.length()); note_t* song = new note_t[input.length()/2]; // since each note is at least 2 characters long, max length is string/2 songLen = 0; for(int i=1;i<input.length();i++){ if(accent_marker==false && dur_marker==false && note_marker==false && (input[i]=='A' || input[i]=='B' || input[i]=='C' || input[i]=='D' || input[i]=='E' || input[i]=='F' || input[i]=='G')){ note_marker=true; } else if(note_marker==true && (input[i]=='^' || input[i]=='#')){ accent_marker=true; } else if((note_marker==true && isdigit(input[i]) && accent_marker==false) || (note_marker==true && isdigit(input[i]) && accent_marker==true)){ dur_marker=true; } if(note_marker==true && accent_marker==true && dur_marker == true){ note=input.substr(i-2,2); //printf("The note is %s\r\n",note.c_str()); duration=atof(input.substr(i,1).c_str()); //printf("Duration is %d\r\n",duration); note_marker=false; dur_marker=false; accent_marker=false; note_t newNote = {note[0], 1, 5, duration}; song[songLen++] = newNote; } else if(note_marker==true && dur_marker==true && accent_marker==false){ note=input.substr(i-1,1); //printf("The note is %s\r\n",note.c_str()); duration=atof(input.substr(i,1).c_str()); //printf("Duration is %d\r\n",duration); note_marker=false; dur_marker=false; accent_marker=false; note_t newNote = {note[0], 1, 5, duration}; song[songLen++] = newNote; } } melody_thread.start(callback(playMelody, song)); } else if(t_loc==-1){ //if no melody marker present //pc.printf("Note sequence NOT detected\r\n"); if(r_loc==0 && v_loc==-1 && input.length()>1){ //check if first letter is R //pc.printf("Checking for sole R input type...\r\n"); for(int j=1; j<input.length();j++){ if(!isdigit(input[j]) && input[j]!='-' && input[j]!='.'){ r_val=false; } } if(r_val==true){ r=atof(input.substr(1).c_str()); pc.printf("Spin for %.3f number of rotations\r\n",r); position_control((float) r, vel_max); } else{ pc.printf("Invalid input\r\n"); } } else if(r_loc==0 && v_loc!=-1 && v_loc < input.length()-1){ //check if first letter is R and V is also present //pc.printf("Checking for combined R and V input type...\r\n"); for(int j=1; j<v_loc;j++){ if(!isdigit(input[j]) && input[j]!='-' && input[j]!='.'){ r_val=false; } } for(int j=v_loc+1; j<input.length();j++){ if(!isdigit(input[j]) && input[j]!='-' && input[j]!='.'){ v_val=false; } } if(r_val==true && v_val==true){ r=atof(input.substr(1,v_loc-1).c_str()); v=atof(input.substr(v_loc+1).c_str()); if(v<0){ v=abs(v); } pc.printf("Spin for %.3f number of rotations at %.3f speed \r\n",r,v); position_control((float) r, (float) v); } else{ pc.printf("Invalid input\r\n"); } } else if(v_loc==0 && input.length()>1){ //check if first letter is V //pc.printf("Checking for sole V input type...\r\n"); for(int j=1; j<input.length();j++){ if(!isdigit(input[j]) && input[j]!='-' && input[j]!='.'){ v_val=false; } } if(v_val==true){ v=atof(input.substr(1).c_str()); pc.printf("Spin at %.3f speed\r\n",v); position_control(std::numeric_limits<float>::max(),(float)v); } else{ pc.printf("Invalid input\r\n"); } } else{ pc.printf("Invalid input\r\n"); } } } } int main(){ //Start of Program pc.printf("STARTING SKAFMO BRUSHLESS MOTOR PROJECT! \n\r"); led3 = 0; //Run the motor synchronisation: orState is subtracted from future rotor state inputs orState = motorHome(); //pc.printf("Synchronization Complete: Rotor and Motor aligned with Offset: %d\r\n", orState); //Interrupts (Optical Disk State Change): Drives to next state, Measures whole revolution count, Measures angular velocity over a whole revolution InterruptI1.rise(&changestate_isr); InterruptI1.fall(&changestate_isr); InterruptI2.rise(&changestate_isr); InterruptI2.fall(&changestate_isr); InterruptI3.rise(&changestate_isr); InterruptI3.fall(&changestate_isr); //Interrupts (Incremental Encoder CHA Phase) InterruptCHA.rise(&pid_isr); //Initial Target Settings //float rotation_set = 100.00; //float velocity_set = 10.00; // Melody in a Thread // PID in Thread //If speed not defined, use vel_max! If Rotation not defined, use revstates_max //float rotation_set = revstates_max; //float velocity_set = vel_max; //pid_thread.join(); serial_com(); } /*_______________________Testing and Tuning Function__________________________*/ /*Measures Angular Velocity using PhotoInterrupters by checking time taken to go from State 4 to State 4 in this case. Avoid sensor phasing as it measures one complete cycle */ void meas_velocity(){ intState = readRotorState(); driveto = (intState-orState+(direction*lead)+6)%6; motorOut(driveto); while (1) { pc.printf("Rotations per second: %f \n\r", angular_vel); } } // Function has no PID void rotation_control(int8_t num_revs, int8_t sign){ revstates_count = num_revs*num_states; intState = readRotorState(); driveto = (intState-orState+(sign*lead)+6)%6; motorOut(driveto); while(!completed){ //pc.printf("Angular velocity: %f \n", angular_vel); pc.printf("Partial Angular: %f \n", partial_vel); //pc.printf("Count: %d \r\n", (count/6)); } } void PID_tuning(){ dutyout = 0.5; intState = readRotorState(); driveto = (intState-orState+lead+6)%6; motorOut(driveto); while (1) { // Testing Step Response by increasing D.C. from 0.5 to 0.7 // Gradient is equal to Kc if(count > 3000){ dutyout = 0.7; } pc.printf("Duty Cycle: %f ", dutyout); pc.printf("Rotations per second: %f ", angular_vel); pc.printf("Count: %d \n\r", count); } }