UVW 3 phases Brushless DC motor control
Dependencies: QEI mbed-rtos mbed
Fork of BLDCmotor by
controller.cpp@12:a4b17bb682eb, 2012-12-21 (annotated)
- Committer:
- kosaka
- Date:
- Fri Dec 21 22:06:56 2012 +0000
- Revision:
- 12:a4b17bb682eb
- Child:
- 13:791e20f1af43
121222a
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
kosaka | 12:a4b17bb682eb | 1 | // BLDCmotor.cpp: 各種3相同期モータに対するセンサあり運転のシミュレーション |
kosaka | 12:a4b17bb682eb | 2 | // Kosaka Lab. 121215 |
kosaka | 12:a4b17bb682eb | 3 | #include "mbed.h" |
kosaka | 12:a4b17bb682eb | 4 | #include "QEI.h" |
kosaka | 12:a4b17bb682eb | 5 | |
kosaka | 12:a4b17bb682eb | 6 | #include "controller.h" |
kosaka | 12:a4b17bb682eb | 7 | #include "UVWpwm.h" |
kosaka | 12:a4b17bb682eb | 8 | #include "fast_math.h" |
kosaka | 12:a4b17bb682eb | 9 | Serial pc(USBTX, USBRX); // Display on tera term in PC |
kosaka | 12:a4b17bb682eb | 10 | |
kosaka | 12:a4b17bb682eb | 11 | motor_parameters p; // モータの定数、信号など |
kosaka | 12:a4b17bb682eb | 12 | current_loop_parameters il; // 電流制御マイナーループの定数、変数 |
kosaka | 12:a4b17bb682eb | 13 | velocity_loop_parameters vl; // 速度制御メインループの定数、変数 |
kosaka | 12:a4b17bb682eb | 14 | |
kosaka | 12:a4b17bb682eb | 15 | QEI encoder (CH_A, CH_B, NC, N_ENC, QEI::X4_ENCODING); |
kosaka | 12:a4b17bb682eb | 16 | // QEI(PinName channelA, mbed pin for channel A input. |
kosaka | 12:a4b17bb682eb | 17 | // PinName channelB, mbed pin for channel B input. |
kosaka | 12:a4b17bb682eb | 18 | // PinName index, mbed pin for channel Z input. (index channel input Z phase th=0), (pass NC if not needed). |
kosaka | 12:a4b17bb682eb | 19 | // int pulsesPerRev, Number of pulses in one revolution(=360 deg). |
kosaka | 12:a4b17bb682eb | 20 | // Encoding encoding = X2_ENCODING, X2 is default. X2 uses interrupts on the rising and falling edges of only channel A where as |
kosaka | 12:a4b17bb682eb | 21 | // X4 uses them on both channels. |
kosaka | 12:a4b17bb682eb | 22 | // ) |
kosaka | 12:a4b17bb682eb | 23 | // void reset (void) |
kosaka | 12:a4b17bb682eb | 24 | // Reset the encoder. |
kosaka | 12:a4b17bb682eb | 25 | // int getCurrentState (void) |
kosaka | 12:a4b17bb682eb | 26 | // Read the state of the encoder. |
kosaka | 12:a4b17bb682eb | 27 | // int getPulses (void) |
kosaka | 12:a4b17bb682eb | 28 | // Read the number of pulses recorded by the encoder. |
kosaka | 12:a4b17bb682eb | 29 | // int getRevolutions (void) |
kosaka | 12:a4b17bb682eb | 30 | // Read the number of revolutions recorded by the encoder on the index channel. |
kosaka | 12:a4b17bb682eb | 31 | /*********** User setting for control parameters (end) ***************/ |
kosaka | 12:a4b17bb682eb | 32 | |
kosaka | 12:a4b17bb682eb | 33 | AnalogOut analog_out(DA_PORT); |
kosaka | 12:a4b17bb682eb | 34 | AnalogIn VshuntR_Uplus(p19); // *3.3 [V], Volt of shunt R_SHUNT[Ohm]. The motor current i = v_shunt_r/R_SHUNT [A] |
kosaka | 12:a4b17bb682eb | 35 | AnalogIn VshuntR_Uminus(p20); // *3.3 [V], Volt of shunt R_SHUNT[Ohm]. The motor current i = v_shunt_r/R_SHUNT [A] |
kosaka | 12:a4b17bb682eb | 36 | AnalogIn VshuntR_Vplus(p16); // *3.3 [V], Volt of shunt R_SHUNT[Ohm]. The motor current i = v_shunt_r/R_SHUNT [A] |
kosaka | 12:a4b17bb682eb | 37 | AnalogIn VshuntR_Vminus(p17); // *3.3 [V], Volt of shunt R_SHUNT[Ohm]. The motor current i = v_shunt_r/R_SHUNT [A] |
kosaka | 12:a4b17bb682eb | 38 | |
kosaka | 12:a4b17bb682eb | 39 | unsigned long _count; // sampling number |
kosaka | 12:a4b17bb682eb | 40 | float _time; // time[s] |
kosaka | 12:a4b17bb682eb | 41 | float _r; // reference signal |
kosaka | 12:a4b17bb682eb | 42 | float _th=0; // [rad], motor angle, control output of angle controller |
kosaka | 12:a4b17bb682eb | 43 | float _i=0; // [A], motor current, control output of current controller |
kosaka | 12:a4b17bb682eb | 44 | float _e=0; // e=r-y for PID controller |
kosaka | 12:a4b17bb682eb | 45 | float _eI=0; // integral of e for PID controller |
kosaka | 12:a4b17bb682eb | 46 | float _iref; // reference current iref [A], output of angle th_contorller |
kosaka | 12:a4b17bb682eb | 47 | float _u; // control input[V], motor input volt. |
kosaka | 12:a4b17bb682eb | 48 | float _ei=0; // e=r-y for current PID controller |
kosaka | 12:a4b17bb682eb | 49 | float _eiI=0; // integral of e for current PID controller |
kosaka | 12:a4b17bb682eb | 50 | unsigned char _f_u_plus=1;// sign(u) |
kosaka | 12:a4b17bb682eb | 51 | unsigned char _f_umax=0;// flag showing u is max or not |
kosaka | 12:a4b17bb682eb | 52 | unsigned char _f_imax=0;// flag showing i is max or not |
kosaka | 12:a4b17bb682eb | 53 | float debug[20]; // for debug |
kosaka | 12:a4b17bb682eb | 54 | float disp[10]; // for printf to avoid interrupted by quicker process |
kosaka | 12:a4b17bb682eb | 55 | DigitalOut led1(LED1); |
kosaka | 12:a4b17bb682eb | 56 | DigitalOut led2(LED2); |
kosaka | 12:a4b17bb682eb | 57 | DigitalOut led3(LED3); |
kosaka | 12:a4b17bb682eb | 58 | DigitalOut led4(LED4); |
kosaka | 12:a4b17bb682eb | 59 | |
kosaka | 12:a4b17bb682eb | 60 | #ifdef GOOD_DATA |
kosaka | 12:a4b17bb682eb | 61 | float data[1000][5]; // memory to save data offline instead of "online fprintf". |
kosaka | 12:a4b17bb682eb | 62 | unsigned int count3; // |
kosaka | 12:a4b17bb682eb | 63 | unsigned int count2=(int)(TS2/TS0); // |
kosaka | 12:a4b17bb682eb | 64 | unsigned short _count_data=0; |
kosaka | 12:a4b17bb682eb | 65 | #endif |
kosaka | 12:a4b17bb682eb | 66 | DigitalOut debug_p26(p26); // p17 for debug |
kosaka | 12:a4b17bb682eb | 67 | DigitalOut debug_p25(p25); // p17 for debug |
kosaka | 12:a4b17bb682eb | 68 | DigitalOut debug_p24(p24); // p17 for debug |
kosaka | 12:a4b17bb682eb | 69 | //AnalogIn VCC(p19); // *3.3 [V], Volt of VCC for motor |
kosaka | 12:a4b17bb682eb | 70 | //AnalogIn VCC2(p20); // *3.3 [V], Volt of (VCC-R i), R=2.5[Ohm]. R is for preventing too much i when deadtime is failed. |
kosaka | 12:a4b17bb682eb | 71 | |
kosaka | 12:a4b17bb682eb | 72 | unsigned short f_find_origin; // flag to find the origin of the rotor angle theta |
kosaka | 12:a4b17bb682eb | 73 | |
kosaka | 12:a4b17bb682eb | 74 | #if 1 //BUG!! if move sqrt2 to fast_math.h, sim starts and completed without working!? |
kosaka | 12:a4b17bb682eb | 75 | float sqrt2(float x){ // √xのx=1まわりのテイラー展開 √x = 1 + 1/2*(x-1) -1/4*(x-1)^2 + ... |
kosaka | 12:a4b17bb682eb | 76 | // return((1+x)*0.5); // 一次近似 |
kosaka | 12:a4b17bb682eb | 77 | return(x+(1-x*x)*0.25); // 二次近似 |
kosaka | 12:a4b17bb682eb | 78 | } |
kosaka | 12:a4b17bb682eb | 79 | #endif |
kosaka | 12:a4b17bb682eb | 80 | |
kosaka | 12:a4b17bb682eb | 81 | void init_parameters(){ // IPMSMの機器定数等の設定, 制御器の初期化 |
kosaka | 12:a4b17bb682eb | 82 | float r2, r3; |
kosaka | 12:a4b17bb682eb | 83 | |
kosaka | 12:a4b17bb682eb | 84 | |
kosaka | 12:a4b17bb682eb | 85 | // 対象の機器定数 PA 5HP scroll from IPEC2000 "High Efficiency Control for Interior Permanent Magnet Synchronous Motor" |
kosaka | 12:a4b17bb682eb | 86 | // outside diameter of stator 150 mm |
kosaka | 12:a4b17bb682eb | 87 | // outside diameter of rotor 84.0 mm |
kosaka | 12:a4b17bb682eb | 88 | // width of rotor 70.0 mm |
kosaka | 12:a4b17bb682eb | 89 | // maximum speed 7500 r/min (min=900rpm) |
kosaka | 12:a4b17bb682eb | 90 | // maximum torque 15.0 Nm |
kosaka | 12:a4b17bb682eb | 91 | // Ψa 0.176 Wb |
kosaka | 12:a4b17bb682eb | 92 | // Ld 3.50 mH |
kosaka | 12:a4b17bb682eb | 93 | // Lq 6.30 mH |
kosaka | 12:a4b17bb682eb | 94 | // Ra 0.143Ω |
kosaka | 12:a4b17bb682eb | 95 | // Rc 200Ω |
kosaka | 12:a4b17bb682eb | 96 | #ifdef SIMULATION |
kosaka | 12:a4b17bb682eb | 97 | p.Ld = 0.0035; // H |
kosaka | 12:a4b17bb682eb | 98 | p.Lq = 0.0063; // H |
kosaka | 12:a4b17bb682eb | 99 | p.Lq0 = p.Lq; |
kosaka | 12:a4b17bb682eb | 100 | p.Lq1 = 0; |
kosaka | 12:a4b17bb682eb | 101 | p.R = 0.143; // Ω |
kosaka | 12:a4b17bb682eb | 102 | p.phi = 0.176; // V s |
kosaka | 12:a4b17bb682eb | 103 | p.Jm = 0.00018; // Nms^2 |
kosaka | 12:a4b17bb682eb | 104 | #endif |
kosaka | 12:a4b17bb682eb | 105 | p.th[0] = 0; |
kosaka | 12:a4b17bb682eb | 106 | p.th[1] = 0; |
kosaka | 12:a4b17bb682eb | 107 | p.w = 0; |
kosaka | 12:a4b17bb682eb | 108 | p.iab[0] =0; p.iab[1] = 0; // iab = [iα;iβ]; |
kosaka | 12:a4b17bb682eb | 109 | p.vab[0] =0; p.vab[1] = 0; // vab = [vα;vβ]; |
kosaka | 12:a4b17bb682eb | 110 | p.p = 2; // 極対数 |
kosaka | 12:a4b17bb682eb | 111 | // UVW座標からαβ座標への変換行列Cuvwの設定 |
kosaka | 12:a4b17bb682eb | 112 | r2 = sqrt(2.);//1.414213562373095;//2^(1/2); |
kosaka | 12:a4b17bb682eb | 113 | r3 = sqrt(3.);//1.732050807568877;//3^(1/2); |
kosaka | 12:a4b17bb682eb | 114 | // p.Cuvw =[ r2/r3 -1/r2/r3 -1/r2/r3; ... |
kosaka | 12:a4b17bb682eb | 115 | // 0 1/r2 -1/r2 ]; |
kosaka | 12:a4b17bb682eb | 116 | p.Cuvw[0][0] = r2/r3; p.Cuvw[0][1] = -1./r2/r3; p.Cuvw[0][2] = -1./r2/r3; |
kosaka | 12:a4b17bb682eb | 117 | p.Cuvw[1][0] = 0; p.Cuvw[1][1] = 1/r2 ; p.Cuvw[1][2] = -1./r2; |
kosaka | 12:a4b17bb682eb | 118 | |
kosaka | 12:a4b17bb682eb | 119 | p.w = 0; |
kosaka | 12:a4b17bb682eb | 120 | |
kosaka | 12:a4b17bb682eb | 121 | // 制御器の初期化 |
kosaka | 12:a4b17bb682eb | 122 | vl.iq_ref=0; // q軸電流指令[A] |
kosaka | 12:a4b17bb682eb | 123 | vl.w_lpf = 0; // 検出した速度[rad/s] |
kosaka | 12:a4b17bb682eb | 124 | vl.eI_w = 0; // 速度制御用偏差の積分値(積分項) |
kosaka | 12:a4b17bb682eb | 125 | il.eI_idq[0] = 0; // 電流制御用偏差の積分値(積分項) |
kosaka | 12:a4b17bb682eb | 126 | il.eI_idq[1] = 0; // 電流制御用偏差の積分値(積分項) |
kosaka | 12:a4b17bb682eb | 127 | } |
kosaka | 12:a4b17bb682eb | 128 | |
kosaka | 12:a4b17bb682eb | 129 | void idq_control(float idq_act[2]){ |
kosaka | 12:a4b17bb682eb | 130 | // dq座標電流PID制御器(電流マイナーループのフィードバック制御) |
kosaka | 12:a4b17bb682eb | 131 | // 入力:指令dq座標電流 idq_ref [A], 実dq座標電流 idq_act [A], PI制御積分項 eI, サンプル時間 ts [s] |
kosaka | 12:a4b17bb682eb | 132 | // 出力:αβ軸電圧指令 vdq_ref [A] |
kosaka | 12:a4b17bb682eb | 133 | // [vdq_ref,eI_idq] = idq_control(idq_ref,idq_act,eI_idq,ts); |
kosaka | 12:a4b17bb682eb | 134 | float Kp_d, Kp_q, Ki_d, Ki_q, e[2]; |
kosaka | 12:a4b17bb682eb | 135 | // 電流制御ゲイン |
kosaka | 12:a4b17bb682eb | 136 | Kp_d = iKPd; // P gain (d-axis) |
kosaka | 12:a4b17bb682eb | 137 | Ki_d = iKId; // I gain (d-axis) |
kosaka | 12:a4b17bb682eb | 138 | Kp_q = iKPq; // P gain (q-axis) |
kosaka | 12:a4b17bb682eb | 139 | Ki_q = iKIq; // I gain (q-axis) |
kosaka | 12:a4b17bb682eb | 140 | |
kosaka | 12:a4b17bb682eb | 141 | // dq電流偏差の計算 |
kosaka | 12:a4b17bb682eb | 142 | e[0] = il.idq_ref[0] - idq_act[0]; |
kosaka | 12:a4b17bb682eb | 143 | e[1] = il.idq_ref[1] - idq_act[1]; |
kosaka | 12:a4b17bb682eb | 144 | |
kosaka | 12:a4b17bb682eb | 145 | // dq電流偏差の積分項の計算 |
kosaka | 12:a4b17bb682eb | 146 | il.eI_idq[0] = il.eI_idq[0] + TS0*e[0]; |
kosaka | 12:a4b17bb682eb | 147 | il.eI_idq[1] = il.eI_idq[1] + TS0*e[1]; |
kosaka | 12:a4b17bb682eb | 148 | |
kosaka | 12:a4b17bb682eb | 149 | // PI制御 |
kosaka | 12:a4b17bb682eb | 150 | // vdq_ref = [Kp_d 0;0 Kp_q]*e + [Ki_d 0;0 Ki_q]*eI; |
kosaka | 12:a4b17bb682eb | 151 | il.vdq_ref[0] = Kp_d*e[0] + Ki_d*il.eI_idq[0]; |
kosaka | 12:a4b17bb682eb | 152 | il.vdq_ref[1] = Kp_q*e[1] + Ki_q*il.eI_idq[1]; |
kosaka | 12:a4b17bb682eb | 153 | |
kosaka | 12:a4b17bb682eb | 154 | // koko anti-windup |
kosaka | 12:a4b17bb682eb | 155 | } |
kosaka | 12:a4b17bb682eb | 156 | |
kosaka | 12:a4b17bb682eb | 157 | void current_loop(){ // 電流制御マイナーループ |
kosaka | 12:a4b17bb682eb | 158 | float th, c, s, Cdq[2][2], iu, iv, iab[2], idq_act[2], vab_ref[2],tmp; |
kosaka | 12:a4b17bb682eb | 159 | if( f_find_origin==1 ){ |
kosaka | 12:a4b17bb682eb | 160 | th = 0; |
kosaka | 12:a4b17bb682eb | 161 | }else{ |
kosaka | 12:a4b17bb682eb | 162 | // 位置θをセンサで検出 |
kosaka | 12:a4b17bb682eb | 163 | #ifndef SIMULATION |
kosaka | 12:a4b17bb682eb | 164 | p.th[0] = (float)encoder.getPulses()/(float)N_ENC*2.0*PI; // get angle [rad] from encoder |
kosaka | 12:a4b17bb682eb | 165 | #endif |
kosaka | 12:a4b17bb682eb | 166 | th = p.th[0]; |
kosaka | 12:a4b17bb682eb | 167 | } |
kosaka | 12:a4b17bb682eb | 168 | |
kosaka | 12:a4b17bb682eb | 169 | // αβ座標からdq座標への変換行列Cdqの設定 |
kosaka | 12:a4b17bb682eb | 170 | #if 1 //BUG!! if move sqrt2 to fast_math.h, sim starts and completed without working!? |
kosaka | 12:a4b17bb682eb | 171 | c = cos(th); |
kosaka | 12:a4b17bb682eb | 172 | s = sin(th); |
kosaka | 12:a4b17bb682eb | 173 | #else |
kosaka | 12:a4b17bb682eb | 174 | c = (float)(_cos(th/(PI/3.)*(float)DEG60+0.5))/65535.; |
kosaka | 12:a4b17bb682eb | 175 | s = (float)(_sin(th/(PI/3.)*(float)DEG60+0.5))/65535.; |
kosaka | 12:a4b17bb682eb | 176 | #endif |
kosaka | 12:a4b17bb682eb | 177 | Cdq[0][0]= c; Cdq[0][1]=s; //Cdq ={{ c, s} |
kosaka | 12:a4b17bb682eb | 178 | Cdq[1][0]=-s; Cdq[1][1]=c; // {-s, c]}; |
kosaka | 12:a4b17bb682eb | 179 | |
kosaka | 12:a4b17bb682eb | 180 | // 電流センサによってiu, iv を検出 |
kosaka | 12:a4b17bb682eb | 181 | #ifndef SIMULATION |
kosaka | 12:a4b17bb682eb | 182 | p.iuvw[0] = (VshuntR_Uplus - VshuntR_Uminus) /R_SHUNT; // get iu [A] from shunt resistance; |
kosaka | 12:a4b17bb682eb | 183 | p.iuvw[1] = (VshuntR_Vplus - VshuntR_Vminus) /R_SHUNT; // get iv [A] from shunt resistance; |
kosaka | 12:a4b17bb682eb | 184 | #endif |
kosaka | 12:a4b17bb682eb | 185 | iu = p.iuvw[0]; |
kosaka | 12:a4b17bb682eb | 186 | iv = p.iuvw[1]; |
kosaka | 12:a4b17bb682eb | 187 | // iw = -(iu + iv); // iu+iv+iw=0であることを利用してiw を計算 |
kosaka | 12:a4b17bb682eb | 188 | |
kosaka | 12:a4b17bb682eb | 189 | // iab = p.Cuvw*[iu;iv;iw]; |
kosaka | 12:a4b17bb682eb | 190 | // iab[0] = p.Cuvw[0][0]*iu + p.Cuvw[0][1]*iv + p.Cuvw[0][2]*iw; |
kosaka | 12:a4b17bb682eb | 191 | // iab[1] = p.Cuvw[1][0]*iu + p.Cuvw[1][1]*iv + p.Cuvw[1][2]*iw; |
kosaka | 12:a4b17bb682eb | 192 | // iab[0] = p.Cuvw[0][0]*iu + p.Cuvw[0][1]*(iv+iw); |
kosaka | 12:a4b17bb682eb | 193 | // iab[1] = p.Cuvw[1][1]*(iv-iw); |
kosaka | 12:a4b17bb682eb | 194 | iab[0] = (p.Cuvw[0][0]-p.Cuvw[0][1])*iu; |
kosaka | 12:a4b17bb682eb | 195 | iab[1] = p.Cuvw[1][1]*(iu+2*iv); |
kosaka | 12:a4b17bb682eb | 196 | |
kosaka | 12:a4b17bb682eb | 197 | // αβ座標電流をdq座標電流に変換 |
kosaka | 12:a4b17bb682eb | 198 | //idq_act = Cdq * iab; |
kosaka | 12:a4b17bb682eb | 199 | idq_act[0] = Cdq[0][0]*iab[0] + Cdq[0][1]*iab[1]; |
kosaka | 12:a4b17bb682eb | 200 | idq_act[1] = Cdq[1][0]*iab[0] + Cdq[1][1]*iab[1]; |
kosaka | 12:a4b17bb682eb | 201 | |
kosaka | 12:a4b17bb682eb | 202 | // dq電流制御(電流フィードバック制御) |
kosaka | 12:a4b17bb682eb | 203 | // [vdq_ref,eI_idq] = idq_control(idq_ref,idq_act,eI_idq,ts); |
kosaka | 12:a4b17bb682eb | 204 | #ifdef USE_CURRENT_CONTROL |
kosaka | 12:a4b17bb682eb | 205 | idq_control(idq_act); |
kosaka | 12:a4b17bb682eb | 206 | #else |
kosaka | 12:a4b17bb682eb | 207 | il.vdq_ref[0] = il.idq_ref[0]; |
kosaka | 12:a4b17bb682eb | 208 | il.vdq_ref[1] = il.idq_ref[1]; |
kosaka | 12:a4b17bb682eb | 209 | #endif |
kosaka | 12:a4b17bb682eb | 210 | // dq軸電圧指令ベクトルの大きさをMAX制限(コンバータ出力電圧値に設定) |
kosaka | 12:a4b17bb682eb | 211 | // if( norm(vdq_ref) > vdqmax ){ vdq_ref= vdqmax/norm(vdq_ref)*vdq_ref} |
kosaka | 12:a4b17bb682eb | 212 | if( (tmp=il.vdq_ref[0]*il.vdq_ref[0]+il.vdq_ref[1]*il.vdq_ref[1])>SQRvdqMAX ){ |
kosaka | 12:a4b17bb682eb | 213 | tmp = sqrt2(SQRvdqMAX/tmp); |
kosaka | 12:a4b17bb682eb | 214 | il.vdq_ref[0] = tmp*il.vdq_ref[0]; //= vdqmax/norm(vdq_ref)*vdq_ref |
kosaka | 12:a4b17bb682eb | 215 | il.vdq_ref[1] = tmp*il.vdq_ref[1]; |
kosaka | 12:a4b17bb682eb | 216 | // koko anti-windup |
kosaka | 12:a4b17bb682eb | 217 | } |
kosaka | 12:a4b17bb682eb | 218 | |
kosaka | 12:a4b17bb682eb | 219 | // dq座標指令電圧 vd_ref, vq_refからiα, iβを計算 |
kosaka | 12:a4b17bb682eb | 220 | // vab_ref = Cdq'*vdq_ref; |
kosaka | 12:a4b17bb682eb | 221 | vab_ref[0] = Cdq[0][0]*il.vdq_ref[0] + Cdq[1][0]*il.vdq_ref[1]; |
kosaka | 12:a4b17bb682eb | 222 | vab_ref[1] = Cdq[0][1]*il.vdq_ref[0] + Cdq[1][1]*il.vdq_ref[1]; |
kosaka | 12:a4b17bb682eb | 223 | |
kosaka | 12:a4b17bb682eb | 224 | // モータに印加するUVW相電圧を計算 (vα, vβからvu, vv, vwを計算) |
kosaka | 12:a4b17bb682eb | 225 | // vu = √(2/3)*va; |
kosaka | 12:a4b17bb682eb | 226 | // vv = -1/√6*va + 1/√2*vb; |
kosaka | 12:a4b17bb682eb | 227 | // vw = -1/√6*va - 1/√2*vb; |
kosaka | 12:a4b17bb682eb | 228 | // p.Cuvw =[ r2/r3 -1/r2/r3 -1/r2/r3; ... |
kosaka | 12:a4b17bb682eb | 229 | // 0 1/r2 -1/r2 ]; |
kosaka | 12:a4b17bb682eb | 230 | // p.vuvw = p.Cuvw'*vab_ref; |
kosaka | 12:a4b17bb682eb | 231 | p.vuvw[0] = p.Cuvw[0][0]*vab_ref[0]; |
kosaka | 12:a4b17bb682eb | 232 | p.vuvw[1] = p.Cuvw[0][1]*vab_ref[0] + p.Cuvw[1][1]*vab_ref[1]; |
kosaka | 12:a4b17bb682eb | 233 | p.vuvw[2] = -p.vuvw[0] - p.vuvw[1]; |
kosaka | 12:a4b17bb682eb | 234 | // p.vuvw[0] = p.Cuvw[0][0]*vab_ref[0] + p.Cuvw[1][0]*vab_ref[1]; |
kosaka | 12:a4b17bb682eb | 235 | // p.vuvw[2] = p.Cuvw[0][2]*vab_ref[0] + p.Cuvw[1][2]*vab_ref[1]; |
kosaka | 12:a4b17bb682eb | 236 | |
kosaka | 12:a4b17bb682eb | 237 | p.th[1] = p.th[0]; // thを更新 |
kosaka | 12:a4b17bb682eb | 238 | } |
kosaka | 12:a4b17bb682eb | 239 | |
kosaka | 12:a4b17bb682eb | 240 | |
kosaka | 12:a4b17bb682eb | 241 | void vel_control(){ |
kosaka | 12:a4b17bb682eb | 242 | // 速度制御器:速度偏差が入力され、q軸電流指令を出力。 |
kosaka | 12:a4b17bb682eb | 243 | // 入力:指令速度 w_ref [rad/s], 実速度 w_lpf [rad/s], PI制御積分項 eI, サンプル時間 TS1 [s] |
kosaka | 12:a4b17bb682eb | 244 | // 出力:q軸電流指令 iq_ref [A] |
kosaka | 12:a4b17bb682eb | 245 | // [iq_ref,eI_w] = vel_control(w_ref,w_lpf,eI_w,ts); |
kosaka | 12:a4b17bb682eb | 246 | float Kp, Ki, e; |
kosaka | 12:a4b17bb682eb | 247 | // 速度制御PIDゲイン |
kosaka | 12:a4b17bb682eb | 248 | Kp = wKp; // 速度制御PIDのPゲイン |
kosaka | 12:a4b17bb682eb | 249 | Ki = wKi; // 速度制御PIDのIゲイン |
kosaka | 12:a4b17bb682eb | 250 | |
kosaka | 12:a4b17bb682eb | 251 | // 速度偏差の計算 |
kosaka | 12:a4b17bb682eb | 252 | e = vl.w_ref - vl.w_lpf; |
kosaka | 12:a4b17bb682eb | 253 | |
kosaka | 12:a4b17bb682eb | 254 | // 速度偏差の積分値の計算 |
kosaka | 12:a4b17bb682eb | 255 | vl.eI_w = vl.eI_w + TS1*e; |
kosaka | 12:a4b17bb682eb | 256 | |
kosaka | 12:a4b17bb682eb | 257 | // PI制御 |
kosaka | 12:a4b17bb682eb | 258 | vl.iq_ref = Kp*e + Ki*vl.eI_w; |
kosaka | 12:a4b17bb682eb | 259 | // koko anti-windup |
kosaka | 12:a4b17bb682eb | 260 | } |
kosaka | 12:a4b17bb682eb | 261 | |
kosaka | 12:a4b17bb682eb | 262 | void velocity_loop(){ // 速度制御メインループ(w_ref&beta_ref to idq_ref) |
kosaka | 12:a4b17bb682eb | 263 | float tmp, idq_ref[2]; |
kosaka | 12:a4b17bb682eb | 264 | |
kosaka | 12:a4b17bb682eb | 265 | // 速度ωを求めるために、位置θをオイラー微分して、一次ローパスフィルタに通す。 |
kosaka | 12:a4b17bb682eb | 266 | tmp = p.th[0]-p.th[1]; |
kosaka | 12:a4b17bb682eb | 267 | while( tmp> PI ){ tmp -= 2*PI;} |
kosaka | 12:a4b17bb682eb | 268 | while( tmp<-PI ){ tmp += 2*PI;} |
kosaka | 12:a4b17bb682eb | 269 | vl.w_lpf = (1-iLPF)*vl.w_lpf + tmp/TS0 *iLPF; |
kosaka | 12:a4b17bb682eb | 270 | |
kosaka | 12:a4b17bb682eb | 271 | // 速度制御:速度偏差が入力され、q軸電流指令を出力。 |
kosaka | 12:a4b17bb682eb | 272 | // [iq_ref,eI_w] = vel_control(w_ref,w_act,eI_w,ts); |
kosaka | 12:a4b17bb682eb | 273 | vel_control(); |
kosaka | 12:a4b17bb682eb | 274 | |
kosaka | 12:a4b17bb682eb | 275 | // q軸電流指令のMAX制限(異常に大きい指令値を制限する) |
kosaka | 12:a4b17bb682eb | 276 | if( vl.iq_ref > iqMAX ){ |
kosaka | 12:a4b17bb682eb | 277 | vl.iq_ref = iqMAX; |
kosaka | 12:a4b17bb682eb | 278 | }else if( vl.iq_ref < -iqMAX ){ |
kosaka | 12:a4b17bb682eb | 279 | vl.iq_ref = -iqMAX; |
kosaka | 12:a4b17bb682eb | 280 | } |
kosaka | 12:a4b17bb682eb | 281 | |
kosaka | 12:a4b17bb682eb | 282 | // 電流ベクトル制御 |
kosaka | 12:a4b17bb682eb | 283 | if( vl.iq_ref>=0 ){ tmp = vl.tan_beta_ref;} // 負のトルクを発生させるときはidは負のままでiqを正から負にする |
kosaka | 12:a4b17bb682eb | 284 | else{ tmp = -vl.tan_beta_ref;}// Tm = p((phi+(Ld-Lq)id) iqより |
kosaka | 12:a4b17bb682eb | 285 | //idq_ref = {{-tmp, 1}}*iq_ref; |
kosaka | 12:a4b17bb682eb | 286 | idq_ref[0] = -tmp*vl.iq_ref; idq_ref[1] = vl.iq_ref; |
kosaka | 12:a4b17bb682eb | 287 | |
kosaka | 12:a4b17bb682eb | 288 | // dq軸電流の目標値を速度制御メインループから電流制御マイナーループへ渡す。 |
kosaka | 12:a4b17bb682eb | 289 | il.idq_ref[0] = idq_ref[0]; |
kosaka | 12:a4b17bb682eb | 290 | il.idq_ref[1] = idq_ref[1]; |
kosaka | 12:a4b17bb682eb | 291 | } |
kosaka | 12:a4b17bb682eb | 292 | |
kosaka | 12:a4b17bb682eb | 293 | void vuvw2pwm(){ // vu, vv, vwより、UVW相の上アームPWMを発生 |
kosaka | 12:a4b17bb682eb | 294 | float duty_u, duty_v, duty_w; |
kosaka | 12:a4b17bb682eb | 295 | |
kosaka | 12:a4b17bb682eb | 296 | duty_u = (p.vuvw[0]/vdqMAX+1)*0.5; |
kosaka | 12:a4b17bb682eb | 297 | duty_v = (p.vuvw[1]/vdqMAX+1)*0.5; |
kosaka | 12:a4b17bb682eb | 298 | duty_w = (p.vuvw[2]/vdqMAX+1)*0.5; |
kosaka | 12:a4b17bb682eb | 299 | uvw[0].duty = duty_u; |
kosaka | 12:a4b17bb682eb | 300 | uvw[1].duty = duty_v; |
kosaka | 12:a4b17bb682eb | 301 | uvw[2].duty = duty_w; |
kosaka | 12:a4b17bb682eb | 302 | } |
kosaka | 12:a4b17bb682eb | 303 | |
kosaka | 12:a4b17bb682eb | 304 | #ifdef SIMULATION |
kosaka | 12:a4b17bb682eb | 305 | void sim_motor(){ |
kosaka | 12:a4b17bb682eb | 306 | // モータシミュレータ |
kosaka | 12:a4b17bb682eb | 307 | // 入力信号:UVW相電圧p.vuvw [V]、負荷トルクp.TL [Nm] |
kosaka | 12:a4b17bb682eb | 308 | // 出力信号:モータ角度p.th[0] [rad], モータ速度p.w [rad/s], モータUVW相電流p.iuvw [A] |
kosaka | 12:a4b17bb682eb | 309 | // p = motor(p, ts); // IPM, dq座標 |
kosaka | 12:a4b17bb682eb | 310 | float c, s, Cdq[2][2], idq_dot[2], id,iq, vdq[2], idq[2], Tall,TL, Cdq_inv[2][2]; |
kosaka | 12:a4b17bb682eb | 311 | analog_out=p.vuvw[0]/100.+0.5;//debug |
kosaka | 12:a4b17bb682eb | 312 | // vu, vv, vwからvα, vβを計算 |
kosaka | 12:a4b17bb682eb | 313 | p.vab[0] = p.Cuvw[0][0]*p.vuvw[0] + p.Cuvw[0][1]*p.vuvw[1] + p.Cuvw[0][2]*p.vuvw[2]; |
kosaka | 12:a4b17bb682eb | 314 | p.vab[1] = p.Cuvw[1][0]*p.vuvw[0] + p.Cuvw[1][1]*p.vuvw[1] + p.Cuvw[1][2]*p.vuvw[2]; |
kosaka | 12:a4b17bb682eb | 315 | //printf("vab=%f, %f ",p.vab[0],p.vab[1]);scanf("%f",&c); |
kosaka | 12:a4b17bb682eb | 316 | |
kosaka | 12:a4b17bb682eb | 317 | // αβ座標からdq座標への変換行列Cdqの設定 |
kosaka | 12:a4b17bb682eb | 318 | c = cos(p.th[0]); |
kosaka | 12:a4b17bb682eb | 319 | s = sin(p.th[0]); |
kosaka | 12:a4b17bb682eb | 320 | // Cdq =[ c s; ... |
kosaka | 12:a4b17bb682eb | 321 | // -s c]; |
kosaka | 12:a4b17bb682eb | 322 | Cdq[0][0] = c; Cdq[0][1] = s; |
kosaka | 12:a4b17bb682eb | 323 | Cdq[1][0] =-s; Cdq[1][1] = c; |
kosaka | 12:a4b17bb682eb | 324 | |
kosaka | 12:a4b17bb682eb | 325 | // vα, vβからvd, vqを計算 |
kosaka | 12:a4b17bb682eb | 326 | // vd = c*p.va + s*p.vb; |
kosaka | 12:a4b17bb682eb | 327 | // vq =-s*p.va + c*p.vb; |
kosaka | 12:a4b17bb682eb | 328 | // vdq = Cdq * p.vab; |
kosaka | 12:a4b17bb682eb | 329 | vdq[0] = Cdq[0][0]*p.vab[0] + Cdq[0][1]*p.vab[1]; |
kosaka | 12:a4b17bb682eb | 330 | vdq[1] = Cdq[1][0]*p.vab[0] + Cdq[1][1]*p.vab[1]; |
kosaka | 12:a4b17bb682eb | 331 | |
kosaka | 12:a4b17bb682eb | 332 | // iα, iβからid, iqを計算 |
kosaka | 12:a4b17bb682eb | 333 | // id = c*p.ia + s*p.ib; |
kosaka | 12:a4b17bb682eb | 334 | // iq =-s*p.ia + c*p.ib; |
kosaka | 12:a4b17bb682eb | 335 | // idq = Cdq * p.iab; |
kosaka | 12:a4b17bb682eb | 336 | idq[0] = Cdq[0][0]*p.iab[0] + Cdq[0][1]*p.iab[1]; |
kosaka | 12:a4b17bb682eb | 337 | idq[1] = Cdq[1][0]*p.iab[0] + Cdq[1][1]*p.iab[1]; |
kosaka | 12:a4b17bb682eb | 338 | |
kosaka | 12:a4b17bb682eb | 339 | // get id,iq |
kosaka | 12:a4b17bb682eb | 340 | // id_dot = (1.0/p.Ld) * ( vd - (p.R*id - p.w*p.Lq*iq) ); |
kosaka | 12:a4b17bb682eb | 341 | // iq_dot = (1.0/p.Lq) * ( vq - (p.R*iq + p.w*p.Ld*id + p.w*p.phi) ); |
kosaka | 12:a4b17bb682eb | 342 | // idq_dot = [p.Ld 0;0 p.Lq]\( vdq - p.R*idq - p.w*[0 -p.Lq;p.Ld 0]*idq - p.w*[0;p.phi]); |
kosaka | 12:a4b17bb682eb | 343 | idq_dot[0] = (1.0/p.Ld) * ( vdq[0] - (p.R*idq[0] - p.w*p.Lq*idq[1]) ); |
kosaka | 12:a4b17bb682eb | 344 | idq_dot[1] = (1.0/p.Lq) * ( vdq[1] - (p.R*idq[1] + p.w*p.Ld*idq[0] + p.w*p.phi) ); |
kosaka | 12:a4b17bb682eb | 345 | // id = id + ts * id_dot; |
kosaka | 12:a4b17bb682eb | 346 | // iq = iq + ts * iq_dot; |
kosaka | 12:a4b17bb682eb | 347 | p.idq[0] = idq[0] + TS0*idq_dot[0]; |
kosaka | 12:a4b17bb682eb | 348 | p.idq[1] = idq[1] + TS0*idq_dot[1]; |
kosaka | 12:a4b17bb682eb | 349 | id = p.idq[0]; |
kosaka | 12:a4b17bb682eb | 350 | iq = p.idq[1]; |
kosaka | 12:a4b17bb682eb | 351 | |
kosaka | 12:a4b17bb682eb | 352 | // 磁気飽和を考慮したLqの計算 |
kosaka | 12:a4b17bb682eb | 353 | p.Lq = p.Lq0 + p.Lq1*abs(iq); |
kosaka | 12:a4b17bb682eb | 354 | if( p.Lq < p.Ld ) |
kosaka | 12:a4b17bb682eb | 355 | p.Lq = p.Ld; |
kosaka | 12:a4b17bb682eb | 356 | |
kosaka | 12:a4b17bb682eb | 357 | // モータトルクの計算 |
kosaka | 12:a4b17bb682eb | 358 | p.Tm = p.p * (p.phi + (p.Ld-p.Lq)*id) * iq; |
kosaka | 12:a4b17bb682eb | 359 | |
kosaka | 12:a4b17bb682eb | 360 | // モータ速度ωの計算 |
kosaka | 12:a4b17bb682eb | 361 | if( abs(p.w) > 5*2*PI ) |
kosaka | 12:a4b17bb682eb | 362 | if( p.w>=0 ) TL= p.TL; |
kosaka | 12:a4b17bb682eb | 363 | else TL=-p.TL; |
kosaka | 12:a4b17bb682eb | 364 | else |
kosaka | 12:a4b17bb682eb | 365 | TL = p.w/(5*2*PI)*p.TL; |
kosaka | 12:a4b17bb682eb | 366 | |
kosaka | 12:a4b17bb682eb | 367 | Tall = p.Tm - TL; |
kosaka | 12:a4b17bb682eb | 368 | p.w = p.w + TS0 * (1.0 / p.Jm) * Tall; |
kosaka | 12:a4b17bb682eb | 369 | |
kosaka | 12:a4b17bb682eb | 370 | // モータ角度θの計算 |
kosaka | 12:a4b17bb682eb | 371 | p.th[0] = p.th[0] + TS0 * p.w; |
kosaka | 12:a4b17bb682eb | 372 | if( p.th[0]>4*PI) |
kosaka | 12:a4b17bb682eb | 373 | p.th[0] = p.th[0] - 4*PI; |
kosaka | 12:a4b17bb682eb | 374 | |
kosaka | 12:a4b17bb682eb | 375 | if( p.th[0]<0 ) |
kosaka | 12:a4b17bb682eb | 376 | p.th[0] = p.th[0] + 4*PI; |
kosaka | 12:a4b17bb682eb | 377 | |
kosaka | 12:a4b17bb682eb | 378 | // dq座標からαβ座標への変換行列Cdq_invの設定 |
kosaka | 12:a4b17bb682eb | 379 | c = cos(p.th[0]); |
kosaka | 12:a4b17bb682eb | 380 | s = sin(p.th[0]); |
kosaka | 12:a4b17bb682eb | 381 | // Cdq_inv =[ c -s; ... |
kosaka | 12:a4b17bb682eb | 382 | // s c]; |
kosaka | 12:a4b17bb682eb | 383 | Cdq_inv[0][0] = c; Cdq_inv[0][1] =-s; |
kosaka | 12:a4b17bb682eb | 384 | Cdq_inv[1][0] = s; Cdq_inv[1][1] = c; |
kosaka | 12:a4b17bb682eb | 385 | |
kosaka | 12:a4b17bb682eb | 386 | // id, iqからiα, iβを計算 |
kosaka | 12:a4b17bb682eb | 387 | //p.iab = Cdq_inv * p.idq; |
kosaka | 12:a4b17bb682eb | 388 | p.iab[0] = Cdq_inv[0][0]*p.idq[0] + Cdq_inv[0][1]*p.idq[1]; |
kosaka | 12:a4b17bb682eb | 389 | p.iab[1] = Cdq_inv[1][0]*p.idq[0] + Cdq_inv[1][1]*p.idq[1]; |
kosaka | 12:a4b17bb682eb | 390 | |
kosaka | 12:a4b17bb682eb | 391 | // αβ座標からUVW座標への変換行列Cuvw_inv=Cuvw' |
kosaka | 12:a4b17bb682eb | 392 | // iα, iβからiu, iv, iwを計算 |
kosaka | 12:a4b17bb682eb | 393 | // iu = r2/r3*ia; |
kosaka | 12:a4b17bb682eb | 394 | // iv = -1/r2/r3*ia + 1/r2*ib; |
kosaka | 12:a4b17bb682eb | 395 | // iw = -1/r2/r3*ia - 1/r2*ib; |
kosaka | 12:a4b17bb682eb | 396 | //p.iuvw = p.Cuvw' * p.iab; |
kosaka | 12:a4b17bb682eb | 397 | p.iuvw[0] = p.Cuvw[0][0]*p.iab[0] + p.Cuvw[1][0]*p.iab[1]; |
kosaka | 12:a4b17bb682eb | 398 | p.iuvw[1] = p.Cuvw[0][1]*p.iab[0] + p.Cuvw[1][1]*p.iab[1]; |
kosaka | 12:a4b17bb682eb | 399 | p.iuvw[2] = p.Cuvw[0][2]*p.iab[0] + p.Cuvw[1][2]*p.iab[1]; |
kosaka | 12:a4b17bb682eb | 400 | } |
kosaka | 12:a4b17bb682eb | 401 | #endif |
kosaka | 12:a4b17bb682eb | 402 | |
kosaka | 12:a4b17bb682eb | 403 | void data2mbedUSB(){ // save data to mbed USB drive |
kosaka | 12:a4b17bb682eb | 404 | if( _count_data<1000 ){ |
kosaka | 12:a4b17bb682eb | 405 | data[_count_data][0]=p.th[0]/*vl.w_ref*/; data[_count_data][1]=p.vuvw[0]; |
kosaka | 12:a4b17bb682eb | 406 | data[_count_data][2]=vl.w_lpf; data[_count_data][3]=_count*TS0; data[_count_data][4]=il.vdq_ref[1]; |
kosaka | 12:a4b17bb682eb | 407 | _count_data++; |
kosaka | 12:a4b17bb682eb | 408 | } |
kosaka | 12:a4b17bb682eb | 409 | #if 0 |
kosaka | 12:a4b17bb682eb | 410 | if( _count_data<500 ){ |
kosaka | 12:a4b17bb682eb | 411 | debug[0]=p.vab[0]; debug[1]=p.vab[1]; debug[2]=il.vdq_ref[0]; debug[3]=il.vdq_ref[1]; debug[4]=p.iab[0]; |
kosaka | 12:a4b17bb682eb | 412 | debug[5]=p.iab[1]; debug[6]=p.vuvw[0]; debug[7]=p.vuvw[1]; debug[8]=p.vuvw[2]; debug[9]=p.iuvw[0]; |
kosaka | 12:a4b17bb682eb | 413 | debug[10]=p.iuvw[1]; debug[11]=p.iuvw[2]; debug[12]=p.idq[0]; debug[13]=p.idq[1]; debug[14]=p.TL; |
kosaka | 12:a4b17bb682eb | 414 | debug[15]=p.Tm; debug[16]=p.w; debug[17]=vl.w_lpf; debug[18]=p.th[0]; debug[19]=_count*TS0;//_time; |
kosaka | 12:a4b17bb682eb | 415 | //BUG for(j=0;j<19;j++){ fprintf( fp, "%f, ",debug[j]);} fprintf( fp, "%f\n",debug[19]); |
kosaka | 12:a4b17bb682eb | 416 | for(j=0;j<19;j++){ printf("%f, ",debug[j]);} printf("%f\n",debug[19]); |
kosaka | 12:a4b17bb682eb | 417 | // for(j=0;j<19;j++){ pc.printf("%f, ",debug[j]);} pc.printf("%f\n",debug[19]); |
kosaka | 12:a4b17bb682eb | 418 | } |
kosaka | 12:a4b17bb682eb | 419 | #endif |
kosaka | 12:a4b17bb682eb | 420 | } |
kosaka | 12:a4b17bb682eb | 421 | void timerTS0(){ // timer called every TS0[s]. |
kosaka | 12:a4b17bb682eb | 422 | debug_p26 = 1; |
kosaka | 12:a4b17bb682eb | 423 | _count++; |
kosaka | 12:a4b17bb682eb | 424 | _time += TS0; |
kosaka | 12:a4b17bb682eb | 425 | |
kosaka | 12:a4b17bb682eb | 426 | current_loop(); // 電流制御マイナーループ(idq_ref to vuvw) |
kosaka | 12:a4b17bb682eb | 427 | vuvw2pwm(); // vuvw to pwm |
kosaka | 12:a4b17bb682eb | 428 | #ifdef SIMULATION |
kosaka | 12:a4b17bb682eb | 429 | // モータシミュレータ |
kosaka | 12:a4b17bb682eb | 430 | sim_motor(); // IPM, dq座標 |
kosaka | 12:a4b17bb682eb | 431 | #endif |
kosaka | 12:a4b17bb682eb | 432 | debug_p26 = 0; |
kosaka | 12:a4b17bb682eb | 433 | } |
kosaka | 12:a4b17bb682eb | 434 | void timerTS1(void const *argument){ |
kosaka | 12:a4b17bb682eb | 435 | debug_p25 = 1; |
kosaka | 12:a4b17bb682eb | 436 | velocity_loop(); // 速度制御メインループ(w_ref&beta_ref to idq_ref) |
kosaka | 12:a4b17bb682eb | 437 | debug_p25 = 0; |
kosaka | 12:a4b17bb682eb | 438 | } |
kosaka | 12:a4b17bb682eb | 439 | |
kosaka | 12:a4b17bb682eb | 440 | void display2PC(){ // display to tera term on PC |
kosaka | 12:a4b17bb682eb | 441 | pc.printf("%8.1f[s]\t%8.5f[V]\t%4d [Hz]\t%d\r\n", _time, il.vdq_ref[0], (int)(vl.w_lpf/(2*PI)+0.5), (int)(vl.w_ref/(2*PI)+0.5)); // print to tera term |
kosaka | 12:a4b17bb682eb | 442 | // pc.printf("%8.1f[s]\t%8.5f[V]\t%4d [deg]\t%8.2f\r\n", _time, _u, (int)(_th/(2*PI)*360.0), _r);//debug[0]*3.3/R_SHUNT); // print to tera term |
kosaka | 12:a4b17bb682eb | 443 | } |
kosaka | 12:a4b17bb682eb | 444 | void timerTS2(){ |
kosaka | 12:a4b17bb682eb | 445 | } |
kosaka | 12:a4b17bb682eb | 446 | void timerTS3(){ |
kosaka | 12:a4b17bb682eb | 447 | data2mbedUSB(); // data2mbedUSB() is called every TS3[s]. |
kosaka | 12:a4b17bb682eb | 448 | } |
kosaka | 12:a4b17bb682eb | 449 | void timerTS4(){ |
kosaka | 12:a4b17bb682eb | 450 | display2PC(); // display to tera term on PC. display2PC() is called every TS4[s]. |
kosaka | 12:a4b17bb682eb | 451 | } |