From 145a6c4c49b1aac9a8b8065ac5e48ba50754ba7f Mon Sep 17 00:00:00 2001 From: px4dev <px4@purgatory.org> Date: Sat, 4 Aug 2012 20:05:47 -0700 Subject: [PATCH] Work in progress; standard mixer API and utility --- apps/drivers/drv_accel.h | 2 +- apps/drivers/drv_baro.h | 2 +- apps/drivers/drv_gyro.h | 2 +- apps/drivers/drv_mag.h | 2 +- apps/drivers/drv_mixer.h | 91 ++++++++ apps/drivers/drv_orb_dev.h | 2 +- apps/drivers/drv_pwm_output.h | 2 +- apps/px4/fmu/Makefile | 4 + apps/px4/fmu/fmu.cpp | 75 ++++++- apps/systemcmds/mixer/Makefile | 42 ++++ apps/systemcmds/mixer/mixer.c | 342 +++++++++++++++++++++++++++++ apps/systemlib/mixer.c | 167 +++++++++++++- apps/systemlib/mixer.h | 101 ++++++--- nuttx/configs/px4fmu/nsh/appconfig | 1 + 14 files changed, 787 insertions(+), 48 deletions(-) create mode 100644 apps/drivers/drv_mixer.h create mode 100644 apps/systemcmds/mixer/Makefile create mode 100644 apps/systemcmds/mixer/mixer.c diff --git a/apps/drivers/drv_accel.h b/apps/drivers/drv_accel.h index bf13b2c32b..370cc5d877 100644 --- a/apps/drivers/drv_accel.h +++ b/apps/drivers/drv_accel.h @@ -75,7 +75,7 @@ ORB_DECLARE(sensor_accel); * ioctl() definitions */ -#define _ACCELIOCBASE (_SNIOCBASE + 0x20) +#define _ACCELIOCBASE (0x2000) #define _ACCELIOC(_n) (_IOC(_ACCELIOCBASE, _n)) /** set the driver polling rate to (arg) Hz, or one of the ACC_POLLRATE constants */ diff --git a/apps/drivers/drv_baro.h b/apps/drivers/drv_baro.h index 4cfb354547..323b25c835 100644 --- a/apps/drivers/drv_baro.h +++ b/apps/drivers/drv_baro.h @@ -65,7 +65,7 @@ ORB_DECLARE(sensor_baro); * ioctl() definitions */ -#define _BAROIOCBASE (_SNIOCBASE + 0x10) +#define _BAROIOCBASE (0x2100) #define _BAROIOC(_n) (_IOC(_BAROIOCBASE, _n)) /** set the driver polling rate to (arg) Hz, or one of the BARO_POLLRATE constants */ diff --git a/apps/drivers/drv_gyro.h b/apps/drivers/drv_gyro.h index 21f6493b13..82e23f62af 100644 --- a/apps/drivers/drv_gyro.h +++ b/apps/drivers/drv_gyro.h @@ -75,7 +75,7 @@ ORB_DECLARE(sensor_gyro); * ioctl() definitions */ -#define _GYROIOCBASE (_SNIOCBASE + 0x10) +#define _GYROIOCBASE (0x2200) #define _GYROIOC(_n) (_IOC(_GYROIOCBASE, _n)) /** set the driver polling rate to (arg) Hz, or one of the GYRO_POLLRATE constants */ diff --git a/apps/drivers/drv_mag.h b/apps/drivers/drv_mag.h index 7e90e9e463..673a3988fd 100644 --- a/apps/drivers/drv_mag.h +++ b/apps/drivers/drv_mag.h @@ -75,7 +75,7 @@ ORB_DECLARE(sensor_mag); * ioctl() definitions */ -#define _MAGIOCBASE (_SNIOCBASE + 0x30) +#define _MAGIOCBASE (0x2300) #define _MAGIOC(_n) (_IOC(_MAGIOBASE, _n)) /** set the driver polling rate to (arg) Hz, or one of the MAG_POLLRATE constants */ diff --git a/apps/drivers/drv_mixer.h b/apps/drivers/drv_mixer.h new file mode 100644 index 0000000000..10cda792a9 --- /dev/null +++ b/apps/drivers/drv_mixer.h @@ -0,0 +1,91 @@ +/**************************************************************************** + * + * Copyright (C) 2012 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/** + * @file Mixer ioctl interface. + * + * This interface can/should be exported by any device that supports + * control -> actuator mixing. + */ + +#ifndef _DRV_MIXER_H +#define _DRV_MIXER_H + +#include <stdint.h> +#include <sys/ioctl.h> + +#include <systemlib/mixer.h> + +/** + * Structure used for receiving mixers. + * + * Note that the mixers array is not actually an array of mixers; it + * simply represents the first mixer in the buffer. + */ +struct MixInfo +{ + unsigned num_controls; + struct MixMixer mixer; +}; + +/** + * Handy macro for determining the allocation size of a MixInfo structure. + */ +#define MIXINFO_SIZE(_num_controls) (sizeof(struct MixInfo) + ((_num_controls) * sizeof(struct MixScaler))) + +/* + * ioctl() definitions + */ + +#define _MIXERIOCBASE (0x2400) +#define _MIXERIOC(_n) (_IOC(_MIXERIOCBASE, _n)) + +/** get the number of actuators that require mixers in *(unsigned)arg */ +#define MIXERIOCGETMIXERCOUNT _MIXERIOC(0) + +/** + * Copy a mixer from the device into *(struct MixInfo *)arg. + * + * The num_controls field indicates the number of controls for which space + * is allocated following the MixInfo structure. If the allocation + * is too small, no mixer data is retured. The control_count field in + * the MixInfo.mixer structure is always updated. + */ +#define MIXERIOCGETMIXER(_mixer) _MIXERIOC(0x20 + _mixer) + +/** + * Copy a mixer from *(struct MixMixer *)arg to the device. + */ +#define MIXERIOCSETMIXER(_mixer) _MIXERIOC(0x40 + _mixer) + +#endif /* _DRV_ACCEL_H */ diff --git a/apps/drivers/drv_orb_dev.h b/apps/drivers/drv_orb_dev.h index bacef1cd3a..b3fc01a5fa 100644 --- a/apps/drivers/drv_orb_dev.h +++ b/apps/drivers/drv_orb_dev.h @@ -58,7 +58,7 @@ /** maximum ogbject name length */ #define ORB_MAXNAME 32 -#define _ORBIOCBASE (_DIOCBASE + 0x80) +#define _ORBIOCBASE (0x2500) #define _ORBIOC(_n) (_IOC(_ORBIOCBASE, _n)) /* diff --git a/apps/drivers/drv_pwm_output.h b/apps/drivers/drv_pwm_output.h index 551f9b1a65..73e3310aed 100644 --- a/apps/drivers/drv_pwm_output.h +++ b/apps/drivers/drv_pwm_output.h @@ -94,7 +94,7 @@ ORB_DECLARE(output_pwm); * Note that ioctls and ObjDev updates should not be mixed, as the * behaviour of the system in this case is not defined. */ -#define _PWM_SERVO_BASE 0x7500 +#define _PWM_SERVO_BASE 0x2600 /** arm all servo outputs handle by this driver */ #define PWM_SERVO_ARM _IOC(_PWM_SERVO_BASE, 0) diff --git a/apps/px4/fmu/Makefile b/apps/px4/fmu/Makefile index 7f1f836e30..8317748729 100644 --- a/apps/px4/fmu/Makefile +++ b/apps/px4/fmu/Makefile @@ -35,4 +35,8 @@ # Interface driver for the PX4FMU board # +APPNAME = fmu +PRIORITY = SCHED_PRIORITY_DEFAULT +STACKSIZE = 2048 + include $(APPDIR)/mk/app.mk diff --git a/apps/px4/fmu/fmu.cpp b/apps/px4/fmu/fmu.cpp index 6c7d9f7426..ff47261e7d 100644 --- a/apps/px4/fmu/fmu.cpp +++ b/apps/px4/fmu/fmu.cpp @@ -40,6 +40,7 @@ #include <sys/types.h> #include <stdint.h> #include <stdbool.h> +#include <stdlib.h> #include <semaphore.h> #include <string.h> #include <fcntl.h> @@ -54,6 +55,7 @@ #include <drivers/device/device.h> #include <drivers/drv_pwm_output.h> #include <drivers/drv_gpio.h> +#include <drivers/drv_mixer.h> #include <uORB/topics/actuator_controls.h> #include <systemlib/mixer.h> @@ -76,6 +78,8 @@ public: virtual int init(); private: + static const unsigned _max_actuators = 4; + Mode _mode; int _task; int _t_actuators; @@ -85,7 +89,7 @@ private: volatile bool _task_should_exit; bool _armed; - MixMixer *_mixer[4]; + MixMixer *_mixer[_max_actuators]; static void task_main_trampoline(int argc, char *argv[]); void task_main(); @@ -262,6 +266,9 @@ int FMUServo::ioctl(struct file *filp, int cmd, unsigned long arg) { int ret = OK; + int channel; + struct MixInfo *mi; + struct MixMixer *mm, *tmm; switch (cmd) { case PWM_SERVO_ARM: @@ -282,7 +289,7 @@ FMUServo::ioctl(struct file *filp, int cmd, unsigned long arg) case PWM_SERVO_SET(0): case PWM_SERVO_SET(1): if (arg < 2100) { - int channel = cmd - PWM_SERVO_SET(0); + channel = cmd - PWM_SERVO_SET(0); up_pwm_servo_set(channel, arg); } else { ret = -EINVAL; @@ -298,11 +305,73 @@ FMUServo::ioctl(struct file *filp, int cmd, unsigned long arg) /* FALLTHROUGH */ case PWM_SERVO_GET(0): case PWM_SERVO_GET(1): { - int channel = cmd - PWM_SERVO_SET(0); + channel = cmd - PWM_SERVO_SET(0); *(servo_position_t *)arg = up_pwm_servo_get(channel); break; } + case MIXERIOCGETMIXERCOUNT: + if (_mode == MODE_4PWM) { + *(unsigned *)arg = 4; + } else { + *(unsigned *)arg = 2; + } + break; + + case MIXERIOCGETMIXER(3): + case MIXERIOCGETMIXER(2): + if (_mode != MODE_4PWM) { + ret = -EINVAL; + break; + } + /* FALLTHROUGH */ + case MIXERIOCGETMIXER(1): + case MIXERIOCGETMIXER(0): + channel = cmd - MIXERIOCGETMIXER(0); + + /* caller's MixInfo */ + mi = (struct MixInfo *)arg; + + /* if MixInfo claims to be big enough, copy mixer info */ + if (mi->num_controls >= _mixer[channel]->control_count) { + memcpy(&mi->mixer, _mixer[channel], MIXER_SIZE(_mixer[channel]->control_count)); + } else { + /* just update MixInfo with actual size of the mixer */ + mi->mixer.control_count = _mixer[channel]->control_count; + } + break; + + case MIXERIOCSETMIXER(3): + case MIXERIOCSETMIXER(2): + if (_mode != MODE_4PWM) { + ret = -EINVAL; + break; + } + /* FALLTHROUGH */ + case MIXERIOCSETMIXER(1): + case MIXERIOCSETMIXER(0): + channel = cmd - MIXERIOCGETMIXER(0); + + /* caller- supplied mixer */ + mm = (struct MixMixer *)arg; + + /* allocate local storage and copy from the caller*/ + if (mm != nullptr) { + tmm = (struct MixMixer *)malloc(MIXER_SIZE(mm->control_count)); + memcpy(tmm, mm, MIXER_SIZE(mm->control_count)); + } else { + tmm = nullptr; + } + + /* swap in new mixer for old */ + mm = _mixer[channel]; + _mixer[channel] = tmm; + + /* if there was an old mixer, free it */ + if (mm != nullptr) + free(mm); + break; + default: ret = -ENOTTY; break; diff --git a/apps/systemcmds/mixer/Makefile b/apps/systemcmds/mixer/Makefile new file mode 100644 index 0000000000..a83085bfc5 --- /dev/null +++ b/apps/systemcmds/mixer/Makefile @@ -0,0 +1,42 @@ +############################################################################ +# +# Copyright (C) 2012 PX4 Development Team. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name PX4 nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ + +# +# Build the mixer tool. +# + +APPNAME = mixer +PRIORITY = SCHED_PRIORITY_DEFAULT +STACKSIZE = 2048 + +include $(APPDIR)/mk/app.mk diff --git a/apps/systemcmds/mixer/mixer.c b/apps/systemcmds/mixer/mixer.c new file mode 100644 index 0000000000..c5adf8dbfc --- /dev/null +++ b/apps/systemcmds/mixer/mixer.c @@ -0,0 +1,342 @@ +/**************************************************************************** + * + * Copyright (C) 2012 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/** + * @file Mixer utility. + */ + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <getopt.h> +#include <unistd.h> +#include <fcntl.h> + +#include <systemlib/mixer.h> +#include <drivers/drv_mixer.h> +#include <uORB/topics/actuator_controls.h> + +__EXPORT int mixer_main(int argc, char *argv[]); + +static void usage(void); +static void load(const char *devname, const char *fname); +static void save(const char *devname, const char *fname); +static void show(const char *devname); + +enum Operation { + LOAD, + SAVE, + SHOW, + NONE +}; + +int +mixer_main(int argc, char *argv[]) +{ + const char *devname = NULL; + const char *fname = NULL; + int ch; + enum Operation todo = NONE; + + if (argc < 2) + usage(); + if (!strcmp(argv[1], "load")) { + todo = LOAD; + } else if (!strcmp(argv[1], "save")) { + todo = SAVE; + } else if (!strcmp(argv[1], "show")) { + todo = SHOW; + } else { + usage(); + } + + while ((ch = getopt(argc, argv, "d:f:")) != EOF) { + switch (ch) { + case 'd': + devname = optarg; + break; + case 'f': + fname = optarg; + break; + default: + usage(); + } + } + + switch (todo) { + case LOAD: + if ((devname == NULL) || (fname == NULL)) + usage(); + load(devname, fname); + break; + + case SAVE: + if ((devname == NULL) || (fname == NULL)) + usage(); + save(devname, fname); + break; + + case SHOW: + if (devname == NULL) + usage(); + show(devname); + break; + default: + exit(1); + } + return 0; +} + +static void +usage(void) +{ + fprintf(stderr, "usage:\n"); + fprintf(stderr, " mixer {load|save|show} -d <device> [-f <filename>]\n"); + exit(1); +} + +static void +load(const char *devname, const char *fname) +{ + int defs = -1; + int dev = -1; + unsigned num_mixers = 0; + int ret, result = 1; + struct MixMixer *mixer; + + /* open the device */ + if ((dev = open(devname, 0)) < 0) { + fprintf(stderr, "can't open %s\n", devname); + goto out; + } + + /* open the definition file */ + if ((defs = open(fname, O_RDONLY)) < 0) { + fprintf(stderr, "can't open %s\n", fname); + goto out; + } + + /* find out how many mixers the device supports */ + ioctl(dev, MIXERIOCGETMIXERCOUNT, (unsigned long)&num_mixers); + if (num_mixers < 1) { + fprintf(stderr, "can't get mixer count from %s\n", devname); + goto out; + } + + /* send mixers to the device */ + for (unsigned i = 0; i < num_mixers; i++) { + ret = mixer_load(defs, &mixer); + if (ret < 0) + goto out; + + /* end of file? */ + if (ret == 0) + break; + + /* sanity check the mixer */ + if (mixer_check(mixer, NUM_ACTUATOR_CONTROLS)) { + fprintf(stderr, "mixer %u fails sanity check\n", i); + goto out; + } + + /* send the mixer to the device */ + ret = ioctl(dev, MIXERIOCSETMIXER(i), (unsigned long)mixer); + if (ret < 0) + goto out; + + free(mixer); + mixer = NULL; + } + + printf("mixer: loaded %d mixers to %s\n", num_mixers, devname); + result = 0; + +out: + /* free the mixers array */ + if (mixer != NULL) + free(mixer); + if (defs != -1) + close(defs); + if (dev != -1) + close(dev); + + exit(result); +} + +static int +getmixer(int dev, unsigned mixer_number, struct MixInfo **mip) +{ + struct MixInfo *mi = *mip; + int ret; + + /* first-round initialisation */ + if (mi == NULL) { + mi->num_controls = 0; + mi = (struct MixInfo *)malloc(MIXINFO_SIZE(mi->num_controls)); + } + + /* loop trying to get the next mixer until the buffer is big enough */ + do { + /* try to get the mixer into the buffer as it stands */ + ret = ioctl(dev, MIXERIOCGETMIXER(mixer_number), (unsigned long)mi); + if (ret < 0) { + fprintf(stderr, "MIXERIOCGETMIXER error\n"); + return -1; + } + + /* did the mixer fit? */ + if (mi->mixer.control_count <= mi->num_controls) + break; + + /* re-allocate to suit */ + mi->num_controls = mi->mixer.control_count; + mi = (struct MixInfo *)realloc(mi, MIXINFO_SIZE(mi->num_controls)); + + /* oops, blew up the heap */ + if (mi == NULL) + return -1; + + } while(true); + + *mip = mi; + return 0; +} + +static void +save(const char *devname, const char *fname) +{ + struct MixInfo *mi = NULL; + int defs = -1; + int dev = -1; + unsigned num_mixers = 0; + int ret, result = 1; + + /* open the device */ + if ((dev = open(devname, 0)) < 0) { + fprintf(stderr, "can't open %s\n", devname); + goto out; + } + + /* find out how many mixers the device supports */ + ioctl(dev, MIXERIOCGETMIXERCOUNT, (unsigned long)&num_mixers); + if (num_mixers < 1) { + fprintf(stderr, "can't get mixer count from %s\n", devname); + goto out; + } + + /* open the definition file */ + if ((defs = open(fname, O_WRONLY | O_CREAT)) < 0) { + fprintf(stderr, "can't open %s\n", fname); + goto out; + } + + /* get mixers from the device and save them */ + for (unsigned i = 0; i < num_mixers; i++) { + + ret = getmixer(dev, i, &mi); + if (ret < 0) + goto out; + + ret = mixer_save(defs, &mi->mixer); + if (ret < 0) + goto out; + } + + result = 0; + +out: + /* free the mixinfo */ + if (mi != NULL) + free(mi); + if (defs != -1) + close(defs); + if (dev != -1) + close(dev); + + exit(result); +} + +static void +show(const char *devname) +{ + struct MixInfo *mi; + int dev = -1; + unsigned num_mixers = 0; + int ret; + + /* open the device */ + if ((dev = open(devname, 0)) < 0) { + fprintf(stderr, "can't open %s\n", devname); + goto out; + } + + /* find out how many mixers the device supports */ + ioctl(dev, MIXERIOCGETMIXERCOUNT, (unsigned long)&num_mixers); + if (num_mixers < 1) { + fprintf(stderr, "can't get mixer count from %s\n", devname); + goto out; + } + + /* get mixers from the device and print them */ + for (unsigned i = 0; i < num_mixers; i++) { + + ret = getmixer(dev, i, &mi); + if (ret < 0) + goto out; + + printf("mixer %d : %d controls\n", i, mi->mixer.control_count); + printf(" -ve scale +ve scale offset low limit high limit"); + printf("output %f %f %f %f %f\n", + mi->mixer.output_scaler.negative_scale, + mi->mixer.output_scaler.positive_scale, + mi->mixer.output_scaler.offset, + mi->mixer.output_scaler.lower_limit, + mi->mixer.output_scaler.upper_limit); + for (unsigned j = 0; j < mi->mixer.control_count; j++) { + printf("%d: %f %f %f %f %f\n", + mi->mixer.control_scaler[j].negative_scale, + mi->mixer.control_scaler[j].positive_scale, + mi->mixer.control_scaler[j].offset, + mi->mixer.control_scaler[j].lower_limit, + mi->mixer.control_scaler[j].upper_limit); + } + } + +out: + /* free the mixinfo */ + if (mi != NULL) + free(mi); + if (dev != -1) + close(dev); + exit(0); +} diff --git a/apps/systemlib/mixer.c b/apps/systemlib/mixer.c index 553680fed9..cc7baebc2d 100644 --- a/apps/systemlib/mixer.c +++ b/apps/systemlib/mixer.c @@ -43,22 +43,31 @@ * See mixer.h for more details. */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + #include "mixer.h" static int scale_check(struct MixScaler *scale) { - if (scale->offset > 1.0f) - return -1; - if (scale->offset > 1.0f) - return -1; - if (scale->lower_limit > scale->upper_limit) - return -1; - if (scale->lower_limit < -1.0f) - return -1; - if (scale->upper_limit > 1.0f) - return -1; - return 0; + if (scale->offset > 1.0f) + return -1; + + if (scale->offset > 1.0f) + return -1; + + if (scale->lower_limit > scale->upper_limit) + return -1; + + if (scale->lower_limit < -1.0f) + return -1; + + if (scale->upper_limit > 1.0f) + return -1; + + return 0; } int @@ -66,17 +75,21 @@ mixer_check(struct MixMixer *mixer, unsigned control_count) { if (mixer->control_count < 1) return -1; + if (mixer->control_count > control_count) return -1; + if (!scale_check(&mixer->output_scaler)) return -1; for (unsigned i = 0; i < mixer->control_count; i++) { if (mixer->control_scaler[i].control >= control_count) return -1; + if (!scale_check(&mixer->control_scaler[i])) return -1; } + return 0; } @@ -87,11 +100,14 @@ scale(struct MixScaler *scaler, float input) if (input < 0.0f) { output = (input * scaler->negative_scale) + scaler->offset; + } else { output = (input * scaler->positive_scale) + scaler->offset; } + if (output > scaler->upper_limit) { output = scaler->upper_limit; + } else if (output < scaler->lower_limit) { output = scaler->lower_limit; } @@ -112,3 +128,132 @@ mixer_mix(struct MixMixer *mixer, float *controls) return scale(&mixer->output_scaler, sum); } + +static int +mixer_getline(int fd, char *line, unsigned maxlen) +{ + int ret; + char c; + + while (--maxlen) { + ret = read(fd, &c, 1); + if (ret <= 0) + return ret; + if (c == '\r') + continue; + if (c == '\n') { + *line = '\0'; + return 1; + } + *line++ = c; + } + /* line too long */ + return -1; +} + +static int +mixer_load_scaler(const char *buf, struct MixScaler *scaler) +{ + if (sscanf(buf, "S: %u %f %f %f %f %f", + &scaler->control, &scaler->negative_scale, &scaler->positive_scale, + &scaler->offset, &scaler->lower_limit, &scaler->upper_limit) != 6) + return -1; + + return 0; +} + +int +mixer_load(int fd, struct MixMixer **mp) +{ + int ret, result = -1; + struct MixMixer *mixer = NULL; + char buf[100]; + unsigned scalers; + + ret = mixer_getline(fd, buf, sizeof(buf)); + + /* end of file? */ + if (ret == 0) + result = 0; + + /* can't proceed */ + if (ret < 1) + goto out; + + /* get header */ + if (sscanf(buf, "M: %u", &scalers) != 1) + goto out; + + /* must have at least one scaler */ + if (scalers < 1) + goto out; + + /* allocate mixer */ + scalers--; + mixer = (struct MixMixer *)malloc(MIXER_SIZE(scalers)); + + if (mixer == NULL) + goto out; + + mixer->control_count = scalers; + + ret = mixer_getline(fd, buf, sizeof(buf)); + + if (ret < 1) + goto out; + + if (mixer_load_scaler(buf, &mixer->output_scaler)) + goto out; + + for (unsigned i = 0; i < scalers; i++) { + if (mixer_getline(fd, buf, sizeof(buf))) + goto out; + if (mixer_load_scaler(buf, &mixer->control_scaler[i])) + goto out; + } + + result = 1; + +out: + /* on error, discard allocated mixer */ + if ((result <= 0) && (mixer != NULL)) + free(mixer); + *mp = mixer; + return result; +} + +static int +mixer_save_scaler(char *buf, struct MixScaler *scaler) +{ + return sprintf(buf, "S: %u %f %f %f %f %f\n", + scaler->control, scaler->negative_scale, scaler->positive_scale, + scaler->offset, scaler->lower_limit, scaler->upper_limit); +} + +int +mixer_save(int fd, struct MixMixer *mixer) +{ + char buf[100]; + int len, ret; + + /* write the mixer header */ + len = sprintf(buf, "M: %u\n", mixer->control_count); + ret = write(fd, buf, len); + if (ret != len) + return -1; + + /* write the output scaler */ + len = mixer_save_scaler(buf, &mixer->output_scaler); + write(fd, buf, len); + if (ret != len) + return -1; + + /* write the control scalers */ + for (unsigned j = 0; j < mixer->control_count; j++) { + len = mixer_save_scaler(buf, &mixer->control_scaler[j]); + write(fd, buf, len); + if (ret != len) + return -1; + } + return 0; +} diff --git a/apps/systemlib/mixer.h b/apps/systemlib/mixer.h index f95d03f315..cd110f01e5 100644 --- a/apps/systemlib/mixer.h +++ b/apps/systemlib/mixer.h @@ -31,6 +31,9 @@ * ****************************************************************************/ +#ifndef _SYSTEMLIB_MIXER_H +#define _SYSTEMLIB_MIXER_H + /** * @file mixer.h * @@ -55,7 +58,7 @@ * * An actuator derives its value from the combination of one or more * control values. Each of the control values is scaled according to - * the actuator's configuration and then combined to produce the + * the actuator's configuration and then combined to produce the * actuator value, which may then be further scaled to suit the specific * output type. * @@ -100,7 +103,7 @@ * Mixing * ------ * - * Mixing is performed by summing the scaled control values. + * Mixing is performed by summing the scaled control values. * * * Controls @@ -109,7 +112,7 @@ * Each mixer is presented with an array of controls from which it * selects the set that will be mixed for each actuator. * - * The precise assignment of controls may vary depending on the + * The precise assignment of controls may vary depending on the * application, but the following assignments should be used * when appropriate. * @@ -121,22 +124,25 @@ * 3 | primary thrust */ - struct MixScaler - { - unsigned control; /**< control consumed by this scaler */ - float negative_scale; /**< scale for inputs < 0 */ - float positive_scale; /**< scale for inputs > 0 */ - float offset; /**< bias applied to output */ - float lower_limit; /**< minimum output value */ - float upper_limit; /**< maximum output value */ - }; +struct MixScaler { + unsigned control; /**< control consumed by this scaler */ + float negative_scale; /**< scale for inputs < 0 */ + float positive_scale; /**< scale for inputs > 0 */ + float offset; /**< bias applied to output */ + float lower_limit; /**< minimum output value */ + float upper_limit; /**< maximum output value */ +}; + +struct MixMixer { + unsigned control_count; /**< number of control scalers */ + struct MixScaler output_scaler; /**< scaler applied to mixer output */ + struct MixScaler control_scaler[0]; /**< array of control scalers */ +}; - struct MixMixer - { - unsigned control_count; /**< number of control scalers */ - struct MixScaler output_scaler; /**< scaler applied to mixer output */ - struct MixScaler control_scaler[0]; /**< array of control scalers */ - }; +/** + * Handy macro for determining the allocation size of a mixer. + */ +#define MIXER_SIZE(_num_scalers) (sizeof(struct MixMixer) + ((_num_scalers) * sizeof(struct MixScaler))) __BEGIN_DECLS @@ -150,16 +156,55 @@ __BEGIN_DECLS * @param controls Array of input control values. * @return The mixed output. */ - __EXPORT float mixer_mix(struct MixMixer *mixer, float *controls); +__EXPORT float mixer_mix(struct MixMixer *mixer, float *controls); + +/** + * Check a mixer configuration for sanity. + * + * @param mixer The mixer configuration to be checked. + * @param control_count The number of controls in the system. + * @return Zero if the mixer configuration is sane, + * nonzero otherwise. + */ +__EXPORT int mixer_check(struct MixMixer *mixer, unsigned control_count); + +/** + * Read a mixer definition from a file. + * + * A mixer definition is a text representation of the configuration of a + * mixer. The definition consists of a single-line header indicating the + * number of scalers and then one line defining each scaler. The first + * scaler in the file is always the output scaler, followed by the input + * scalers. + * + * M: <control count + 1> + * S: <control> <negative_scale> <positive_scale> <offset> <lower_limit> <upper_limit> + * S: ... + * + * The <control> value for the output scaler is ignored. + * + * Multiple mixer definitions may be stored in a single file; it is assumed that + * the reader will know how many to expect and read accordingly. Mixers may be + * 'skipped' in a file by setting indicating that the mixer has only one scaler + * (the output scaler). This results in a mixer with zero controls, which will + * always generate output corresponding to the output scaler offset. + * + * @param fd The file to read the definitions from. + * @param mixer Mixer is returned here. + * @return 1 if a mixer was read, zero on EOF or negative on error. + */ +__EXPORT int mixer_load(int fd, struct MixMixer **mixer); + +/** + * Save a mixer definition to a file. + * + * @param fd The file to write the definitions to. + * @param mixer The mixer definition to save. + * @return Zero on success, negative on error. + */ +__EXPORT int mixer_save(int fd, struct MixMixer *mixers); - /** - * Check a mixer configuration for sanity. - * - * @param mixer The mixer configuration to be checked. - * @param control_count The number of controls in the system. - * @return Zero if the mixer configuration is sane, - * nonzero otherwise. - */ - __EXPORT int mixer_check(struct MixMixer *mixer, unsigned control_count); __END_DECLS + +#endif /* _SYSTEMLIB_MIXER_H */ diff --git a/nuttx/configs/px4fmu/nsh/appconfig b/nuttx/configs/px4fmu/nsh/appconfig index 85762f4c6b..b9965bfe21 100644 --- a/nuttx/configs/px4fmu/nsh/appconfig +++ b/nuttx/configs/px4fmu/nsh/appconfig @@ -48,6 +48,7 @@ CONFIGURED_APPS += systemcmds/reboot CONFIGURED_APPS += systemcmds/perf CONFIGURED_APPS += systemcmds/top CONFIGURED_APPS += systemcmds/boardinfo +CONFIGURED_APPS += systemcmds/mixer #CONFIGURED_APPS += systemcmds/calibration CONFIGURED_APPS += uORB -- GitLab