2011-06-13 02:04:49 +02:00
|
|
|
/**
|
|
|
|
******************************************************************************
|
|
|
|
* @addtogroup PIOS PIOS Core hardware abstraction layer
|
|
|
|
* @{
|
|
|
|
* @addtogroup PIOS_SBUS Futaba S.Bus receiver functions
|
|
|
|
* @brief Code to read Futaba S.Bus input
|
|
|
|
* @{
|
|
|
|
*
|
|
|
|
* @file pios_sbus.c
|
|
|
|
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2011.
|
|
|
|
* @brief USART commands. Inits USARTs, controls USARTs & Interrupt handlers. (STM32 dependent)
|
|
|
|
* @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)
|
2011-06-18 22:19:57 +02:00
|
|
|
|
|
|
|
/* Global Variables */
|
|
|
|
|
2011-06-25 15:27:28 +02:00
|
|
|
/* Provide a RCVR driver */
|
2011-07-15 03:52:07 +02:00
|
|
|
static int32_t PIOS_SBUS_Get(uint32_t rcvr_id, uint8_t channel);
|
2011-06-25 15:27:28 +02:00
|
|
|
|
|
|
|
const struct pios_rcvr_driver pios_sbus_rcvr_driver = {
|
|
|
|
.read = PIOS_SBUS_Get,
|
|
|
|
};
|
|
|
|
|
2011-06-18 22:19:57 +02:00
|
|
|
/* Local Variables */
|
|
|
|
static uint16_t channel_data[SBUS_NUMBER_OF_CHANNELS];
|
|
|
|
static uint8_t received_data[SBUS_FRAME_LENGTH - 2];
|
|
|
|
static uint8_t receive_timer;
|
|
|
|
static uint8_t failsafe_timer;
|
|
|
|
static uint8_t frame_found;
|
2011-06-13 02:04:49 +02:00
|
|
|
|
2011-07-03 01:25:35 +02:00
|
|
|
static void PIOS_SBUS_Supervisor(uint32_t sbus_id);
|
|
|
|
|
2011-06-13 02:04:49 +02:00
|
|
|
/**
|
2011-06-19 13:30:13 +02:00
|
|
|
* reset_channels() function clears all channel data in case of
|
2011-06-18 22:19:57 +02:00
|
|
|
* lost signal or explicit failsafe flag from the S.Bus data stream
|
2011-06-13 02:04:49 +02:00
|
|
|
*/
|
2011-06-18 22:19:57 +02:00
|
|
|
static void reset_channels(void)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < SBUS_NUMBER_OF_CHANNELS; i++) {
|
|
|
|
channel_data[i] = 0;
|
|
|
|
}
|
|
|
|
}
|
2011-06-13 02:04:49 +02:00
|
|
|
|
2011-06-18 22:19:57 +02:00
|
|
|
/**
|
2011-06-19 13:30:13 +02:00
|
|
|
* unroll_channels() function computes channel_data[] from received_data[]
|
|
|
|
* For efficiency it unrolls first 8 channels without loops. If other
|
|
|
|
* 8 channels are needed they can be unrolled using the same code
|
|
|
|
* starting from s[11] instead of s[0]. Two extra digital channels are
|
|
|
|
* accessible using (s[22] & SBUS_FLAG_DGx) logical expressions.
|
2011-06-18 22:19:57 +02:00
|
|
|
*/
|
|
|
|
static void unroll_channels(void)
|
|
|
|
{
|
|
|
|
uint8_t *s = received_data;
|
|
|
|
uint16_t *d = channel_data;
|
2011-06-13 02:04:49 +02:00
|
|
|
|
2011-06-18 22:19:57 +02:00
|
|
|
#if (SBUS_NUMBER_OF_CHANNELS != 8)
|
|
|
|
#error Current S.Bus code unrolls only first 8 channels
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define F(v,s) ((v) >> s) & 0x7ff
|
|
|
|
*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);
|
2011-06-19 13:30:13 +02:00
|
|
|
*d++ = F(s[9] | s[10] << 8, 5);
|
2011-06-18 22:19:57 +02:00
|
|
|
}
|
2011-06-13 02:04:49 +02:00
|
|
|
|
|
|
|
/**
|
2011-06-19 13:30:13 +02:00
|
|
|
* process_byte() function processes incoming byte from S.Bus stream
|
2011-06-18 22:19:57 +02:00
|
|
|
*/
|
2011-06-19 13:30:13 +02:00
|
|
|
static void process_byte(uint8_t b)
|
2011-06-13 02:04:49 +02:00
|
|
|
{
|
2011-06-18 22:19:57 +02:00
|
|
|
static uint8_t byte_count;
|
|
|
|
|
|
|
|
if (frame_found == 0) {
|
|
|
|
/* no frame found yet, waiting for start byte */
|
|
|
|
if (b == SBUS_SOF_BYTE) {
|
|
|
|
byte_count = 0;
|
|
|
|
frame_found = 1;
|
2011-06-13 02:04:49 +02:00
|
|
|
}
|
|
|
|
} else {
|
2011-06-18 22:19:57 +02:00
|
|
|
/* do not store start and end of frame bytes */
|
|
|
|
if (byte_count < SBUS_FRAME_LENGTH - 2) {
|
|
|
|
/* store next byte */
|
|
|
|
received_data[byte_count++] = b;
|
|
|
|
} else {
|
|
|
|
if (b == SBUS_EOF_BYTE) {
|
|
|
|
/* full frame received */
|
|
|
|
uint8_t flags = 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 */
|
|
|
|
reset_channels();
|
|
|
|
} else {
|
|
|
|
/* data looking good */
|
|
|
|
unroll_channels();
|
|
|
|
failsafe_timer = 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* discard whole frame */
|
2011-06-13 02:04:49 +02:00
|
|
|
}
|
2011-06-18 22:19:57 +02:00
|
|
|
|
|
|
|
/* prepare for the next frame */
|
|
|
|
frame_found = 0;
|
2011-06-13 02:04:49 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-07-26 06:27:03 +02:00
|
|
|
static uint16_t PIOS_SBUS_RxInCallback(uint32_t context, uint8_t * buf, uint16_t buf_len, uint16_t * headroom, bool * need_yield)
|
|
|
|
{
|
|
|
|
/* process byte(s) and clear receive timer */
|
|
|
|
for (uint8_t i = 0; i < buf_len; i++) {
|
|
|
|
process_byte(buf[i]);
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2011-06-19 13:30:13 +02:00
|
|
|
/**
|
|
|
|
* Initialise S.Bus receiver interface
|
|
|
|
*/
|
2011-07-26 06:27:03 +02:00
|
|
|
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)
|
2011-06-19 13:30:13 +02:00
|
|
|
{
|
bootcfg: use UAVobj to control boot-time HW config
This should mark an end to the compile-time selection of HW
configurations.
Minor changes in board initialization for all platforms:
- Most config structs are marked static to prevent badly written
drivers from directly referring to config data.
- Adapt to changes in .irq fields in config data.
- Adapt to changes in USART IRQ handling.
Major changes in board initialization for CC:
- Use HwSettings UAVObj to decide which drivers to attach to
the "main" port and the flexi port, and select the appropriate
device configuration data.
- HwSettings allows choosing between Disabled, Telemetry, SBUS,
Spektrum,GPS, and I2C for each of the two ports.
- Use ManualControlSettings.InputMode to init/configure the
appropriate receiver module, and register its available rx channels
with the PIOS_RCVR layer. Can choose between PWM, Spektrum and PPM
at board init time. PPM driver is broken, and SBUS will work once
it is added to this UAVObj as an option.
- CC build now includes code for SBUS, Spektrum and PWM receivers in
every firmware image.
PIOS_USART driver:
- Now handles its own low-level IRQs internally
- If NULL upper-level IRQ handler is bound in at board init time
then rx/tx is satisfied by internal PIOS_USART buffered IO routines
which are (typically) attached to the COM layer.
- If an alternate upper-level IRQ handler is bound in at board init
then that handler is called and expected to clear down the USART
IRQ sources. This is used by Spektrum and SBUS drivers.
PIOS_SBUS and PIOS_SPEKTRUM drivers:
- Improved data/API hiding
- No longer assume they know where their config data is stored which
allows for boot-time alternate configurations for the driver.
- Now registers an upper-level IRQ handlerwith the USART layer to
decouple the driver from which USART it is actually attached to.
2011-07-06 02:21:00 +02:00
|
|
|
/* 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);
|
2011-06-19 13:30:13 +02:00
|
|
|
|
2011-07-26 06:27:03 +02:00
|
|
|
(driver->bind_rx_cb)(lower_id, PIOS_SBUS_RxInCallback, 0);
|
|
|
|
|
2011-07-03 01:25:35 +02:00
|
|
|
if (!PIOS_RTC_RegisterTickCallback(PIOS_SBUS_Supervisor, 0)) {
|
bootcfg: use UAVobj to control boot-time HW config
This should mark an end to the compile-time selection of HW
configurations.
Minor changes in board initialization for all platforms:
- Most config structs are marked static to prevent badly written
drivers from directly referring to config data.
- Adapt to changes in .irq fields in config data.
- Adapt to changes in USART IRQ handling.
Major changes in board initialization for CC:
- Use HwSettings UAVObj to decide which drivers to attach to
the "main" port and the flexi port, and select the appropriate
device configuration data.
- HwSettings allows choosing between Disabled, Telemetry, SBUS,
Spektrum,GPS, and I2C for each of the two ports.
- Use ManualControlSettings.InputMode to init/configure the
appropriate receiver module, and register its available rx channels
with the PIOS_RCVR layer. Can choose between PWM, Spektrum and PPM
at board init time. PPM driver is broken, and SBUS will work once
it is added to this UAVObj as an option.
- CC build now includes code for SBUS, Spektrum and PWM receivers in
every firmware image.
PIOS_USART driver:
- Now handles its own low-level IRQs internally
- If NULL upper-level IRQ handler is bound in at board init time
then rx/tx is satisfied by internal PIOS_USART buffered IO routines
which are (typically) attached to the COM layer.
- If an alternate upper-level IRQ handler is bound in at board init
then that handler is called and expected to clear down the USART
IRQ sources. This is used by Spektrum and SBUS drivers.
PIOS_SBUS and PIOS_SPEKTRUM drivers:
- Improved data/API hiding
- No longer assume they know where their config data is stored which
allows for boot-time alternate configurations for the driver.
- Now registers an upper-level IRQ handlerwith the USART layer to
decouple the driver from which USART it is actually attached to.
2011-07-06 02:21:00 +02:00
|
|
|
PIOS_Assert(0);
|
2011-07-03 01:25:35 +02:00
|
|
|
}
|
2011-07-26 06:27:03 +02:00
|
|
|
|
|
|
|
return (0);
|
2011-06-19 13:30:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the value of an input channel
|
|
|
|
* \param[in] channel Number of the channel desired (zero based)
|
|
|
|
* \output -1 channel not available
|
|
|
|
* \output >0 channel value
|
|
|
|
*/
|
2011-07-15 03:52:07 +02:00
|
|
|
static int32_t PIOS_SBUS_Get(uint32_t rcvr_id, uint8_t channel)
|
2011-06-19 13:30:13 +02:00
|
|
|
{
|
|
|
|
/* return error if channel is not available */
|
2011-07-15 03:52:07 +02:00
|
|
|
if (channel >= SBUS_NUMBER_OF_CHANNELS) {
|
2011-09-04 08:23:59 +02:00
|
|
|
return PIOS_RCVR_INVALID;
|
2011-06-19 13:30:13 +02:00
|
|
|
}
|
2011-07-15 03:52:07 +02:00
|
|
|
return channel_data[channel];
|
2011-06-19 13:30:13 +02:00
|
|
|
}
|
|
|
|
|
2011-06-13 02:04:49 +02:00
|
|
|
/**
|
2011-06-19 13:30:13 +02:00
|
|
|
* Input data supervisor is called periodically and provides
|
2011-06-18 22:19:57 +02:00
|
|
|
* 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 data 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.
|
2011-06-13 02:04:49 +02:00
|
|
|
*/
|
2011-07-03 01:25:35 +02:00
|
|
|
static void PIOS_SBUS_Supervisor(uint32_t sbus_id)
|
2011-06-18 22:19:57 +02:00
|
|
|
{
|
|
|
|
/* waiting for new frame if no bytes were received in 3.2ms */
|
|
|
|
if (++receive_timer > 2) {
|
|
|
|
receive_timer = 0;
|
|
|
|
frame_found = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* activate failsafe if no frames have arrived in 102.4ms */
|
|
|
|
if (++failsafe_timer > 64) {
|
|
|
|
reset_channels();
|
2011-06-19 13:30:13 +02:00
|
|
|
failsafe_timer = 0;
|
2011-06-13 02:04:49 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/**
|
2011-06-18 22:19:57 +02:00
|
|
|
* @}
|
|
|
|
* @}
|
|
|
|
*/
|