From a2ac6e2c740b0443e5ef1895bba1dde876fc9093 Mon Sep 17 00:00:00 2001 From: eddyem Date: Sat, 4 May 2019 18:36:02 +0300 Subject: [PATCH] Library ready --- MMPP_lib/Readme.md | 176 ++++++++++++++++++++++++++++ MMPP_lib/common.h | 9 +- MMPP_lib/examples/CMakeLists.txt | 5 +- MMPP_lib/examples/move.c | 4 - MMPP_lib/examples/tm.c | 191 ++++++++++++++++++++++++++++--- MMPP_lib/examples/tmcmdlnopts.c | 12 ++ MMPP_lib/examples/tmcmdlnopts.h | 8 ++ MMPP_lib/examples/wheels.c | 20 ++-- MMPP_lib/libmmpp.c | 95 ++++++++++++++- MMPP_lib/libmmpp.h | 23 +++- MMPP_lib/mmpp.pc.in | 2 +- 11 files changed, 504 insertions(+), 41 deletions(-) create mode 100644 MMPP_lib/Readme.md diff --git a/MMPP_lib/Readme.md b/MMPP_lib/Readme.md new file mode 100644 index 0000000..6926f22 --- /dev/null +++ b/MMPP_lib/Readme.md @@ -0,0 +1,176 @@ +MMPPlib +================== + +This is a library for MMPP (multimode photometer-polarimeter) easy management. Be careful using it in multi-threading applications because almost all its functions are **not thread-safe**! + +The library functions can be divided onto two parts: turrets management and motors management. The original tools for this were [HSFW_management](https://github.com/eddyem/eddys_snippets/tree/master/HSFW_management) and [MMPP_control](https://github.com/eddyem/mmpp/tree/master/MMPP_control). Now you can easily write your own C/C++ application using this library. The two examples will facilitate this job. + +## HSFW turrets management + +The example files `wheels.c`, `wheelscmdlnopts.c` and `wheelscmdlnopts.h` demonstrate how you can use base functions of turrets' management. + +Each wheel describes by appropriate structure: + + typedef struct{ + int fd; // file descriptor of device + char *serial; // serial number + char ID; // identificator + char name[9]; // wheel name + int maxpos; // max position + } wheel_descr; + +The first thing you need to do when start working with turrets is to discovery them. This is done by function + + int find_wheels(wheel_descr **wheels, wheel_error *err); + +This function returns amount of wheels found (or 0 if none). The `wheels` parameter is array of `wheel_descr`, one element for each wheel. The second, `err`, is error state (if wheels not found): + + typedef enum{ + WHERR_ALLOK // no errors + ,WHERR_UDEV // udev error + ,WHERR_CANTOPEN // can't open file device + } wheel_error; + +You can got `WHERR_CANTOPEN` error if your rights are not sufficient to open the device file. + +To properly delete memory and close devices call function + + void del_wheels(wheel_descr *w, int N); + +which arguments are: `w` -- array of descriptors, and `N` -- their amount in array. + +After device is opened you can work with it using functions: + + wheel_status wheel_getpos(wheel_descr *w); + bool wheel_clear_err(wheel_descr *w); + bool move_wheel(wheel_descr *w, int filter_pos); + bool wheel_home(wheel_descr *w); + +The `wheel_getpos` returns not only a position but also an error code if something went wrong: + + typedef enum{ + WHEEL_MOVING = -2 // still moving + ,WHEEL_ERROR = -1 // communication error + ,WHEEL_POSERR = 0 // wrong wheel position + ,WHEEL_POS1, WHEEL_POS2, WHEEL_POS3, WHEEL_POS4, WHEEL_POS5, WHEEL_POS6 + ,WHEEL_POS7, WHEEL_POS8, WHEEL_POS9, WHEEL_POS10 + } wheel_status; + +So its positive values are wheel position and negative are error codes. + +Other three functions returns boolean value: `false` in case of error. +You can run `wheel_clear_err` to reset error state of device. +The `move_wheel` will rotate given wheel into position `filter_pos` (if this position isn't available or in case of other error it will return `false`). +Sometimes you may want to home wheel manually, then call `wheel_home`. + +## MMPP motors management + +These are the base functions you need to manage MMPP motors: + + int mmpp_tryopen(char *dev, int spd); + void mmpp_close(); + int mot_handshake(); + bool mot_getstatus(int Nmcu, motor_state *s); + bool get_rst(int N, bool clear); + bool get_alive(int N); + int stop_all(); + int get_temp(double *t1, double *t2); + int init_motors(); + int mot_wait(); + ttysend_status tty_sendcmd(char *cmd); + bool get_ADC(int N, ADC_state *s); + ttysend_status movemotor(int mcu, int motnum, int steps, int absmove); + void reset_MCU(int N); + +There is also three special functions, don't use them without a real need: + + char *tty_get(); + int tty_send(char *cmd); + char* tty_sendraw(char *string); + +Here are all base types used in those functions. The motor state: + + typedef enum{ + STP_UNKNOWN, // wrong state + STP_SLEEP, // don't moving + STP_ACCEL, // start moving with acceleration + STP_MOVE, // moving with constant speed + STP_MVSLOW, // moving with slowest constant speed + STP_DECEL, // moving with deceleration + STP_STOP, // stop motor right now (by demand) + STP_STOPZERO, // stop motor and zero its position (on end-switch) + STP_MOVE0, // move towards 0 endswitch (negative direction) + STP_MOVE1 // move towards 1 endswitch (positive direction) + } stp_state; + +Base answers on `tty_sendcmd`: + + typedef enum{ + SEND_ERR, // communication error + SEND_ALLOK, // no errors + SEND_ACTIVE, // motor is still moving + SEND_TOOBIG, // amount of steps too big + SEND_ZEROMOVE, // give 0 steps to move + SEND_ESWITCH, // staying on end-switch & try to move further + SEND_NEEDINIT, // motor needs initialisation + SEND_NEGATMOVE, // try to move to negative position + SEND_OTHER, // unknown state + } ttysend_status; + +The end- (proximity or limit) switches' state: + + typedef enum{ + ESW_ERROR, // wrong value + ESW_RELEASED, // opened + ESW_HALL, // hall sensor + ESW_BUTTON // user button + } ESW_status; + +If reset state detected, you can know the reason of reset: + + typedef enum{ + RESET_NONE, // no sw/wd reset occured + RESET_SW, // software reset have been before last status call + RESET_WDG // watchdog reset -//- + } reset_status; + +The state of each MCU: + + typedef struct{ + reset_status rst; // reset status (was MCU reseted by watchdog or software?) + stp_state state[2]; // status of stepper motor + int stepsleft[2]; // steps left to reach target position + int curpos[2]; // current position (negative for non-initialized state or error) + ESW_status ESW_status[2][2];// End-switches status, [i][j], i - motor, j - esw 0 or 1 + } motor_state; + +And ADC values: + + typedef struct{ + double Vdd; // value of Vdd (+3.3V) + double Imot; // motors' current (Amperes) + double Vmot; // motors' voltage (+12V) + } ADC_state; + +So, before start working with the device you need to open it with `mmpp_tryopen`, its parameter `dev` is device name, and `spd` is communication speed (9600 by default). This function returns `0` if all OK. +Opened device at the end of work should be closed by `mmpp_close`. + +Run `mot_handshake` if you doubt that all OK with MCUs, this function will return `0` if at least one MCU found and `1` if none. After running this function you can run `get_alive` to determine which MCU have connection problems (`get_alive` returns `false` if given MCU didn't answer on a handshake). + +The `mot_getstatus` returns `false` if cannot communicate with given MCU. Otherwise it will fill the `s` parameter. After running of this function you can determinate reset status non only by `rst` parameter, but also by function `get_rst` (its parameter `clear` should be set to `true` if you want to clear global reset status). + +To stop all movement, run `stop_all`. + +Just after powering the device all motors are in uninitialized condition. To initialize them you need to move all onto zero's end-switch using `init_motors`. This function is **blocking**!!! So if you want non-blocking initialization write it yourself like at example `tm.c`. This function returns `0` if all OK, or it will return the last problem MCU and motor value as `Nmcu*10+motnum`. +Another blocking function is `mot_wait` which will blocks until all motors stopped. + +To move motor run `movemotor`. It have four parameters: `mcu` (MCU number, 1 or 2), `motnum` (motor number, 0 or 1), `steps` (steps amount) and `absmove` (set it to `true` if `steps` are absolute position). Its return value can tell about possible problems. + +If you want to get MCU's temperature, run `get_temp`. Its both parameters are temperature values in degrees of Celsius. +The ADC state available with`get_ADC`. + +To reset MCU just run `reset_MCU`. To make certain that reset occurred, run `mot_getstatus` and check both `curpos`, they should be `-1`. + +Function`tty_sendcmd` allows to send some special commands with return status analyse. If you want to make particular communications, use `tty_send` to send command without subsequent reading or `tty_sendraw` with answer reading. To read MCU's answer separately you can call `tty_get`. + +For examples of these functions usage look into files `tm.c`, `tmcmdlnopts.c` and `tmcmdlnopts.h`. diff --git a/MMPP_lib/common.h b/MMPP_lib/common.h index b0f5b08..28e620f 100644 --- a/MMPP_lib/common.h +++ b/MMPP_lib/common.h @@ -46,11 +46,16 @@ fprintf(stderr, "%s (%s, line %d): ", __func__, __FILE__, __LINE__); \ fprintf(stderr, __VA_ARGS__); \ fprintf(stderr, "\n");} while(0) + #define red(...) do{printf(COLOR_RED); printf(__VA_ARGS__); printf(COLOR_OLD);}while(0) + #define green(...) do{printf(COLOR_GREEN); printf(__VA_ARGS__); printf(COLOR_OLD);}while(0) #else - #define FNAME() do{}while(0) - #define DBG(...) do{}while(0) + #define FNAME() + #define DBG(...) + #define red(...) + #define green(...) #endif //EBUG + /* * Memory allocation */ diff --git a/MMPP_lib/examples/CMakeLists.txt b/MMPP_lib/examples/CMakeLists.txt index 9081b17..fa63ef7 100644 --- a/MMPP_lib/examples/CMakeLists.txt +++ b/MMPP_lib/examples/CMakeLists.txt @@ -3,8 +3,7 @@ project(examples) link_libraries(mmpp) include_directories(../) -#target_link_libraries(hello -lm) -#add_executable(movestages movecmdlnopts.c move.c) -#target_link_libraries(movestages -lusefull_macros) add_executable(testmove tmcmdlnopts.c tm.c) target_link_libraries(testmove -lusefull_macros) +add_executable(wheels wheelscmdlnopts.c wheels.c) +target_link_libraries(wheels -lusefull_macros) diff --git a/MMPP_lib/examples/move.c b/MMPP_lib/examples/move.c index b6234a2..2100c56 100644 --- a/MMPP_lib/examples/move.c +++ b/MMPP_lib/examples/move.c @@ -79,10 +79,6 @@ static int parsemotans(ttysend_status ans, const char *prefix){ return 0; } -/** - * move motor - * @return 0 if motor can't move, else return 1 - */ /** * @brief movemotor - move motor * @param mcu - MCU# (controller No, 1 or 2) diff --git a/MMPP_lib/examples/tm.c b/MMPP_lib/examples/tm.c index 3865495..cd19bf4 100644 --- a/MMPP_lib/examples/tm.c +++ b/MMPP_lib/examples/tm.c @@ -16,6 +16,10 @@ * along with this program. If not, see . */ +/****************************************************************************** + * Motors testing tool * + ******************************************************************************/ + #include "tmcmdlnopts.h" #include #include @@ -50,6 +54,93 @@ void __attribute__((noreturn)) signals(int sig){ exit(sig); } +static double convangle(double val){ + int X = (int)(val / 360.); + val -= 360. * (double)X; + return val; +} + +static void parsestatus(ttysend_status st){ + if(quiet) return; + switch(st){ + case SEND_ERR: + red("communication error"); + break; + case SEND_ALLOK: + green("all OK"); + break; + case SEND_ACTIVE: + red("motor is still moving"); + break; + case SEND_TOOBIG: + red("the steps amount is too large"); + break; + case SEND_ZEROMOVE: + green("already at position"); + break; + case SEND_ESWITCH: + red("on end-switch and can't move further"); + break; + case SEND_NEEDINIT: + red("motors aren't initialised"); + break; + case SEND_NEGATMOVE: + red("try to move into negative position"); + break; + case SEND_OTHER: + default: + red("other error (wrong motor/MCU number?)"); + } + printf("\n"); +} + +static void pollstatus(){ + motor_state S; + bool mvng[3] = {false, true, true}, starting = true; + int curpos[4]; + double adc[6]; + while(mvng[1] || mvng[2]){ + for(int Nmcu = 1; Nmcu < 3; ++Nmcu){ + if(!mot_getstatus(Nmcu, &S)){ + mvng[Nmcu] = false; + continue; + } + if(S.state[0] == STP_SLEEP && S.state[1] == STP_SLEEP) + mvng[Nmcu] = false; + curpos[2*(Nmcu-1)+0] = S.curpos[0]; + curpos[2*(Nmcu-1)+1] = S.curpos[1]; + if(G->getADC){ + ADC_state s; + for(int Nmcu = 1; Nmcu < 3; ++Nmcu){ + if(get_ADC(Nmcu, &s)){ + adc[3*(Nmcu-1)+0] = s.Vdd; + adc[3*(Nmcu-1)+1] = s.Imot; + adc[3*(Nmcu-1)+2] = s.Vmot; + } + } + } + } + if(!mvng[1] && !mvng[2]){ + if(starting){ + starting = false; + continue; + } + }else starting = false; + if(!quiet){ + printf("1: %6d, %6d", curpos[0],curpos[1]); + if(G->getADC){ + printf(", VDD=%-5.1f Imot=%-5.1f Vmot=%-5.1f", adc[0], adc[1], adc[2]); + } + printf(" 2: %6d, %6d", curpos[2],curpos[3]); + if(G->getADC){ + printf(", VDD=%-5.1f Imot=%-5.1f Vmot=%-5.1f", adc[3], adc[4], adc[5]); + } + printf(" \r"); + } + } + printf("\n\n"); +} + int main(int argc, char **argv){ initial_setup(); signal(SIGTERM, signals); // kill (-15) @@ -69,41 +160,105 @@ int main(int argc, char **argv){ } for(int i = 1; i < 3; ++i){ if(get_alive(i)){ - green("MCU #%d found\n", i); - if(get_rst(i, true)) - red("Controller #%d was in reset state\n", i); - }else red("MCU #%d not found\n", i); + if(!quiet) green("MCU #%d found\n", i); + }else WARNX(_("MCU #%d not found"), i); } if(G->stopall){ int r = stop_all(); - if(r) red("Error for %d motors of 4\n", r); - else green("Successfully send command to stop all\n"); + if(r) WARNX(_("Error for %d motors of 4"), r); + else MSG("Successfully send command to stop all", NULL); } if(G->gettemp){ double t1, t2; if(get_temp(&t1, &t2)){ - green("Got MCU temp:\n"); - if(t1 > -300.) printf("\tMCU#1 - %gdegrC\n", t1); - if(t2 > -300.) printf("\tMCU#2 - %gdegrC\n", t2); - }else red("Can't get MCU temp\n"); + MSG("Got MCU temp:", NULL); + if(t1 > -300.) printf("\tTMCU1=%gdegrC\n", t1); + if(t2 > -300.) printf("\tTMCU2=%gdegrC\n", t2); + }else WARNX(_("Can't get MCU temp")); } if(G->getstatus){ for(int N = 1; N < 3; ++N){ motor_state s; if(mot_getstatus(N, &s)){ - green("MCU#%d state:\n", N); + if(get_rst(N, true)) + WARNX(_("Controller #%d was in reset state"), N); + if(!quiet) green("MCU#%d state:\n", N); for(int i = 0; i < 2; ++i){ - printf("\tstate[%d]=%d\n",i, s.state[i]); - printf("\tstepslefs[%d]=%d\n", i, s.stepsleft[i]); - printf("\tcurpos[%d]=%d\n", i, s.curpos[i]); + printf("\tSTATE[%d]=%d\n",i, s.state[i]); + printf("\tSTEPSLEFT[%d]=%d\n", i, s.stepsleft[i]); + printf("\tCURPOS[%d]=%d\n", i, s.curpos[i]); for(int j = 0; j < 2; ++j) - printf("\tESW_status[%d][%d]=%d\n", i, j, s.ESW_status[i][j]); + printf("\tESW_STATE[%d][%d]=%d\n", i, j, s.ESW_status[i][j]); } } } } - int ini = init_motors(); - if(ini) red("Can't init motors: %d\n", ini); - else green("Motors are ready!\n"); + if(G->sendraw){ + char **raw = G->sendraw; + do{ + MSG(_("Send raw string"), *raw); + char *got = tty_sendraw(*raw); + if(got){ + MSG(_("Receive"), got); + if(quiet) printf("%s", got); + }else WARNX(_("Nothing received")); + }while(*(++raw)); + } + if(G->getADC){ + MSG(_("Get ADC values"), NULL); + ADC_state s; + for(int i = 1; i < 3; ++i){ + if(get_ADC(i, &s)){ + printf("VDD%d=%g\n", i, s.Vdd); + printf("IMOT%d=%g\n", i, s.Imot); + printf("VMOT%d=%g\n", i, s.Vmot); + }else WARNX(_("Can't get ADC values for MCU#%d"), i); + } + } + if(G->rot1angle > -999. || G->rot2angle > -999. || G->l1steps != INT_MAX || G->l2steps != INT_MAX){ + // all other commands are tied with moving, so check if motors are inited + MSG("Init motors", NULL); + // move all uninitialized motors to their zero position + int ini = init_motors(); // BLOCKING call!!! + // you can use non-blocking initialisation by proper rewriting of `init_motors` + if(ini){ + WARNX(_("Can't init motors: %d\n"), ini); + signals(RET_CANTINIT); + }else green("Motors are ready!\n"); + ttysend_status st; + if(G->rot1angle > -999.){ + double angle = convangle(G->rot1angle); + int steps = (int)((STEPSREV1/360.) * angle); + MSG("Try to rotate polaroid ...", NULL); + st = movemotor(1, 1, steps, G->absmove); + parsestatus(st); + } + if(G->rot2angle > -999.){ + double angle = convangle(G->rot2angle); + int steps = (int)((STEPSREV2/360.) * angle); + MSG("Try to rotate waveplate ...", NULL); + st = movemotor(2, 1, steps, G->absmove); + parsestatus(st); + } + if(G->l1steps != INT_MAX){ + MSG("Try to move polaroid stage ...", NULL); + st = movemotor(1, 0, G->l1steps, G->absmove); + parsestatus(st); + } + if(G->l2steps != INT_MAX){ + MSG("Try to move waveplate stage ...", NULL); + st = movemotor(2, 0, G->l2steps, G->absmove); + parsestatus(st); + } + } + pollstatus(); + if(G->reset){ + int **N = G->reset; + while(*N){ + if(!quiet) green("Reset controller #%d\n", **N); + reset_MCU(**N); + ++N; + } + } signals(0); } diff --git a/MMPP_lib/examples/tmcmdlnopts.c b/MMPP_lib/examples/tmcmdlnopts.c index 9cbfe2f..4eab37d 100644 --- a/MMPP_lib/examples/tmcmdlnopts.c +++ b/MMPP_lib/examples/tmcmdlnopts.c @@ -36,6 +36,10 @@ glob_pars const Gdefault = { .comdev = "/dev/ttyUSB0" ,.pidfile = "/tmp/MMPP_con.pid" ,.speed = BAUD_RATE + ,.rot1angle = -1000. + ,.rot2angle = -1000. + ,.l1steps = INT_MAX + ,.l2steps = INT_MAX }; /* @@ -51,6 +55,14 @@ static myoption cmdlnopts[] = { {"stopall", NO_ARGS, NULL, 's', arg_none, APTR(&G.stopall), N_("stop all motors")}, {"gettemp", NO_ARGS, NULL, 't', arg_none, APTR(&G.gettemp), N_("get MCU temperature")}, {"status", NO_ARGS, NULL, 'S', arg_none, APTR(&G.getstatus), N_("get device status")}, + {"sendraw", MULT_PAR, NULL, 'W', arg_string, APTR(&G.sendraw), N_("send raw command (you can use this flag several times)")}, + {"getADC", NO_ARGS, NULL, 'A', arg_none, APTR(&G.getADC), N_("get ADC values for both MCUs")}, + {"absmove", NO_ARGS, NULL, 'a', arg_none, APTR(&G.absmove), N_("absolute move (without this flag moving is relative)")}, + {"rot1", NEED_ARG, NULL, 'R', arg_double, APTR(&G.rot1angle), N_("rotate polaroid to given angle")}, + {"rot2", NEED_ARG, NULL, 'r', arg_double, APTR(&G.rot2angle), N_("rotate lambda/4 to given angle")}, + {"lin1", NEED_ARG, NULL, 'L', arg_int, APTR(&G.l1steps), N_("move polaroid linear stage to N steps")}, + {"lin2", NEED_ARG, NULL, 'l', arg_int, APTR(&G.l2steps), N_("move wave-plate linear stage to N steps")}, + {"reset", MULT_PAR, NULL, 'E', arg_int, APTR(&G.reset), N_("reset given mcu (may be included several times)")}, end_option }; diff --git a/MMPP_lib/examples/tmcmdlnopts.h b/MMPP_lib/examples/tmcmdlnopts.h index cfa1d12..2234453 100644 --- a/MMPP_lib/examples/tmcmdlnopts.h +++ b/MMPP_lib/examples/tmcmdlnopts.h @@ -31,6 +31,14 @@ typedef struct{ int stopall; // stop all motors int speed; // TTY speed int getstatus; // get status of all devices + char **sendraw; // send raw command[s] + int getADC; // get ADC values + double rot1angle; // rotator 1 angle + double rot2angle; // rotator 2 angle + int l1steps; // move linear stage 1 (polaroid) for N steps + int l2steps; // move linear stage 2 (L/4) for N steps + int absmove; // absolute move (to given position from zero-esw) + int **reset; // reset given MCU's } glob_pars; // default & global parameters diff --git a/MMPP_lib/examples/wheels.c b/MMPP_lib/examples/wheels.c index 2b55a2a..4b3beb5 100644 --- a/MMPP_lib/examples/wheels.c +++ b/MMPP_lib/examples/wheels.c @@ -16,6 +16,10 @@ * along with this program. If not, see . */ +/****************************************************************************** + * Wheels testing tool * + ******************************************************************************/ + #include "wheelscmdlnopts.h" #include #include @@ -91,31 +95,33 @@ int main(int argc, char **argv){ printf("\tmaxpos: %d\n\n", wheels[i].maxpos); } } - if(G->gohome){ + if(G->gohome){ // non-blocking moving to home position for(int i = 0; i < found; ++i){ if(!wheel_home(&wheels[i])) WARNX(_("Can't move wheel %c to home position"), wheels[i].ID); else{ green("Wheel %c is moving to home position\n"); } } + // now we can wait until all wheels reach home position for(int i = 0; i < found; ++i){ while(WHEEL_MOVING == wheel_getpos(&wheels[i])){ usleep(100000); } + // we should check current position because wheel can be blocked and don't move if(wheel_getpos(&wheels[i]) != 1) WARNX(_("Wheel %c didn't reach home position"), wheels[i].ID); } } int Nw = 0, Ng = 0; - if(G->wh_ids){ + if(G->wh_ids){ // count arguments of --wheel-id while(G->wh_ids[Nw]) ++Nw; } - if(G->gotopos){ + if(G->gotopos){ // count arguments of --goto while(G->gotopos[Ng]) ++Ng; } - if(Nw != Ng){ + if(Nw != Ng){ // it's better to write --goto after each --wheel-id WARNX(_("Amoung of `--wheel-id` should be equal to amount of `--goto`!")); - }else{ + }else{ // here is an example of searching wheel by its ID and blocking moving for(int i = 0; i < Nw; ++i){ char ID = *G->wh_ids[i]; DBG("id: %c, goto: %d", ID, *G->gotopos[i]); @@ -126,12 +132,12 @@ int main(int argc, char **argv){ if(!move_wheel(w, pos)){ WARNX(_("Can't rotate wheel %c to position %d"), ID, pos); wheel_clear_err(w); - }else{ + }else{ // wait until wheel is moving while(WHEEL_MOVING == wheel_getpos(w)){ DBG("still moving"); usleep(100000); } - int curpos = wheel_getpos(w); + int curpos = wheel_getpos(w); // poll again to check current position if(curpos != pos) WARNX(_("Wheel %c can't reach position %d, current position: %d"), ID, pos, curpos); else green("Wheel %c is on position %d\n", ID, pos); } diff --git a/MMPP_lib/libmmpp.c b/MMPP_lib/libmmpp.c index 7eb7377..2cdd5b7 100644 --- a/MMPP_lib/libmmpp.c +++ b/MMPP_lib/libmmpp.c @@ -44,6 +44,7 @@ static bool reset[3] = {false,false,false}; // reset occured * @return 0 if all OK */ int mmpp_tryopen(char *devnm, int spd){ + if(dev) close_tty(&dev); dev = new_tty(devnm, spd, 256); if(!dev) return 1; if(!tty_open(dev, true)) return 1; @@ -54,6 +55,7 @@ int mmpp_tryopen(char *devnm, int spd){ * @brief tty_close - close TTY device */ void mmpp_close(){ + if(!dev) return; close_tty(&dev); } @@ -89,7 +91,7 @@ int mot_handshake(){ } /** - * Get temperature of both MCU + * Get temperature of both MCUs * @param t1, t2 (o) - temperatures of first and second MCUs (==-300 if error) * @return amount of successful calls */ @@ -156,25 +158,28 @@ int init_motors(){ } } if(!needinit) return 0; - DBG("Need to init, start!"); + red("Need to init, start!\n"); for(Nmcu = 1; Nmcu < 3; ++Nmcu){ for(motnum = 0; motnum < 2; ++motnum){ int pos = S[Nmcu].curpos[motnum]; if(pos >= 0) continue; // check if we are on zero endswitch if(S[Nmcu].ESW_status[motnum][0] == ESW_HALL){ // move a little from zero esw + red("from esw\n"); sprintf(buf, "%dM%dM100", Nmcu, motnum); if(SEND_ERR == tty_sendcmd(buf)){ return RETVAL(); } mot_wait(); } - sprintf(buf, "%dM%dM-400", Nmcu, motnum); + sprintf(buf, "%dM%dM-40000", Nmcu, motnum); if(SEND_ALLOK != tty_sendcmd(buf)){ return RETVAL(); } }} mot_wait(); + green("check\n"); + // check current positions for(Nmcu = 1; Nmcu < 3; ++Nmcu){ if(!mot_getstatus(Nmcu, &S[Nmcu])) return 10*Nmcu+2; for(motnum = 0; motnum < 2; ++motnum){ @@ -203,7 +208,7 @@ int mot_wait(){ motor_state S; if(!mot_getstatus(Nmcu, &S)){ ++failcount; - } + }else failcount = 0; if(S.state[0] == STP_SLEEP && S.state[1] == STP_SLEEP) mov[Nmcu] = false; } @@ -220,6 +225,7 @@ int mot_wait(){ * @return static buffer with data read or NULL */ char *tty_get(){ + if(!dev) return NULL; static char buf[TBUFLEN]; char *ptr = buf; size_t L = 0, l = TBUFLEN; @@ -247,6 +253,7 @@ char *tty_get(){ * @return 0 if failed */ int tty_send(char *cmd){ + if(!dev) return 0; size_t l = 0; char *s = cpy2buf(cmd, &l); if(!s) return 0; @@ -260,6 +267,7 @@ int tty_send(char *cmd){ * @return string received or NULL in case of error */ char* tty_sendraw(char *string){ + if(!dev) return NULL; DBG("sendraw %s", string); if(!tty_send(string)) return NULL; return tty_get(); @@ -371,5 +379,84 @@ bool get_rst(int N, bool clear){ * @return alive[N] */ bool get_alive(int N){ + if(N < 1 || N > 2) return false; return alive[N]; } + +/** + * @brief get_ADC - ADC values + * @param N - number of MCU (1 or 2) + * @param s (o) - state of all ADC channels + * @return true if all OK + */ +bool get_ADC(int N, ADC_state *s){ + if(N < 1 || N > 2 || !s || !alive[N]) return false; + char buff[] = "xGAy", cmds[3] = "DIM", *ansv[] = {"VDD", "IMOT", "VMOT"}; + double *vals[3] = {&s->Vdd, &s->Imot, &s->Vmot}; + int got = 0; + buff[0] = '0' + N; + for(int i = 0; i < 3; ++i){ + buff[3] = cmds[i]; + char *ans = tty_sendraw(buff); + if(!ans) continue; + char *v = keyval(ansv[i], ans); + if(v){ + double t; + if(str2double(&t, v)){ + *vals[i] = t / 100.; + DBG("Got %s=%g", ansv[i], *vals[i]); + ++got; + } + } + } + if(got != 3) return false; + return true; +} + +/** + * @brief reset_MCU - reset given controller + * @param N - MCU # (1 or 2) + */ +void reset_MCU(int N){ + if(N < 1 || N > 2) return; + char cmd[] = "xR"; + cmd[0] = '0' + N; + tty_sendraw(cmd); +} + +/** + * @brief movemotor - move motor + * @param mcu - MCU# (controller No, 1 or 2) + * @param motnum - motor# (0 or 1) + * @param steps - steps amount + * @param absmove - !=0 if steps are absolute position + * @return + */ +ttysend_status movemotor(int mcu, int motnum, int steps, int absmove){ + if(mcu < 1 || mcu > 3 || motnum < 0 || motnum > 1) return SEND_OTHER; + char buf[32]; + motor_state mstate; + if(!mot_getstatus(mcu, &mstate)) return SEND_ERR; + if(mstate.state[motnum] != STP_SLEEP) return SEND_ACTIVE; + int curpos = mstate.curpos[motnum]; + if(curpos < 0){ // need to init + return SEND_NEEDINIT; + } + if(absmove){ + if(motnum == 1){ // convert rotator angle to positive + int perrev = (mcu == 1) ? STEPSREV1 : STEPSREV2; + steps %= perrev; + if(steps < 0) steps += perrev; + } + if(steps < 0){ + return SEND_NEGATMOVE; + } + steps -= curpos; + } + if(steps == 0){ + return SEND_ZEROMOVE; + } + DBG("try to move motor%d of mcu %d for %d steps", motnum, mcu, steps); + snprintf(buf, 32, "%dM%dM%d", mcu, motnum, steps); + return tty_sendcmd(buf); +} diff --git a/MMPP_lib/libmmpp.h b/MMPP_lib/libmmpp.h index abd062f..b373208 100644 --- a/MMPP_lib/libmmpp.h +++ b/MMPP_lib/libmmpp.h @@ -19,6 +19,10 @@ #ifndef LIBMMPP_H__ #define LIBMMPP_H__ +#ifdef __cplusplus +extern "C" { +#endif + #include // default baudrate for communication @@ -55,7 +59,9 @@ typedef enum{ SEND_TOOBIG, // amount of steps too big SEND_ZEROMOVE, // give 0 steps to move SEND_ESWITCH, // staying on end-switch & try to move further - SEND_OTHER + SEND_NEEDINIT, // motor needs initialisation + SEND_NEGATMOVE, // try to move to negative position + SEND_OTHER, // unknown state } ttysend_status; // end-switch state typedef enum{ @@ -78,18 +84,27 @@ typedef struct{ int curpos[2]; // current position (negative for non-initialized state or error) ESW_status ESW_status[2][2];// End-switches status, [i][j], i - motor, j - esw 0 or 1 } motor_state; +// ADC state +typedef struct{ + double Vdd; // value of Vdd (+3.3V) + double Imot; // motors' current (Amperes) + double Vmot; // motors' voltage (+12V) +} ADC_state; int mmpp_tryopen(char *dev, int spd); void mmpp_close(); int mot_handshake(); +bool mot_getstatus(int Nmcu, motor_state *s); bool get_rst(int N, bool clear); bool get_alive(int N); int stop_all(); int get_temp(double *t1, double *t2); -bool mot_getstatus(int Nmcu, motor_state *s); int init_motors(); int mot_wait(); ttysend_status tty_sendcmd(char *cmd); +bool get_ADC(int N, ADC_state *s); +ttysend_status movemotor(int mcu, int motnum, int steps, int absmove); +void reset_MCU(int N); char *tty_get(); int tty_send(char *cmd); @@ -133,4 +148,8 @@ bool wheel_clear_err(wheel_descr *w); bool move_wheel(wheel_descr *w, int filter_pos); bool wheel_home(wheel_descr *w); +#ifdef __cplusplus +} +#endif + #endif // LIBMMPP_H__ diff --git a/MMPP_lib/mmpp.pc.in b/MMPP_lib/mmpp.pc.in index 441b10e..bba5d41 100644 --- a/MMPP_lib/mmpp.pc.in +++ b/MMPP_lib/mmpp.pc.in @@ -4,7 +4,7 @@ libdir=${exec_prefix}/lib includedir=${prefix}/include Name: @PROJ@ -Description: Library with a lot of usefull snippets +Description: MMPP (multi-mode photometer-polarimeter) control library Version: @VERSION@ Libs: -L${libdir} -l@PROJ@ Cflags: -I${includedir}