mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-11-16 04:51:36 +08:00
454 lines
12 KiB
C++
454 lines
12 KiB
C++
/****************************************************************************
|
|
*
|
|
* Copyright (c) 2012-2020 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 <string>
|
|
#include <px4_log.h>
|
|
#include <px4_platform_common/tasks.h>
|
|
#include <px4_platform_common/getopt.h>
|
|
#include <uORB/PublicationMulti.hpp>
|
|
#include <drivers/drv_hrt.h>
|
|
#include <uORB/topics/input_rc.h>
|
|
#include <lib/parameters/param.h>
|
|
#include <poll.h>
|
|
#include <termios.h>
|
|
|
|
#include "protocol.h"
|
|
|
|
extern "C" { __EXPORT int apps_sbus_main(int argc, char *argv[]); }
|
|
|
|
namespace apps_sbus
|
|
{
|
|
|
|
std::string _port = "";
|
|
int _uart_fd = -1;
|
|
IOPacket _packet;
|
|
bool _initialized = false;
|
|
bool _is_running = false;
|
|
uint64_t _rc_last_valid; ///< last valid timestamp
|
|
uint16_t _rc_valid_update_count = 0;
|
|
|
|
static px4_task_t _task_handle = -1;
|
|
|
|
uORB::PublicationMulti<input_rc_s> _rc_pub{ORB_ID(input_rc)};
|
|
|
|
int bus_exchange(IOPacket *packet)
|
|
{
|
|
int ret = 0;
|
|
int read_retries = 3;
|
|
int read_succeeded = 0;
|
|
int packet_size = sizeof(IOPacket);
|
|
|
|
tcflush(_uart_fd, TCIOFLUSH);
|
|
|
|
ret = ::write(_uart_fd, packet, packet_size);
|
|
|
|
usleep(100);
|
|
|
|
struct pollfd fd_monitor;
|
|
fd_monitor.fd = _uart_fd;
|
|
fd_monitor.events = POLLIN;
|
|
|
|
while (read_retries) {
|
|
(void) ::poll(&fd_monitor, 1, 1000);
|
|
ret = ::read(_uart_fd, packet, sizeof(IOPacket));
|
|
|
|
if (ret) {
|
|
// PX4_INFO("Read %d bytes", ret);
|
|
|
|
/* Check CRC */
|
|
uint8_t crc = packet->crc;
|
|
packet->crc = 0;
|
|
|
|
if (crc != crc_packet(packet)) {
|
|
PX4_ERR("PX4IO packet CRC error");
|
|
return -EIO;
|
|
|
|
} else if (PKT_CODE(*packet) == PKT_CODE_CORRUPT) {
|
|
PX4_ERR("PX4IO packet corruption");
|
|
return -EIO;
|
|
|
|
} else {
|
|
read_succeeded = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
PX4_ERR("Read attempt %d failed", read_retries);
|
|
read_retries--;
|
|
}
|
|
|
|
|
|
if (! read_succeeded) {
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int io_reg_get(uint8_t page, uint8_t offset, uint16_t *values, unsigned num_values)
|
|
{
|
|
/* range check the transfer */
|
|
// if (num_values > ((_max_transfer) / sizeof(*values))) {
|
|
// PX4_ERR("io_reg_get: too many registers (%u, max %u)", num_values, _max_transfer / 2);
|
|
// return -1;
|
|
// }
|
|
|
|
// int ret = _interface->read((page << 8) | offset, reinterpret_cast<void *>(values), num_values);
|
|
int ret = 0;
|
|
|
|
_packet.count_code = num_values | PKT_CODE_READ;
|
|
_packet.page = page;
|
|
_packet.offset = offset;
|
|
|
|
_packet.crc = 0;
|
|
_packet.crc = crc_packet(&_packet);
|
|
|
|
ret = bus_exchange(&_packet);
|
|
|
|
if (ret != 0) {
|
|
// PX4_ERR("px4io io_reg_get(%hhu,%hhu,%u): data error %d", page, offset, num_values, ret);
|
|
return -1;
|
|
}
|
|
|
|
memcpy(values, &_packet.regs[0], num_values * 2);
|
|
|
|
return OK;
|
|
}
|
|
|
|
uint32_t io_reg_get(uint8_t page, uint8_t offset)
|
|
{
|
|
uint16_t value;
|
|
|
|
if (io_reg_get(page, offset, &value, 1) != OK) {
|
|
// Registers are only 16 bit so any value over 0xFFFF can signal a fault
|
|
return 0xFFFFFFFF;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
int initialize()
|
|
{
|
|
if (_initialized) {
|
|
// Already successfully initialized
|
|
return 0;
|
|
}
|
|
|
|
if (_uart_fd < 0) {
|
|
_uart_fd = open("/dev/ttyHS1", O_RDWR | O_NONBLOCK);
|
|
|
|
if (_uart_fd < 0) {
|
|
PX4_ERR("Open failed in %s", __FUNCTION__);
|
|
return -1;
|
|
|
|
} else {
|
|
PX4_INFO("serial port fd %d", _uart_fd);
|
|
}
|
|
|
|
// Configuration copied from dsm_config
|
|
struct termios uart_config;
|
|
|
|
int termios_state;
|
|
|
|
/* fill the struct for the new configuration */
|
|
tcgetattr(_uart_fd, &uart_config);
|
|
|
|
/* properly configure the terminal (see also https://en.wikibooks.org/wiki/Serial_Programming/termios ) */
|
|
|
|
//
|
|
// Input flags - Turn off input processing
|
|
//
|
|
// convert break to null byte, no CR to NL translation,
|
|
// no NL to CR translation, don't mark parity errors or breaks
|
|
// no input parity check, don't strip high bit off,
|
|
// no XON/XOFF software flow control
|
|
//
|
|
uart_config.c_iflag &= ~(IGNBRK | BRKINT | ICRNL |
|
|
INLCR | PARMRK | INPCK | ISTRIP | IXON);
|
|
//
|
|
// Output flags - Turn off output processing
|
|
//
|
|
// no CR to NL translation, no NL to CR-NL translation,
|
|
// no NL to CR translation, no column 0 CR suppression,
|
|
// no Ctrl-D suppression, no fill characters, no case mapping,
|
|
// no local output processing
|
|
//
|
|
// config.c_oflag &= ~(OCRNL | ONLCR | ONLRET |
|
|
// ONOCR | ONOEOT| OFILL | OLCUC | OPOST);
|
|
uart_config.c_oflag = 0;
|
|
|
|
//
|
|
// No line processing
|
|
//
|
|
// echo off, echo newline off, canonical mode off,
|
|
// extended input processing off, signal chars off
|
|
//
|
|
uart_config.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG);
|
|
|
|
/* no parity, one stop bit, disable flow control */
|
|
uart_config.c_cflag &= ~(CSTOPB | PARENB | CRTSCTS);
|
|
|
|
/* set baud rate */
|
|
if ((termios_state = cfsetispeed(&uart_config, B921600)) < 0) {
|
|
PX4_ERR("ERR: %d (cfsetispeed)", termios_state);
|
|
return -1;
|
|
}
|
|
|
|
if ((termios_state = cfsetospeed(&uart_config, B921600)) < 0) {
|
|
PX4_ERR("ERR: %d (cfsetospeed)", termios_state);
|
|
return -1;
|
|
}
|
|
|
|
if ((termios_state = tcsetattr(_uart_fd, TCSANOW, &uart_config)) < 0) {
|
|
PX4_ERR("ERR: %d (tcsetattr)", termios_state);
|
|
return -1;
|
|
}
|
|
|
|
}
|
|
|
|
if (_uart_fd < 0) {
|
|
PX4_ERR("Open failed in %s", __FUNCTION__);
|
|
return -1;
|
|
}
|
|
|
|
// Verify connectivity and version number
|
|
unsigned protocol = io_reg_get(PX4IO_PAGE_CONFIG, PX4IO_P_CONFIG_PROTOCOL_VERSION);
|
|
|
|
if (protocol != PX4IO_PROTOCOL_VERSION) {
|
|
PX4_ERR("apps_sbus version error: %u", protocol);
|
|
_uart_fd = -1;
|
|
return -1;
|
|
}
|
|
|
|
_initialized = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void apps_sbus_task()
|
|
{
|
|
|
|
uint16_t status_regs[2] {};
|
|
input_rc_s rc_val;
|
|
const unsigned prolog = (PX4IO_P_RAW_RC_BASE - PX4IO_P_RAW_RC_COUNT);
|
|
uint16_t rc_regs[input_rc_s::RC_INPUT_MAX_CHANNELS + prolog];
|
|
uint32_t channel_count = 0;
|
|
|
|
_is_running = true;
|
|
|
|
while (true) {
|
|
|
|
usleep(20000); // Update every 20ms
|
|
|
|
memset(&rc_val, 0, sizeof(input_rc_s));
|
|
|
|
if (io_reg_get(PX4IO_PAGE_STATUS, PX4IO_P_STATUS_FLAGS, &status_regs[0],
|
|
sizeof(status_regs) / sizeof(status_regs[0])) == OK) {
|
|
// PX4_INFO("apps_sbus status 0x%.4x", status_regs[0]);
|
|
// PX4_INFO("apps_sbus alarms 0x%.4x", status_regs[1]);
|
|
} else {
|
|
// PX4_ERR("Failed to read status / alarm registers");
|
|
continue;
|
|
}
|
|
|
|
/* fetch values from IO */
|
|
|
|
// When starting the RC flag will not be okay if the receiver isn't
|
|
// getting a signal from the transmitter. Once it does, then this flag
|
|
// will say okay even if later the signal is lost.
|
|
if (!(status_regs[0] & PX4IO_P_STATUS_FLAGS_RC_OK)) {
|
|
// PX4_INFO("RC lost status flag set");
|
|
rc_val.rc_lost = true;
|
|
|
|
} else {
|
|
// PX4_INFO("RC lost status flag is not set");
|
|
rc_val.rc_lost = false;
|
|
}
|
|
|
|
if (status_regs[0] & PX4IO_P_STATUS_FLAGS_RC_SBUS) {
|
|
rc_val.input_source = input_rc_s::RC_INPUT_SOURCE_PX4IO_SBUS;
|
|
// PX4_INFO("Got valid SBUS");
|
|
|
|
} else {
|
|
rc_val.input_source = input_rc_s::RC_INPUT_SOURCE_UNKNOWN;
|
|
// PX4_INFO("SBUS not valid");
|
|
}
|
|
|
|
rc_val.timestamp = hrt_absolute_time();
|
|
|
|
// No point in reading the registers if we haven't acquired a transmitter signal yet
|
|
if (! rc_val.rc_lost) {
|
|
if (io_reg_get(PX4IO_PAGE_RAW_RC_INPUT, PX4IO_P_RAW_RC_COUNT, &rc_regs[0],
|
|
sizeof(rc_regs) / sizeof(rc_regs[0])) != OK) {
|
|
// PX4_ERR("Failed to read RC registers");
|
|
continue;
|
|
// } else {
|
|
// PX4_INFO("Successfully read RC registers");
|
|
// PX4_INFO("Prolog: %u 0x%.4x 0x%.4x 0x%.4x 0x%.4x 0x%.4x",
|
|
// rc_regs[0], rc_regs[1], rc_regs[2], rc_regs[3], rc_regs[4], rc_regs[5]);
|
|
}
|
|
|
|
channel_count = rc_regs[PX4IO_P_RAW_RC_COUNT];
|
|
|
|
// const uint16_t rc_valid_update_count = rc_regs[PX4IO_P_RAW_FRAME_COUNT];
|
|
// const bool rc_updated = (rc_valid_update_count != _rc_valid_update_count);
|
|
//
|
|
// if (!rc_updated) {
|
|
// PX4_INFO("Didn't get an RC update indication. %u %u", rc_valid_update_count, _rc_valid_update_count);
|
|
// continue;
|
|
// }
|
|
//
|
|
// _rc_valid_update_count = rc_valid_update_count;
|
|
//
|
|
// PX4_INFO("Got an RC update indication");
|
|
|
|
/* limit the channel count */
|
|
if (channel_count > input_rc_s::RC_INPUT_MAX_CHANNELS) {
|
|
// PX4_INFO("Got %u for channel count. Limiting to 18", channel_count);
|
|
channel_count = input_rc_s::RC_INPUT_MAX_CHANNELS;
|
|
}
|
|
|
|
rc_val.channel_count = channel_count;
|
|
// PX4_INFO("RC channel count: %u", rc_val.channel_count);
|
|
|
|
// rc_val.rc_ppm_frame_length = rc_regs[PX4IO_P_RAW_RC_DATA];
|
|
rc_val.rc_ppm_frame_length = 0;
|
|
|
|
rc_val.rc_failsafe = (rc_regs[PX4IO_P_RAW_RC_FLAGS] & PX4IO_P_RAW_RC_FLAGS_FAILSAFE);
|
|
// rc_val.rc_lost = !(rc_regs[PX4IO_P_RAW_RC_FLAGS] & PX4IO_P_RAW_RC_FLAGS_RC_OK);
|
|
rc_val.rc_lost = rc_val.rc_failsafe;
|
|
rc_val.rc_lost_frame_count = rc_regs[PX4IO_P_RAW_LOST_FRAME_COUNT];
|
|
rc_val.rc_total_frame_count = rc_regs[PX4IO_P_RAW_FRAME_COUNT];
|
|
|
|
if (!rc_val.rc_lost && !rc_val.rc_failsafe) {
|
|
_rc_last_valid = rc_val.timestamp;
|
|
rc_val.rssi = rc_regs[PX4IO_P_RAW_RC_NRSSI];
|
|
rc_val.link_quality = rc_regs[PX4IO_P_RAW_RC_NRSSI];
|
|
|
|
/* last thing set are the actual channel values as 16 bit values */
|
|
for (unsigned i = 0; i < channel_count; i++) {
|
|
rc_val.values[i] = rc_regs[prolog + i];
|
|
// PX4_INFO("RC channel %u: %.4u", i, rc_val.values[i]);
|
|
}
|
|
|
|
/* zero the remaining fields */
|
|
for (unsigned i = channel_count; i < (sizeof(rc_val.values) / sizeof(rc_val.values[0])); i++) {
|
|
rc_val.values[i] = 0;
|
|
}
|
|
}
|
|
|
|
rc_val.timestamp_last_signal = _rc_last_valid;
|
|
}
|
|
|
|
_rc_pub.publish(rc_val);
|
|
|
|
}
|
|
}
|
|
|
|
int start(int argc, char *argv[])
|
|
{
|
|
|
|
int ch;
|
|
int myoptind = 1;
|
|
const char *myoptarg = nullptr;
|
|
|
|
while ((ch = px4_getopt(argc, argv, "p:", &myoptind, &myoptarg)) != EOF) {
|
|
switch (ch) {
|
|
case 'p':
|
|
_port = myoptarg;
|
|
PX4_INFO("Setting port to %s", _port.c_str());
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (! _initialized) {
|
|
if (initialize()) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (_is_running) {
|
|
PX4_WARN("Already started");
|
|
return 0;
|
|
}
|
|
|
|
_task_handle = px4_task_spawn_cmd("apps_sbus_main",
|
|
SCHED_DEFAULT,
|
|
SCHED_PRIORITY_DEFAULT,
|
|
2000,
|
|
(px4_main_t) &apps_sbus_task,
|
|
(char *const *)argv);
|
|
|
|
if (_task_handle < 0) {
|
|
PX4_ERR("task start failed");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
usage()
|
|
{
|
|
PX4_INFO("Usage: apps_sbus start [options]");
|
|
PX4_INFO("Options: -p <number> uart port number");
|
|
}
|
|
|
|
} // End namespance apps_sbus
|
|
|
|
int apps_sbus_main(int argc, char *argv[])
|
|
{
|
|
int myoptind = 1;
|
|
|
|
if (argc <= 1) {
|
|
apps_sbus::usage();
|
|
return -1;
|
|
}
|
|
|
|
const char *verb = argv[myoptind];
|
|
|
|
if (!strcmp(verb, "start")) {
|
|
return apps_sbus::start(argc - 1, argv + 1);
|
|
|
|
} else {
|
|
apps_sbus::usage();
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|