2017-11-22 00:11:49 -04:00
|
|
|
/*
|
|
|
|
* AP_SBusOut.cpp
|
|
|
|
*
|
|
|
|
* Created on: Aug 19, 2017
|
|
|
|
* Author: Mark Whitehorn
|
|
|
|
*
|
|
|
|
* method sbus1_out was ported from ardupilot/modules/PX4Firmware/src/lib/rc/sbus.c
|
|
|
|
* which has the following license:
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
#include "AP_SBusOut.h"
|
|
|
|
#include <AP_SerialManager/AP_SerialManager.h>
|
|
|
|
#include <SRV_Channel/SRV_Channel.h>
|
|
|
|
|
|
|
|
extern const AP_HAL::HAL& hal;
|
|
|
|
|
2017-11-27 21:13:40 -04:00
|
|
|
#define SBUS_DEBUG 0
|
2017-11-22 00:11:49 -04:00
|
|
|
|
|
|
|
// SBUS1 constant definitions
|
|
|
|
// pulse widths measured using FrSky Sbus/PWM converter
|
|
|
|
#define SBUS_BSIZE 25
|
|
|
|
#define SBUS_CHANNELS 16
|
|
|
|
#define SBUS_MIN 880.0f
|
|
|
|
#define SBUS_MAX 2156.0f
|
|
|
|
#define SBUS_SCALE (2048.0f / (SBUS_MAX - SBUS_MIN))
|
|
|
|
|
|
|
|
const AP_Param::GroupInfo AP_SBusOut::var_info[] = {
|
2017-12-08 17:31:24 -04:00
|
|
|
// @Param: RATE
|
2017-11-22 00:11:49 -04:00
|
|
|
// @DisplayName: SBUS default output rate
|
|
|
|
// @Description: This sets the SBUS output frame rate in Hz.
|
|
|
|
// @Range: 25 250
|
|
|
|
// @User: Advanced
|
|
|
|
// @Units: Hz
|
|
|
|
AP_GROUPINFO("RATE", 1, AP_SBusOut, sbus_rate, 50),
|
|
|
|
|
|
|
|
AP_GROUPEND
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// constructor
|
|
|
|
AP_SBusOut::AP_SBusOut(void)
|
|
|
|
{
|
|
|
|
// set defaults from the parameter table
|
|
|
|
AP_Param::setup_object_defaults(this, var_info);
|
|
|
|
}
|
|
|
|
|
2018-10-29 02:51:22 -03:00
|
|
|
/*
|
|
|
|
format a SBUS output frame into a 25 byte buffer
|
|
|
|
*/
|
|
|
|
void AP_SBusOut::sbus_format_frame(uint16_t *channels, uint8_t num_channels, uint8_t buffer[SBUS_BSIZE])
|
|
|
|
{
|
|
|
|
uint8_t index = 1;
|
|
|
|
uint8_t offset = 0;
|
|
|
|
|
2018-11-27 23:36:21 -04:00
|
|
|
memset(buffer, 0, SBUS_BSIZE);
|
2018-10-29 02:51:22 -03:00
|
|
|
buffer[0] = 0x0f;
|
|
|
|
|
|
|
|
/* construct sbus frame representing channels 1 through 16 (max) */
|
|
|
|
uint8_t nchan = MIN(num_channels, SBUS_CHANNELS);
|
|
|
|
for (unsigned i = 0; i < nchan; ++i) {
|
|
|
|
/*protect from out of bounds values and limit to 11 bits*/
|
|
|
|
uint16_t pwmval = MAX(channels[i], SBUS_MIN);
|
|
|
|
uint16_t value = (uint16_t)((pwmval - SBUS_MIN) * SBUS_SCALE);
|
|
|
|
if (value > 0x07ff) {
|
|
|
|
value = 0x07ff;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if SBUS_DEBUG
|
|
|
|
static uint16_t lastch9 = 0;
|
|
|
|
if ((i==8) && (pwmval != lastch9)) {
|
|
|
|
lastch9 = pwmval;
|
|
|
|
hal.console->printf("channel 9 pwm: %04d\n", pwmval);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
while (offset >= 8) {
|
|
|
|
++index;
|
|
|
|
offset -= 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
buffer[index] |= (value << (offset)) & 0xff;
|
|
|
|
buffer[index + 1] |= (value >> (8 - offset)) & 0xff;
|
|
|
|
buffer[index + 2] |= (value >> (16 - offset)) & 0xff;
|
|
|
|
offset += 11;
|
|
|
|
}
|
|
|
|
}
|
2017-11-22 00:11:49 -04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* build and send sbus1 frame representing first 16 servo channels
|
|
|
|
* input arg is pointer to uart
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
AP_SBusOut::update()
|
|
|
|
{
|
|
|
|
if (!initialised) {
|
|
|
|
initialised = true;
|
|
|
|
init();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sbus1_uart == nullptr) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// constrain output rate using sbus_frame_interval
|
|
|
|
static uint32_t last_micros = 0;
|
|
|
|
uint32_t now = AP_HAL::micros();
|
2018-10-29 02:51:22 -03:00
|
|
|
if ((now - last_micros) <= sbus_frame_interval) {
|
|
|
|
return;
|
|
|
|
}
|
2017-11-22 00:11:49 -04:00
|
|
|
|
2018-10-29 02:51:22 -03:00
|
|
|
last_micros = now;
|
2017-11-22 00:11:49 -04:00
|
|
|
|
2018-10-29 02:51:22 -03:00
|
|
|
/* construct sbus frame representing channels 1 through 16 (max) */
|
|
|
|
uint8_t nchan = MIN(NUM_SERVO_CHANNELS, SBUS_CHANNELS);
|
|
|
|
uint16_t channels[SBUS_CHANNELS] {};
|
2017-11-22 00:11:49 -04:00
|
|
|
|
2018-10-29 02:51:22 -03:00
|
|
|
for (unsigned i = 0; i < nchan; ++i) {
|
|
|
|
SRV_Channel *c = SRV_Channels::srv_channel(i);
|
|
|
|
if (c == nullptr) {
|
|
|
|
continue;
|
2017-11-22 00:11:49 -04:00
|
|
|
}
|
2018-10-29 02:51:22 -03:00
|
|
|
channels[i] = c->get_output_pwm();
|
|
|
|
}
|
|
|
|
uint8_t buffer[SBUS_BSIZE];
|
|
|
|
sbus_format_frame(channels, nchan, buffer);
|
2017-11-22 00:11:49 -04:00
|
|
|
|
|
|
|
#if SBUS_DEBUG
|
2018-10-29 02:51:22 -03:00
|
|
|
hal.gpio->pinMode(55, HAL_GPIO_OUTPUT);
|
|
|
|
hal.gpio->write(55, 1);
|
2017-11-22 00:11:49 -04:00
|
|
|
#endif
|
|
|
|
|
|
|
|
sbus1_uart->write(buffer, sizeof(buffer));
|
|
|
|
|
|
|
|
#if SBUS_DEBUG
|
2018-10-29 02:51:22 -03:00
|
|
|
hal.gpio->pinMode(55, HAL_GPIO_OUTPUT);
|
|
|
|
hal.gpio->write(55, 0);
|
2017-11-22 00:11:49 -04:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void AP_SBusOut::init() {
|
|
|
|
uint16_t rate = sbus_rate.get();
|
|
|
|
|
|
|
|
#if SBUS_DEBUG
|
2017-11-22 13:40:44 -04:00
|
|
|
hal.console->printf("AP_SBusOut: init %d Hz\n", rate);
|
2017-11-22 00:11:49 -04:00
|
|
|
#endif
|
|
|
|
|
2017-11-28 09:21:52 -04:00
|
|
|
// subtract 500usec from requested frame interval to allow for latency
|
2017-11-22 00:11:49 -04:00
|
|
|
sbus_frame_interval = (1000UL * 1000UL) / rate - 500;
|
|
|
|
// at 100,000 bps, a 300 bit sbus frame takes 3msec to transfer
|
|
|
|
// require a minimum 700usec interframe gap
|
|
|
|
if (sbus_frame_interval < 3700) {
|
|
|
|
sbus_frame_interval = 3700;
|
|
|
|
}
|
|
|
|
|
2019-02-10 01:03:33 -04:00
|
|
|
AP_SerialManager *serial_manager = AP_SerialManager::get_singleton();
|
2017-11-22 00:11:49 -04:00
|
|
|
if (!serial_manager) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
sbus1_uart = serial_manager->find_serial(AP_SerialManager::SerialProtocol_Sbus1,0);
|
|
|
|
}
|
|
|
|
|