LCOV - code coverage report
Current view: top level - AP_SBusOut - AP_SBusOut.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 37 60 61.7 %
Date: 2026-09-09 11:02:08 Functions: 4 4 100.0 %

          Line data    Source code
       1             : /*
       2             :  * AP_SBusOut.cpp
       3             :  *
       4             :  *  Created on: Aug 19, 2017
       5             :  *      Author: Mark Whitehorn
       6             :  *
       7             :  * method sbus1_out was ported from ardupilot/modules/PX4Firmware/src/lib/rc/sbus.c
       8             :  * which has the following license:
       9             :  *
      10             :  *   Copyright (c) 2012-2014 PX4 Development Team. All rights reserved.
      11             :  *
      12             :  * Redistribution and use in source and binary forms, with or without
      13             :  * modification, are permitted provided that the following conditions
      14             :  * are met:
      15             :  *
      16             :  * 1. Redistributions of source code must retain the above copyright
      17             :  *    notice, this list of conditions and the following disclaimer.
      18             :  * 2. Redistributions in binary form must reproduce the above copyright
      19             :  *    notice, this list of conditions and the following disclaimer in
      20             :  *    the documentation and/or other materials provided with the
      21             :  *    distribution.
      22             :  * 3. Neither the name PX4 nor the names of its contributors may be
      23             :  *    used to endorse or promote products derived from this software
      24             :  *    without specific prior written permission.
      25             :  *
      26             :  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
      27             :  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
      28             :  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
      29             :  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
      30             :  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
      31             :  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
      32             :  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
      33             :  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
      34             :  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
      35             :  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
      36             :  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
      37             :  * POSSIBILITY OF SUCH DAMAGE.
      38             :  *
      39             :  */
      40             : 
      41             : #include "AP_SBusOut_config.h"
      42             : 
      43             : #if AP_SBUSOUTPUT_ENABLED
      44             : 
      45             : #include "AP_SBusOut.h"
      46             : #include <AP_Math/AP_Math.h>
      47             : #include <AP_SerialManager/AP_SerialManager.h>
      48             : #include <SRV_Channel/SRV_Channel.h>
      49             : 
      50             : extern const AP_HAL::HAL& hal;
      51             : 
      52             : #define SBUS_DEBUG 0
      53             : 
      54             : #define SBUS_BSIZE 25
      55             : #define SBUS_CHANNELS 16
      56             : #define SBUS_MIN 875
      57             : 
      58             : const AP_Param::GroupInfo AP_SBusOut::var_info[] = {
      59             :     // @Param: RATE
      60             :     // @DisplayName: SBUS default output rate
      61             :     // @Description: This sets the SBUS output frame rate in Hz.
      62             :     // @Range: 25 250
      63             :     // @User: Advanced
      64             :     // @Units: Hz
      65             :     AP_GROUPINFO("RATE",  1, AP_SBusOut, sbus_rate, 50),
      66             : 
      67             :     AP_GROUPEND
      68             : };
      69             : 
      70             : 
      71             : // constructor
      72        2094 : AP_SBusOut::AP_SBusOut(void)
      73             : {
      74             :     // set defaults from the parameter table
      75        2094 :     AP_Param::setup_object_defaults(this, var_info);
      76        2094 : }
      77             : 
      78             : /*
      79             :   format a SBUS output frame into a 25 byte buffer
      80             :  */
      81        2560 : void AP_SBusOut::sbus_format_frame(uint16_t *channels, uint8_t num_channels, uint8_t buffer[SBUS_BSIZE])
      82             : {
      83        2560 :     uint8_t index = 1;
      84        2560 :     uint8_t offset = 0;
      85             : 
      86        2560 :     memset(buffer, 0, SBUS_BSIZE);
      87        2560 :     buffer[0] = 0x0f;
      88             : 
      89             :     /* construct sbus frame representing channels 1 through 16 (max) */
      90        2560 :     const uint8_t nchan = MIN(num_channels, SBUS_CHANNELS);
      91       23040 :     for (unsigned i = 0; i < nchan; ++i) {
      92             :         /*protect from out of bounds values and limit to 11 bits*/
      93       20480 :         const uint16_t pwmval = MAX(channels[i], SBUS_MIN);
      94       20480 :         const uint32_t v1 = uint32_t(pwmval - SBUS_MIN) * 1600U;
      95       20480 :         uint16_t value = uint16_t(v1 / 1000U) + 1;
      96       20480 :         if (value > 0x07ff) {
      97           0 :             value = 0x07ff;
      98             :         }
      99             : 
     100             : #if SBUS_DEBUG
     101             :         static uint16_t lastch9 = 0;
     102             :         if ((i==8) && (pwmval != lastch9)) {
     103             :             lastch9 = pwmval;
     104             :             hal.console->printf("channel 9 pwm: %04d\n", pwmval);
     105             :         }
     106             : #endif
     107             : 
     108       43520 :         while (offset >= 8) {
     109       23040 :             ++index;
     110       23040 :             offset -= 8;
     111             :         }
     112             : 
     113       20480 :         buffer[index] |= (value << (offset)) & 0xff;
     114       20480 :         buffer[index + 1] |= (value >> (8 - offset)) & 0xff;
     115       20480 :         buffer[index + 2] |= (value >> (16 - offset)) & 0xff;
     116       20480 :         offset += 11;
     117             :     }
     118        2560 : }
     119             : 
     120             : /*
     121             :  * build and send sbus1 frame representing first 16 servo channels
     122             :  * input arg is pointer to uart
     123             :  */
     124             : void
     125    41692164 : AP_SBusOut::update()
     126             : {
     127    41692164 :     if (!initialised) {
     128        2056 :         initialised = true;
     129        2056 :         init();
     130             :     }
     131             : 
     132    41692164 :     if (sbus1_uart == nullptr) {
     133    41692164 :         return;
     134             :     }
     135             : 
     136             :     // constrain output rate using sbus_frame_interval
     137             :     static uint32_t last_micros = 0;
     138           0 :     uint32_t now = AP_HAL::micros();
     139           0 :     if ((now - last_micros) <= sbus_frame_interval) {
     140           0 :         return;
     141             :     }
     142             : 
     143           0 :     last_micros = now;
     144             : 
     145             :     /* construct sbus frame representing channels 1 through 16 (max) */
     146           0 :     uint8_t nchan = MIN(NUM_SERVO_CHANNELS, SBUS_CHANNELS);
     147           0 :     uint16_t channels[SBUS_CHANNELS] {};
     148             : 
     149           0 :     for (unsigned i = 0; i < nchan; ++i) {
     150           0 :         SRV_Channel *c = SRV_Channels::srv_channel(i);
     151           0 :         if (c == nullptr) {
     152           0 :             continue;
     153             :         }
     154           0 :         channels[i] = c->get_output_pwm();
     155             :     }
     156             :     uint8_t buffer[SBUS_BSIZE];
     157           0 :     sbus_format_frame(channels, nchan, buffer);
     158             : 
     159             : #if SBUS_DEBUG
     160             :     hal.gpio->pinMode(55, HAL_GPIO_OUTPUT);
     161             :     hal.gpio->write(55, 1);
     162             : #endif
     163             : 
     164           0 :     sbus1_uart->write(buffer, sizeof(buffer));
     165             : 
     166             : #if SBUS_DEBUG
     167             :     hal.gpio->pinMode(55, HAL_GPIO_OUTPUT);
     168             :     hal.gpio->write(55, 0);
     169             : #endif
     170             : }
     171             : 
     172        2056 : void AP_SBusOut::init() {
     173        2056 :     uint16_t rate = sbus_rate.get();
     174             : 
     175             : #if SBUS_DEBUG
     176             :     hal.console->printf("AP_SBusOut: init %d Hz\n", rate);
     177             : #endif
     178             : 
     179             :     // subtract 500usec from requested frame interval to allow for latency
     180        2056 :     sbus_frame_interval = (1000UL * 1000UL) / rate - 500;
     181             :     // at 100,000 bps, a 300 bit sbus frame takes 3msec to transfer
     182             :     // require a minimum 700usec interframe gap
     183        2056 :     if (sbus_frame_interval < 3700) {
     184           0 :         sbus_frame_interval = 3700;
     185             :     }
     186             : 
     187        2056 :     AP_SerialManager *serial_manager = AP_SerialManager::get_singleton();
     188        2056 :     if (!serial_manager) {
     189           0 :         return;
     190             :     }
     191        2056 :     sbus1_uart = serial_manager->find_serial(AP_SerialManager::SerialProtocol_Sbus1,0);
     192             : 
     193        2056 :     if (sbus1_uart == nullptr) {
     194        2056 :         return;
     195             :     }
     196             : 
     197             :     // update baud param in case user looks at it
     198           0 :     serial_manager->set_and_default_baud(AP_SerialManager::SerialProtocol_Sbus1, 0, 100000);
     199             : 
     200           0 :     auto &uart = *sbus1_uart;
     201             : 
     202           0 :     uart.begin(100000, 16, 32);
     203           0 :     uart.configure_parity(2);    // enable even parity
     204           0 :     uart.set_stop_bits(2);
     205           0 :     uart.set_unbuffered_writes(true);
     206           0 :     uart.set_flow_control(AP_HAL::UARTDriver::FLOW_CONTROL_DISABLE);
     207             : }
     208             : 
     209             : #endif  // AP_SBUSOUTPUT_ENABLED

Generated by: LCOV version 1.14