LCOV - code coverage report
Current view: top level - SRV_Channel - SRV_Channels.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 69 111 62.2 %
Date: 2026-09-09 11:02:08 Functions: 9 14 64.3 %

          Line data    Source code
       1             : /*
       2             :    This program is free software: you can redistribute it and/or modify
       3             :    it under the terms of the GNU General Public License as published by
       4             :    the Free Software Foundation, either version 3 of the License, or
       5             :    (at your option) any later version.
       6             : 
       7             :    This program is distributed in the hope that it will be useful,
       8             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
       9             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      10             :    GNU General Public License for more details.
      11             : 
      12             :    You should have received a copy of the GNU General Public License
      13             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      14             :  */
      15             : /*
      16             :   SRV_Channel.cpp - object to separate input and output channel
      17             :   ranges, trim and reversal
      18             :  */
      19             : 
      20             : #include <AP_HAL/AP_HAL.h>
      21             : #include <AP_Math/AP_Math.h>
      22             : #include <AP_Scheduler/AP_Scheduler.h>
      23             : #include <AP_Vehicle/AP_Vehicle_Type.h>
      24             : 
      25             : #include "SRV_Channel.h"
      26             : #include <AP_Logger/AP_Logger.h>
      27             : #include <AP_KDECAN/AP_KDECAN.h>
      28             : 
      29             : #if HAL_MAX_CAN_PROTOCOL_DRIVERS
      30             :   #include <AP_CANManager/AP_CANManager.h>
      31             :   #include <AP_DroneCAN/AP_DroneCAN.h>
      32             :   #include <AP_PiccoloCAN/AP_PiccoloCAN.h>
      33             : #endif
      34             : 
      35             : #if NUM_SERVO_CHANNELS == 0
      36             : #pragma GCC diagnostic ignored "-Wtype-limits"
      37             : #endif
      38             : 
      39             : extern const AP_HAL::HAL& hal;
      40             : 
      41             : SRV_Channel *SRV_Channels::channels;
      42             : SRV_Channels *SRV_Channels::_singleton;
      43             : 
      44             : uint16_t SRV_Channels::override_counter[NUM_SERVO_CHANNELS];
      45             : 
      46             : uint32_t SRV_Channels::disabled_mask;
      47             : uint32_t SRV_Channels::digital_mask;
      48             : uint32_t SRV_Channels::reversible_mask;
      49             : uint32_t SRV_Channels::invalid_mask;
      50             : 
      51             : bool SRV_Channels::disabled_passthrough;
      52             : bool SRV_Channels::initialised;
      53             : bool SRV_Channels::emergency_stop;
      54             : Bitmask<SRV_Channel::k_nr_aux_servo_functions> SRV_Channels::function_mask;
      55             : SRV_Channels::srv_function SRV_Channels::functions[SRV_Channel::k_nr_aux_servo_functions];
      56             : SRV_Channels::slew_list *SRV_Channels::_slew;
      57             : 
      58             : const AP_Param::GroupInfo SRV_Channels::var_info[] = {
      59             : #if (NUM_SERVO_CHANNELS >= 1)
      60             :     // @Group: 1_
      61             :     // @Path: SRV_Channel.cpp
      62             :     AP_SUBGROUPINFO(obj_channels[0], "1_",  1, SRV_Channels, SRV_Channel),
      63             : #endif
      64             : 
      65             : #if (NUM_SERVO_CHANNELS >= 2)
      66             :     // @Group: 2_
      67             :     // @Path: SRV_Channel.cpp
      68             :     AP_SUBGROUPINFO(obj_channels[1], "2_",  2, SRV_Channels, SRV_Channel),
      69             : #endif
      70             : 
      71             : #if (NUM_SERVO_CHANNELS >= 3)
      72             :     // @Group: 3_
      73             :     // @Path: SRV_Channel.cpp
      74             :     AP_SUBGROUPINFO(obj_channels[2], "3_",  3, SRV_Channels, SRV_Channel),
      75             : #endif
      76             : 
      77             : #if (NUM_SERVO_CHANNELS >= 4)
      78             :     // @Group: 4_
      79             :     // @Path: SRV_Channel.cpp
      80             :     AP_SUBGROUPINFO(obj_channels[3], "4_",  4, SRV_Channels, SRV_Channel),
      81             : #endif
      82             : 
      83             : #if (NUM_SERVO_CHANNELS >= 5)
      84             :     // @Group: 5_
      85             :     // @Path: SRV_Channel.cpp
      86             :     AP_SUBGROUPINFO(obj_channels[4], "5_",  5, SRV_Channels, SRV_Channel),
      87             : #endif
      88             : 
      89             : #if (NUM_SERVO_CHANNELS >= 6)
      90             :     // @Group: 6_
      91             :     // @Path: SRV_Channel.cpp
      92             :     AP_SUBGROUPINFO(obj_channels[5], "6_",  6, SRV_Channels, SRV_Channel),
      93             : #endif
      94             : 
      95             : #if (NUM_SERVO_CHANNELS >= 7)
      96             :     // @Group: 7_
      97             :     // @Path: SRV_Channel.cpp
      98             :     AP_SUBGROUPINFO(obj_channels[6], "7_",  7, SRV_Channels, SRV_Channel),
      99             : #endif
     100             : 
     101             : #if (NUM_SERVO_CHANNELS >= 8)
     102             :     // @Group: 8_
     103             :     // @Path: SRV_Channel.cpp
     104             :     AP_SUBGROUPINFO(obj_channels[7], "8_",  8, SRV_Channels, SRV_Channel),
     105             : #endif
     106             : 
     107             : #if (NUM_SERVO_CHANNELS >= 9)
     108             :     // @Group: 9_
     109             :     // @Path: SRV_Channel.cpp
     110             :     AP_SUBGROUPINFO(obj_channels[8], "9_",  9, SRV_Channels, SRV_Channel),
     111             : #endif
     112             : 
     113             : #if (NUM_SERVO_CHANNELS >= 10)
     114             :     // @Group: 10_
     115             :     // @Path: SRV_Channel.cpp
     116             :     AP_SUBGROUPINFO(obj_channels[9], "10_",  10, SRV_Channels, SRV_Channel),
     117             : #endif
     118             : 
     119             : #if (NUM_SERVO_CHANNELS >= 11)
     120             :     // @Group: 11_
     121             :     // @Path: SRV_Channel.cpp
     122             :     AP_SUBGROUPINFO(obj_channels[10], "11_",  11, SRV_Channels, SRV_Channel),
     123             : #endif
     124             : 
     125             : #if (NUM_SERVO_CHANNELS >= 12)
     126             :     // @Group: 12_
     127             :     // @Path: SRV_Channel.cpp
     128             :     AP_SUBGROUPINFO(obj_channels[11], "12_",  12, SRV_Channels, SRV_Channel),
     129             : #endif
     130             : 
     131             : #if (NUM_SERVO_CHANNELS >= 13)
     132             :     // @Group: 13_
     133             :     // @Path: SRV_Channel.cpp
     134             :     AP_SUBGROUPINFO(obj_channels[12], "13_",  13, SRV_Channels, SRV_Channel),
     135             : #endif
     136             : 
     137             : #if (NUM_SERVO_CHANNELS >= 14)
     138             :     // @Group: 14_
     139             :     // @Path: SRV_Channel.cpp
     140             :     AP_SUBGROUPINFO(obj_channels[13], "14_",  14, SRV_Channels, SRV_Channel),
     141             : #endif
     142             : 
     143             : #if (NUM_SERVO_CHANNELS >= 15)
     144             :     // @Group: 15_
     145             :     // @Path: SRV_Channel.cpp
     146             :     AP_SUBGROUPINFO(obj_channels[14], "15_",  15, SRV_Channels, SRV_Channel),
     147             : #endif
     148             : 
     149             : #if (NUM_SERVO_CHANNELS >= 16)
     150             :     // @Group: 16_
     151             :     // @Path: SRV_Channel.cpp
     152             :     AP_SUBGROUPINFO(obj_channels[15], "16_",  16, SRV_Channels, SRV_Channel),
     153             : #endif
     154             : 
     155             : #ifndef  HAL_BUILD_AP_PERIPH
     156             :     // @Param{Plane}: _AUTO_TRIM
     157             :     // @DisplayName: Automatic servo trim
     158             :     // @Description: This enables automatic servo trim in flight. Servos will be trimed in stabilized flight modes when the aircraft is close to level. Changes to servo trim will be saved every 10 seconds and will persist between flights. The automatic trim won't go more than 20% away from a centered trim.
     159             :     // @Values: 0:Disable,1:Enable
     160             :     // @User: Advanced
     161             :     AP_GROUPINFO_FRAME("_AUTO_TRIM",  17, SRV_Channels, auto_trim, 0, AP_PARAM_FRAME_PLANE),
     162             : #endif
     163             : 
     164             :     // @Param: _RATE
     165             :     // @DisplayName: Servo default output rate
     166             :     // @Description: Default output rate in Hz for all PWM outputs.
     167             :     // @Range: 25 400
     168             :     // @User: Advanced
     169             :     // @Units: Hz
     170             :     AP_GROUPINFO("_RATE",  18, SRV_Channels, default_rate, 50),
     171             : 
     172             : #if AP_VOLZ_ENABLED
     173             :     // @Group: _VOLZ_
     174             :     // @Path: ../AP_Volz_Protocol/AP_Volz_Protocol.cpp
     175             :     AP_SUBGROUPINFO(volz, "_VOLZ_",  19, SRV_Channels, AP_Volz_Protocol),
     176             : #endif
     177             : 
     178             : #if AP_SBUSOUTPUT_ENABLED
     179             :     // @Group: _SBUS_
     180             :     // @Path: ../AP_SBusOut/AP_SBusOut.cpp
     181             :     AP_SUBGROUPINFO(sbus, "_SBUS_",  20, SRV_Channels, AP_SBusOut),
     182             : #endif
     183             : 
     184             : #if HAL_SUPPORT_RCOUT_SERIAL
     185             :     // @Group: _BLH_
     186             :     // @Path: ../AP_BLHeli/AP_BLHeli.cpp
     187             :     AP_SUBGROUPINFO(blheli, "_BLH_",  21, SRV_Channels, AP_BLHeli),
     188             : #endif
     189             : 
     190             : #if AP_ROBOTISSERVO_ENABLED
     191             :     // @Group: _ROB_
     192             :     // @Path: ../AP_RobotisServo/AP_RobotisServo.cpp
     193             :     AP_SUBGROUPINFO(robotis, "_ROB_",  22, SRV_Channels, AP_RobotisServo),
     194             : #endif
     195             : 
     196             : #if AP_FETTEC_ONEWIRE_ENABLED
     197             :     // @Group: _FTW_
     198             :     // @Path: ../AP_FETtecOneWire/AP_FETtecOneWire.cpp
     199             :     AP_SUBGROUPINFO(fetteconwire, "_FTW_",  25, SRV_Channels, AP_FETtecOneWire),
     200             : #endif
     201             : 
     202             :     // @Param: _DSHOT_RATE
     203             :     // @DisplayName: Servo DShot output rate
     204             :     // @Description: DShot output rate for all outputs as a multiple of the loop rate. 0 sets the output rate to be fixed at 1Khz for low loop rates. This value should never be set below 500Hz.
     205             :     // @Values: 0:1Khz,1:loop-rate,2:double loop-rate,3:triple loop-rate,4:quadruple loop rate
     206             :     // @User: Advanced
     207             :     AP_GROUPINFO("_DSHOT_RATE",  23, SRV_Channels, dshot_rate, 0),
     208             : 
     209             :     // @Param: _DSHOT_ESC
     210             :     // @DisplayName: Servo DShot ESC type
     211             :     // @Description: DShot ESC type for all outputs. The ESC type affects the range of DShot commands available and the bit widths used. None means that no dshot commands will be executed. Some ESC types support Extended DShot Telemetry (EDT) which allows telemetry other than RPM data to be returned when using bi-directional dshot. If you enable EDT you must install EDT capable firmware for correct operation.
     212             :     // @Values: 0:None,1:BLHeli32/Kiss/AM32,2:BLHeli_S/BlueJay,3:BLHeli32/AM32/Kiss+EDT,4:BLHeli_S/BlueJay+EDT
     213             :     // @User: Advanced
     214             :     AP_GROUPINFO("_DSHOT_ESC",  24, SRV_Channels, dshot_esc_type, 0),
     215             : 
     216             :     // @Param: _GPIO_MASK
     217             :     // @DisplayName: Servo GPIO mask
     218             :     // @Description: Bitmask of outputs which will be available as GPIOs. Any output with either the function set to -1 or with the corresponding bit set in this mask will be available for use as a GPIO pin
     219             :     // @Bitmask: 0:Servo 1, 1:Servo 2, 2:Servo 3, 3:Servo 4, 4:Servo 5, 5:Servo 6, 6:Servo 7, 7:Servo 8, 8:Servo 9, 9:Servo 10, 10:Servo 11, 11:Servo 12, 12:Servo 13, 13:Servo 14, 14:Servo 15, 15:Servo 16, 16:Servo 17, 17:Servo 18, 18:Servo 19, 19:Servo 20, 20:Servo 21, 21:Servo 22, 22:Servo 23, 23:Servo 24, 24:Servo 25, 25:Servo 26, 26:Servo 27, 27:Servo 28, 28:Servo 29, 29:Servo 30, 30:Servo 31, 31:Servo 32
     220             :     // @User: Advanced
     221             :     // @RebootRequired: True
     222             :     AP_GROUPINFO("_GPIO_MASK",  26, SRV_Channels, gpio_mask, 0),  
     223             : 
     224             :     // indexes 27-43 used by SERVO_32_ENABLEd group of params
     225             :  
     226             :     // @Param: _RC_FS_MSK
     227             :     // @DisplayName: Servo RC Failsafe Mask
     228             :     // @Description: Bitmask of scaled passthru output channels which will be set to their trim value during rc failsafe instead of holding their last position before failsafe.
     229             :     // @Bitmask: 0:RCIN1Scaled, 1:RCIN2Scaled, 2:RCIN3Scaled, 3:RCIN4Scaled, 4:RCIN5Scaled, 5:RCIN6Scaled, 6:RCIN7Scaled, 7:RCIN8Scaled, 8:RCIN9Scaled, 9:RCIN10Scaled, 10:RCIN11Scaled, 11:SRCIN12Scaled, 12:RCIN13Scaled, 13:RCIN14Scaled, 14:RCIN15Scaled, 15:RCIN16Scaled
     230             :     // @User: Advanced
     231             :     AP_GROUPINFO("_RC_FS_MSK", 44, SRV_Channels, rc_fs_mask, 0),
     232             :  
     233             : #if (NUM_SERVO_CHANNELS >= 17)
     234             :     // @Param: _32_ENABLE
     235             :     // @DisplayName: Enable outputs 17 to 31
     236             :     // @Description: This allows for up to 32 outputs, enabling parameters for outputs above 16
     237             :     // @User: Advanced
     238             :     // @Values: 0:Disabled,1:Enabled
     239             :     AP_GROUPINFO_FLAGS("_32_ENABLE", 43, SRV_Channels, enable_32_channels, 0, AP_PARAM_FLAG_ENABLE),
     240             : #endif
     241             : 
     242             : #if (NUM_SERVO_CHANNELS >= 17)
     243             :     // @Group: 17_
     244             :     // @Path: SRV_Channel.cpp
     245             :     AP_SUBGROUPINFO(obj_channels[16], "17_",  27, SRV_Channels, SRV_Channel),
     246             : #endif
     247             : 
     248             : #if (NUM_SERVO_CHANNELS >= 18)
     249             :     // @Group: 18_
     250             :     // @Path: SRV_Channel.cpp
     251             :     AP_SUBGROUPINFO(obj_channels[17], "18_", 28, SRV_Channels, SRV_Channel),
     252             : #endif
     253             : 
     254             : #if (NUM_SERVO_CHANNELS >= 19)
     255             :     // @Group: 19_
     256             :     // @Path: SRV_Channel.cpp
     257             :     AP_SUBGROUPINFO(obj_channels[18], "19_",  29, SRV_Channels, SRV_Channel),
     258             : #endif
     259             : 
     260             : #if (NUM_SERVO_CHANNELS >= 20)
     261             :     // @Group: 20_
     262             :     // @Path: SRV_Channel.cpp
     263             :     AP_SUBGROUPINFO(obj_channels[19], "20_",  30, SRV_Channels, SRV_Channel),
     264             : #endif
     265             : 
     266             : #if (NUM_SERVO_CHANNELS >= 21)
     267             :     // @Group: 21_
     268             :     // @Path: SRV_Channel.cpp
     269             :     AP_SUBGROUPINFO(obj_channels[20], "21_",  31, SRV_Channels, SRV_Channel),
     270             : #endif
     271             : 
     272             : #if (NUM_SERVO_CHANNELS >= 22)
     273             :     // @Group: 22_
     274             :     // @Path: SRV_Channel.cpp
     275             :     AP_SUBGROUPINFO(obj_channels[21], "22_",  32, SRV_Channels, SRV_Channel),
     276             : #endif
     277             : 
     278             : #if (NUM_SERVO_CHANNELS >= 23)
     279             :     // @Group: 23_
     280             :     // @Path: SRV_Channel.cpp
     281             :     AP_SUBGROUPINFO(obj_channels[22], "23_",  33, SRV_Channels, SRV_Channel),
     282             : #endif
     283             : 
     284             : #if (NUM_SERVO_CHANNELS >= 24)
     285             :     // @Group: 24_
     286             :     // @Path: SRV_Channel.cpp
     287             :     AP_SUBGROUPINFO(obj_channels[23], "24_",  34, SRV_Channels, SRV_Channel),
     288             : #endif
     289             : 
     290             : #if (NUM_SERVO_CHANNELS >= 25)
     291             :     // @Group: 25_
     292             :     // @Path: SRV_Channel.cpp
     293             :     AP_SUBGROUPINFO(obj_channels[24], "25_",  35, SRV_Channels, SRV_Channel),
     294             : #endif
     295             : 
     296             : #if (NUM_SERVO_CHANNELS >= 26)
     297             :     // @Group: 26_
     298             :     // @Path: SRV_Channel.cpp
     299             :     AP_SUBGROUPINFO(obj_channels[25], "26_",  36, SRV_Channels, SRV_Channel),
     300             : #endif
     301             : 
     302             : #if (NUM_SERVO_CHANNELS >= 27)
     303             :     // @Group: 27_
     304             :     // @Path: SRV_Channel.cpp
     305             :     AP_SUBGROUPINFO(obj_channels[26], "27_",  37, SRV_Channels, SRV_Channel),
     306             : #endif
     307             : 
     308             : #if (NUM_SERVO_CHANNELS >= 28)
     309             :     // @Group: 28_
     310             :     // @Path: SRV_Channel.cpp
     311             :     AP_SUBGROUPINFO(obj_channels[27], "28_",  38, SRV_Channels, SRV_Channel),
     312             : #endif
     313             : 
     314             : #if (NUM_SERVO_CHANNELS >= 29)
     315             :     // @Group: 29_
     316             :     // @Path: SRV_Channel.cpp
     317             :     AP_SUBGROUPINFO(obj_channels[28], "29_",  39, SRV_Channels, SRV_Channel),
     318             : #endif
     319             : 
     320             : #if (NUM_SERVO_CHANNELS >= 30)
     321             :     // @Group: 30_
     322             :     // @Path: SRV_Channel.cpp
     323             :     AP_SUBGROUPINFO(obj_channels[29], "30_",  40, SRV_Channels, SRV_Channel),
     324             : #endif
     325             : 
     326             : #if (NUM_SERVO_CHANNELS >= 31)
     327             :     // @Group: 31_
     328             :     // @Path: SRV_Channel.cpp
     329             :     AP_SUBGROUPINFO(obj_channels[30], "31_",  41, SRV_Channels, SRV_Channel),
     330             : #endif
     331             : 
     332             : #if (NUM_SERVO_CHANNELS >= 32)
     333             :     // @Group: 32_
     334             :     // @Path: SRV_Channel.cpp
     335             :     AP_SUBGROUPINFO(obj_channels[31], "32_",  42, SRV_Channels, SRV_Channel),
     336             : #endif
     337             : 
     338             :     AP_GROUPEND
     339             : };
     340             : 
     341             : /*
     342             :   constructor
     343             :  */
     344       69102 : SRV_Channels::SRV_Channels(void)
     345             : {
     346        2094 :     _singleton = this;
     347        2094 :     channels = obj_channels;
     348             : 
     349             :     // set defaults from the parameter table
     350        2094 :     AP_Param::setup_object_defaults(this, var_info);
     351             : 
     352             :     // setup ch_num on channels
     353       69102 :     for (uint8_t i=0; i<NUM_SERVO_CHANNELS; i++) {
     354       67008 :         channels[i].ch_num = i;
     355             : #if NUM_SERVO_CHANNELS > 16
     356       67008 :         if (i >= 16) {
     357             :             // default to GPIO, this disables the pin and stops logging
     358       33504 :             channels[i].function.set_default(SRV_Channel::k_GPIO);
     359             :         }
     360             : #endif
     361             :     }
     362        2094 : }
     363             : 
     364             : // SRV_Channels initialization
     365        2086 : void SRV_Channels::init(uint32_t motor_mask, AP_HAL::RCOutput::output_mode mode)
     366             : {
     367             :     // initialize BLHeli late so that all of the masks it might setup don't get trodden on by motor initialization
     368             : #if HAL_SUPPORT_RCOUT_SERIAL
     369        2086 :     blheli.init(motor_mask, mode);
     370             : #endif
     371             : #ifndef HAL_BUILD_AP_PERIPH
     372        2086 :     hal.rcout->set_dshot_rate(_singleton->dshot_rate, AP::scheduler().get_loop_rate_hz());
     373             : #endif
     374        2086 : }
     375             : 
     376             : /*
     377             :   save adjusted trims
     378             :  */
     379           0 : void SRV_Channels::save_trim(void)
     380             : {
     381           0 :     for (uint8_t i=0; i<NUM_SERVO_CHANNELS; i++) {
     382           0 :         if (trimmed_mask & (1U<<i)) {
     383           0 :             channels[i].servo_trim.set_and_save(channels[i].servo_trim.get());
     384             :         }
     385             :     }
     386           0 :     trimmed_mask = 0;
     387           0 : }
     388             : 
     389         691 : void SRV_Channels::setup_failsafe_trim_all_non_motors(void)
     390             : {
     391       22803 :     for (uint8_t i = 0; i < NUM_SERVO_CHANNELS; i++) {
     392       22112 :         if (!SRV_Channel::is_motor(channels[i].get_function())) {
     393       21306 :             hal.rcout->set_failsafe_pwm(1U<<channels[i].ch_num, channels[i].servo_trim);
     394             :         }
     395             :     }
     396         691 : }
     397             : 
     398             : /*
     399             :   run calc_pwm for all channels
     400             :  */
     401    41920478 : void SRV_Channels::calc_pwm(void)
     402             : {
     403             :     // slew rate limit functions
     404    83825623 :     for (slew_list *slew = _slew; slew; slew = slew->next) {
     405    41905145 :         if (is_positive(slew->max_change)) {
     406             :             // treat negative or zero slew rate as disabled
     407    36796787 :             functions[slew->func].output_scaled = constrain_float(functions[slew->func].output_scaled, slew->last_scaled_output - slew->max_change, slew->last_scaled_output + slew->max_change);
     408             :         }
     409    41905145 :         slew->last_scaled_output = functions[slew->func].output_scaled;
     410             :     }
     411             : 
     412    41920478 :     WITH_SEMAPHORE(_singleton->override_counter_sem);
     413             : 
     414  1383375774 :     for (uint8_t i=0; i<NUM_SERVO_CHANNELS; i++) {
     415             :         // check if channel has been locked out for this loop
     416             :         // if it has, decrement the loop count for that channel
     417  1341455296 :         if (override_counter[i] == 0) {
     418  1341455296 :             channels[i].set_override(false);
     419             :         } else {
     420           0 :             channels[i].set_override(true);
     421           0 :             override_counter[i]--;
     422             :         }
     423  1341455296 :         if (channels[i].valid_function()) {
     424   671271768 :             channels[i].calc_pwm(functions[channels[i].function.get()].output_scaled);
     425             :         }
     426             :     }
     427    41920478 : }
     428             : 
     429             : // set output value for a specific function channel as a pwm value
     430           0 : void SRV_Channels::set_output_pwm_chan(uint8_t chan, uint16_t value)
     431             : {
     432           0 :     if (chan < NUM_SERVO_CHANNELS) {
     433           0 :         channels[chan].set_output_pwm(value);
     434             :     }
     435           0 : }
     436             : 
     437             : // get output value for a specific channel as a pwm value
     438           0 : bool SRV_Channels::get_output_pwm_chan(uint8_t chan, uint16_t &value)
     439             : {
     440           0 :     if (chan >= NUM_SERVO_CHANNELS) {
     441           0 :         return false;
     442             :     }
     443           0 :     value = channels[chan].get_output_pwm();
     444           0 :     return true;
     445             : }
     446             : 
     447             : #if AP_SCRIPTING_ENABLED && AP_SCHEDULER_ENABLED
     448             : // set output value for a specific function channel as a pwm value with loop based timeout
     449             : // timeout_ms of zero will clear override of the channel
     450             : // minimum override is 1 MAIN_LOOP
     451           0 : void SRV_Channels::set_output_pwm_chan_timeout(uint8_t chan, uint16_t value, uint16_t timeout_ms)
     452             : {
     453           0 :     WITH_SEMAPHORE(_singleton->override_counter_sem);
     454             : 
     455           0 :     if (chan < NUM_SERVO_CHANNELS) {
     456           0 :         const uint32_t loop_period_us = AP::scheduler().get_loop_period_us();
     457             :         // round up so any non-zero requested value will result in at least one loop
     458           0 :         const uint32_t loop_count = ((timeout_ms * 1000U) + (loop_period_us - 1U)) / loop_period_us;
     459           0 :         override_counter[chan] = constrain_int32(loop_count, 0, UINT16_MAX);
     460           0 :         channels[chan].set_override(true);
     461           0 :         const bool had_pwm = SRV_Channel::have_pwm_mask & (1U<<chan);
     462           0 :         channels[chan].set_output_pwm(value,true);
     463           0 :         if (!had_pwm) {
     464             :             // clear the have PWM mask so the channel will default back to the scaled value when timeout expires
     465             :             // this is also cleared by set_output_scaled but that requires it to be re-called as some point
     466             :             // after the timeout is applied
     467             :             // note that we can't default back to a pre-override PWM value as it is not stored
     468             :             // checking had_pwm means the PWM will not change after the timeout, this was the existing behaviour
     469           0 :             SRV_Channel::have_pwm_mask &= ~(1U<<chan);
     470             :         }
     471             :     }
     472           0 : }
     473             : #endif  // AP_SCRIPTING_ENABLED
     474             : 
     475             : /*
     476             :   wrapper around hal.rcout->cork()
     477             :  */
     478    50213822 : void SRV_Channels::cork()
     479             : {
     480    50213822 :     hal.rcout->cork();
     481    50213822 : }
     482             : 
     483             : /*
     484             :   wrapper around hal.rcout->push()
     485             :  */
     486    41692164 : void SRV_Channels::push()
     487             : {
     488    41692164 :     hal.rcout->push();
     489             : 
     490             : #if AP_VOLZ_ENABLED
     491             :     // give volz library a chance to update
     492    41692164 :     volz.update();
     493             : #endif
     494             : 
     495             : #if AP_SBUSOUTPUT_ENABLED
     496             :     // give sbus library a chance to update
     497    41692164 :     sbus.update();
     498             : #endif
     499             : 
     500             : #if AP_ROBOTISSERVO_ENABLED
     501             :     // give robotis library a chance to update
     502    41692164 :     robotis.update();
     503             : #endif
     504             : 
     505             : #if HAL_SUPPORT_RCOUT_SERIAL
     506             :     // give blheli telemetry a chance to update
     507    41692164 :     blheli.update_telemetry();
     508             : #endif
     509             : 
     510             : #if AP_FETTEC_ONEWIRE_ENABLED
     511    41692164 :     fetteconwire.update();
     512             : #endif
     513             : 
     514             : #if AP_KDECAN_ENABLED
     515    41692164 :     if (AP::kdecan() != nullptr) {
     516    41692164 :         AP::kdecan()->update();
     517             :     }
     518             : #endif
     519             : 
     520             : #if HAL_ENABLE_DRONECAN_DRIVERS
     521             :     // push outputs to CAN
     522    41692164 :     uint8_t can_num_drivers = AP::can().get_num_drivers();
     523   125076492 :     for (uint8_t i = 0; i < can_num_drivers; i++) {
     524    83384328 :         switch (AP::can().get_driver_type(i)) {
     525      157616 :             case AP_CAN::Protocol::DroneCAN: {
     526      157616 :                 AP_DroneCAN *ap_dronecan = AP_DroneCAN::get_dronecan(i);
     527      157616 :                 if (ap_dronecan == nullptr) {
     528           0 :                     continue;
     529             :                 }
     530      157616 :                 ap_dronecan->SRV_push_servos();
     531      157616 :                 break;
     532             :             }
     533             : #if AP_PICCOLOCAN_ENABLED
     534           0 :             case AP_CAN::Protocol::PiccoloCAN: {
     535           0 :                 AP_PiccoloCAN *ap_pcan = AP_PiccoloCAN::get_pcan(i);
     536           0 :                 if (ap_pcan == nullptr) {
     537           0 :                     continue;
     538             :                 }
     539           0 :                 ap_pcan->update();
     540           0 :                 break;
     541             :             }
     542             : #endif
     543    83226712 :             case AP_CAN::Protocol::None:
     544             :             default:
     545    83226712 :                 break;
     546             :         }
     547             :     }
     548             : #endif // HAL_ENABLE_DRONECAN_DRIVERS
     549    41692164 : }
     550             : 
     551          69 : void SRV_Channels::zero_rc_outputs()
     552             : {
     553             :     /* Send an invalid signal to the motors to prevent spinning due to
     554             :      * neutral (1500) pwm pulse being cut short.  For that matter,
     555             :      * send an invalid signal to all channels to prevent
     556             :      * undesired/unexpected behavior
     557             :      */
     558          69 :     auto &srv = AP::srv();
     559          69 :     srv.cork();
     560        2277 :     for (uint8_t i=0; i<NUM_SERVO_CHANNELS; i++) {
     561        2208 :         hal.rcout->write(i, 0);
     562             :     }
     563          69 :     srv.push();
     564          69 : }
     565             : 
     566             : /*
     567             :   return true if a channel should be available as a GPIO
     568             :  */
     569           0 : bool SRV_Channels::is_GPIO(uint8_t channel)
     570             : {
     571           0 :     if (channel_function(channel) == SRV_Channel::k_GPIO) {
     572           0 :         return true;
     573             :     }
     574           0 :     if (_singleton != nullptr && (_singleton->gpio_mask & (1U<<channel)) != 0) {
     575             :         // user has set this channel in SERVO_GPIO_MASK
     576           0 :         return true;
     577             :     }
     578           0 :     return false;
     579             : }
     580             : 
     581             : // Set E - stop
     582          10 : void SRV_Channels::set_emergency_stop(bool state) {
     583             : #if HAL_LOGGING_ENABLED
     584          10 :     if (state != emergency_stop) {
     585           8 :         AP_Logger *logger = AP_Logger::get_singleton();
     586           8 :         if (logger && logger->logging_enabled()) {
     587           8 :             logger->Write_Event(state ? LogEvent::MOTORS_EMERGENCY_STOPPED : LogEvent::MOTORS_EMERGENCY_STOP_CLEARED);
     588             :         }
     589             :     }
     590             : #endif
     591          10 :     emergency_stop = state;
     592          10 : }
     593             : 
     594             : namespace AP {
     595             : 
     596    50367134 : SRV_Channels &srv()
     597             : {
     598    50367134 :     return *SRV_Channels::get_singleton();
     599             : }
     600             : 
     601             : };

Generated by: LCOV version 1.14