//////////////////////////////////////////////////////////////////////// // // Movado: Pico-2 motion controller (PID control at 1000Hz) // version 1.3 (July 7, 2009) // // (C) Hideki Kozima (xkozima@myu.ac.jp), subject to GPLv2 // // For technical information // http://www.carebots.org/robot-clayspec.html // // Packet structure // 6-byte sequence: // 1,addr:5,n:2 // 0,inst:3,msbs:4 // 0,hept1:7 // 0,hept2:7 // 0,hept3:7 // 0,hept4:7 // Elements of a packet: // addr: node address (0x00..0x1f) // note: for addr in 0x00..0x07 -> Nenc=16 // for addr in 0x08..0x0f -> Nenc=200 // for addr in 0x10..0x1e -> Nenc=256 // note: addr=0x1f (31) for broadcast to all nodes // n : motor number (0,1,2,3) // inst: instruction (0-7) // msbs: bit7s for hept1-4 // hept: constitute "quad" (data1-4) with msbs // data1 = ((msbs & 0x08)? 0x80: 0) | hept1; // data2 = ((msbs & 0x04)? 0x80: 0) | hept2; // data3 = ((msbs & 0x02)? 0x80: 0) | hept3; // data4 = ((msbs & 0x01)? 0x80: 0) | hept4; // quad (4bytes = 32bits): // quad:32 = (data1<<24) | (data2<<16) | (data3<<8) | data4; // // Fixed-point number // s1616 (fix): integer:16,fraction:16 <-> quad:32 // s0824 : integer:08,fraction:24 (for ERPC8, DTf8) // s2408 : integer:24,fraction:08 (for internal use) // // Command syntax/semantics // inst=0 (move) // move to pos [rad] in CAM // fix pos:32 = quad; // (Constant-Acceleration Mode) // move(addr, n, pos); // inst=1 and quad<0 (movac) // move to pos with acc in CAM // fix acc:32 = -(quad & 0xffff0000); // fix pos:32 = (quad << 16); // movac(addr, n, acc, pos); // inst=2 and quad<0 (movel) // move to pos with vel in CAM // fix vel:32 = -(quad & 0xffff0000); // fix pos:32 = (quad << 16); // movel(addr, n, vel, pos); // inst=1 and quad>=0 (setacc) // set acc [rad/s/s] for move // fix acc:32 = quad; // setacc(addr, n, acc); // inst=2 (setvel) // set max vel [rad/s] for move/mova // fix vel:32 = quad; // setvel(addr, n, vel); // inst=3 (setpos) // do ballistic motion to pos [rad] // fix pos:32 = quad; // setpos(addr, n, pos); // inst=4 (sub) // sub-commands (see below) // uchar jnst:8 = data1; // uchar index:8 = data2; // ushort word:16 = (data3 << 8) | data4; // sub(addr, n, jnst, index, word); // inst=5 (getacc) // return acc [rad/s/s] for move/mova // fix acc:32 = getacc(addr, n); // return(addr, n, acc); // inst=6 (getvel) // return current vel [rad/s] // fix vel:32 = getvel(addr, n); // return(addr, n, vel); // inst=7 (getpos) // return current pos [rad] // fix pos:32 = getpos(addr, n); // return(addr, n, pos); // // Sub-command syntax/semantics // inst jnst index word // 100 00000000 0 set_upper 16bits to F[index][n] // 100 00000000 11111111 set_lower 16bits (call this first) // 100 00000001 <=index> set_word to W[index][n] // 100 01110000 00000000 move_rcs: move to pos in CAM // 100 01110000 00000001 setacc_rcs: set acc for move_rcs // 100 01110000 00000010 setvel_rcs: set vel for move_rcs // 100 01110000 00000011 setpos_rcs: ballistic motion // 100 01111111 00000000 --- set_zero (set zero position) // 100 01111111 00000001 set_mode to MODE // 100 01111111 00000010 set_camod to CAMOD // 100 01111111 00000100 --- do_cen (go to center = (MIN+MAX)/2) // 100 01111111 00000101 do_max (find max with VCAM and EMG) // 100 01111111 00000110 do_min (find min with VCAM and EMG) // 100 01111111 00000111 do_minmax (do_min then do_max) // 100 01111111 00001000 do_minmaxcen (do_minmax, cen, zero) // 100 01111111 00001001 do_minmaxmax (do_minmax, max, zero) // 100 01111111 00001010 do_minmaxmin (do_minmax, min, zero) // 100 01111111 11111111 --- reset Movado // 100 10000000 0 * get_upper 16bits from F[index][n] // 100 10000000 11111111 * get_lower 16bits (call this later) // 100 10000001 <=index> * get_word from W[index][n] // 100 10000010 --- * get_version (ex.0x0102 for "1.2") // 100 11110000 00000101 * getacc_rcs: return acc for rcs // 100 11110000 00000110 * getvel_rcs: return vel for rcs // 100 11110000 00000111 * getpos_rcs: return pos for rcs // 100 11111111 <=inter> get_report (reports pos sequence) // // Response packet // Movado returns a response packet for the following commands. // (Response packets do not change addr and n in the query packets.) // inst quad // 101 * getacc <- current acc // 110 * getvel <- current vel // 111 * getpos <- current pos // Movado returns a response packet for the following sub-commands. // (Response packets do not change addr, n, jnst, index.) // 100 10000000 0 * get_upper: up <- F[index][n]>>16 // 100 10000000 11111111 * get_lower: lo <- lower 16bits // 100 10000001 <=index> * get_word: word <- W[index][n] // 100 10000011 --- * get_version: ver <- VER // Movado returns a sequence of packets for "get_report" sub-command. // (Response packets do not change addr,n for this sub-command.) // 100 11111111 <=inter> get_report: quad <- pos // inter: the interval time [ms] for reporting // count: the number of packets to send back // After having received a "get_report" sub-command, // Movado waits for "move", "mova", or "setpos" command, // which triggers sending back of packets // with the interval of [ms]. // // Indices for F (fix) // XPOS 0x00 goal position (rad) // XVEL 0x01 velocity in CAM (rad/s) // ACAM 0x02 acceleration in CAM (rad/s/s) // VCAM 0x03 max velocity in CAM (rad/s) // PCAM 0x04 goal position in CAM (rad) // RXPOS 0x05 RCS goal position (0x24000000 for 1.5ms) // RXVEL 0x06 RCS velocity in CAM // RACAM 0x07 RCS acceleration in CAM // RVCAM 0x08 RCS max velocity in CAM // RPCAM 0x09 RCS goal position in CAM // PMAR 0x0a safe margin in rad // POS 0x0b current position // VEL 0x0c current velocity (after LPF) // XTOR 0x0d goal torque (EXPERIMENTAL) // TOR 0x0e current torque (EXPERIMENTAL) // ERPC8 0x0f encoder resolution (s0824) // MIN 0x10 min position (set by MMF) // MAX 0x11 max position (set by MMF) // KP 0x12 gain for proportional control // KI 0x13 gain for integral control // KD 0x14 gain for differential control // VMP 0x15 motor power voltage (in V) // VMAG 0x16 voltage magnifier (auto set: 32768 / VMP) // VINT 0x17 accumlator for integral cont // POLD 0x18 old position for velocity calc // LPFA 0x19 alpha factor (for LPF(VEL)) // LPFD 0x1a decay factor (auto set: 1.0 - LPFA) // ACAM0 0x1b acceleration for move commands // VCAM0 0x1c max velocity for move commands // // Indices for W (word) // CAMOD 0x00 submode in CAM // MODE 0x01 operation mode // RCAMOD 0x02 RCS submode in CAM // RMODE 0x03 RCS operation mode // MMFLG 0x04 minmax finder flag // MMEMG 0x05 minmax error magnitude // REP_INT 0x06 report interval (of Dt) // REP_CNT 0x07 report count (tmp) // REP_ACT 0x08 report count in action // REP_TIC 0x09 report interval tick // //////////////////////////////////////////////////////////////////////// // // fundamentals #include "sh7046f.h" // header for sh7046f #include "bios.c" // bios program for Pico-2 // program version #define VER 0x0103 // ex. 0x010f for 1.15 // Pico-2 controls 4 axes in parallel #define NN 4 // number of axes (motors) // packet length #define PACLEN 6 // packet length in bytes // address of this pico2 (5bits) <- to be set by SW5-1 uchar ADDR; // communication address // control cycle (DTf: period, FQf: frequency) #define DTf8 0x00004189 // 0.001s in s0824 format #define FQf 0x03e80000 // 1000Hz in s1616 format #define CMi 767 // CMT count for 4000Hz = 768*8P // fix type typedef int fix; // fixed point s1616 // // main of Movado int main () { void VAR_init(void), // parameter initializer PAC_do(uchar*), // packet interpretor ACT_do(void); // actuation process // check if rebooted from WOVF (watch-dog timer overflow) if (WDT_check()) { LED_set(0x1f); // "help me" on LEDs for (;;); // infinite loop } // init LED, address LED_init(); LED_set(0x00); SWT_init(); ADDR = SWT_get(); // init variable block VAR_init(); // initialization for PWM/RCS/ENC PWM_init(); RCS_init(); ENC_init(); // SCI setup with address // PACLEN=6 (1-addr5-n2, 0-inst3-msbs4, // 0-hept7, 0-hept7, 0-hept7, 0-hept7 ) SCI_init(ADDR, PACLEN, (void *) PAC_do); // CMT setup (cselB2=0x00, CMi=767) // 1000Hz (control) -> 4000Hz (strobe) -> P/768 -> CMi=767 CMT0_init(0x00, CMi, ACT_do); // SEN setup (motor current sensors) // SEN_init(); // WDT setup (csel=0x04: 2.6667ms=375Hz) WDT_init(0x04); // start event-loop set_imask(0); for (;;) { } return 0; } //////////////////////////////////////////////////////////////////////// // // FIX: fixed point calculation (s1616) // xxxx:xxxx:xxxx:xxxx.xxxx:xxxx:xxxx:xxxx (32bits) // | | | | // 2^15 2^0 2^-1 2^-16 fix FIX_safe_s3216 (long long f64) { if ((f64 & 0xffff80000000LL) == 0xffff80000000LL) return f64 & 0xffffffffLL; // minus (normal) else if (f64 & 0x800000000000LL) return 0x80000000; // minus (out of range) else if (f64 & 0x7fff80000000LL) return 0x7fffffff; // plus (out of range) else return f64; // plus (normal) } fix FIX_safe_s4816 (long long f64) { if ((f64 & 0xffffffff80000000LL) == 0xffffffff80000000LL) return f64 & 0xffffffffLL; // minus (normal) else if (f64 & 0x8000000000000000LL) return 0x80000000; // minus (out of range) else if (f64 & 0x7fffffff80000000LL) return 0x7fffffff; // plus (out of range) else return f64; // plus (normal) } fix FIX_mul (fix f1, fix f2) { long long f64; f64 = (((long long) f1) * ((long long) f2)) >> 16; return FIX_safe_s3216(f64); } fix FIX_div (fix f1, fix f2) { long long f64; f64 = (((long long) f1) << 16) / ((long long) f2); return FIX_safe_s4816(f64); } fix FIX_add (fix f1, fix f2) { long long f64; f64 = ((long long) f1) + ((long long) f2); return FIX_safe_s4816(f64); } fix FIX_sub (fix f1, fix f2) { long long f64; f64 = ((long long) f1) - ((long long) f2); return FIX_safe_s4816(f64); } //////////////////////////////////////////////////////////////////////// // // var: parameter blocks // // fix quads (32bit fixed point s1616, packed for 4 axes) #define NF 29 // number of fix quads fix F[NF][NN]; #define FO_MOT 0 // offset for DC-motor quads #define FO_RCS 5 // offset for RC-servo quads // ( XPOS, XVEL, ACAM, VCAM, PCAM, // RXPOS,RXVEL,RACAM,RVCAM,RPCAM ) #define XPOS 0x00 // goal position #define XVEL 0x01 // velocity in CAM (in rad/s) #define ACAM 0x02 // acceleration in CAM #define VCAM 0x03 // max velocity in CAM #define PCAM 0x04 // goal position in CAM #define RXPOS 0x05 // RCS goal position #define RXVEL 0x06 // RCS velocity in CAM (in rad/s) #define RACAM 0x07 // RCS acceleration in CAM #define RVCAM 0x08 // RCS max velocity in CAM #define RPCAM 0x09 // RCS goal position in CAM #define RXPOS_DEF 0x24000000 // default (center) for RXPOS #define RACAM_DEF 0x04000000 // default RACAM (approx 30deg/s/s) #define RVCAM_DEF 0x04000000 // RVCAM default (approx 30deg/s) #define PMAR 0x0a // margin for MIN/MAX (in rad) #define PMAR_DEF 0x00010000 // default PMAR (1.0rad) #define POS 0x0b // current position #define VEL 0x0c // current velocity (after LPF) #define XTOR 0x0d // goal torque (EXPERIMENTAL) #define TOR 0x0e // current torque (EXPERIMENTAL) #define ERPC8 0x0f // encoder resolution (s0824) #define ERPC8_16 0x001921FB // rad/count (for Nenc=16) #define ERPC8_200 0x000202B8 // rad/count (for Nenc=200) #define ERPC8_256 0x00019220 // rad/count (for Nenc=256) #define MIN 0x10 // min position (set by MMF) #define MAX 0x11 // max position (set by MMF) #define MIN_DEF 0x80000000 // default MIN position (MININT) #define MAX_DEF 0x7fffffff // default MAX position (MAXINT) #define KP 0x12 // gain for proportional control #define KI 0x13 // gain for integral control #define KD 0x14 // gain for differential control #define KP_DEF 0x00020000 // default KP = 2.00000 #define KI_DEF 0x00280000 // default KI = 40.00000 #define KD_DEF 0x00000ccd // default KD = 0.05000 #define VMP 0x15 // motor power voltage (in V) #define VMP_DEF 0x000c0000 // VMP default (for 12.0V) #define VMAG 0x16 // voltage magnifier (<= VAR_init) // VMAG = 32768 / VMP #define VINT 0x17 // accumlator for integral cont #define POLD 0x18 // old position for velocity calc #define LPFA 0x19 // alpha factor (for LPF(VEL)) #define LPFA_16 0x00001000 // alpha = 1/16 #define LPFA_200 0x00004000 // alpha = 1/4 #define LPFA_256 0x00004000 // alpha = 1/4 #define LPFD 0x1a // decay factor (<= VAR_init) // LPFD = 1.0 - LPFA #define ACAM0 0x1b // acceleration for move commands #define ACAM0_DEF 0x00d406ae // default ACAM0 = 0.5*(2*Pi*67.49) #define VCAM0 0x1c // max velocity for move commands #define VCAM0_DEF 0x00d406ae // default VCAM0 = 0.5*(2*Pi*67.49) // // word block (16bit; unsigned short) #define NW 10 // number of word quads ushort W[NW][NN]; #define WO_MOT 0 // offset for DC-motor quads #define WO_RCS 2 // offset for RC-servo quads // ( CAMOD, MODE, // RCAMOD, RMODE ) #define CAMOD 0x00 // submode in CAM #define MODE 0x01 // operation mode #define RCAMOD 0x02 // RCS submode in CAM #define RMODE 0x03 // RCS operation mode #define CAMOD_NO 0x0000 // no CAM operation #define CAMOD_GO 0xffff // start CAM with ACAM (move/ac/el) #define CAMOD_FF 0x000f // goal ahead, moving forward #define CAMOD_FB 0x00f0 // goal ahead, moving backward #define CAMOD_BF 0x0f00 // goal back, moving forward #define CAMOD_BB 0xf000 // goal back, moving backward #define MODE_NOP 0 // no operation #define MODE_POS 1 // position control #define MODE_VEL 2 // velocity control (reserved) #define MODE_TOR 3 // torque control (reserved) #define MMFLG 0x04 // minmax finder flag #define MM_NOP 0x0000 // not in minmax mode #define MM_MIN 0xf000 // mining flag for MMF #define MM_MAX 0x000f // maxing flag for MMF #define MM_GO_CEN 0x03c0 // go-to-cen flag for MMF #define MM_GO_MIN 0x0c00 // go-to-min flag for MMF #define MM_GO_MAX 0x0030 // go-to-max flag for MMF #define MMEMG 0x05 // minmax error magnitude #define REP_INT 0x06 // report interval (of Dt) #define REP_CNT 0x07 // report count (tmp) #define REP_ACT 0x08 // report count in action #define REP_TIC 0x09 // report interval tick // // initialize variables void VAR_init () { int n; fix erpc8, lpfa; // clear F/W blocks for (n = 0; n < NN; n++) { int index; // clear fix block for (index = 0; index < NF; index++) F[index][n] = 0; // clear word block for (index = 0; index < NW; index++) W[index][n] = 0; } // set encoder resolution and lpf parameters (according to ADDR) if (ADDR & 0x10) { // 0x10..0x1e -> 256ppr/1000Hz erpc8 = ERPC8_256; lpfa = LPFA_256; } else if (ADDR & 0x08) { // 0x08..0x0f -> 200ppr/1000Hz erpc8 = ERPC8_200; lpfa = LPFA_200; } else { // 0x00..0x07 -> 16ppr/100Hz erpc8 = ERPC8_16; lpfa = LPFA_16; } // init F/W blocks (for non-zero init value) for (n = 0; n < NN; n++) { // F F[KP][n] = KP_DEF; F[KI][n] = KI_DEF; F[KD][n] = KD_DEF; F[ACAM0][n] = ACAM0_DEF; F[VCAM0][n] = VCAM0_DEF; F[MIN][n] = MIN_DEF; F[MAX][n] = MAX_DEF; F[PMAR][n] = PMAR_DEF; F[ERPC8][n] = erpc8; F[LPFA][n] = lpfa; F[LPFD][n] = FIX_sub(0x00010000, lpfa); F[VMP][n] = VMP_DEF; F[VMAG][n] = FIX_div(0x7fffffff, VMP_DEF); F[RXPOS][n] = RXPOS_DEF; F[RACAM][n] = RACAM_DEF; F[RVCAM][n] = RVCAM_DEF; // W W[MODE][n] = MODE_POS; W[RMODE][n] = MODE_POS; } } //////////////////////////////////////////////////////////////////////// // // ACT: actuation process void ACT_do (void) { fix ACT_enc(uchar); // encoder read in rad void PAC_report(int, fix), // position report for "report" MMF_do(uchar), // minmax finder module CAM_do(uchar, uchar, uchar), // trapezoid control module PID_do(uchar); // PID control module static uchar n = 0; // axis number {0,1,2,3} fix pos, vel; // current pos, vel (tmp) // update POS and VEL pos = ACT_enc(n); F[POS][n] = pos; vel = FIX_mul(FIX_sub(pos, F[POLD][n]), FQf); F[POLD][n] = pos; // low-pass filter for velocity // (needed for stabilizing the differentiation) // LPF = (1.0 - LPFA) * VEL + LPFA * VEL F[VEL][n] = FIX_add(FIX_mul(F[LPFD][n], F[VEL][n]), FIX_mul(F[LPFA][n], vel) ); // report (if needed) if (W[REP_ACT][n]) { if (W[REP_TIC][n] == 0) { PAC_report(n, pos); // report POS W[REP_ACT][n]--; // decrement counter W[REP_TIC][n] = W[REP_INT][n]; // reset interval } W[REP_TIC][n]--; } // control the n-th motor switch (W[MODE][n]) { case MODE_NOP: // no-operator (and raw mode) break; case MODE_POS: // position control // if minmax then if (W[MMFLG][n]) MMF_do(n); // if CAM then if (W[CAMOD][n]) CAM_do(n, FO_MOT, WO_MOT); // PID control PID_do(n); break; case MODE_VEL: // velocity control (experimental) PWM_set(n, FIX_mul(F[XVEL][n], F[VMAG][n]) >> 16); break; case MODE_TOR: // torque control (experimental) break; } // control the n-th RCS switch (W[RMODE][n]) { case MODE_NOP: // no-operator (and raw mode) break; case MODE_POS: // position control // if cam then if (W[RCAMOD][n]) CAM_do(n, FO_RCS, WO_RCS); // actuate the RCS RCS_set(n, F[RXPOS][n] >> 16); break; } // update counters n++; if (n == NN) { ushort tor0, tor1, tor2, tor3; // reset/update the counters n = 0; // sense torque/current //SEN_gets(&tor0, &tor1, &tor2, &tor3); //F[TOR][0] = tor0 << 16; //F[TOR][1] = tor1 << 16; //F[TOR][2] = tor2 << 16; //F[TOR][3] = tor3 << 16; // clear watch-dog timer WDT_reset(); } } // fix ACT_enc () // returns encoder position in rad (radian) // in fixed point s1616 format; fix ACT_enc (uchar n) { // ENC_get(n): Encoder count in 24bit integer // EPRC8[n] : Encoder resolution (rad/count; s0824) // --------------- s1616 ----------------- // ----- s2408 ----- -- s0824 -- return FIX_mul((ENC_get(n) << 8), F[ERPC8][n]); } //////////////////////////////////////////////////////////////////////// // // PID: controller void PID_do (uchar n) { fix xposf, // goal position (after limitter) erf, // error (s1616) vpf, // output of proportional control vdf, // output of differential control vof; // final output // limit range of motion in [MIN, MAX] xposf = F[XPOS][n]; if (xposf < F[MIN][n]) xposf = F[MIN][n]; if (xposf > F[MAX][n]) xposf = F[MAX][n]; // calculate error // erf = xposf - POSf erf = FIX_sub(xposf, F[POS][n]); // proportional control // vpf = KPf * erf vpf = FIX_mul(F[KP][n], erf); // integral control // VINT += KIf * DTf * ERf F[VINT][n] = FIX_add(F[VINT][n], // --- s2408 --- -s0824- FIX_mul(FIX_mul((F[KI][n] >> 8), DTf8), erf)); if (F[VINT][n] > F[VMP][n]) F[VINT][n] = F[VMP][n]; if (F[VINT][n] < -F[VMP][n]) F[VINT][n] = -F[VMP][n]; // differential control (position-based version) // vdf = KDf * (POLD - POS) / DTf --- (DTf: DeltaT=0.001s) // = KDf * (POLD - POS) * FQf --- (FQf: Freq=1/DeltaT) // = - KDf * VEL vdf = -FIX_mul(F[KD][n], F[VEL][n]); // sum up the outputs // vof = (vpf + VINT + vdf); vof = FIX_add(vpf, FIX_add(F[VINT][n], vdf)); if (vof > F[VMP][n]) vof = F[VMP][n]; if (vof < -F[VMP][n]) vof = -F[VMP][n]; // power meter for debugging if (vof >= 0) LED_op(0x0f, (vof >> 16) & 0x0f); else LED_op(0x0f, ((-vof) >> 16) & 0x0f); // PWM output (s1616 -> short) PWM_set(n, FIX_mul(vof, F[VMAG][n]) >> 16); } //////////////////////////////////////////////////////////////////////// // // CAM: constant acceleration mode (MOT/RCS shared version) // for MOT (motor), provide FO_MOT and WO_MOT for fo and wo; // for RCS (servo), provide FO_RCS and WO_RCS for fo and wo; // (these values will give bias for var-block index) void CAM_do (uchar n, uchar fo, uchar wo) { fix acam, vcam, xvel; acam = FIX_mul(FIX_mul(F[ACAM+fo][n] >> 8, DTf8) >> 8, DTf8); vcam = FIX_mul(F[VCAM+fo][n] >> 8, DTf8); xvel = F[XVEL+fo][n]; // ACAM0 should be more than 15.26 (=1000*1000/65536) // (in any case, acam should be more than 0x00000001) if (acam <= 0) acam = 0x00000001; // CAM operation if (xvel > acam) { // I'm moving forward if (F[XPOS+fo][n] > F[PCAM+fo][n]) { // and the goal is behind! W[CAMOD+wo][n] = CAMOD_BF; // decrease speed, anyway xvel = FIX_sub(xvel, acam); if (xvel < -vcam) xvel = -vcam; } else { int vabsf, pcrif; // the goal is ahead // (or I'm passing above the goal forward) W[CAMOD+wo][n] = CAMOD_FF; // compute the critical point for braking vabsf = xvel; pcrif = FIX_sub(F[PCAM+fo][n], FIX_div(FIX_mul(vabsf, FIX_add(vabsf, acam)), FIX_add(acam, acam) )); // If I have passed (or am passing) the point, // then decrease speed // otherwise increase speed if (F[XPOS+fo][n] >= FIX_sub(pcrif, vabsf)) { xvel = FIX_sub(xvel, acam); if (xvel < -vcam) xvel = -vcam; } else { xvel = FIX_add(xvel, acam); if (xvel > vcam) xvel = vcam; } } } else if (xvel < -acam) { // I'm moving backward if (F[XPOS+fo][n] < F[PCAM+fo][n]) { // and the goal is ahead! W[CAMOD+wo][n] = CAMOD_FB; // increase speed, anyway xvel = FIX_add(xvel, acam); if (xvel > vcam) xvel = vcam; } else { int vabsf, pcrif; // the goal is behind // (or I'm passing above the goal backward) W[CAMOD+wo][n] = CAMOD_BB; // compute the critical point for braking vabsf = -xvel; pcrif = FIX_add(F[PCAM+fo][n], FIX_div(FIX_mul(vabsf, FIX_add(vabsf, acam)), FIX_add(acam, acam) )); // If I have passed (or am passing) the point, // then increase speed // otherwise decrease speed if (F[XPOS+fo][n] <= FIX_add(pcrif, vabsf)) { xvel = FIX_add(xvel, acam); if (xvel > vcam) xvel = vcam; } else { xvel = FIX_sub(xvel, acam); if (xvel < -vcam) xvel = -vcam; } } } else { // I'm almost stand still (VNOW is almost zero) if (F[XPOS+fo][n] < FIX_sub(F[PCAM+fo][n], acam)) { // the goal is ahead W[CAMOD+wo][n] = CAMOD_FF; // increase speed to move forward xvel = FIX_add(xvel, acam); if (xvel > vcam) xvel = vcam; } else if (FIX_add(F[PCAM+fo][n], acam) < F[XPOS+fo][n]) { // the goal is behind W[CAMOD+wo][n] = CAMOD_BB; // decrease speed to move backward xvel = FIX_sub(xvel, acam); if (xvel < -vcam) xvel = -vcam; } else { // I'm almost at the goal // (force to stop moving at the goal) F[XPOS+fo][n] = F[PCAM+fo][n]; xvel = 0; W[CAMOD+wo][n] = CAMOD_NO; return; } } // finally update the goal position and goal velocity F[XPOS+fo][n] = FIX_add(F[XPOS+fo][n], xvel); F[XVEL+fo][n] = xvel; } //////////////////////////////////////////////////////////////////////// // // MMF: minmax finder void MMF_do (uchar n) { fix mvel, merr; // indicate MMF-ing LED_op(0x10, 0x10); // minmax velocity (VCAM0/FQf) // (for different velocity, change setvel before do_min/max/etc) mvel = FIX_mul(F[VCAM0][n] >> 8, DTf8); // error for collision detection // (ushort MMEMG [rad]) merr = FIX_mul(mvel, W[MMEMG][n] << 16); if (W[MMFLG][n] & MM_MIN) { // find min end // if in CAM, stop it (otherwise XPOS would get stuck) W[CAMOD][n] = CAMOD_NO; // check collision with min end if (-FIX_sub(F[XPOS][n], F[POS][n]) > merr) { // collision!! // set MIN at POS+margin F[MIN][n] = FIX_add(F[POS][n], F[PMAR][n]); // stop motor at MIN F[XPOS][n] = F[MIN][n]; // clear MM_MIN flag W[MMFLG][n] &= ~(MM_MIN); LED_op(0x10, 0x00); } else { // no collision, so go backward F[XPOS][n] = FIX_sub(F[XPOS][n], mvel); } } else if (W[MMFLG][n] & MM_MAX) { // find max end // if in CAM, stop it (otherwise XPOS would get stuck) W[CAMOD][n] = CAMOD_NO; // check collision with max end if (FIX_sub(F[XPOS][n], F[POS][n]) > merr) { // collision!! // set MIN at POS-margin F[MAX][n] = FIX_sub(F[POS][n], F[PMAR][n]); // stop motor at MAX F[XPOS][n] = F[MAX][n]; // clear MM_MAX flag W[MMFLG][n] &= ~(MM_MAX); LED_op(0x10, 0x00); } else { // no collision, so go forward F[XPOS][n] = FIX_add(F[XPOS][n], mvel); } } else if (W[MMFLG][n] & (MM_GO_CEN | MM_GO_MAX | MM_GO_MIN)) { fix xposf, diff; // set goal position if (W[MMFLG][n] & MM_GO_MAX) xposf = F[MAX][n]; else if (W[MMFLG][n] & MM_GO_MIN) xposf = F[MIN][n]; else // center xposf = FIX_mul(FIX_add(F[MIN][n], F[MAX][n]), 0x00008000); // forward/backword/terminte diff = FIX_sub(xposf, F[XPOS][n]); if (diff > mvel) { // move forward F[XPOS][n] = FIX_add(F[XPOS][n], mvel); } else if (diff < -mvel) { // move backward F[XPOS][n] = FIX_sub(F[XPOS][n], mvel); } else { // terminate fix offset; // save offset offset = xposf; // zero (make current position "zero") F[XPOS][n] = 0; F[POS][n] = 0; F[POLD][n] = 0; F[VEL][n] = 0; F[VINT][n] = 0; ENC_set(n, 0); // move MIN/MAX F[MIN][n] = FIX_sub(F[MIN][n], offset); F[MAX][n] = FIX_sub(F[MAX][n], offset); // clear the flag W[MMFLG][n] = MM_NOP; LED_op(0x10, 0x00); } } } //////////////////////////////////////////////////////////////////////// // // PAC: packet translator void PAC_to_NIQ (uchar *packet, ushort *n, ushort *inst, uint *quad) { uchar msbs; uchar data1, data2, data3, data4; // motor number (N) *n = packet[0] & 0x03; // inst (I) from upper 3 bits of packet[1] // (lower 4 bits are msbs for arg(g1-4)) *inst = packet[1] >> 4; msbs = packet[1] & 0x0f; // quad (Q) = data1..4 data1 = packet[2] | ((msbs & 0x08)? 0x80: 0); data2 = packet[3] | ((msbs & 0x04)? 0x80: 0); data3 = packet[4] | ((msbs & 0x02)? 0x80: 0); data4 = packet[5] | ((msbs & 0x01)? 0x80: 0); *quad = (data1 << 24) | (data2 << 16) | (data3 << 8) | data4; } void PAC_from_NIQ (uchar *packet, ushort n, ushort inst, uint quad) { packet[0] = (ADDR << 2) | (n & 0x03); packet[1] = (inst & 0x07) << 4 | ((quad & 0x80000000)? 0x08: 0) | ((quad & 0x00800000)? 0x04: 0) | ((quad & 0x00008000)? 0x02: 0) | ((quad & 0x00000080)? 0x01: 0); packet[2] = (quad & 0x7f000000) >> 24; packet[3] = (quad & 0x007f0000) >> 16; packet[4] = (quad & 0x00007f00) >> 8; packet[5] = (quad & 0x0000007f); } // PAC_do: packet interpreter ushort Low16; // lower 16bits for fix void PAC_do (uchar *packet) { ushort n, inst; uint quad; uchar r_packet[PACLEN]; void PAC_report_trigger(int); PAC_to_NIQ(packet, &n, &inst, &quad); if (inst == 0) { // move: move to pos in CAM with preset acc F[ACAM][n] = F[ACAM0][n]; F[VCAM][n] = F[VCAM0][n]; F[PCAM][n] = (fix) quad; W[CAMOD][n] = CAMOD_GO; // if needed, trigger reporting if (W[REP_CNT][n]) PAC_report_trigger(n); } else if (inst == 1) { fix arg; arg = (fix) quad; if (arg < 0) { // movac: move to pos in CAM with -acc // (arg = acc:n16,pos:s16) F[ACAM][n] = - (arg & 0xffff0000); F[VCAM][n] = F[VCAM0][n]; F[PCAM][n] = (fix) (arg << 16); W[CAMOD][n] = CAMOD_GO; // if needed, trigger reporting if (W[REP_CNT][n]) PAC_report_trigger(n); } else { // setacc: acceleration for move F[ACAM0][n] = arg; } } else if (inst == 2) { fix arg; arg = (fix) quad; if (arg < 0) { // movel: move to pos in CAM with -vel // (arg = vel:n16,pos:s16) F[ACAM][n] = F[ACAM0][n]; F[VCAM][n] = - (arg & 0xffff0000); F[PCAM][n] = (fix) (arg << 16); W[CAMOD][n] = CAMOD_GO; // if needed, trigger reporting if (W[REP_CNT][n]) PAC_report_trigger(n); } else { // setvel: velocity for move F[VCAM0][n] = arg; } } else if (inst == 3) { // setpos: ballistic motion // cancel CAM W[CAMOD][n] = CAMOD_NO; // set to XPOS F[XPOS][n] = (fix) quad; // if needed, trigger reporting if (W[REP_CNT][n]) PAC_report_trigger(n); } else if (inst == 5) { // getacc: current acceleration quad = (uint) F[ACAM][n]; PAC_from_NIQ(r_packet, n, inst, quad); SCI_transmit(r_packet); } else if (inst == 6) { // getvel: current velocity quad = (uint) F[VEL][n]; PAC_from_NIQ(r_packet, n, inst, quad); SCI_transmit(r_packet); } else if (inst == 7) { // getpos: current position quad = (uint) F[POS][n]; PAC_from_NIQ(r_packet, n, inst, quad); SCI_transmit(r_packet); } else if (inst == 4) { ushort jnst, index, word; // sub-command jnst = quad >> 24; index = (quad & 0x00ff0000) >> 16; word = quad & 0x0000ffff; if (jnst == 0x00) { // set_fix if (index == 0xff) Low16 = word; else if (index < NF) { F[index][n] = (fix) ((word << 16) | Low16); // auto set variables (VMAG, LPFD) if (index == VMP) { // VMAG = 32768 / VMP; F[VMAG][n] = FIX_div(0x7fffffff, F[VMP][n]); } else if (index == LPFA) { // LPFD = 1.0 - LPFA F[LPFD][n] = FIX_sub(0x00010000, F[LPFA][n]); } } } else if (jnst == 0x01) { // set_word if (index < NW) W[index][n] = word; } else if (jnst == 0x70) { switch (index) { case 0x00: // move_rcs F[RPCAM][n] = (fix) (word << 16); W[RCAMOD][n] = CAMOD_GO; break; case 0x01: // setacc_rcs F[RACAM][n] = (fix) (word << 16); break; case 0x02: // setvel_rcs F[RVCAM][n] = (fix) (word << 16); break; case 0x03: // setpos_rcs W[RCAMOD][n] = CAMOD_NO; F[RXPOS][n] = (fix) (word << 16); break; } } else if (jnst == 0x7f) { switch (index) { case 0x00: // set_zero W[CAMOD][n] = CAMOD_NO; F[XPOS][n] = 0; F[POS][n] = 0; F[POLD][n] = 0; F[VEL][n] = 0; F[VINT][n] = 0; ENC_set(n, 0); break; case 0x01: // set_mode W[MODE][n] = word; break; case 0x02: // set_camod W[CAMOD][n] = word; break; case 0x04: // go_cen W[MMFLG][n] = MM_GO_CEN; break; case 0x05: // do_max W[MMFLG][n] = MM_MAX; W[MMEMG][n] = word; F[MAX][n] = MAX_DEF; break; case 0x06: // do_min W[MMFLG][n] = MM_MIN; W[MMEMG][n] = word; F[MIN][n] = MIN_DEF; break; case 0x07: // do_minmax W[MMFLG][n] = (MM_MIN | MM_MAX); W[MMEMG][n] = word; F[MIN][n] = MIN_DEF; F[MAX][n] = MAX_DEF; break; case 0x08: // do_minmaxcen W[MMFLG][n] = (MM_MIN | MM_MAX | MM_GO_CEN); W[MMEMG][n] = word; F[MIN][n] = MIN_DEF; F[MAX][n] = MAX_DEF; break; case 0x09: // do_minmaxmax W[MMFLG][n] = (MM_MIN | MM_MAX | MM_GO_MAX); W[MMEMG][n] = word; F[MIN][n] = MIN_DEF; F[MAX][n] = MAX_DEF; break; case 0x0a: // do_minmaxmin W[MMFLG][n] = (MM_MIN | MM_MAX | MM_GO_MIN); W[MMEMG][n] = word; F[MIN][n] = MIN_DEF; F[MAX][n] = MAX_DEF; break; case 0xff: // reset VAR_init(); for (n = 0; n < NN; n++) ENC_set(n, 0); break; } } else if (jnst == 0x80) { // get_fix F[index][n] if (index == 0xff) quad = (quad & 0xffff0000) | Low16; else if (index < NF) { uint tmp; tmp = (uint) F[index][n]; Low16 = tmp & 0xffff; quad = (quad & 0xffff0000) | (tmp >> 16); } else quad = 0xffffffff; // to indicate error PAC_from_NIQ(r_packet, n, inst, quad); SCI_transmit(r_packet); } else if (jnst == 0x81) { // get_word W[index][n] if (index < NW) quad = (quad & 0xffff0000) | W[index][n]; else quad = 0xffffffff; // to indicate error PAC_from_NIQ(r_packet, n, inst, quad); SCI_transmit(r_packet); } else if (jnst == 0x82) { // get_version quad = (quad & 0xffff0000) | VER; PAC_from_NIQ(r_packet, n, inst, quad); SCI_transmit(r_packet); } else if (jnst == 0xf0) { switch (index) { case 0x05: // getacc_rcs word = (ushort) (F[RACAM][n] >> 16); quad = (quad & 0xffff0000) | word; break; case 0x06: // getvel_rcs word = (ushort) (F[RVCAM][n] >> 16); quad = (quad & 0xffff0000) | word; break; case 0x07: // getpos_rcs if (W[RCAMOD][n]) word = (ushort) (F[RPCAM][n] >> 16); else word = (ushort) (F[RXPOS][n] >> 16); quad = (quad & 0xffff0000) | word; break; default: quad = 0xffffffff; } PAC_from_NIQ(r_packet, n, inst, quad); SCI_transmit(r_packet); } else if (jnst == 0xff) { // get_report W[REP_INT][n] = index; // interval (# of control loops) W[REP_CNT][n] = word; // report counts } else if (jnst >= 0x80) { // unknown query (return "error" packet) quad = 0xffffffff; PAC_from_NIQ(r_packet, n, inst, quad); SCI_transmit(r_packet); } } } void PAC_report_trigger (int n) { W[REP_ACT][n] = W[REP_CNT][n]; W[REP_CNT][n] = 0; W[REP_TIC][n] = 0; } void PAC_report (int n, fix pos) { uchar r_packet[PACLEN]; PAC_from_NIQ(r_packet, n, 7, pos); // report packet (I=7) SCI_transmit(r_packet); } ////////////////////////////////////////////////////////////////////////