diff options
-rw-r--r-- | src/drivers/drv_pwm_output.h | 4 | ||||
-rw-r--r-- | src/drivers/px4io/px4io.cpp | 53 | ||||
-rw-r--r-- | src/modules/px4iofirmware/controls.c | 53 | ||||
-rw-r--r-- | src/modules/px4iofirmware/mixer.cpp | 71 | ||||
-rw-r--r-- | src/modules/px4iofirmware/module.mk | 1 | ||||
-rw-r--r-- | src/modules/px4iofirmware/protocol.h | 7 | ||||
-rw-r--r-- | src/modules/px4iofirmware/px4io.h | 10 | ||||
-rw-r--r-- | src/modules/px4iofirmware/registers.c | 16 | ||||
-rw-r--r-- | src/modules/px4iofirmware/safelink.c | 163 | ||||
-rw-r--r-- | src/modules/px4iofirmware/sbus.c | 158 |
10 files changed, 471 insertions, 65 deletions
diff --git a/src/drivers/drv_pwm_output.h b/src/drivers/drv_pwm_output.h index 972573f9f..8b623119f 100644 --- a/src/drivers/drv_pwm_output.h +++ b/src/drivers/drv_pwm_output.h @@ -202,6 +202,10 @@ ORB_DECLARE(output_pwm); /** force safety switch off (to disable use of safety switch) */ #define PWM_SERVO_SET_FORCE_SAFETY_OFF _IOC(_PWM_SERVO_BASE, 23) +#define SAFELINK_CONTROL_INPUT _IOC(_PWM_SERVO_BASE, 24) + +#define SAFELINK_CONTROL_OUTPUT _IOC(_PWM_SERVO_BASE, 25) + /* * * diff --git a/src/drivers/px4io/px4io.cpp b/src/drivers/px4io/px4io.cpp index 24da4c68b..50e6e6059 100644 --- a/src/drivers/px4io/px4io.cpp +++ b/src/drivers/px4io/px4io.cpp @@ -1901,7 +1901,8 @@ PX4IO::print_status(bool extended_status) ((flags & PX4IO_P_STATUS_FLAGS_MIXER_OK) ? " MIXER_OK" : " MIXER_FAIL"), ((flags & PX4IO_P_STATUS_FLAGS_ARM_SYNC) ? " ARM_SYNC" : " ARM_NO_SYNC"), ((flags & PX4IO_P_STATUS_FLAGS_INIT_OK) ? " INIT_OK" : " INIT_FAIL"), - ((flags & PX4IO_P_STATUS_FLAGS_FAILSAFE) ? " FAILSAFE" : "")); + ((flags & PX4IO_P_STATUS_FLAGS_FAILSAFE) ? " FAILSAFE" : ""), + ((flags & PX4IO_P_STATUS_FLAGS_SAFELINK_FS) ? " SAFELINK_FAILSAFE" : "")); uint16_t alarms = io_reg_get(PX4IO_PAGE_STATUS, PX4IO_P_STATUS_ALARMS); printf("alarms 0x%04x%s%s%s%s%s%s%s%s\n", alarms, @@ -1994,7 +1995,9 @@ PX4IO::print_status(bool extended_status) ((features & PX4IO_P_SETUP_FEATURES_SBUS1_OUT) ? " S.BUS1_OUT" : ""), ((features & PX4IO_P_SETUP_FEATURES_SBUS2_OUT) ? " S.BUS2_OUT" : ""), ((features & PX4IO_P_SETUP_FEATURES_PWM_RSSI) ? " RSSI_PWM" : ""), - ((features & PX4IO_P_SETUP_FEATURES_ADC_RSSI) ? " RSSI_ADC" : "") + ((features & PX4IO_P_SETUP_FEATURES_ADC_RSSI) ? " RSSI_ADC" : ""), + ((features & PX4IO_P_SETUP_FEATURES_SAFELINK_IN) ? " SAFELINK_IN" : ""), + ((features & PX4IO_P_SETUP_FEATURES_SAFELINK_OUT) ? " SAFELINK_OUT" : "") ); uint16_t arming = io_reg_get(PX4IO_PAGE_SETUP, PX4IO_P_SETUP_ARMING); printf("arming 0x%04x%s%s%s%s%s%s%s\n", @@ -2475,6 +2478,26 @@ PX4IO::ioctl(file * filep, int cmd, unsigned long arg) break; + case SAFELINK_CONTROL_INPUT: + + if (arg == 1) { + ret = io_reg_modify(PX4IO_PAGE_SETUP, PX4IO_P_SETUP_FEATURES, 0, PX4IO_P_SETUP_FEATURES_SAFELINK_IN); + } else { + ret = io_reg_modify(PX4IO_PAGE_SETUP, PX4IO_P_SETUP_FEATURES, PX4IO_P_SETUP_FEATURES_SAFELINK_IN, 0); + } + + break; + + case SAFELINK_CONTROL_OUTPUT: + + if (arg == 1) { + ret = io_reg_modify(PX4IO_PAGE_SETUP, PX4IO_P_SETUP_FEATURES, 0, PX4IO_P_SETUP_FEATURES_SAFELINK_OUT); + } else { + ret = io_reg_modify(PX4IO_PAGE_SETUP, PX4IO_P_SETUP_FEATURES, PX4IO_P_SETUP_FEATURES_SAFELINK_OUT, 0); + } + + break; + default: /* see if the parent class can make any use of it */ ret = CDev::ioctl(filep, cmd, arg); @@ -3213,6 +3236,32 @@ px4io_main(int argc, char *argv[]) exit(0); } + if (!strcmp(argv[1], "safelink_in")) { + /* we can cheat and call the driver directly, as it + * doesn't reference filp in ioctl() + */ + int ret = g_dev->ioctl(nullptr, SAFELINK_CONTROL_INPUT, 1); + + if (ret != 0) { + errx(ret, "SAFELINK input failed"); + } + + exit(0); + } + + if (!strcmp(argv[1], "safelink_out")) { + /* we can cheat and call the driver directly, as it + * doesn't reference filp in ioctl() + */ + int ret = g_dev->ioctl(nullptr, SAFELINK_CONTROL_OUTPUT, 1); + + if (ret != 0) { + errx(ret, "SAFELINK output failed"); + } + + exit(0); + } + if (!strcmp(argv[1], "rssi_analog")) { /* we can cheat and call the driver directly, as it * doesn't reference filp in ioctl() diff --git a/src/modules/px4iofirmware/controls.c b/src/modules/px4iofirmware/controls.c index 185cb20dd..4f5a86fcb 100644 --- a/src/modules/px4iofirmware/controls.c +++ b/src/modules/px4iofirmware/controls.c @@ -67,8 +67,11 @@ controls_init(void) /* DSM input (USART1) */ dsm_init("/dev/ttyS0"); - /* S.bus input (USART3) */ - sbus_init("/dev/ttyS2"); + /* S.BUS input (USART3) */ + int sbus_fd = sbus_init("/dev/ttyS2"); + + /* Safelink init (USART3) */ + safelink_init(sbus_fd); /* default to a 1:1 input map, all enabled */ for (unsigned i = 0; i < PX4IO_RC_INPUT_CHANNELS; i++) { @@ -132,34 +135,42 @@ controls_tick() { } perf_end(c_gather_dsm); - perf_begin(c_gather_sbus); + bool sbus_updated; - bool sbus_failsafe, sbus_frame_drop; - bool sbus_updated = sbus_input(r_raw_rc_values, &r_raw_rc_count, &sbus_failsafe, &sbus_frame_drop, PX4IO_RC_INPUT_CHANNELS); + if (r_page_setup[PX4IO_P_SETUP_FEATURES] & PX4IO_P_SETUP_FEATURES_SAFELINK_IN) { + bool safelink_failsafe; + bool safelink_updated = safelink_input(r_safelink_values, &r_safelink_count, &safelink_failsafe, PX4IO_SERVO_COUNT); + } else { - if (sbus_updated) { - r_status_flags |= PX4IO_P_STATUS_FLAGS_RC_SBUS; + perf_begin(c_gather_sbus); - rssi = 255; + bool sbus_failsafe, sbus_frame_drop; + sbus_updated = sbus_input(r_raw_rc_values, &r_raw_rc_count, &sbus_failsafe, &sbus_frame_drop, PX4IO_RC_INPUT_CHANNELS); - if (sbus_frame_drop) { - r_raw_rc_flags |= PX4IO_P_RAW_RC_FLAGS_FRAME_DROP; - rssi = 100; - } else { - r_raw_rc_flags &= ~(PX4IO_P_RAW_RC_FLAGS_FRAME_DROP); - } + if (sbus_updated) { + r_status_flags |= PX4IO_P_STATUS_FLAGS_RC_SBUS; + + rssi = 255; + + if (sbus_frame_drop) { + r_raw_rc_flags |= PX4IO_P_RAW_RC_FLAGS_FRAME_DROP; + rssi = 100; + } else { + r_raw_rc_flags &= ~(PX4IO_P_RAW_RC_FLAGS_FRAME_DROP); + } + + if (sbus_failsafe) { + r_raw_rc_flags |= PX4IO_P_RAW_RC_FLAGS_FAILSAFE; + rssi = 0; + } else { + r_raw_rc_flags &= ~(PX4IO_P_RAW_RC_FLAGS_FAILSAFE); + } - if (sbus_failsafe) { - r_raw_rc_flags |= PX4IO_P_RAW_RC_FLAGS_FAILSAFE; - rssi = 0; - } else { - r_raw_rc_flags &= ~(PX4IO_P_RAW_RC_FLAGS_FAILSAFE); } + perf_end(c_gather_sbus); } - perf_end(c_gather_sbus); - /* * XXX each S.bus frame will cause a PPM decoder interrupt * storm (lots of edges). It might be sensible to actually diff --git a/src/modules/px4iofirmware/mixer.cpp b/src/modules/px4iofirmware/mixer.cpp index 2f721bf1e..3a82a5821 100644 --- a/src/modules/px4iofirmware/mixer.cpp +++ b/src/modules/px4iofirmware/mixer.cpp @@ -79,7 +79,8 @@ enum mixer_source { MIX_FMU, MIX_OVERRIDE, MIX_FAILSAFE, - MIX_OVERRIDE_FMU_OK + MIX_OVERRIDE_FMU_OK, + MIX_SAFELINK }; static mixer_source source; @@ -159,6 +160,18 @@ mixer_tick(void) */ if (source == MIX_FAILSAFE) { r_status_flags |= PX4IO_P_STATUS_FLAGS_FAILSAFE; + + /* + * Check if we can fall back in safelink failsave + */ + + if ((r_page_setup[PX4IO_P_SETUP_FEATURES] & PX4IO_P_SETUP_FEATURES_SAFELINK_OUT) && (r_safelink_count > 0)) { + source = MIX_SAFELINK; + r_status_flags |= PX4IO_P_STATUS_FLAGS_SAFELINK_FS; + } else { + r_status_flags &= ~(PX4IO_P_STATUS_FLAGS_SAFELINK_FS); + } + } else { r_status_flags &= ~(PX4IO_P_STATUS_FLAGS_FAILSAFE); } @@ -187,15 +200,35 @@ mixer_tick(void) && (r_status_flags & PX4IO_P_STATUS_FLAGS_INIT_OK) && (r_status_flags & PX4IO_P_STATUS_FLAGS_FMU_OK); + unsigned actuator_count = 0; + /* * Run the mixers. */ - if (source == MIX_FAILSAFE) { + if (source == MIX_SAFELINK) { + + actuator_count = r_safelink_count; + + /* copy failsafe values to the servo outputs */ + for (unsigned i = 0; i < PX4IO_SERVO_COUNT; i++) { + r_page_servos[i] = r_safelink_values[i]; + + /* safe actuators for FMU feedback */ + r_page_actuators[i] = FLOAT_TO_REG((r_page_servos[i] - 1500) / 600.0f); + } + + } else if (source == MIX_FAILSAFE) { + + actuator_count = 0; /* copy failsafe values to the servo outputs */ for (unsigned i = 0; i < PX4IO_SERVO_COUNT; i++) { r_page_servos[i] = r_page_servo_failsafe[i]; + /* get the count of the last valid actuator */ + if ((r_page_servos[i] != 0) && ((i + 1) > actuator_count)) + actuator_count = i + 1; + /* safe actuators for FMU feedback */ r_page_actuators[i] = FLOAT_TO_REG((r_page_servos[i] - 1500) / 600.0f); } @@ -211,6 +244,7 @@ mixer_tick(void) /* poor mans mutex */ in_mixer = true; mixed = mixer_group.mix(&outputs[0], PX4IO_SERVO_COUNT); + actuator_count = mixed; in_mixer = false; /* the pwm limit call takes care of out of band errors */ @@ -255,12 +289,19 @@ mixer_tick(void) for (unsigned i = 0; i < PX4IO_SERVO_COUNT; i++) up_pwm_servo_set(i, r_page_servos[i]); - /* set S.BUS1 or S.BUS2 outputs */ + /* update S.BUS1 outputs */ + if (r_setup_features & PX4IO_P_SETUP_FEATURES_SBUS1_OUT) { + sbus1_output(r_page_servos, actuator_count); + } + /* update S.BUS2 outputs */ if (r_setup_features & PX4IO_P_SETUP_FEATURES_SBUS2_OUT) { - sbus2_output(r_page_servos, PX4IO_SERVO_COUNT); - } else if (r_setup_features & PX4IO_P_SETUP_FEATURES_SBUS1_OUT) { - sbus1_output(r_page_servos, PX4IO_SERVO_COUNT); + sbus2_output(r_page_servos, actuator_count); + } + + /* update safelink outputs */ + if (source != MIX_FAILSAFE && r_setup_features & PX4IO_P_SETUP_FEATURES_SAFELINK_OUT) { + safelink_output(r_page_servos, actuator_count); } } else if (mixer_servos_armed && should_always_enable_pwm) { @@ -268,12 +309,20 @@ mixer_tick(void) for (unsigned i = 0; i < PX4IO_SERVO_COUNT; i++) up_pwm_servo_set(i, r_page_servo_disarmed[i]); - /* set S.BUS1 or S.BUS2 outputs */ - if (r_setup_features & PX4IO_P_SETUP_FEATURES_SBUS1_OUT) - sbus1_output(r_page_servos, PX4IO_SERVO_COUNT); + /* update S.BUS1 outputs */ + if (r_setup_features & PX4IO_P_SETUP_FEATURES_SBUS1_OUT) { + sbus1_output(r_page_servos, actuator_count); + } + + /* update S.BUS2 outputs */ + if (r_setup_features & PX4IO_P_SETUP_FEATURES_SBUS2_OUT) { + sbus2_output(r_page_servos, actuator_count); + } - if (r_setup_features & PX4IO_P_SETUP_FEATURES_SBUS2_OUT) - sbus2_output(r_page_servos, PX4IO_SERVO_COUNT); + /* update safelink outputs */ + if (source != MIX_FAILSAFE && r_setup_features & PX4IO_P_SETUP_FEATURES_SAFELINK_OUT) { + safelink_output(r_page_servos, actuator_count); + } } } diff --git a/src/modules/px4iofirmware/module.mk b/src/modules/px4iofirmware/module.mk index 01869569f..fff88fe99 100644 --- a/src/modules/px4iofirmware/module.mk +++ b/src/modules/px4iofirmware/module.mk @@ -7,6 +7,7 @@ SRCS = adc.c \ registers.c \ safety.c \ sbus.c \ + safelink.c \ ../systemlib/up_cxxinitialize.c \ ../systemlib/perf_counter.c \ mixer.cpp \ diff --git a/src/modules/px4iofirmware/protocol.h b/src/modules/px4iofirmware/protocol.h index d5a6b1ec4..b55ac698b 100644 --- a/src/modules/px4iofirmware/protocol.h +++ b/src/modules/px4iofirmware/protocol.h @@ -111,6 +111,7 @@ #define PX4IO_P_STATUS_FLAGS_INIT_OK (1 << 10) /* initialisation of the IO completed without error */ #define PX4IO_P_STATUS_FLAGS_FAILSAFE (1 << 11) /* failsafe is active */ #define PX4IO_P_STATUS_FLAGS_SAFETY_OFF (1 << 12) /* safety is off */ +#define PX4IO_P_STATUS_FLAGS_SAFELINK_FS (1 << 13) /* SAFELINK failsave is active */ #define PX4IO_P_STATUS_ALARMS 3 /* alarm flags - alarms latch, write 1 to a bit to clear it */ #define PX4IO_P_STATUS_ALARMS_VBATT_LOW (1 << 0) /* [1] VBatt is very close to regulator dropout */ @@ -162,6 +163,10 @@ #define PX4IO_PAGE_PWM_INFO 7 #define PX4IO_RATE_MAP_BASE 0 /* 0..CONFIG_ACTUATOR_COUNT bitmaps of PWM rate groups */ +#define PX4IO_PAGE_SAFELINK 8 /**< redundancy link page */ +#define PX4IO_P_SAFELINK_COUNT 0 /**< number of inputs */ +#define PX4IO_P_SAFELINK_BASE 1 /**< index of safelink actuator zero */ + /* setup page */ #define PX4IO_PAGE_SETUP 50 #define PX4IO_P_SETUP_FEATURES 0 @@ -169,6 +174,8 @@ #define PX4IO_P_SETUP_FEATURES_SBUS2_OUT (1 << 1) /**< enable S.Bus v2 output */ #define PX4IO_P_SETUP_FEATURES_PWM_RSSI (1 << 2) /**< enable PWM RSSI parsing */ #define PX4IO_P_SETUP_FEATURES_ADC_RSSI (1 << 3) /**< enable ADC RSSI parsing */ +#define PX4IO_P_SETUP_FEATURES_SAFELINK_IN (1 << 4) /**< enable SAFELINK input parsing on S.BUS port */ +#define PX4IO_P_SETUP_FEATURES_SAFELINK_OUT (1 << 5) /**< enable SAFELINK output on S.BUS port */ #define PX4IO_P_SETUP_ARMING 1 /* arming controls */ #define PX4IO_P_SETUP_ARMING_IO_ARM_OK (1 << 0) /* OK to arm the IO side */ diff --git a/src/modules/px4iofirmware/px4io.h b/src/modules/px4iofirmware/px4io.h index b00a96717..b809ccf7e 100644 --- a/src/modules/px4iofirmware/px4io.h +++ b/src/modules/px4iofirmware/px4io.h @@ -77,6 +77,7 @@ extern uint16_t r_page_servos[]; /* PX4IO_PAGE_SERVOS */ extern uint16_t r_page_raw_rc_input[]; /* PX4IO_PAGE_RAW_RC_INPUT */ extern uint16_t r_page_rc_input[]; /* PX4IO_PAGE_RC_INPUT */ extern uint16_t r_page_adc[]; /* PX4IO_PAGE_RAW_ADC_INPUT */ +extern uint16_t r_page_safelink[]; /* PX4IO_PAGE_SAFELINK */ extern volatile uint16_t r_page_setup[]; /* PX4IO_PAGE_SETUP */ extern volatile uint16_t r_page_controls[]; /* PX4IO_PAGE_CONTROLS */ @@ -111,6 +112,8 @@ extern uint16_t r_page_servo_disarmed[]; /* PX4IO_PAGE_DISARMED_PWM */ #define r_setup_rc_thr_failsafe r_page_setup[PX4IO_P_SETUP_RC_THR_FAILSAFE_US] #define r_control_values (&r_page_controls[0]) +#define r_safelink_values (&r_page_safelink[PX4IO_P_SAFELINK_BASE]) +#define r_safelink_count r_page_safelink[PX4IO_P_SAFELINK_COUNT] /* * System state structure. @@ -222,6 +225,13 @@ extern bool sbus_input(uint16_t *values, uint16_t *num_values, bool *sbus_failsa extern void sbus1_output(uint16_t *values, uint16_t num_values); extern void sbus2_output(uint16_t *values, uint16_t num_values); +extern void sbus1_output(uint16_t *values, uint16_t num_values); +extern void sbus2_output(uint16_t *values, uint16_t num_values); + +extern int safelink_init(int fd); +extern void safelink_output(uint16_t *values, uint16_t num_values); +extern bool safelink_input(uint16_t *values, uint16_t *num_values, bool *failsafe, uint16_t max_channels); + /** global debug level for isr_debug() */ extern volatile uint8_t debug_level; diff --git a/src/modules/px4iofirmware/registers.c b/src/modules/px4iofirmware/registers.c index b37259997..03849cf65 100644 --- a/src/modules/px4iofirmware/registers.c +++ b/src/modules/px4iofirmware/registers.c @@ -141,6 +141,16 @@ uint16_t r_page_rc_input[] = { uint16_t r_page_scratch[32]; /** + * PAGE 8 + * + * Inputs from redundant autopilot + */ +uint16_t r_page_safelink[] = { + [PX4IO_P_SAFELINK_COUNT] = 0, + [PX4IO_P_SAFELINK_BASE ... (PX4IO_P_SAFELINK_BASE + PX4IO_SERVO_COUNT)] = 0 +}; + +/** * PAGE 100 * * Setup registers @@ -178,7 +188,9 @@ volatile uint16_t r_page_setup[] = #define PX4IO_P_SETUP_FEATURES_VALID (PX4IO_P_SETUP_FEATURES_SBUS1_OUT | \ PX4IO_P_SETUP_FEATURES_SBUS2_OUT | \ PX4IO_P_SETUP_FEATURES_ADC_RSSI | \ - PX4IO_P_SETUP_FEATURES_PWM_RSSI) + PX4IO_P_SETUP_FEATURES_PWM_RSSI | \ + PX4IO_P_SETUP_FEATURES_SAFELINK_IN | \ + PX4IO_P_SETUP_FEATURES_SAFELINK_OUT) #else #define PX4IO_P_SETUP_FEATURES_VALID 0 #endif @@ -494,6 +506,8 @@ registers_set_one(uint8_t page, uint8_t offset, uint16_t value) PX4IO_P_SETUP_FEATURES_SBUS2_OUT); } + // XXX handle safelink enabling / disabling here. + /* apply changes */ r_setup_features = value; diff --git a/src/modules/px4iofirmware/safelink.c b/src/modules/px4iofirmware/safelink.c new file mode 100644 index 000000000..f5c91d0fa --- /dev/null +++ b/src/modules/px4iofirmware/safelink.c @@ -0,0 +1,163 @@ +/**************************************************************************** + * + * Copyright (c) 2012-2014 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 safelink.c + * + * Serial protocol encoder / decoder for the <put a good name here> + * simple redundancy protocol (SRP) + */ + +#include <nuttx/config.h> + +#include <fcntl.h> +#include <unistd.h> +#include <termios.h> +#include <stdbool.h> + +#include <drivers/drv_hrt.h> +#include "px4io.h" + +#define SAFELINK_FRAME_SIZE (2 + 2 * PX4IO_SERVO_COUNT + 2) +#define SAFELINK_STX1 0xFA +#define SAFELINK_STX2 0x01 /**< version */ + +static int safelink_fd = -1; + +static hrt_abstime last_rx_time; +static hrt_abstime last_frame_time; + +static uint8_t frame[SAFELINK_FRAME_SIZE]; + +static unsigned partial_frame_count; + +unsigned safelink_frame_drops; + +static bool safelink_decode(hrt_abstime frame_time, uint16_t *values, uint16_t *num_values, bool *failsafe, uint16_t max_channels); + +int +safelink_init(int fd) +{ + safelink_fd = fd; + return OK; + + r_safelink_count = 0; +} + +void +safelink_output(uint16_t *values, uint16_t num_values) +{ + uint8_t buf[SAFELINK_FRAME_SIZE]; + buf[0] = SAFELINK_STX1; + buf[1] = SAFELINK_STX2; + + for (unsigned i = 0; (i < num_values) && (i < PX4IO_SERVO_COUNT); i++) { + uint8_t *dp = &buf[2 + (2 * i)]; + dp[0] = values[i] >> 8; + dp[1] = values[i]; + } + + buf[SAFELINK_FRAME_SIZE - 2] = 0x11; + buf[SAFELINK_FRAME_SIZE - 1] = 0xFF; +} + +bool +safelink_input(uint16_t *values, uint16_t *num_values, bool *failsafe, uint16_t max_channels) +{ + ssize_t ret; + hrt_abstime now; + bool decode_ret = false; + + /* + * The S.BUS protocol doesn't provide reliable framing, + * so we detect frame boundaries by the inter-frame delay. + * + * The minimum frame spacing is 7ms; with 25 bytes at 100000bps + * frame transmission time is ~2ms. + * + * We expect to only be called when bytes arrive for processing, + * and if an interval of more than 3ms passes between calls, + * the first byte we read will be the first byte of a frame. + * + * In the case where byte(s) are dropped from a frame, this also + * provides a degree of protection. Of course, it would be better + * if we didn't drop bytes... + */ + now = hrt_absolute_time(); + + if ((now - last_rx_time) > 3000) { + if (partial_frame_count > 0) { + safelink_frame_drops++; + partial_frame_count = 0; + } + } + + /* + * Fetch bytes, but no more than we would need to complete + * the current frame. + */ + ret = read(safelink_fd, &frame[partial_frame_count], SAFELINK_FRAME_SIZE - partial_frame_count); + + /* if the read failed for any reason, just give up here */ + if (ret < 1) + return false; + + if (partial_frame_count < SAFELINK_FRAME_SIZE) + decode_ret = false; + + safelink_decode(now, values, num_values, failsafe, max_channels); + + /* return false as default */ + return decode_ret; +} + +static bool safelink_decode(hrt_abstime frame_time, uint16_t *values, uint16_t *num_values, bool *failsafe, uint16_t max_channels) +{ + /* check frame boundary markers to avoid out-of-sync cases */ + if ((frame[0] != SAFELINK_STX1) || frame[1] != SAFELINK_STX2) { + safelink_frame_drops++; + return false; + } + + /* XXX check checksum here */ + + + /* if we make it down here, we have valid data */ + last_frame_time = frame_time; + + for (unsigned i = 0; (i < max_channels) && (i < PX4IO_SERVO_COUNT); i++) { + uint8_t *dp = &frame[2 + (2 * i)]; + values[i] = (dp[0] << 8) | dp[1]; + } +} + diff --git a/src/modules/px4iofirmware/sbus.c b/src/modules/px4iofirmware/sbus.c index 0f0414148..1db22b7fc 100644 --- a/src/modules/px4iofirmware/sbus.c +++ b/src/modules/px4iofirmware/sbus.c @@ -52,7 +52,10 @@ #include "protocol.h" #include "debug.h" -#define SBUS_FRAME_SIZE 25 +#define SBUS_FRAME_SIZE 25 +#define SBUS2_FRAME_SIZE_RX_VOLTAGE 3 +#define SBUS2_FRAME_SIZE_GPS_DIGIT 3 + #define SBUS_INPUT_CHANNELS 16 #define SBUS_FLAGS_BYTE 23 #define SBUS_FAILSAFE_BIT 3 @@ -81,7 +84,15 @@ static int sbus_fd = -1; static hrt_abstime last_rx_time; static hrt_abstime last_frame_time; -static uint8_t frame[SBUS_FRAME_SIZE]; +static enum SBUS2_DECODE_STATE { + SBUS2_DECODE_STATE_DESYNC = 0xFFF, + SBUS2_DECODE_STATE_SBUS1_SYNC = 0x00, + SBUS2_DECODE_STATE_SBUS2_SYNC = 0x1FF, + SBUS2_DECODE_STATE_SBUS2_RX_VOLTAGE = 0x04, + SBUS2_DECODE_STATE_SBUS2_GPS = 0x14 +} sbus_decode_state; + +static uint8_t frame[SBUS_FRAME_SIZE + (SBUS_FRAME_SIZE / 2)]; static unsigned partial_frame_count; @@ -113,6 +124,9 @@ sbus_init(const char *device) } else { debug("S.Bus: open failed"); } + + sbus_decode_state = SBUS2_DECODE_STATE_DESYNC; + return sbus_fd; } @@ -137,7 +151,7 @@ sbus_input(uint16_t *values, uint16_t *num_values, bool *sbus_failsafe, bool *sb hrt_abstime now; /* - * The S.bus protocol doesn't provide reliable framing, + * The S.BUS protocol doesn't provide reliable framing, * so we detect frame boundaries by the inter-frame delay. * * The minimum frame spacing is 7ms; with 25 bytes at 100000bps @@ -177,18 +191,99 @@ sbus_input(uint16_t *values, uint16_t *num_values, bool *sbus_failsafe, bool *sb */ partial_frame_count += ret; - /* - * If we don't have a full frame, return + /* this is set by the decoding state machine and will default to false + * once everything that was decodable has been decoded. */ - if (partial_frame_count < SBUS_FRAME_SIZE) - return false; + bool decode_ret = true; + + /* keep decoding until we have consumed the buffer or have given up */ + for (unsigned d = 0; (d < 10) && (partial_frame_count > 0) && (decode_ret); d++) { + unsigned n_consumed = 0; + + switch (sbus_decode_state) { + case SBUS2_DECODE_STATE_DESYNC: + /* fall through */ + case SBUS2_DECODE_STATE_SBUS1_SYNC: + /* fall through */ + case SBUS2_DECODE_STATE_SBUS2_SYNC: + { + /* decode whatever we got */ + if (partial_frame_count < SBUS_FRAME_SIZE) + decode_ret = false; + + /* + * Great, it looks like we might have a frame. Go ahead and + * decode it. + */ + n_consumed = SBUS_FRAME_SIZE; + return sbus_decode(now, values, num_values, sbus_failsafe, sbus_frame_drop, max_channels); + } + break; + case SBUS2_DECODE_STATE_SBUS2_RX_VOLTAGE: + { + if (partial_frame_count < SBUS2_FRAME_SIZE_RX_VOLTAGE) + decode_ret = false; + + /* find out which payload we're dealing with in this slot */ + switch(frame[0]) { + case 0x03: + { + uint16_t rx_voltage = (frame[1] << 8) | frame[2]; + n_consumed = 3; + } + break; + case 0x0F: + /* the battery slot is unused and followed by a normal frame */ + sbus_decode_state = SBUS2_DECODE_STATE_SBUS2_SYNC; + break; + default: + /* this is not what we expect it to be, go back to sync */ + sbus_decode_state = SBUS2_DECODE_STATE_DESYNC; + /* throw unknown bytes away */ + n_consumed = 3; + } + } + break; + case SBUS2_DECODE_STATE_SBUS2_GPS: + { + if (partial_frame_count < SBUS2_FRAME_SIZE_GPS_DIGIT) + decode_ret = false; + + /* find out which payload we're dealing with in this slot */ + switch(frame[0]) { + case 0x13: + { + uint16_t gps_something = (frame[1] << 8) | frame[2]; + n_consumed = 3; + } + break; + case 0x0F: + /* the GPS slot is unused and followed by a normal frame */ + sbus_decode_state = SBUS2_DECODE_STATE_SBUS2_SYNC; + break; + default: + /* this is not what we expect it to be, go back to sync */ + sbus_decode_state = SBUS2_DECODE_STATE_DESYNC; + /* throw unknown bytes away */ + n_consumed = 3; + } + } + break; + default: + decode_ret = false; + } - /* - * Great, it looks like we might have a frame. Go ahead and - * decode it. - */ - partial_frame_count = 0; - return sbus_decode(now, values, num_values, sbus_failsafe, sbus_frame_drop, max_channels); + /* move buffer to start after this step, keep decoding if any bytes remain */ + + /* XXX should be still more efficient than single-byte ringbuffer accesses */ + uint8_t frame_buf[SBUS_FRAME_SIZE]; + memcpy(&frame_buf[0], &frame[n_consumed], partial_frame_count - n_consumed); + memcpy(&frame[0], &frame_buf[0], partial_frame_count - n_consumed); + partial_frame_count = partial_frame_count - n_consumed; + } + + /* return false as default */ + return decode_ret; } /* @@ -238,24 +333,27 @@ sbus_decode(hrt_abstime frame_time, uint16_t *values, uint16_t *num_values, bool switch (frame[24]) { case 0x00: - /* this is S.BUS 1 */ - break; - case 0x03: - /* S.BUS 2 SLOT0: RX battery and external voltage */ - break; - case 0x83: - /* S.BUS 2 SLOT1 */ - break; - case 0x43: - case 0xC3: - case 0x23: - case 0xA3: - case 0x63: - case 0xE3: - break; + /* this is S.BUS 1 */ + sbus_decode_state = SBUS2_DECODE_STATE_SBUS1_SYNC; + break; + case 0x04: + /* receiver voltage */ + sbus_decode_state = SBUS2_DECODE_STATE_SBUS2_RX_VOLTAGE; + break; + case 0x14: + /* GPS / baro */ + sbus_decode_state = SBUS2_DECODE_STATE_SBUS2_GPS; + break; + case 0x24: + /* Unknown SBUS2 data */ + sbus_decode_state = SBUS2_DECODE_STATE_SBUS2_SYNC; + break; + case 0x34: + /* Unknown SBUS2 data */ + sbus_decode_state = SBUS2_DECODE_STATE_SBUS2_SYNC; + break; default: - /* we expect one of the bits above, but there are some we don't know yet */ - break; + return false; } /* we have received something we think is a frame */ |