mirror of
https://bitbucket.org/librepilot/librepilot.git
synced 2024-12-10 18:24:11 +01:00
58d0812309
In the previous version the decoder could in rare cases get synced from the middle of data stream in case of data byte equal to the S.Bus start of frame (SOF) byte (wrong data will be rejected but it was not perfect). Now it waits for the real start of frame and then checks the SOF byte.
341 lines
9.3 KiB
C
341 lines
9.3 KiB
C
/**
|
|
******************************************************************************
|
|
* @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
|
|
*/
|
|
|
|
/* Project Includes */
|
|
#include "pios.h"
|
|
#include "pios_sbus_priv.h"
|
|
|
|
#if defined(PIOS_INCLUDE_SBUS)
|
|
|
|
/* 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);
|
|
|
|
|
|
/* Local Variables */
|
|
const struct pios_rcvr_driver pios_sbus_rcvr_driver = {
|
|
.read = PIOS_SBus_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;
|
|
};
|
|
|
|
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 *)pvPortMalloc(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
|
|
|
|
/* 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;
|
|
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) {
|
|
/* full frame received */
|
|
uint8_t flags = state->received_data[SBUS_FRAME_LENGTH - 3];
|
|
if (flags & SBUS_FLAG_FL) {
|
|
/* frame lost, do not update */
|
|
} else if (flags & SBUS_FLAG_FS) {
|
|
/* failsafe flag active */
|
|
PIOS_SBus_ResetChannels(state);
|
|
} else {
|
|
/* data looking good */
|
|
PIOS_SBus_UnrollChannels(state);
|
|
state->failsafe_timer = 0;
|
|
}
|
|
} 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;
|
|
}
|
|
}
|
|
|
|
#endif /* PIOS_INCLUDE_SBUS */
|
|
|
|
/**
|
|
* @}
|
|
* @}
|
|
*/
|