Compare commits

...

34 Commits

Author SHA1 Message Date
Eric Katzfey cc48676eaa Implemented flush for Serial UART API 2024-03-07 10:27:56 -08:00
Eric Katzfey 65a73496e7 Removed commented out code 2024-03-06 16:53:11 -08:00
Eric Katzfey bf5bce8718 Fixed processing bug on Qurt platform due to compiler bug 2024-03-06 16:52:20 -08:00
Eric Katzfey 129b11424c First version working with new Serial API on voxl2 2024-03-06 16:30:59 -08:00
Eric Katzfey 313003d2ac Made fixes for Qurt platform build and merge issues 2024-03-06 08:51:29 -08:00
Eric Katzfey 635d4557f7 Merge branch 'main' into wip_new_uart_api 2024-03-05 08:56:53 -08:00
Eric Katzfey ff133660c0 Fixed the fmu_v5_protected build failure 2024-02-15 18:32:21 -08:00
Eric Katzfey 42547a1319 Commented out SIH simulation for modalai_fc-v2 build to fit into flash 2024-01-05 14:21:15 -08:00
Eric Katzfey b2ee342fd6 Merge branch 'main' of github.com:PX4/PX4-Autopilot into wip_new_uart_api 2024-01-04 11:03:46 -08:00
Eric Katzfey 5b0013ebec Merge branch 'wip_new_uart_api' of github.com:PX4/PX4-Autopilot into wip_new_uart_api 2023-12-27 09:11:40 -08:00
Eric Katzfey 564c37d696 Fixed delete and non-null pointer bug 2023-12-27 09:09:30 -08:00
Eric Katzfey f6318c4926 More cleanup and fixes from testing failures 2023-12-27 09:09:30 -08:00
Eric Katzfey 231f072dc0 Added getPort back in to Serial interface. Only setPort needed to be removed 2023-12-27 09:09:30 -08:00
Eric Katzfey 88611f2228 Fixed formatting 2023-12-27 09:09:30 -08:00
Eric Katzfey 5938831e50 Added baudrate validation functions to the Serial implementations 2023-12-27 09:09:30 -08:00
Eric Katzfey ad0ce0b7ed Adding some copyright notices and general cleanup 2023-12-27 09:09:30 -08:00
Eric Katzfey 9accf81299 Changed the default baudrate handling around a little to make it cleaner 2023-12-27 09:09:30 -08:00
Eric Katzfey 2dbae8488e Fixed Clang tidy errors 2023-12-27 09:09:30 -08:00
Eric Katzfey 53281e5840 Fixed linking error in SLPI build 2023-12-27 09:09:30 -08:00
Eric Katzfey 820570a5b1 Fixed another GPS SPI option to make it only for Linux builds 2023-12-27 09:09:30 -08:00
Eric Katzfey 9edd3b8532 Fixed formatting and made SPI a Linux only choice to GPS 2023-12-27 09:09:30 -08:00
Eric Katzfey 5d80d23668 First working version. Tested on ModalAI FlightCore V2 2023-12-27 09:09:30 -08:00
Eric Katzfey 936d7c4f28 Fixed delete and non-null pointer bug 2023-07-26 14:15:44 -07:00
Eric Katzfey 81ab314a4c More cleanup and fixes from testing failures 2023-07-20 15:43:07 -07:00
Eric Katzfey 9c5b1477d4 Added getPort back in to Serial interface. Only setPort needed to be removed 2023-07-20 13:21:27 -07:00
Eric Katzfey 884763b577 Fixed formatting 2023-07-20 13:14:28 -07:00
Eric Katzfey 4672106699 Added baudrate validation functions to the Serial implementations 2023-07-20 13:11:35 -07:00
Eric Katzfey 1b92db9d89 Adding some copyright notices and general cleanup 2023-07-20 10:39:30 -07:00
Eric Katzfey 14a3df8b60 Changed the default baudrate handling around a little to make it cleaner 2023-07-20 10:39:30 -07:00
Eric Katzfey 83f3bc4dff Fixed Clang tidy errors 2023-07-20 10:39:30 -07:00
Eric Katzfey 8b43a8a5f6 Fixed linking error in SLPI build 2023-07-20 10:39:30 -07:00
Eric Katzfey 960c2ab797 Fixed another GPS SPI option to make it only for Linux builds 2023-07-20 10:39:30 -07:00
Eric Katzfey 4c5e05de13 Fixed formatting and made SPI a Linux only choice to GPS 2023-07-20 10:39:30 -07:00
Eric Katzfey 96c05ac23a First working version. Tested on ModalAI FlightCore V2 2023-07-20 10:39:30 -07:00
24 changed files with 2011 additions and 257 deletions

View File

@ -60,7 +60,7 @@ CONFIG_MODULES_NAVIGATOR=y
CONFIG_MODULES_RC_UPDATE=y CONFIG_MODULES_RC_UPDATE=y
CONFIG_MODULES_ROVER_POS_CONTROL=y CONFIG_MODULES_ROVER_POS_CONTROL=y
CONFIG_MODULES_SENSORS=y CONFIG_MODULES_SENSORS=y
CONFIG_MODULES_SIMULATION_SIMULATOR_SIH=y # CONFIG_MODULES_SIMULATION_SIMULATOR_SIH=y
CONFIG_MODULES_TEMPERATURE_COMPENSATION=y CONFIG_MODULES_TEMPERATURE_COMPENSATION=y
CONFIG_MODULES_UXRCE_DDS_CLIENT=y CONFIG_MODULES_UXRCE_DDS_CLIENT=y
CONFIG_MODULES_VTOL_ATT_CONTROL=y CONFIG_MODULES_VTOL_ATT_CONTROL=y

View File

@ -4,11 +4,13 @@ CONFIG_DRIVERS_ACTUATORS_VOXL_ESC=y
CONFIG_DRIVERS_BAROMETER_INVENSENSE_ICP101XX=y CONFIG_DRIVERS_BAROMETER_INVENSENSE_ICP101XX=y
CONFIG_DRIVERS_DISTANCE_SENSOR_VL53L0X=y CONFIG_DRIVERS_DISTANCE_SENSOR_VL53L0X=y
CONFIG_DRIVERS_DISTANCE_SENSOR_VL53L1X=y CONFIG_DRIVERS_DISTANCE_SENSOR_VL53L1X=y
CONFIG_DRIVERS_GPS=y
CONFIG_DRIVERS_LIGHTS_RGBLED_NCP5623C=y CONFIG_DRIVERS_LIGHTS_RGBLED_NCP5623C=y
CONFIG_DRIVERS_MAGNETOMETER_ISENTEK_IST8308=y CONFIG_DRIVERS_MAGNETOMETER_ISENTEK_IST8308=y
CONFIG_DRIVERS_MAGNETOMETER_ISENTEK_IST8310=y CONFIG_DRIVERS_MAGNETOMETER_ISENTEK_IST8310=y
CONFIG_DRIVERS_MAGNETOMETER_QMC5883L=y CONFIG_DRIVERS_MAGNETOMETER_QMC5883L=y
CONFIG_DRIVERS_POWER_MONITOR_VOXLPM=y CONFIG_DRIVERS_POWER_MONITOR_VOXLPM=y
CONFIG_DRIVERS_RC_CRSF_RC=y
CONFIG_DRIVERS_QSHELL_QURT=y CONFIG_DRIVERS_QSHELL_QURT=y
CONFIG_MODULES_COMMANDER=y CONFIG_MODULES_COMMANDER=y
CONFIG_MODULES_CONTROL_ALLOCATOR=y CONFIG_MODULES_CONTROL_ALLOCATOR=y

View File

@ -52,6 +52,7 @@ add_library(px4_platform STATIC
shutdown.cpp shutdown.cpp
spi.cpp spi.cpp
pab_manifest.c pab_manifest.c
Serial.cpp
${SRCS} ${SRCS}
) )
target_link_libraries(px4_platform prebuild_targets px4_work_queue) target_link_libraries(px4_platform prebuild_targets px4_work_queue)

143
platforms/common/Serial.cpp Normal file
View File

@ -0,0 +1,143 @@
/****************************************************************************
*
* Copyright (C) 2023 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 <px4_platform_common/Serial.hpp>
namespace device
{
Serial::Serial(const char *port, uint32_t baudrate, ByteSize bytesize, Parity parity, StopBits stopbits,
FlowControl flowcontrol) :
_impl(port, baudrate, bytesize, parity, stopbits, flowcontrol)
{
// If no baudrate was specified then set it to a reasonable default value
if (baudrate == 0) {
(void) _impl.setBaudrate(9600);
}
}
Serial::~Serial()
{
}
bool Serial::open()
{
return _impl.open();
}
bool Serial::isOpen() const
{
return _impl.isOpen();
}
bool Serial::close()
{
return _impl.close();
}
ssize_t Serial::read(uint8_t *buffer, size_t buffer_size)
{
return _impl.read(buffer, buffer_size);
}
ssize_t Serial::readAtLeast(uint8_t *buffer, size_t buffer_size, size_t character_count, uint32_t timeout_us)
{
return _impl.readAtLeast(buffer, buffer_size, character_count, timeout_us);
}
ssize_t Serial::write(const void *buffer, size_t buffer_size)
{
return _impl.write(buffer, buffer_size);
}
void Serial::flush()
{
return _impl.flush();
}
uint32_t Serial::getBaudrate() const
{
return _impl.getBaudrate();
}
bool Serial::setBaudrate(uint32_t baudrate)
{
return _impl.setBaudrate(baudrate);
}
ByteSize Serial::getBytesize() const
{
return _impl.getBytesize();
}
bool Serial::setBytesize(ByteSize bytesize)
{
return _impl.setBytesize(bytesize);
}
Parity Serial::getParity() const
{
return _impl.getParity();
}
bool Serial::setParity(Parity parity)
{
return _impl.setParity(parity);
}
StopBits Serial::getStopbits() const
{
return _impl.getStopbits();
}
bool Serial::setStopbits(StopBits stopbits)
{
return _impl.setStopbits(stopbits);
}
FlowControl Serial::getFlowcontrol() const
{
return _impl.getFlowcontrol();
}
bool Serial::setFlowcontrol(FlowControl flowcontrol)
{
return _impl.setFlowcontrol(flowcontrol);
}
const char *Serial::getPort() const
{
return _impl.getPort();
}
} // namespace device

View File

@ -0,0 +1,99 @@
/****************************************************************************
*
* Copyright (C) 2023 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.
*
****************************************************************************/
#pragma once
#include <SerialImpl.hpp>
#include <px4_platform_common/SerialCommon.hpp>
using device::SerialConfig::ByteSize;
using device::SerialConfig::Parity;
using device::SerialConfig::StopBits;
using device::SerialConfig::FlowControl;
namespace device __EXPORT
{
class Serial
{
public:
Serial(const char *port, uint32_t baudrate = 57600,
ByteSize bytesize = ByteSize::EightBits, Parity parity = Parity::None,
StopBits stopbits = StopBits::One, FlowControl flowcontrol = FlowControl::Disabled);
virtual ~Serial();
// Open sets up the port and gets it configured based on desired configuration
bool open();
bool isOpen() const;
bool close();
ssize_t read(uint8_t *buffer, size_t buffer_size);
ssize_t readAtLeast(uint8_t *buffer, size_t buffer_size, size_t character_count = 1, uint32_t timeout_us = 0);
ssize_t write(const void *buffer, size_t buffer_size);
void flush();
// If port is already open then the following configuration functions
// will reconfigure the port. If the port is not yet open then they will
// simply store the configuration in preparation for the port to be opened.
uint32_t getBaudrate() const;
bool setBaudrate(uint32_t baudrate);
ByteSize getBytesize() const;
bool setBytesize(ByteSize bytesize);
Parity getParity() const;
bool setParity(Parity parity);
StopBits getStopbits() const;
bool setStopbits(StopBits stopbits);
FlowControl getFlowcontrol() const;
bool setFlowcontrol(FlowControl flowcontrol);
const char *getPort() const;
private:
// Disable copy constructors
Serial(const Serial &);
Serial &operator=(const Serial &);
// platform implementation
SerialImpl _impl;
};
} // namespace device

View File

@ -0,0 +1,70 @@
/****************************************************************************
*
* Copyright (C) 2023 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.
*
****************************************************************************/
#pragma once
namespace device
{
namespace SerialConfig
{
// ByteSize: number of data bits
enum class ByteSize {
FiveBits = 5,
SixBits = 6,
SevenBits = 7,
EightBits = 8,
};
// Parity: enable parity checking
enum class Parity {
None = 0,
Odd = 1,
Even = 2,
};
// StopBits: number of stop bits
enum class StopBits {
One = 1,
Two = 2
};
// FlowControl: enable flow control
enum class FlowControl {
Disabled = 0,
Enabled = 1,
};
} // namespace SerialConfig
} // namespace device

View File

@ -13,11 +13,21 @@ __END_DECLS
#define px4_clock_gettime system_clock_gettime #define px4_clock_gettime system_clock_gettime
#endif #endif
#if defined(ENABLE_LOCKSTEP_SCHEDULER) #if defined(ENABLE_LOCKSTEP_SCHEDULER) || defined(__PX4_QURT)
__BEGIN_DECLS __BEGIN_DECLS
__EXPORT int px4_clock_settime(clockid_t clk_id, const struct timespec *tp); __EXPORT int px4_clock_settime(clockid_t clk_id, const struct timespec *tp);
__END_DECLS
#else
#define px4_clock_settime system_clock_settime
#endif
#if defined(ENABLE_LOCKSTEP_SCHEDULER)
__BEGIN_DECLS
__EXPORT int px4_usleep(useconds_t usec); __EXPORT int px4_usleep(useconds_t usec);
__EXPORT unsigned int px4_sleep(unsigned int seconds); __EXPORT unsigned int px4_sleep(unsigned int seconds);
__EXPORT int px4_pthread_cond_timedwait(pthread_cond_t *cond, __EXPORT int px4_pthread_cond_timedwait(pthread_cond_t *cond,
@ -27,7 +37,6 @@ __END_DECLS
#else #else
#define px4_clock_settime system_clock_settime
#define px4_usleep system_usleep #define px4_usleep system_usleep
#define px4_sleep system_sleep #define px4_sleep system_sleep
#define px4_pthread_cond_timedwait system_pthread_cond_timedwait #define px4_pthread_cond_timedwait system_pthread_cond_timedwait

View File

@ -0,0 +1,391 @@
/****************************************************************************
*
* Copyright (C) 2023 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 <SerialImpl.hpp>
#include <string.h> // strncpy
#include <termios.h>
#include <px4_log.h>
#include <fcntl.h>
#include <errno.h>
#include <poll.h>
#include <drivers/drv_hrt.h>
#define MODULE_NAME "SerialImpl"
namespace device
{
SerialImpl::SerialImpl(const char *port, uint32_t baudrate, ByteSize bytesize, Parity parity, StopBits stopbits,
FlowControl flowcontrol) :
_baudrate(baudrate),
_bytesize(bytesize),
_parity(parity),
_stopbits(stopbits),
_flowcontrol(flowcontrol)
{
if (port) {
strncpy(_port, port, sizeof(_port) - 1);
_port[sizeof(_port) - 1] = '\0';
} else {
_port[0] = 0;
}
}
SerialImpl::~SerialImpl()
{
if (isOpen()) {
close();
}
}
bool SerialImpl::validateBaudrate(uint32_t baudrate)
{
return ((baudrate == 9600) ||
(baudrate == 19200) ||
(baudrate == 38400) ||
(baudrate == 57600) ||
(baudrate == 115200) ||
(baudrate == 230400) ||
(baudrate == 460800) ||
(baudrate == 921600));
}
bool SerialImpl::configure()
{
/* process baud rate */
int speed;
if (! validateBaudrate(_baudrate)) {
PX4_ERR("ERR: unknown baudrate: %lu", _baudrate);
return false;
}
switch (_baudrate) {
case 9600: speed = B9600; break;
case 19200: speed = B19200; break;
case 38400: speed = B38400; break;
case 57600: speed = B57600; break;
case 115200: speed = B115200; break;
case 230400: speed = B230400; break;
#ifndef B460800
#define B460800 460800
#endif
case 460800: speed = B460800; break;
#ifndef B921600
#define B921600 921600
#endif
case 921600: speed = B921600; break;
default:
PX4_ERR("ERR: unknown baudrate: %lu", _baudrate);
return false;
}
struct termios uart_config;
int termios_state;
/* fill the struct for the new configuration */
if ((termios_state = tcgetattr(_serial_fd, &uart_config)) < 0) {
PX4_ERR("ERR: %d (tcgetattr)", termios_state);
return false;
}
/* 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, speed)) < 0) {
PX4_ERR("ERR: %d (cfsetispeed)", termios_state);
return false;
}
if ((termios_state = cfsetospeed(&uart_config, speed)) < 0) {
PX4_ERR("ERR: %d (cfsetospeed)", termios_state);
return false;
}
if ((termios_state = tcsetattr(_serial_fd, TCSANOW, &uart_config)) < 0) {
PX4_ERR("ERR: %d (tcsetattr)", termios_state);
return false;
}
return true;
}
bool SerialImpl::open()
{
if (isOpen()) {
return true;
}
// Open the serial port
int serial_fd = ::open(_port, O_RDWR | O_NOCTTY);
if (serial_fd < 0) {
PX4_ERR("failed to open %s err: %d", _port, errno);
return false;
}
_serial_fd = serial_fd;
// Configure the serial port
if (! configure()) {
PX4_ERR("failed to configure %s err: %d", _port, errno);
return false;
}
_open = true;
return _open;
}
bool SerialImpl::isOpen() const
{
return _open;
}
bool SerialImpl::close()
{
if (_serial_fd >= 0) {
::close(_serial_fd);
}
_serial_fd = -1;
_open = false;
return true;
}
ssize_t SerialImpl::read(uint8_t *buffer, size_t buffer_size)
{
if (!_open) {
PX4_ERR("Cannot read from serial device until it has been opened");
return -1;
}
int ret = ::read(_serial_fd, buffer, buffer_size);
if (ret < 0) {
PX4_DEBUG("%s read error %d", _port, ret);
}
return ret;
}
ssize_t SerialImpl::readAtLeast(uint8_t *buffer, size_t buffer_size, size_t character_count, uint32_t timeout_us)
{
if (!_open) {
PX4_ERR("Cannot readAtLeast from serial device until it has been opened");
return -1;
}
if (buffer_size < character_count) {
PX4_ERR("%s: Buffer not big enough to hold desired amount of read data", __FUNCTION__);
return -1;
}
const hrt_abstime start_time_us = hrt_absolute_time();
int total_bytes_read = 0;
while ((total_bytes_read < (int) character_count) && (hrt_elapsed_time(&start_time_us) < timeout_us)) {
// Poll for incoming UART data.
pollfd fds[1];
fds[0].fd = _serial_fd;
fds[0].events = POLLIN;
hrt_abstime remaining_time = timeout_us - hrt_elapsed_time(&start_time_us);
if (remaining_time <= 0) { break; }
int ret = poll(fds, sizeof(fds) / sizeof(fds[0]), remaining_time);
if (ret > 0) {
if (fds[0].revents & POLLIN) {
ret = read(&buffer[total_bytes_read], buffer_size - total_bytes_read);
if (ret > 0) {
total_bytes_read += ret;
}
} else {
PX4_ERR("Got a poll error");
return -1;
}
}
}
return total_bytes_read;
}
ssize_t SerialImpl::write(const void *buffer, size_t buffer_size)
{
if (!_open) {
PX4_ERR("Cannot write to serial device until it has been opened");
return -1;
}
int written = ::write(_serial_fd, buffer, buffer_size);
::fsync(_serial_fd);
if (written < 0) {
PX4_ERR("%s write error %d", _port, written);
}
return written;
}
void SerialImpl::flush()
{
if (_open) {
tcflush(_serial_fd, TCIOFLUSH);
}
}
const char *SerialImpl::getPort() const
{
return _port;
}
uint32_t SerialImpl::getBaudrate() const
{
return _baudrate;
}
bool SerialImpl::setBaudrate(uint32_t baudrate)
{
if (! validateBaudrate(baudrate)) {
PX4_ERR("ERR: invalid baudrate: %lu", baudrate);
return false;
}
// check if already configured
if ((baudrate == _baudrate) && _open) {
return true;
}
_baudrate = baudrate;
// process baud rate change now if port is already open
if (_open) {
return configure();
}
return true;
}
ByteSize SerialImpl::getBytesize() const
{
return _bytesize;
}
bool SerialImpl::setBytesize(ByteSize bytesize)
{
return bytesize == ByteSize::EightBits;
}
Parity SerialImpl::getParity() const
{
return _parity;
}
bool SerialImpl::setParity(Parity parity)
{
return parity == Parity::None;
}
StopBits SerialImpl::getStopbits() const
{
return _stopbits;
}
bool SerialImpl::setStopbits(StopBits stopbits)
{
return stopbits == StopBits::One;
}
FlowControl SerialImpl::getFlowcontrol() const
{
return _flowcontrol;
}
bool SerialImpl::setFlowcontrol(FlowControl flowcontrol)
{
return flowcontrol == FlowControl::Disabled;
}
} // namespace device

View File

@ -0,0 +1,106 @@
/****************************************************************************
*
* Copyright (C) 2023 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.
*
****************************************************************************/
#pragma once
#include <stdint.h>
#include <unistd.h>
#include <px4_platform_common/SerialCommon.hpp>
using device::SerialConfig::ByteSize;
using device::SerialConfig::Parity;
using device::SerialConfig::StopBits;
using device::SerialConfig::FlowControl;
namespace device
{
class SerialImpl
{
public:
SerialImpl(const char *port, uint32_t baudrate, ByteSize bytesize, Parity parity, StopBits stopbits,
FlowControl flowcontrol);
virtual ~SerialImpl();
bool open();
bool isOpen() const;
bool close();
ssize_t read(uint8_t *buffer, size_t buffer_size);
ssize_t readAtLeast(uint8_t *buffer, size_t buffer_size, size_t character_count = 1, uint32_t timeout_us = 0);
ssize_t write(const void *buffer, size_t buffer_size);
void flush();
const char *getPort() const;
uint32_t getBaudrate() const;
bool setBaudrate(uint32_t baudrate);
ByteSize getBytesize() const;
bool setBytesize(ByteSize bytesize);
Parity getParity() const;
bool setParity(Parity parity);
StopBits getStopbits() const;
bool setStopbits(StopBits stopbits);
FlowControl getFlowcontrol() const;
bool setFlowcontrol(FlowControl flowcontrol);
private:
int _serial_fd{-1};
bool _open{false};
char _port[32] {};
uint32_t _baudrate{0};
ByteSize _bytesize{ByteSize::EightBits};
Parity _parity{Parity::None};
StopBits _stopbits{StopBits::One};
FlowControl _flowcontrol{FlowControl::Disabled};
bool validateBaudrate(uint32_t baudrate);
bool configure();
};
} // namespace device

View File

@ -3,6 +3,8 @@
add_library(px4_layer add_library(px4_layer
${KERNEL_SRCS} ${KERNEL_SRCS}
cdc_acm_check.cpp cdc_acm_check.cpp
${PX4_SOURCE_DIR}/platforms/common/Serial.cpp
SerialImpl.cpp
) )
target_link_libraries(px4_layer target_link_libraries(px4_layer

View File

@ -15,6 +15,8 @@ add_library(px4_layer
usr_board_ctrl.c usr_board_ctrl.c
usr_hrt.cpp usr_hrt.cpp
usr_mcu_version.cpp usr_mcu_version.cpp
${PX4_SOURCE_DIR}/platforms/common/Serial.cpp
SerialImpl.cpp
) )
target_link_libraries(px4_layer target_link_libraries(px4_layer

View File

@ -0,0 +1,105 @@
/****************************************************************************
*
* Copyright (C) 2023 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.
*
****************************************************************************/
#pragma once
#include <stdint.h>
#include <unistd.h>
#include <px4_platform_common/SerialCommon.hpp>
using device::SerialConfig::ByteSize;
using device::SerialConfig::Parity;
using device::SerialConfig::StopBits;
using device::SerialConfig::FlowControl;
namespace device
{
class SerialImpl
{
public:
SerialImpl(const char *port, uint32_t baudrate, ByteSize bytesize, Parity parity, StopBits stopbits,
FlowControl flowcontrol);
virtual ~SerialImpl();
bool open();
bool isOpen() const;
bool close();
ssize_t read(uint8_t *buffer, size_t buffer_size);
ssize_t readAtLeast(uint8_t *buffer, size_t buffer_size, size_t character_count = 1, uint32_t timeout_us = 0);
ssize_t write(const void *buffer, size_t buffer_size);
void flush();
const char *getPort() const;
uint32_t getBaudrate() const;
bool setBaudrate(uint32_t baudrate);
ByteSize getBytesize() const;
bool setBytesize(ByteSize bytesize);
Parity getParity() const;
bool setParity(Parity parity);
StopBits getStopbits() const;
bool setStopbits(StopBits stopbits);
FlowControl getFlowcontrol() const;
bool setFlowcontrol(FlowControl flowcontrol);
private:
int _serial_fd{-1};
bool _open{false};
char _port[32] {};
uint32_t _baudrate{0};
ByteSize _bytesize{ByteSize::EightBits};
Parity _parity{Parity::None};
StopBits _stopbits{StopBits::One};
FlowControl _flowcontrol{FlowControl::Disabled};
bool validateBaudrate(uint32_t baudrate);
bool configure();
};
} // namespace device

View File

@ -46,6 +46,8 @@ add_library(px4_layer
drv_hrt.cpp drv_hrt.cpp
cpuload.cpp cpuload.cpp
print_load.cpp print_load.cpp
${PX4_SOURCE_DIR}/platforms/common/Serial.cpp
SerialImpl.cpp
) )
target_compile_definitions(px4_layer PRIVATE MODULE_NAME="px4") target_compile_definitions(px4_layer PRIVATE MODULE_NAME="px4")
target_compile_options(px4_layer PRIVATE -Wno-cast-align) # TODO: fix and enable target_compile_options(px4_layer PRIVATE -Wno-cast-align) # TODO: fix and enable

View File

@ -0,0 +1,391 @@
/****************************************************************************
*
* Copyright (C) 2023 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 <SerialImpl.hpp>
#include <string.h> // strncpy
#include <termios.h>
#include <px4_log.h>
#include <fcntl.h>
#include <errno.h>
#include <poll.h>
#include <drivers/drv_hrt.h>
namespace device
{
SerialImpl::SerialImpl(const char *port, uint32_t baudrate, ByteSize bytesize, Parity parity, StopBits stopbits,
FlowControl flowcontrol) :
_baudrate(baudrate),
_bytesize(bytesize),
_parity(parity),
_stopbits(stopbits),
_flowcontrol(flowcontrol)
{
if (port) {
strncpy(_port, port, sizeof(_port) - 1);
_port[sizeof(_port) - 1] = '\0';
} else {
_port[0] = 0;
}
}
SerialImpl::~SerialImpl()
{
if (isOpen()) {
close();
}
}
bool SerialImpl::validateBaudrate(uint32_t baudrate)
{
return ((baudrate == 9600) ||
(baudrate == 19200) ||
(baudrate == 38400) ||
(baudrate == 57600) ||
(baudrate == 115200) ||
(baudrate == 230400) ||
(baudrate == 460800) ||
(baudrate == 921600));
}
bool SerialImpl::configure()
{
/* process baud rate */
int speed;
if (! validateBaudrate(_baudrate)) {
PX4_ERR("ERR: unknown baudrate: %u", _baudrate);
return false;
}
switch (_baudrate) {
case 9600: speed = B9600; break;
case 19200: speed = B19200; break;
case 38400: speed = B38400; break;
case 57600: speed = B57600; break;
case 115200: speed = B115200; break;
case 230400: speed = B230400; break;
#ifndef B460800
#define B460800 460800
#endif
case 460800: speed = B460800; break;
#ifndef B921600
#define B921600 921600
#endif
case 921600: speed = B921600; break;
default:
PX4_ERR("ERR: unknown baudrate: %d", _baudrate);
return false;
}
struct termios uart_config;
int termios_state;
/* fill the struct for the new configuration */
if ((termios_state = tcgetattr(_serial_fd, &uart_config)) < 0) {
PX4_ERR("ERR: %d (tcgetattr)", termios_state);
return false;
}
/* 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, speed)) < 0) {
PX4_ERR("ERR: %d (cfsetispeed)", termios_state);
return false;
}
if ((termios_state = cfsetospeed(&uart_config, speed)) < 0) {
PX4_ERR("ERR: %d (cfsetospeed)", termios_state);
return false;
}
if ((termios_state = tcsetattr(_serial_fd, TCSANOW, &uart_config)) < 0) {
PX4_ERR("ERR: %d (tcsetattr)", termios_state);
return false;
}
return true;
}
bool SerialImpl::open()
{
if (isOpen()) {
return true;
}
// Open the serial port
int serial_fd = ::open(_port, O_RDWR | O_NOCTTY);
if (serial_fd < 0) {
PX4_ERR("failed to open %s err: %d", _port, errno);
return false;
}
_serial_fd = serial_fd;
// Configure the serial port
if (! configure()) {
PX4_ERR("failed to configure %s err: %d", _port, errno);
return false;
}
_open = true;
return _open;
}
bool SerialImpl::isOpen() const
{
return _open;
}
bool SerialImpl::close()
{
if (_serial_fd >= 0) {
::close(_serial_fd);
}
_serial_fd = -1;
_open = false;
return true;
}
ssize_t SerialImpl::read(uint8_t *buffer, size_t buffer_size)
{
if (!_open) {
PX4_ERR("Cannot read from serial device until it has been opened");
return -1;
}
int ret = ::read(_serial_fd, buffer, buffer_size);
if (ret < 0) {
PX4_DEBUG("%s read error %d", _port, ret);
}
return ret;
}
ssize_t SerialImpl::readAtLeast(uint8_t *buffer, size_t buffer_size, size_t character_count, uint32_t timeout_us)
{
if (!_open) {
PX4_ERR("Cannot readAtLeast from serial device until it has been opened");
return -1;
}
if (buffer_size < character_count) {
PX4_ERR("%s: Buffer not big enough to hold desired amount of read data", __FUNCTION__);
return -1;
}
const hrt_abstime start_time_us = hrt_absolute_time();
int total_bytes_read = 0;
while ((total_bytes_read < (int) character_count) && (hrt_elapsed_time(&start_time_us) < timeout_us)) {
// Poll for incoming UART data.
pollfd fds[1];
fds[0].fd = _serial_fd;
fds[0].events = POLLIN;
hrt_abstime remaining_time = timeout_us - hrt_elapsed_time(&start_time_us);
if (remaining_time <= 0) { break; }
int ret = poll(fds, sizeof(fds) / sizeof(fds[0]), remaining_time);
if (ret > 0) {
if (fds[0].revents & POLLIN) {
ret = read(&buffer[total_bytes_read], buffer_size - total_bytes_read);
if (ret > 0) {
total_bytes_read += ret;
}
} else {
PX4_ERR("Got a poll error");
return -1;
}
}
}
return total_bytes_read;
}
ssize_t SerialImpl::write(const void *buffer, size_t buffer_size)
{
if (!_open) {
PX4_ERR("Cannot write to serial device until it has been opened");
return -1;
}
int written = ::write(_serial_fd, buffer, buffer_size);
::fsync(_serial_fd);
if (written < 0) {
PX4_ERR("%s write error %d", _port, written);
}
return written;
}
void SerialImpl::flush()
{
if (_open) {
tcflush(_serial_fd, TCIOFLUSH);
}
}
const char *SerialImpl::getPort() const
{
return _port;
}
uint32_t SerialImpl::getBaudrate() const
{
return _baudrate;
}
bool SerialImpl::setBaudrate(uint32_t baudrate)
{
if (! validateBaudrate(baudrate)) {
PX4_ERR("ERR: invalid baudrate: %u", baudrate);
return false;
}
// check if already configured
if ((baudrate == _baudrate) && _open) {
return true;
}
_baudrate = baudrate;
// process baud rate change now if port is already open
if (_open) {
return configure();
}
return true;
}
ByteSize SerialImpl::getBytesize() const
{
return _bytesize;
}
bool SerialImpl::setBytesize(ByteSize bytesize)
{
return bytesize == ByteSize::EightBits;
}
Parity SerialImpl::getParity() const
{
return _parity;
}
bool SerialImpl::setParity(Parity parity)
{
return parity == Parity::None;
}
StopBits SerialImpl::getStopbits() const
{
return _stopbits;
}
bool SerialImpl::setStopbits(StopBits stopbits)
{
return stopbits == StopBits::One;
}
FlowControl SerialImpl::getFlowcontrol() const
{
return _flowcontrol;
}
bool SerialImpl::setFlowcontrol(FlowControl flowcontrol)
{
return flowcontrol == FlowControl::Disabled;
}
} // namespace device

View File

@ -50,5 +50,4 @@ add_library(px4 SHARED
target_link_libraries(px4 target_link_libraries(px4
modules__muorb__slpi modules__muorb__slpi
${module_libraries} ${module_libraries}
px4_layer
) )

View File

@ -0,0 +1,110 @@
/****************************************************************************
*
* Copyright (C) 2023 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.
*
****************************************************************************/
#pragma once
#include <unistd.h>
#include <px4_platform_common/SerialCommon.hpp>
using device::SerialConfig::ByteSize;
using device::SerialConfig::Parity;
using device::SerialConfig::StopBits;
using device::SerialConfig::FlowControl;
namespace device
{
class SerialImpl
{
public:
SerialImpl(const char *port, uint32_t baudrate, ByteSize bytesize, Parity parity, StopBits stopbits,
FlowControl flowcontrol);
virtual ~SerialImpl();
bool open();
bool isOpen() const;
bool close();
ssize_t read(uint8_t *buffer, size_t buffer_size);
ssize_t readAtLeast(uint8_t *buffer, size_t buffer_size, size_t character_count = 1, uint32_t timeout_us = 0);
ssize_t write(const void *buffer, size_t buffer_size);
void flush();
const char *getPort() const;
bool setPort(const char *port);
uint32_t getBaudrate() const;
bool setBaudrate(uint32_t baudrate);
ByteSize getBytesize() const;
bool setBytesize(ByteSize bytesize);
Parity getParity() const;
bool setParity(Parity parity);
StopBits getStopbits() const;
bool setStopbits(StopBits stopbits);
FlowControl getFlowcontrol() const;
bool setFlowcontrol(FlowControl flowcontrol);
private:
int _serial_fd{-1};
bool _open{false};
char _port[32] {};
uint32_t _baudrate{0};
ByteSize _bytesize{ByteSize::EightBits};
Parity _parity{Parity::None};
StopBits _stopbits{StopBits::One};
FlowControl _flowcontrol{FlowControl::Disabled};
bool validateBaudrate(uint32_t baudrate);
// Mutex used to lock the read functions
//pthread_mutex_t read_mutex;
// Mutex used to lock the write functions
//pthread_mutex_t write_mutex;
};
} // namespace device

View File

@ -38,6 +38,7 @@ set(QURT_LAYER_SRCS
px4_qurt_impl.cpp px4_qurt_impl.cpp
main.cpp main.cpp
qurt_log.cpp qurt_log.cpp
SerialImpl.cpp
) )
add_library(px4_layer add_library(px4_layer

View File

@ -0,0 +1,335 @@
/****************************************************************************
*
* Copyright (C) 2023 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 <SerialImpl.hpp>
#include <string.h> // strncpy
#include <px4_log.h>
#include <drivers/device/qurt/uart.h>
#include <drivers/drv_hrt.h>
namespace device
{
SerialImpl::SerialImpl(const char *port, uint32_t baudrate, ByteSize bytesize, Parity parity, StopBits stopbits,
FlowControl flowcontrol) :
_baudrate(baudrate),
_bytesize(bytesize),
_parity(parity),
_stopbits(stopbits),
_flowcontrol(flowcontrol)
{
if (port) {
strncpy(_port, port, sizeof(_port) - 1);
_port[sizeof(_port) - 1] = '\0';
} else {
_port[0] = 0;
}
}
SerialImpl::~SerialImpl()
{
if (isOpen()) {
close();
}
}
bool SerialImpl::validateBaudrate(uint32_t baudrate)
{
if ((baudrate != 9600) &&
(baudrate != 38400) &&
(baudrate != 57600) &&
(baudrate != 115200) &&
(baudrate != 230400) &&
(baudrate != 250000) &&
(baudrate != 420000) &&
(baudrate != 460800) &&
(baudrate != 921600) &&
(baudrate != 1000000) &&
(baudrate != 1843200) &&
(baudrate != 2000000)) {
return false;
}
return true;
}
bool SerialImpl::open()
{
// There's no harm in calling open multiple times on the same port.
// In fact, that's the only way to change the baudrate
_open = false;
_serial_fd = -1;
if (! validateBaudrate(_baudrate)) {
PX4_ERR("Invalid baudrate: %u", _baudrate);
return false;
}
if (_bytesize != ByteSize::EightBits) {
PX4_ERR("Qurt platform only supports ByteSize::EightBits");
return false;
}
if (_parity != Parity::None) {
PX4_ERR("Qurt platform only supports Parity::None");
return false;
}
if (_stopbits != StopBits::One) {
PX4_ERR("Qurt platform only supports StopBits::One");
return false;
}
if (_flowcontrol != FlowControl::Disabled) {
PX4_ERR("Qurt platform only supports FlowControl::Disabled");
return false;
}
// qurt_uart_open will check validity of port and baudrate
int serial_fd = qurt_uart_open(_port, _baudrate);
if (serial_fd < 0) {
PX4_ERR("failed to open %s, fd returned: %d", _port, serial_fd);
return false;
} else {
PX4_INFO("Successfully opened UART %s with baudrate %u", _port, _baudrate);
}
_serial_fd = serial_fd;
_open = true;
return _open;
}
bool SerialImpl::isOpen() const
{
return _open;
}
bool SerialImpl::close()
{
// No close defined for qurt uart yet
// if (_serial_fd >= 0) {
// qurt_uart_close(_serial_fd);
// }
_serial_fd = -1;
_open = false;
return true;
}
ssize_t SerialImpl::read(uint8_t *buffer, size_t buffer_size)
{
if (!_open) {
PX4_ERR("Cannot read from serial device until it has been opened");
return -1;
}
int ret_read = qurt_uart_read(_serial_fd, (char *) buffer, buffer_size, 500);
if (ret_read < 0) {
PX4_DEBUG("%s read error %d", _port, ret_read);
}
return ret_read;
}
ssize_t SerialImpl::readAtLeast(uint8_t *buffer, size_t buffer_size, size_t character_count, uint32_t timeout_us)
{
if (!_open) {
PX4_ERR("Cannot readAtLeast from serial device until it has been opened");
return -1;
}
if (buffer_size < character_count) {
PX4_ERR("%s: Buffer not big enough to hold desired amount of read data", __FUNCTION__);
return -1;
}
const hrt_abstime start_time_us = hrt_absolute_time();
int total_bytes_read = 0;
while (total_bytes_read < (int) character_count) {
if (timeout_us > 0) {
const uint64_t elapsed_us = hrt_elapsed_time(&start_time_us);
if (elapsed_us >= timeout_us) {
// If there was a partial read but not enough to satisfy the minimum then they will be lost
// but this really should never happen when everything is working normally.
// PX4_WARN("%s timeout %d bytes read (%llu us elapsed)", __FUNCTION__, total_bytes_read, elapsed_us);
// Or, instead of returning an error, should we return the number of bytes read (assuming it is greater than zero)?
return total_bytes_read;
}
}
int current_bytes_read = read(&buffer[total_bytes_read], buffer_size - total_bytes_read);
if (current_bytes_read < 0) {
// Again, if there was a partial read but not enough to satisfy the minimum then they will be lost
// but this really should never happen when everything is working normally.
PX4_ERR("%s failed to read uart", __FUNCTION__);
// Or, instead of returning an error, should we return the number of bytes read (assuming it is greater than zero)?
return -1;
}
// Current bytes read could be zero
total_bytes_read += current_bytes_read;
// If we have at least reached our desired minimum number of characters
// then we can return now
if (total_bytes_read >= (int) character_count) {
return total_bytes_read;
}
// Wait a set amount of time before trying again or the remaining time
// until the timeout if we are getting close
const uint64_t elapsed_us = hrt_elapsed_time(&start_time_us);
int64_t time_until_timeout = timeout_us - elapsed_us;
uint64_t time_to_sleep = 5000;
if ((time_until_timeout >= 0) &&
(time_until_timeout < (int64_t) time_to_sleep)) {
time_to_sleep = time_until_timeout;
}
px4_usleep(time_to_sleep);
}
return -1;
}
ssize_t SerialImpl::write(const void *buffer, size_t buffer_size)
{
if (!_open) {
PX4_ERR("Cannot write to serial device until it has been opened");
return -1;
}
int ret_write = qurt_uart_write(_serial_fd, (const char *) buffer, buffer_size);
if (ret_write < 0) {
PX4_ERR("%s write error %d", _port, ret_write);
}
return ret_write;
}
void SerialImpl::flush()
{
if (_open) {
uint8_t buffer[4];
// A read clears out all current data
read(buffer, 4);
}
}
const char *SerialImpl::getPort() const
{
return _port;
}
uint32_t SerialImpl::getBaudrate() const
{
return _baudrate;
}
bool SerialImpl::setBaudrate(uint32_t baudrate)
{
if (! validateBaudrate(baudrate)) {
PX4_ERR("Invalid baudrate: %u", baudrate);
return false;
}
// check if already configured
if (baudrate == _baudrate) {
return true;
}
_baudrate = baudrate;
// process baud rate change now if port is already open
if (_open) {
return open();
}
return true;
}
ByteSize SerialImpl::getBytesize() const
{
return _bytesize;
}
bool SerialImpl::setBytesize(ByteSize bytesize)
{
return bytesize == ByteSize::EightBits;
}
Parity SerialImpl::getParity() const
{
return _parity;
}
bool SerialImpl::setParity(Parity parity)
{
return parity == Parity::None;
}
StopBits SerialImpl::getStopbits() const
{
return _stopbits;
}
bool SerialImpl::setStopbits(StopBits stopbits)
{
return stopbits == StopBits::One;
}
FlowControl SerialImpl::getFlowcontrol() const
{
return _flowcontrol;
}
bool SerialImpl::setFlowcontrol(FlowControl flowcontrol)
{
return flowcontrol == FlowControl::Disabled;
}
} // namespace device

View File

@ -81,7 +81,7 @@ static void hrt_unlock()
px4_sem_post(&_hrt_lock); px4_sem_post(&_hrt_lock);
} }
int px4_clock_settime(clockid_t clk_id, struct timespec *tp) int px4_clock_settime(clockid_t clk_id, const struct timespec *tp)
{ {
return 0; return 0;
} }

View File

@ -45,7 +45,6 @@
#include <poll.h> #include <poll.h>
#endif #endif
#include <termios.h>
#include <cstring> #include <cstring>
#include <drivers/drv_sensor.h> #include <drivers/drv_sensor.h>
@ -57,6 +56,8 @@
#include <px4_platform_common/cli.h> #include <px4_platform_common/cli.h>
#include <px4_platform_common/getopt.h> #include <px4_platform_common/getopt.h>
#include <px4_platform_common/module.h> #include <px4_platform_common/module.h>
#include <px4_platform_common/time.h>
#include <px4_platform_common/Serial.hpp>
#include <uORB/Publication.hpp> #include <uORB/Publication.hpp>
#include <uORB/PublicationMulti.hpp> #include <uORB/PublicationMulti.hpp>
#include <uORB/Subscription.hpp> #include <uORB/Subscription.hpp>
@ -81,6 +82,7 @@
#include <linux/spi/spidev.h> #include <linux/spi/spidev.h>
#endif /* __PX4_LINUX */ #endif /* __PX4_LINUX */
using namespace device;
using namespace time_literals; using namespace time_literals;
#define TIMEOUT_1HZ 1300 //!< Timeout time in mS, 1000 mS (1Hz) + 300 mS delta for error #define TIMEOUT_1HZ 1300 //!< Timeout time in mS, 1000 mS (1Hz) + 300 mS delta for error
@ -169,7 +171,10 @@ public:
void reset_if_scheduled(); void reset_if_scheduled();
private: private:
int _serial_fd{-1}; ///< serial interface to GPS #ifdef __PX4_LINUX
int _spi_fd {-1}; ///< SPI interface to GPS
#endif
Serial *_uart = nullptr; ///< UART interface to GPS
unsigned _baudrate{0}; ///< current baudrate unsigned _baudrate{0}; ///< current baudrate
const unsigned _configured_baudrate{0}; ///< configured baudrate (0=auto-detect) const unsigned _configured_baudrate{0}; ///< configured baudrate (0=auto-detect)
char _port[20] {}; ///< device / serial port path char _port[20] {}; ///< device / serial port path
@ -329,8 +334,11 @@ GPS::GPS(const char *path, gps_driver_mode_t mode, GPSHelper::Interface interfac
char c = _port[strlen(_port) - 1]; // last digit of path (eg /dev/ttyS2) char c = _port[strlen(_port) - 1]; // last digit of path (eg /dev/ttyS2)
set_device_bus(c - 48); // sub 48 to convert char to integer set_device_bus(c - 48); // sub 48 to convert char to integer
#ifdef __PX4_LINUX
} else if (_interface == GPSHelper::Interface::SPI) { } else if (_interface == GPSHelper::Interface::SPI) {
set_device_bus_type(device::Device::DeviceBusType::DeviceBusType_SPI); set_device_bus_type(device::Device::DeviceBusType::DeviceBusType_SPI);
#endif
} }
if (_mode == gps_driver_mode_t::None) { if (_mode == gps_driver_mode_t::None) {
@ -403,10 +411,23 @@ int GPS::callback(GPSCallbackType type, void *data1, int data2, void *user)
return num_read; return num_read;
} }
case GPSCallbackType::writeDeviceData: case GPSCallbackType::writeDeviceData: {
gps->dumpGpsData((uint8_t *)data1, (size_t)data2, gps_dump_comm_mode_t::Full, true); gps->dumpGpsData((uint8_t *)data1, (size_t)data2, gps_dump_comm_mode_t::Full, true);
return ::write(gps->_serial_fd, data1, (size_t)data2); int ret = 0;
if (gps->_uart) {
ret = gps->_uart->write((void *) data1, (size_t) data2);
#ifdef __PX4_LINUX
} else if (gps->_spi_fd >= 0) {
ret = ::write(gps->_spi_fd, data1, (size_t)data2);
#endif
}
return ret;
}
case GPSCallbackType::setBaudrate: case GPSCallbackType::setBaudrate:
return gps->setBaudrate(data2); return gps->setBaudrate(data2);
@ -449,25 +470,33 @@ int GPS::callback(GPSCallbackType type, void *data1, int data2, void *user)
int GPS::pollOrRead(uint8_t *buf, size_t buf_length, int timeout) int GPS::pollOrRead(uint8_t *buf, size_t buf_length, int timeout)
{ {
int ret = 0;
const unsigned character_count = 32; // minimum bytes that we want to read
const int max_timeout = 50;
int timeout_adjusted = math::min(max_timeout, timeout);
handleInjectDataTopic(); handleInjectDataTopic();
#if !defined(__PX4_QURT) if ((_interface == GPSHelper::Interface::UART) && (_uart)) {
ret = _uart->readAtLeast(buf, buf_length, character_count, timeout_adjusted);
/* For non QURT, use the usual polling. */ // SPI is only supported on LInux
#if defined(__PX4_LINUX)
//Poll only for the serial data. In the same thread we also need to handle orb messages, } else if ((_interface == GPSHelper::Interface::SPI) && (_spi_fd >= 0)) {
//so ideally we would poll on both, the serial fd and orb subscription. Unfortunately the
//Poll only for the SPI data. In the same thread we also need to handle orb messages,
//so ideally we would poll on both, the SPI fd and orb subscription. Unfortunately the
//two pollings use different underlying mechanisms (at least under posix), which makes this //two pollings use different underlying mechanisms (at least under posix), which makes this
//impossible. Instead we limit the maximum polling interval and regularly check for new orb //impossible. Instead we limit the maximum polling interval and regularly check for new orb
//messages. //messages.
//FIXME: add a unified poll() API //FIXME: add a unified poll() API
const int max_timeout = 50;
pollfd fds[1]; pollfd fds[1];
fds[0].fd = _serial_fd; fds[0].fd = _spi_fd;
fds[0].events = POLLIN; fds[0].events = POLLIN;
int ret = poll(fds, sizeof(fds) / sizeof(fds[0]), math::min(max_timeout, timeout)); ret = poll(fds, sizeof(fds) / sizeof(fds[0]), timeout_adjusted);
if (ret > 0) { if (ret > 0) {
/* if we have new data from GPS, go handle it */ /* if we have new data from GPS, go handle it */
@ -479,24 +508,12 @@ int GPS::pollOrRead(uint8_t *buf, size_t buf_length, int timeout)
* If we have all requested data available, read it without waiting. * If we have all requested data available, read it without waiting.
* If more bytes are available, we'll go back to poll() again. * If more bytes are available, we'll go back to poll() again.
*/ */
const unsigned character_count = 32; // minimum bytes that we want to read
unsigned baudrate = _baudrate == 0 ? 115200 : _baudrate; unsigned baudrate = _baudrate == 0 ? 115200 : _baudrate;
const unsigned sleeptime = character_count * 1000000 / (baudrate / 10); const unsigned sleeptime = character_count * 1000000 / (baudrate / 10);
#ifdef __PX4_NUTTX
int err = 0;
int bytes_available = 0;
err = ::ioctl(_serial_fd, FIONREAD, (unsigned long)&bytes_available);
if (err != 0 || bytes_available < (int)character_count) {
px4_usleep(sleeptime); px4_usleep(sleeptime);
}
#else ret = ::read(_spi_fd, buf, buf_length);
px4_usleep(sleeptime);
#endif
ret = ::read(_serial_fd, buf, buf_length);
if (ret > 0) { if (ret > 0) {
_num_bytes_read += ret; _num_bytes_read += ret;
@ -507,14 +524,10 @@ int GPS::pollOrRead(uint8_t *buf, size_t buf_length, int timeout)
} }
} }
return ret;
#else
/* For QURT, just use read for now, since this doesn't block, we need to slow it down
* just a bit. */
px4_usleep(10000);
return ::read(_serial_fd, buf, buf_length);
#endif #endif
}
return ret;
} }
void GPS::handleInjectDataTopic() void GPS::handleInjectDataTopic()
@ -583,105 +596,38 @@ bool GPS::injectData(uint8_t *data, size_t len)
{ {
dumpGpsData(data, len, gps_dump_comm_mode_t::Full, true); dumpGpsData(data, len, gps_dump_comm_mode_t::Full, true);
size_t written = ::write(_serial_fd, data, len); size_t written = 0;
::fsync(_serial_fd);
if ((_interface == GPSHelper::Interface::UART) && (_uart)) {
written = _uart->write((const void *) data, len);
#ifdef __PX4_LINUX
} else if (_interface == GPSHelper::Interface::SPI) {
written = ::write(_spi_fd, data, len);
::fsync(_spi_fd);
#endif
}
return written == len; return written == len;
} }
int GPS::setBaudrate(unsigned baud) int GPS::setBaudrate(unsigned baud)
{ {
/* process baud rate */ if (_interface == GPSHelper::Interface::UART) {
int speed; if ((_uart) && (_uart->setBaudrate(baud))) {
switch (baud) {
case 9600: speed = B9600; break;
case 19200: speed = B19200; break;
case 38400: speed = B38400; break;
case 57600: speed = B57600; break;
case 115200: speed = B115200; break;
case 230400: speed = B230400; break;
#ifndef B460800
#define B460800 460800
#endif
case 460800: speed = B460800; break;
#ifndef B921600
#define B921600 921600
#endif
case 921600: speed = B921600; break;
default:
PX4_ERR("ERR: unknown baudrate: %d", baud);
return -EINVAL;
}
struct termios uart_config;
int termios_state;
/* fill the struct for the new configuration */
tcgetattr(_serial_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, speed)) < 0) {
GPS_ERR("ERR: %d (cfsetispeed)", termios_state);
return -1;
}
if ((termios_state = cfsetospeed(&uart_config, speed)) < 0) {
GPS_ERR("ERR: %d (cfsetospeed)", termios_state);
return -1;
}
if ((termios_state = tcsetattr(_serial_fd, TCSANOW, &uart_config)) < 0) {
GPS_ERR("ERR: %d (tcsetattr)", termios_state);
return -1;
}
return 0; return 0;
}
#ifdef __PX4_LINUX
} else if (_interface == GPSHelper::Interface::SPI) {
// Can't set the baudrate on a SPI port but just return a success
return 0;
#endif
}
return -1;
} }
void GPS::initializeCommunicationDump() void GPS::initializeCommunicationDump()
@ -840,32 +786,59 @@ GPS::run()
_helper = nullptr; _helper = nullptr;
} }
if (_serial_fd < 0) { if ((_interface == GPSHelper::Interface::UART) && (_uart == nullptr)) {
/* open the serial port */
_serial_fd = ::open(_port, O_RDWR | O_NOCTTY);
if (_serial_fd < 0) { // Create the UART port instance
PX4_ERR("failed to open %s err: %d", _port, errno); _uart = new Serial(_port);
if (_uart == nullptr) {
PX4_ERR("Error creating serial device %s", _port);
px4_sleep(1);
continue;
}
}
if ((_interface == GPSHelper::Interface::UART) && (! _uart->isOpen())) {
// Configure the desired baudrate if one was specified by the user.
// Otherwise the default baudrate will be used.
if (_configured_baudrate) {
if (! _uart->setBaudrate(_configured_baudrate)) {
PX4_ERR("Error setting baudrate to %u on %s", _configured_baudrate, _port);
px4_sleep(1);
continue;
}
}
// Open the UART. If this is successful then the UART is ready to use.
if (! _uart->open()) {
PX4_ERR("Error opening serial device %s", _port);
px4_sleep(1); px4_sleep(1);
continue; continue;
} }
#ifdef __PX4_LINUX #ifdef __PX4_LINUX
if (_interface == GPSHelper::Interface::SPI) { } else if ((_interface == GPSHelper::Interface::SPI) && (_spi_fd < 0)) {
_spi_fd = ::open(_port, O_RDWR | O_NOCTTY);
if (_spi_fd < 0) {
PX4_ERR("failed to open SPI port %s err: %d", _port, errno);
px4_sleep(1);
continue;
}
int spi_speed = 1000000; // make sure the bus speed is not too high (required on RPi) int spi_speed = 1000000; // make sure the bus speed is not too high (required on RPi)
int status_value = ::ioctl(_serial_fd, SPI_IOC_WR_MAX_SPEED_HZ, &spi_speed); int status_value = ::ioctl(_spi_fd, SPI_IOC_WR_MAX_SPEED_HZ, &spi_speed);
if (status_value < 0) { if (status_value < 0) {
PX4_ERR("SPI_IOC_WR_MAX_SPEED_HZ failed for %s (%d)", _port, errno); PX4_ERR("SPI_IOC_WR_MAX_SPEED_HZ failed for %s (%d)", _port, errno);
} }
status_value = ::ioctl(_serial_fd, SPI_IOC_RD_MAX_SPEED_HZ, &spi_speed); status_value = ::ioctl(_spi_fd, SPI_IOC_RD_MAX_SPEED_HZ, &spi_speed);
if (status_value < 0) { if (status_value < 0) {
PX4_ERR("SPI_IOC_RD_MAX_SPEED_HZ failed for %s (%d)", _port, errno); PX4_ERR("SPI_IOC_RD_MAX_SPEED_HZ failed for %s (%d)", _port, errno);
} }
}
#endif /* __PX4_LINUX */ #endif /* __PX4_LINUX */
} }
@ -1056,9 +1029,17 @@ GPS::run()
} }
} }
if (_serial_fd >= 0) { if ((_interface == GPSHelper::Interface::UART) && (_uart)) {
::close(_serial_fd); (void) _uart->close();
_serial_fd = -1; delete _uart;
_uart = nullptr;
#ifdef __PX4_LINUX
} else if ((_interface == GPSHelper::Interface::SPI) && (_spi_fd >= 0)) {
::close(_spi_fd);
_spi_fd = -1;
#endif
} }
if (_mode_auto) { if (_mode_auto) {
@ -1477,12 +1458,12 @@ GPS *GPS::instantiate(int argc, char *argv[], Instance instance)
break; break;
case 'i': case 'i':
if (!strcmp(myoptarg, "spi")) { if (!strcmp(myoptarg, "uart")) {
interface = GPSHelper::Interface::SPI;
} else if (!strcmp(myoptarg, "uart")) {
interface = GPSHelper::Interface::UART; interface = GPSHelper::Interface::UART;
#ifdef __PX4_LINUX
} else if (!strcmp(myoptarg, "spi")) {
interface = GPSHelper::Interface::SPI;
#endif
} else { } else {
PX4_ERR("unknown interface: %s", myoptarg); PX4_ERR("unknown interface: %s", myoptarg);
error_flag = true; error_flag = true;
@ -1490,12 +1471,12 @@ GPS *GPS::instantiate(int argc, char *argv[], Instance instance)
break; break;
case 'j': case 'j':
if (!strcmp(myoptarg, "spi")) { if (!strcmp(myoptarg, "uart")) {
interface_secondary = GPSHelper::Interface::SPI;
} else if (!strcmp(myoptarg, "uart")) {
interface_secondary = GPSHelper::Interface::UART; interface_secondary = GPSHelper::Interface::UART;
#ifdef __PX4_LINUX
} else if (!strcmp(myoptarg, "spi")) {
interface_secondary = GPSHelper::Interface::SPI;
#endif
} else { } else {
PX4_ERR("unknown interface for secondary: %s", myoptarg); PX4_ERR("unknown interface for secondary: %s", myoptarg);
error_flag = true; error_flag = true;

View File

@ -46,6 +46,4 @@ px4_add_module(
MODULE_CONFIG MODULE_CONFIG
module.yaml module.yaml
DEPENDS
rc
) )

View File

@ -43,6 +43,7 @@
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include <string.h> #include <string.h>
#include <px4_log.h>
#include "QueueBuffer.hpp" #include "QueueBuffer.hpp"
#include "CrsfParser.hpp" #include "CrsfParser.hpp"
#include "Crc8.hpp" #include "Crc8.hpp"
@ -161,6 +162,8 @@ static float MapF(const float x, const float in_min, const float in_max, const f
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
} }
#define CONSTRAIN_CHAN(x) ConstrainF(x, CRSF_CHANNEL_VALUE_MIN, CRSF_CHANNEL_VALUE_MAX)
static bool ProcessChannelData(const uint8_t *data, const uint32_t size, CrsfPacket_t *const new_packet) static bool ProcessChannelData(const uint8_t *data, const uint32_t size, CrsfPacket_t *const new_packet)
{ {
uint32_t raw_channels[CRSF_CHANNEL_COUNT]; uint32_t raw_channels[CRSF_CHANNEL_COUNT];
@ -169,25 +172,24 @@ static bool ProcessChannelData(const uint8_t *data, const uint32_t size, CrsfPac
new_packet->message_type = CRSF_MESSAGE_TYPE_RC_CHANNELS; new_packet->message_type = CRSF_MESSAGE_TYPE_RC_CHANNELS;
// Decode channel data // Decode channel data
raw_channels[0] = (data[0] | data[1] << 8) & 0x07FF; raw_channels[0] = CONSTRAIN_CHAN((data[0] | data[1] << 8) & 0x07FF);
raw_channels[1] = (data[1] >> 3 | data[2] << 5) & 0x07FF; raw_channels[1] = CONSTRAIN_CHAN((data[1] >> 3 | data[2] << 5) & 0x07FF);
raw_channels[2] = (data[2] >> 6 | data[3] << 2 | data[4] << 10) & 0x07FF; raw_channels[2] = CONSTRAIN_CHAN((data[2] >> 6 | data[3] << 2 | data[4] << 10) & 0x07FF);
raw_channels[3] = (data[4] >> 1 | data[5] << 7) & 0x07FF; raw_channels[3] = CONSTRAIN_CHAN((data[4] >> 1 | data[5] << 7) & 0x07FF);
raw_channels[4] = (data[5] >> 4 | data[6] << 4) & 0x07FF; raw_channels[4] = CONSTRAIN_CHAN((data[5] >> 4 | data[6] << 4) & 0x07FF);
raw_channels[5] = (data[6] >> 7 | data[7] << 1 | data[8] << 9) & 0x07FF; raw_channels[5] = CONSTRAIN_CHAN((data[6] >> 7 | data[7] << 1 | data[8] << 9) & 0x07FF);
raw_channels[6] = (data[8] >> 2 | data[9] << 6) & 0x07FF; raw_channels[6] = CONSTRAIN_CHAN((data[8] >> 2 | data[9] << 6) & 0x07FF);
raw_channels[7] = (data[9] >> 5 | data[10] << 3) & 0x07FF; raw_channels[7] = CONSTRAIN_CHAN((data[9] >> 5 | data[10] << 3) & 0x07FF);
raw_channels[8] = (data[11] | data[12] << 8) & 0x07FF; raw_channels[8] = CONSTRAIN_CHAN((data[11] | data[12] << 8) & 0x07FF);
raw_channels[9] = (data[12] >> 3 | data[13] << 5) & 0x07FF; raw_channels[9] = CONSTRAIN_CHAN((data[12] >> 3 | data[13] << 5) & 0x07FF);
raw_channels[10] = (data[13] >> 6 | data[14] << 2 | data[15] << 10) & 0x07FF; raw_channels[10] = CONSTRAIN_CHAN((data[13] >> 6 | data[14] << 2 | data[15] << 10) & 0x07FF);
raw_channels[11] = (data[15] >> 1 | data[16] << 7) & 0x07FF; raw_channels[11] = CONSTRAIN_CHAN((data[15] >> 1 | data[16] << 7) & 0x07FF);
raw_channels[12] = (data[16] >> 4 | data[17] << 4) & 0x07FF; raw_channels[12] = CONSTRAIN_CHAN((data[16] >> 4 | data[17] << 4) & 0x07FF);
raw_channels[13] = (data[17] >> 7 | data[18] << 1 | data[19] << 9) & 0x07FF; raw_channels[13] = CONSTRAIN_CHAN((data[17] >> 7 | data[18] << 1 | data[19] << 9) & 0x07FF);
raw_channels[14] = (data[19] >> 2 | data[20] << 6) & 0x07FF; raw_channels[14] = CONSTRAIN_CHAN((data[19] >> 2 | data[20] << 6) & 0x07FF);
raw_channels[15] = (data[20] >> 5 | data[21] << 3) & 0x07FF; raw_channels[15] = CONSTRAIN_CHAN((data[20] >> 5 | data[21] << 3) & 0x07FF);
for (i = 0; i < CRSF_CHANNEL_COUNT; i++) { for (i = 0; i < CRSF_CHANNEL_COUNT; i++) {
raw_channels[i] = ConstrainF(raw_channels[i], CRSF_CHANNEL_VALUE_MIN, CRSF_CHANNEL_VALUE_MAX);
new_packet->channel_data.channels[i] = MapF((float)raw_channels[i], CRSF_CHANNEL_VALUE_MIN, CRSF_CHANNEL_VALUE_MAX, new_packet->channel_data.channels[i] = MapF((float)raw_channels[i], CRSF_CHANNEL_VALUE_MIN, CRSF_CHANNEL_VALUE_MAX,
1000.0f, 2000.0f); 1000.0f, 2000.0f);
} }

View File

@ -35,8 +35,6 @@
#include "CrsfParser.hpp" #include "CrsfParser.hpp"
#include "Crc8.hpp" #include "Crc8.hpp"
#include <poll.h>
#include <termios.h>
#include <fcntl.h> #include <fcntl.h>
#include <uORB/topics/battery_status.h> #include <uORB/topics/battery_status.h>
@ -118,25 +116,43 @@ void CrsfRc::Run()
{ {
if (should_exit()) { if (should_exit()) {
ScheduleClear(); ScheduleClear();
::close(_rc_fd);
_rc_fd = -1; if (_uart) {
(void) _uart->close();
delete _uart;
_uart = nullptr;
}
exit_and_cleanup(); exit_and_cleanup();
return; return;
} }
if (_rc_fd < 0) { if (_uart == nullptr) {
_rc_fd = ::open(_device, O_RDWR | O_NONBLOCK); // Create the UART port instance
_uart = new Serial(_device);
if (_rc_fd >= 0) { if (_uart == nullptr) {
struct termios t; PX4_ERR("Error creating serial device %s", _device);
px4_sleep(1);
return;
}
}
tcgetattr(_rc_fd, &t); if (! _uart->isOpen()) {
cfsetspeed(&t, CRSF_BAUDRATE); // Configure the desired baudrate if one was specified by the user.
t.c_cflag &= ~(CSTOPB | PARENB | CRTSCTS); // Otherwise the default baudrate will be used.
t.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG); if (! _uart->setBaudrate(CRSF_BAUDRATE)) {
t.c_iflag &= ~(IGNBRK | BRKINT | ICRNL | INLCR | PARMRK | INPCK | ISTRIP | IXON); PX4_ERR("Error setting baudrate to %u on %s", CRSF_BAUDRATE, _device);
t.c_oflag = 0; px4_sleep(1);
tcsetattr(_rc_fd, TCSANOW, &t); return;
}
// Open the UART. If this is successful then the UART is ready to use.
if (! _uart->open()) {
PX4_ERR("Error opening serial device %s", _device);
px4_sleep(1);
return;
}
if (board_rc_swap_rxtx(_device)) { if (board_rc_swap_rxtx(_device)) {
#if defined(TIOCSSWAP) #if defined(TIOCSSWAP)
@ -157,37 +173,21 @@ void CrsfRc::Run()
PX4_INFO("Crsf serial is single wire. Telemetry disabled"); PX4_INFO("Crsf serial is single wire. Telemetry disabled");
} }
tcflush(_rc_fd, TCIOFLUSH); _uart->flush();
Crc8Init(0xd5); Crc8Init(0xd5);
}
_input_rc.rssi_dbm = NAN; _input_rc.rssi_dbm = NAN;
_input_rc.link_quality = -1; _input_rc.link_quality = -1;
CrsfParser_Init(); CrsfParser_Init();
}
// poll with 100mS timeout
pollfd fds[1];
fds[0].fd = _rc_fd;
fds[0].events = POLLIN;
int ret = ::poll(fds, 1, 100);
if (ret < 0) {
PX4_ERR("poll error");
// try again with delay
ScheduleDelayed(100_ms);
return;
} }
const hrt_abstime time_now_us = hrt_absolute_time(); const hrt_abstime time_now_us = hrt_absolute_time();
perf_count_interval(_cycle_interval_perf, time_now_us); perf_count_interval(_cycle_interval_perf, time_now_us);
// Read all available data from the serial RC input UART // Read all available data from the serial RC input UART
int new_bytes = ::read(_rc_fd, &_rcs_buf[0], RC_MAX_BUFFER_SIZE); int new_bytes = _uart->readAtLeast(&_rcs_buf[0], RC_MAX_BUFFER_SIZE, 1, 100);
if (new_bytes > 0) { if (new_bytes > 0) {
_bytes_rx += new_bytes; _bytes_rx += new_bytes;
@ -433,7 +433,8 @@ bool CrsfRc::SendTelemetryBattery(const uint16_t voltage, const uint16_t current
write_uint24_t(buf, offset, fuel); write_uint24_t(buf, offset, fuel);
write_uint8_t(buf, offset, remaining); write_uint8_t(buf, offset, remaining);
WriteFrameCrc(buf, offset, sizeof(buf)); WriteFrameCrc(buf, offset, sizeof(buf));
return write(_rc_fd, buf, offset) == offset; return _uart->write((void *) buf, (size_t) offset);
} }
bool CrsfRc::SendTelemetryGps(const int32_t latitude, const int32_t longitude, const uint16_t groundspeed, bool CrsfRc::SendTelemetryGps(const int32_t latitude, const int32_t longitude, const uint16_t groundspeed,
@ -449,7 +450,7 @@ bool CrsfRc::SendTelemetryGps(const int32_t latitude, const int32_t longitude, c
write_uint16_t(buf, offset, altitude); write_uint16_t(buf, offset, altitude);
write_uint8_t(buf, offset, num_satellites); write_uint8_t(buf, offset, num_satellites);
WriteFrameCrc(buf, offset, sizeof(buf)); WriteFrameCrc(buf, offset, sizeof(buf));
return write(_rc_fd, buf, offset) == offset; return _uart->write((void *) buf, (size_t) offset);
} }
bool CrsfRc::SendTelemetryAttitude(const int16_t pitch, const int16_t roll, const int16_t yaw) bool CrsfRc::SendTelemetryAttitude(const int16_t pitch, const int16_t roll, const int16_t yaw)
@ -461,7 +462,7 @@ bool CrsfRc::SendTelemetryAttitude(const int16_t pitch, const int16_t roll, cons
write_uint16_t(buf, offset, roll); write_uint16_t(buf, offset, roll);
write_uint16_t(buf, offset, yaw); write_uint16_t(buf, offset, yaw);
WriteFrameCrc(buf, offset, sizeof(buf)); WriteFrameCrc(buf, offset, sizeof(buf));
return write(_rc_fd, buf, offset) == offset; return _uart->write((void *) buf, (size_t) offset);
} }
bool CrsfRc::SendTelemetryFlightMode(const char *flight_mode) bool CrsfRc::SendTelemetryFlightMode(const char *flight_mode)
@ -480,7 +481,7 @@ bool CrsfRc::SendTelemetryFlightMode(const char *flight_mode)
offset += length; offset += length;
buf[offset - 1] = 0; // ensure null-terminated string buf[offset - 1] = 0; // ensure null-terminated string
WriteFrameCrc(buf, offset, length + 4); WriteFrameCrc(buf, offset, length + 4);
return write(_rc_fd, buf, offset) == offset; return _uart->write((void *) buf, (size_t) offset);
} }
int CrsfRc::print_status() int CrsfRc::print_status()

View File

@ -40,6 +40,7 @@
#include <px4_platform_common/log.h> #include <px4_platform_common/log.h>
#include <px4_platform_common/module.h> #include <px4_platform_common/module.h>
#include <px4_platform_common/module_params.h> #include <px4_platform_common/module_params.h>
#include <px4_platform_common/Serial.hpp>
#include <px4_platform_common/px4_work_queue/ScheduledWorkItem.hpp> #include <px4_platform_common/px4_work_queue/ScheduledWorkItem.hpp>
#include <drivers/drv_hrt.h> #include <drivers/drv_hrt.h>
#include <lib/perf/perf_counter.h> #include <lib/perf/perf_counter.h>
@ -53,6 +54,8 @@
#include <uORB/topics/sensor_gps.h> #include <uORB/topics/sensor_gps.h>
#include <uORB/topics/vehicle_status.h> #include <uORB/topics/vehicle_status.h>
using namespace device;
class CrsfRc : public ModuleBase<CrsfRc>, public ModuleParams, public px4::ScheduledWorkItem class CrsfRc : public ModuleBase<CrsfRc>, public ModuleParams, public px4::ScheduledWorkItem
{ {
public: public:
@ -87,7 +90,8 @@ private:
bool SendTelemetryFlightMode(const char *flight_mode); bool SendTelemetryFlightMode(const char *flight_mode);
int _rc_fd{-1}; Serial *_uart = nullptr; ///< UART interface to RC
char _device[20] {}; ///< device / serial port path char _device[20] {}; ///< device / serial port path
bool _is_singlewire{false}; bool _is_singlewire{false};