/** ****************************************************************************** * @addtogroup PIOS PIOS Core hardware abstraction layer * @{ * @addtogroup PIOS_SBus Futaba S.Bus receiver functions * @brief Code to read Futaba S.Bus receiver serial stream * @{ * * @file pios_sbus.c * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2011. * @brief Code to read Futaba S.Bus receiver serial stream * @see The GNU Public License (GPL) Version 3 * *****************************************************************************/ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "pios.h" #ifdef PIOS_INCLUDE_SBUS // Define to report number of frames since last dropped instead of weighted ave #undef SBUS_GOOD_FRAME_COUNT #include #include "pios_sbus_priv.h" /* Forward Declarations */ static int32_t PIOS_SBus_Get(uint32_t rcvr_id, uint8_t channel); static uint16_t PIOS_SBus_RxInCallback(uint32_t context, uint8_t *buf, uint16_t buf_len, uint16_t *headroom, bool *need_yield); static void PIOS_SBus_Supervisor(uint32_t sbus_id); static uint8_t PIOS_SBus_Quality_Get(uint32_t rcvr_id); /* Local Variables */ const struct pios_rcvr_driver pios_sbus_rcvr_driver = { .read = PIOS_SBus_Get, .get_quality = PIOS_SBus_Quality_Get }; enum pios_sbus_dev_magic { PIOS_SBUS_DEV_MAGIC = 0x53427573, }; struct pios_sbus_state { uint16_t channel_data[PIOS_SBUS_NUM_INPUTS]; uint8_t received_data[SBUS_FRAME_LENGTH - 2]; uint8_t receive_timer; uint8_t failsafe_timer; uint8_t frame_found; uint8_t byte_count; float quality; #ifdef SBUS_GOOD_FRAME_COUNT uint8_t frame_count; #endif /* SBUS_GOOD_FRAME_COUNT */ }; /* With an S.Bus frame rate of 7ms (130Hz) averaging over 26 samples * gives about a 200ms response. */ #define SBUS_FL_WEIGHTED_AVE 26 struct pios_sbus_dev { enum pios_sbus_dev_magic magic; const struct pios_sbus_cfg *cfg; struct pios_sbus_state state; }; /* Allocate S.Bus device descriptor */ #if defined(PIOS_INCLUDE_FREERTOS) static struct pios_sbus_dev *PIOS_SBus_Alloc(void) { struct pios_sbus_dev *sbus_dev; sbus_dev = (struct pios_sbus_dev *)pios_malloc(sizeof(*sbus_dev)); if (!sbus_dev) { return NULL; } sbus_dev->magic = PIOS_SBUS_DEV_MAGIC; return sbus_dev; } #else static struct pios_sbus_dev pios_sbus_devs[PIOS_SBUS_MAX_DEVS]; static uint8_t pios_sbus_num_devs; static struct pios_sbus_dev *PIOS_SBus_Alloc(void) { struct pios_sbus_dev *sbus_dev; if (pios_sbus_num_devs >= PIOS_SBUS_MAX_DEVS) { return NULL; } sbus_dev = &pios_sbus_devs[pios_sbus_num_devs++]; sbus_dev->magic = PIOS_SBUS_DEV_MAGIC; return sbus_dev; } #endif /* if defined(PIOS_INCLUDE_FREERTOS) */ /* Validate S.Bus device descriptor */ static bool PIOS_SBus_Validate(struct pios_sbus_dev *sbus_dev) { return sbus_dev->magic == PIOS_SBUS_DEV_MAGIC; } /* Reset channels in case of lost signal or explicit failsafe receiver flag */ static void PIOS_SBus_ResetChannels(struct pios_sbus_state *state) { for (int i = 0; i < PIOS_SBUS_NUM_INPUTS; i++) { state->channel_data[i] = PIOS_RCVR_TIMEOUT; } } /* Reset S.Bus receiver state */ static void PIOS_SBus_ResetState(struct pios_sbus_state *state) { state->receive_timer = 0; state->failsafe_timer = 0; state->frame_found = 0; state->quality = 0.0f; #ifdef SBUS_GOOD_FRAME_COUNT state->frame_count = 0; #endif /* SBUS_GOOD_FRAME_COUNT */ PIOS_SBus_ResetChannels(state); } /* Initialise S.Bus receiver interface */ int32_t PIOS_SBus_Init(uint32_t *sbus_id, const struct pios_sbus_cfg *cfg, const struct pios_com_driver *driver, uint32_t lower_id) { PIOS_DEBUG_Assert(sbus_id); PIOS_DEBUG_Assert(cfg); PIOS_DEBUG_Assert(driver); struct pios_sbus_dev *sbus_dev; sbus_dev = (struct pios_sbus_dev *)PIOS_SBus_Alloc(); if (!sbus_dev) { goto out_fail; } /* Bind the configuration to the device instance */ sbus_dev->cfg = cfg; PIOS_SBus_ResetState(&(sbus_dev->state)); *sbus_id = (uint32_t)sbus_dev; /* Enable inverter clock and enable the inverter */ (*cfg->gpio_clk_func)(cfg->gpio_clk_periph, ENABLE); GPIO_Init(cfg->inv.gpio, &cfg->inv.init); GPIO_WriteBit(cfg->inv.gpio, cfg->inv.init.GPIO_Pin, cfg->gpio_inv_enable); /* Set comm driver callback */ (driver->bind_rx_cb)(lower_id, PIOS_SBus_RxInCallback, *sbus_id); if (!PIOS_RTC_RegisterTickCallback(PIOS_SBus_Supervisor, *sbus_id)) { PIOS_DEBUG_Assert(0); } return 0; out_fail: return -1; } /** * Get the value of an input channel * \param[in] channel Number of the channel desired (zero based) * \output PIOS_RCVR_INVALID channel not available * \output PIOS_RCVR_TIMEOUT failsafe condition or missing receiver * \output >=0 channel value */ static int32_t PIOS_SBus_Get(uint32_t rcvr_id, uint8_t channel) { struct pios_sbus_dev *sbus_dev = (struct pios_sbus_dev *)rcvr_id; if (!PIOS_SBus_Validate(sbus_dev)) { return PIOS_RCVR_INVALID; } /* return error if channel is not available */ if (channel >= PIOS_SBUS_NUM_INPUTS) { return PIOS_RCVR_INVALID; } return sbus_dev->state.channel_data[channel]; } /** * Compute channel_data[] from received_data[]. * For efficiency it unrolls first 8 channels without loops and does the * same for other 8 channels. Also 2 discrete channels will be set. */ static void PIOS_SBus_UnrollChannels(struct pios_sbus_state *state) { uint8_t *s = state->received_data; uint16_t *d = state->channel_data; #define F(v, s) (((v) >> (s)) & 0x7ff) /* unroll channels 1-8 */ *d++ = F(s[0] | s[1] << 8, 0); *d++ = F(s[1] | s[2] << 8, 3); *d++ = F(s[2] | s[3] << 8 | s[4] << 16, 6); *d++ = F(s[4] | s[5] << 8, 1); *d++ = F(s[5] | s[6] << 8, 4); *d++ = F(s[6] | s[7] << 8 | s[8] << 16, 7); *d++ = F(s[8] | s[9] << 8, 2); *d++ = F(s[9] | s[10] << 8, 5); /* unroll channels 9-16 */ *d++ = F(s[11] | s[12] << 8, 0); *d++ = F(s[12] | s[13] << 8, 3); *d++ = F(s[13] | s[14] << 8 | s[15] << 16, 6); *d++ = F(s[15] | s[16] << 8, 1); *d++ = F(s[16] | s[17] << 8, 4); *d++ = F(s[17] | s[18] << 8 | s[19] << 16, 7); *d++ = F(s[19] | s[20] << 8, 2); *d++ = F(s[20] | s[21] << 8, 5); /* unroll discrete channels 17 and 18 */ *d++ = (s[22] & SBUS_FLAG_DC1) ? SBUS_VALUE_MAX : SBUS_VALUE_MIN; *d++ = (s[22] & SBUS_FLAG_DC2) ? SBUS_VALUE_MAX : SBUS_VALUE_MIN; } /* Update decoder state processing input byte from the S.Bus stream */ static void PIOS_SBus_UpdateState(struct pios_sbus_state *state, uint8_t b) { /* should not process any data until new frame is found */ if (!state->frame_found) { return; } if (state->byte_count == 0) { if (b != SBUS_SOF_BYTE) { /* discard the whole frame if the 1st byte is not correct */ state->frame_found = 0; } else { /* do not store the SOF byte */ state->byte_count++; } return; } /* do not store last frame byte as well */ if (state->byte_count < SBUS_FRAME_LENGTH - 1) { /* store next byte */ state->received_data[state->byte_count - 1] = b; state->byte_count++; } else { if (b == SBUS_EOF_BYTE || (b & SBUS_R7008SB_EOF_COUNTER_MASK) == 0) { #ifndef SBUS_GOOD_FRAME_COUNT /* Quality trend is towards 0% by default*/ uint8_t quality_trend = 0; #endif /* SBUS_GOOD_FRAME_COUNT */ /* full frame received */ uint8_t flags = state->received_data[SBUS_FRAME_LENGTH - 3]; if (flags & SBUS_FLAG_FL) { /* frame lost, do not update */ #ifdef SBUS_GOOD_FRAME_COUNT state->quality = state->frame_count; state->frame_count = 0; #endif /* SBUS_GOOD_FRAME_COUNT */ } else { #ifdef SBUS_GOOD_FRAME_COUNT if (++state->frame_count == 255) { state->quality = state->frame_count--; } #else /* SBUS_GOOD_FRAME_COUNT */ /* Quality trend is towards 100% */ quality_trend = 100; #endif /* SBUS_GOOD_FRAME_COUNT */ if (flags & SBUS_FLAG_FS) { /* failsafe flag active */ PIOS_SBus_ResetChannels(state); } else { /* data looking good */ PIOS_SBus_UnrollChannels(state); state->failsafe_timer = 0; } } #ifndef SBUS_GOOD_FRAME_COUNT /* Present quality as a weighted average of good frames */ state->quality = ((state->quality * (SBUS_FL_WEIGHTED_AVE - 1)) + quality_trend) / SBUS_FL_WEIGHTED_AVE; #endif /* SBUS_GOOD_FRAME_COUNT */ } else { /* discard whole frame */ } /* prepare for the next frame */ state->frame_found = 0; } } /* Comm byte received callback */ static uint16_t PIOS_SBus_RxInCallback(uint32_t context, uint8_t *buf, uint16_t buf_len, uint16_t *headroom, bool *need_yield) { struct pios_sbus_dev *sbus_dev = (struct pios_sbus_dev *)context; bool valid = PIOS_SBus_Validate(sbus_dev); PIOS_Assert(valid); struct pios_sbus_state *state = &(sbus_dev->state); /* process byte(s) and clear receive timer */ for (uint8_t i = 0; i < buf_len; i++) { PIOS_SBus_UpdateState(state, buf[i]); state->receive_timer = 0; } /* Always signal that we can accept another byte */ if (headroom) { *headroom = SBUS_FRAME_LENGTH; } /* We never need a yield */ *need_yield = false; /* Always indicate that all bytes were consumed */ return buf_len; } /** * Input data supervisor is called periodically and provides * two functions: frame syncing and failsafe triggering. * * S.Bus frames come at 7ms (HS) or 14ms (FS) rate at 100000bps. * RTC timer is running at 625Hz (1.6ms). So with divider 2 it gives * 3.2ms pause between frames which is good for both S.Bus frame rates. * * Data receive function must clear the receive_timer to confirm new * data reception. If no new data received in 100ms, we must call the * failsafe function which clears all channels. */ static void PIOS_SBus_Supervisor(uint32_t sbus_id) { struct pios_sbus_dev *sbus_dev = (struct pios_sbus_dev *)sbus_id; bool valid = PIOS_SBus_Validate(sbus_dev); PIOS_Assert(valid); struct pios_sbus_state *state = &(sbus_dev->state); /* waiting for new frame if no bytes were received in 3.2ms */ if (++state->receive_timer > 2) { state->frame_found = 1; state->byte_count = 0; state->receive_timer = 0; } /* activate failsafe if no frames have arrived in 102.4ms */ if (++state->failsafe_timer > 64) { PIOS_SBus_ResetChannels(state); state->failsafe_timer = 0; } } static uint8_t PIOS_SBus_Quality_Get(uint32_t sbus_id) { struct pios_sbus_dev *sbus_dev = (struct pios_sbus_dev *)sbus_id; bool valid = PIOS_SBus_Validate(sbus_dev); PIOS_Assert(valid); struct pios_sbus_state *state = &(sbus_dev->state); return (uint8_t)(state->quality + 0.5f); } #endif /* PIOS_INCLUDE_SBUS */ /** * @} * @} */