LCOV - code coverage report
Current view: top level - AP_HAL - RCOutput.h (source / functions) Hit Total Coverage
Test: lcov.info Lines: 15 50 30.0 %
Date: 2026-09-09 11:02:08 Functions: 12 45 26.7 %

          Line data    Source code
       1             : #pragma once
       2             : 
       3             : #include "AP_HAL_Namespace.h"
       4             : #include <stdint.h>
       5             : 
       6             : #define RC_OUTPUT_MIN_PULSEWIDTH 400
       7             : #define RC_OUTPUT_MAX_PULSEWIDTH 2100
       8             : 
       9             : /* Define the CH_n names, indexed from 1, if we don't have them already */
      10             : #ifndef CH_1
      11             : #define CH_1 0
      12             : #define CH_2 1
      13             : #define CH_3 2
      14             : #define CH_4 3
      15             : #define CH_5 4
      16             : #define CH_6 5
      17             : #define CH_7 6
      18             : #define CH_8 7
      19             : #define CH_9 8
      20             : #define CH_10 9
      21             : #define CH_11 10
      22             : #define CH_12 11
      23             : #define CH_13 12
      24             : #define CH_14 13
      25             : #define CH_15 14
      26             : #define CH_16 15
      27             : #define CH_17 16
      28             : #define CH_18 17
      29             : #define CH_19 18
      30             : #define CH_20 19
      31             : #define CH_21 20
      32             : #define CH_22 21
      33             : #define CH_23 22
      34             : #define CH_24 23
      35             : #define CH_25 24
      36             : #define CH_26 25
      37             : #define CH_27 26
      38             : #define CH_28 27
      39             : #define CH_29 28
      40             : #define CH_30 29
      41             : #define CH_31 30
      42             : #define CH_32 31
      43             : #define CH_NONE 255
      44             : #endif
      45             : 
      46             : class ByteBuffer;
      47             : 
      48             : class ExpandingString;
      49             : 
      50             : class AP_HAL::RCOutput {
      51             : public:
      52             :     virtual void init() = 0;
      53             : 
      54             :     /* Output freq (1/period) control */
      55             :     virtual void     set_freq(uint32_t chmask, uint16_t freq_hz) = 0;
      56             :     virtual uint16_t get_freq(uint8_t chan) = 0;
      57             : 
      58             :     /* Output active/highZ control, either by single channel at a time
      59             :      * or a mask of channels */
      60             :     virtual void     enable_ch(uint8_t chan) = 0;
      61             :     virtual void     disable_ch(uint8_t chan) = 0;
      62             : 
      63             :     /*
      64             :      * Output a single channel, possibly grouped with previous writes if
      65             :      * cork() has been called before.
      66             :      */
      67             :     virtual void     write(uint8_t chan, uint16_t period_us) = 0;
      68             : 
      69             :     /*
      70             :      * mark the channels in chanmask as reversible. This is needed for some ESC types (such as DShot)
      71             :      * so that output scaling can be performed correctly. The chanmask passed is added (ORed) into any existing mask.
      72             :      * The mask uses servo channel numbering
      73             :      */
      74        2086 :     virtual void     set_reversible_mask(uint32_t chanmask) {}
      75             :     
      76             :     /*
      77             :      * mark the channels in chanmask as reversed.
      78             :      * The chanmask passed is added (ORed) into any existing mask.
      79             :      * The mask uses servo channel numbering
      80             :      */
      81        2086 :     virtual void     set_reversed_mask(uint32_t chanmask) {}
      82           0 :     virtual uint32_t get_reversed_mask() { return 0; }
      83             : 
      84             :     /*
      85             :      * Update channel masks at 1Hz allowing for actions such as dshot commands to be sent
      86             :      */
      87      151226 :     virtual void     update_channel_masks() {}
      88             : 
      89             :     /*
      90             :      * Allow channel mask updates to be temporarily suspended
      91             :      */
      92           0 :     virtual void     disable_channel_mask_updates() {}
      93           0 :     virtual void     enable_channel_mask_updates() {}
      94             : 
      95             :     /*
      96             :      * Delay subsequent calls to write() going to the underlying hardware in
      97             :      * order to group related writes together. When all the needed writes are
      98             :      * done, call push() to commit the changes.
      99             :      */
     100             :     virtual void     cork() = 0;
     101             : 
     102             :     /*
     103             :      * Push pending changes to the underlying hardware. All changes between a
     104             :      * call to cork() and push() are pushed together in a single transaction.
     105             :      */
     106             :     virtual void     push() = 0;
     107             : 
     108             :     /* Read back current output state, as either single channel or
     109             :      * array of channels. On boards that have a separate IO controller,
     110             :      * this returns the latest output value that the IO controller has
     111             :      * reported */
     112             :     virtual uint16_t read(uint8_t chan) = 0;
     113             :     virtual void     read(uint16_t* period_us, uint8_t len) = 0;
     114             : 
     115             :     /* Read the current input state. This returns the last value that was written. */
     116           0 :     virtual uint16_t read_last_sent(uint8_t chan) { return read(chan); }
     117           0 :     virtual void     read_last_sent(uint16_t* period_us, uint8_t len) { read(period_us, len); };
     118             : 
     119             :     /*
     120             :       set PWM to send to a set of channels if the FMU firmware dies
     121             :      */
     122       33251 :     virtual void     set_failsafe_pwm(uint32_t chmask, uint16_t period_us) {}
     123             : 
     124             :     /*
     125             :       force the safety switch on, disabling PWM output from the IO board
     126             :       return false (indicating failure) by default so that boards with no safety switch
     127             :       do not need to implement this method
     128             :      */
     129           0 :     virtual bool     force_safety_on(void) { return false; }
     130             : 
     131             :     /*
     132             :       force the safety switch off, enabling PWM output from the IO board
     133             :      */
     134           0 :     virtual void     force_safety_off(void) {}
     135             : 
     136             :     /*
     137             :       setup scaling of ESC output for ESCs that can output a
     138             :       percentage of power (such as UAVCAN ESCs). The values are in
     139             :       microseconds, and represent minimum and maximum PWM values which
     140             :       will be used to convert channel writes into a percentage
     141             :      */
     142       82902 :     void set_esc_scaling(uint16_t min_pwm, uint16_t max_pwm) {
     143       82902 :         _esc_pwm_min = min_pwm;
     144       82902 :         _esc_pwm_max = max_pwm;
     145       82902 :     }
     146             : 
     147             :     /*
     148             :       returns the pwm value scaled to [-1;1] regrading to set_esc_scaling ranges range without constraints.
     149             :      */
     150             :     float scale_esc_to_unity(uint16_t pwm) const;
     151             : 
     152             :     /*
     153             :       return the erpm and error rate for a channel if available
     154             :      */
     155           0 :     virtual uint16_t get_erpm(uint8_t chan) const { return 0; }
     156           0 :     virtual float get_erpm_error_rate(uint8_t chan) const { return 100.0f; }
     157             :     /*
     158             :       allow all erpm values to be read and for new updates to be detected - primarily for IOMCU
     159             :      */
     160           0 :     virtual bool  new_erpm() { return false; }
     161           0 :     virtual uint32_t  read_erpm(uint16_t* erpm, uint8_t len) { return 0; }
     162             : 
     163             :     /*
     164             :       enable PX4IO SBUS out at the given rate
     165             :      */
     166           0 :     virtual bool enable_px4io_sbus_out(uint16_t rate_hz) { return false; }
     167             : 
     168             :     /*
     169             :      * Optional method to control the update of the motors. Derived classes
     170             :      * can implement it if their HAL layer requires.
     171             :      */
     172           0 :     virtual void timer_tick(void) { }
     173             : 
     174             :     /*
     175             :       setup for serial output to an ESC using the given
     176             :       baudrate. Assumes 1 start bit, 1 stop bit, LSB first and 8
     177             :       databits. This is used for passthrough ESC configuration and
     178             :       firmware flashing
     179             : 
     180             :       While serial output is active normal output to all channels in
     181             :       the chanmask is suspended. Output to some other channels (such
     182             :       as those in the same channel timer groups) may also be stopped,
     183             :       depending on the implementation
     184             :      */
     185           0 :     virtual bool serial_setup_output(uint8_t chan, uint32_t baudrate, uint32_t chanmask) { return false; }
     186             : 
     187             :     /*
     188             :       write a set of bytes to an ESC, using settings from
     189             :       serial_setup_output. This is a blocking call
     190             :      */
     191           0 :     virtual bool serial_write_bytes(const uint8_t *bytes, uint16_t len) { return false; }
     192             : 
     193             :     /*
     194             :       read a series of bytes from a port, using serial parameters from serial_setup_output()
     195             :       return the number of bytes read. This is a blocking call
     196             :      */
     197           0 :     virtual uint16_t serial_read_bytes(uint8_t *buf, uint16_t len, uint32_t timeout_us) { return 0; }
     198             :     
     199             :     /*
     200             :       stop serial output. This restores the previous output mode for
     201             :       the channel and any other channels that were stopped by
     202             :       serial_setup_output()
     203             :      */
     204           0 :     virtual void serial_end(uint32_t chanmask) {}
     205             :     
     206             :     /*
     207             :       reset serial output. This re-initializes the DMA configuration to that configured by
     208             :       serial_setup_output()
     209             :      */
     210           0 :     virtual void serial_reset(uint32_t chanmask) {}
     211             : 
     212             :      /*
     213             :       output modes. Allows for support of PWM, oneshot and dshot 
     214             :     */
     215             :     // this enum is used by BLH_OTYPE and ESC_PWM_TYPE on AP_Periph
     216             :     // double check params are still correct when changing
     217             :     enum output_mode {
     218             :         MODE_PWM_NONE,
     219             :         MODE_PWM_NORMAL,
     220             :         MODE_PWM_ONESHOT,
     221             :         MODE_PWM_ONESHOT125,
     222             :         MODE_PWM_BRUSHED,
     223             :         MODE_PWM_DSHOT150,
     224             :         MODE_PWM_DSHOT300,
     225             :         MODE_PWM_DSHOT600,
     226             :         MODE_PWM_DSHOT1200,
     227             :         MODE_NEOPIXEL,  // same as MODE_PWM_DSHOT at 800kHz but it's an LED
     228             :         MODE_PROFILED,  // same as MODE_PWM_DSHOT using separate clock and data
     229             :         MODE_NEOPIXELRGB,  // same as MODE_NEOPIXEL but RGB ordering
     230             :     };
     231             :     // true when the output mode is of type dshot
     232             :     // static to allow use in the ChibiOS thread stuff
     233             :     static bool is_dshot_protocol(const enum output_mode mode);
     234             : 
     235             :     static bool is_led_protocol(const enum output_mode mode) {
     236             :       switch (mode) {
     237             :       case MODE_NEOPIXEL:
     238             :       case MODE_NEOPIXELRGB:
     239             :       case MODE_PROFILED:
     240             :         return true;
     241             :       default:
     242             :         return false;
     243             :       }
     244             :     }
     245             : 
     246             :     // BLHeli32: https://github.com/bitdump/BLHeli/blob/master/BLHeli_32%20ARM/BLHeli_32%20Firmware%20specs/Digital_Cmd_Spec.txt
     247             :     // BLHeli_S: https://github.com/bitdump/BLHeli/blob/master/BLHeli_S%20SiLabs/Dshotprog%20spec%20BLHeli_S.txt
     248             :     enum BLHeliDshotCommand : uint8_t {
     249             :       DSHOT_RESET = 0,
     250             :       DSHOT_BEEP1 = 1,
     251             :       DSHOT_BEEP2 = 2,
     252             :       DSHOT_BEEP3 = 3,
     253             :       DSHOT_BEEP4 = 4,
     254             :       DSHOT_BEEP5 = 5,
     255             :       DSHOT_ESC_INFO = 6,
     256             :       DSHOT_ROTATE = 7,
     257             :       DSHOT_ROTATE_ALTERNATE = 8,
     258             :       DSHOT_3D_OFF = 9,
     259             :       DSHOT_3D_ON = 10,
     260             :       DSHOT_SAVE = 12,
     261             :       DSHOT_EXTENDED_TELEMETRY_ENABLE = 13,
     262             :       DSHOT_EXTENDED_TELEMETRY_DISABLE = 14,
     263             :       DSHOT_NORMAL = 20,
     264             :       DSHOT_REVERSE = 21,
     265             :       // The following options are only available on BLHeli32
     266             :       DSHOT_LED0_ON = 22,
     267             :       DSHOT_LED1_ON = 23,
     268             :       DSHOT_LED2_ON = 24,
     269             :       DSHOT_LED3_ON = 25,
     270             :       DSHOT_LED0_OFF = 26,
     271             :       DSHOT_LED1_OFF = 27,
     272             :       DSHOT_LED2_OFF = 28,
     273             :       DSHOT_LED3_OFF = 29,
     274             :     };
     275             : 
     276             :     static constexpr uint8_t DSHOT_ZERO_THROTTLE = 48;
     277             : 
     278             :     enum DshotEscType {
     279             :       DSHOT_ESC_NONE = 0,
     280             :       DSHOT_ESC_BLHELI = 1,
     281             :       DSHOT_ESC_BLHELI_S = 2,
     282             :       DSHOT_ESC_BLHELI_EDT = 3,
     283             :       DSHOT_ESC_BLHELI_EDT_S = 4
     284             :     };
     285             : 
     286        2565 :     virtual void    set_output_mode(uint32_t mask, enum output_mode mode) {}
     287             : 
     288           0 :     virtual enum output_mode get_output_mode(uint32_t& mask) {
     289           0 :       mask = 0;
     290           0 :       return MODE_PWM_NORMAL;
     291             :     }
     292             : 
     293             : 
     294             :     /*
     295             :      * get output mode banner to inform user of how outputs are configured
     296             :      */
     297        5104 :     virtual bool get_output_mode_banner(char banner_msg[], uint8_t banner_msg_len) const { return false; }
     298             : 
     299             :     /*
     300             :      * return mask of channels that must be disabled because they share a group with a digital channel
     301             :      */
     302        2103 :     virtual uint32_t get_disabled_channels(uint32_t digital_mask) { return 0; }
     303             : 
     304             :     /*
     305             :       set default update rate
     306             :      */
     307      151226 :     virtual void    set_default_rate(uint16_t rate_hz) {}
     308             : 
     309             :     /*
     310             :       enable telemetry request for a mask of channels. This is used
     311             :       with DShot to get telemetry feedback
     312             :      */
     313           0 :     virtual void set_telem_request_mask(uint32_t mask) {}
     314             : 
     315             :     /*
     316             :       enable bi-directional telemetry request for a mask of channels. This is used
     317             :       with DShot to get telemetry feedback
     318             :      */
     319           0 :     virtual void set_bidir_dshot_mask(uint32_t mask) {}
     320             : 
     321             :     /*
     322             :       mark escs as active for the purpose of sending dshot commands
     323             :      */
     324           0 :     virtual void set_active_escs_mask(uint32_t mask) {}
     325             : 
     326             :     /*
     327             :       Set the dshot rate as a multiple of the loop rate
     328             :      */
     329        2093 :     virtual void set_dshot_rate(uint8_t dshot_rate, uint16_t loop_rate_hz) {}
     330             : 
     331             :     /*
     332             :       Set the dshot period in us, only for use by the IOMCU
     333             :      */
     334           0 :     virtual void set_dshot_period(uint32_t period_us, uint8_t dshot_rate) {}
     335           0 :     virtual uint32_t get_dshot_period_us() const { return 0; }
     336             : 
     337             :     /*
     338             :       Set the dshot ESC type
     339             :      */
     340        5625 :     virtual void set_dshot_esc_type(DshotEscType esc_type) {}
     341             : 
     342           0 :     virtual DshotEscType get_dshot_esc_type() const { return DSHOT_ESC_NONE; }
     343             : 
     344             :     const static uint32_t ALL_CHANNELS = 255;
     345             :     /*
     346             :       Send a dshot command, if command timeout is 0 then 10 commands are sent
     347             :       chan is the servo channel to send the command to
     348             :      */
     349           0 :     virtual void send_dshot_command(uint8_t command, uint8_t chan = ALL_CHANNELS, uint32_t command_timeout_ms = 0, uint16_t repeat_count = 10, bool priority = false) {}
     350             : 
     351             :     /*
     352             :       set the number of motor poles to be used in rpm calculations
     353             :      */
     354           0 :     virtual void set_motor_poles(uint8_t poles) {}
     355             : 
     356             :     /*
     357             :       setup serial led output for a given channel number, with
     358             :       the given max number of LEDs in the chain.
     359             :      */
     360           0 :     virtual bool set_serial_led_num_LEDs(const uint16_t chan, uint8_t num_leds, output_mode mode = MODE_PWM_NONE, uint32_t clock_mask = 0) { return false; }
     361             : 
     362             :     /*
     363             :       setup serial led output data for a given output channel
     364             :       and led number. A led number of -1 means all LEDs. LED 0 is the first LED
     365             :      */
     366           0 :     virtual bool set_serial_led_rgb_data(const uint16_t chan, int8_t led, uint8_t red, uint8_t green, uint8_t blue) { return false; }
     367             : 
     368             :     /*
     369             :       trigger send of serial led
     370             :      */
     371           0 :     virtual bool serial_led_send(const uint16_t chan) { return false; }
     372             : 
     373           0 :     virtual void timer_info(ExpandingString &str) {}
     374             : 
     375             :     /*
     376             :       Can this driver handle gpio as well as RC
     377             :     */
     378           0 :     virtual bool supports_gpio() { return false; };
     379             : 
     380             :     /*
     381             :       Writes gpio state to a channel
     382             :     */
     383           0 :     virtual void write_gpio(uint8_t chan, bool active) {};
     384             : 
     385             :     /*
     386             :       Force group trigger from all callers rather than just from the main thread
     387             :     */
     388           4 :     virtual void force_trigger_groups(bool onoff) {};
     389             : 
     390             :     /*
     391             :      * calculate the prescaler required to achieve the desire bitrate
     392             :      */
     393             :     static uint32_t calculate_bitrate_prescaler(uint32_t timer_clock, uint32_t target_frequency, bool at_least_freq = false);
     394             : 
     395             :     /*
     396             :      * bit width values for different protocols
     397             :      */
     398             :     /*
     399             :      * It seems ESCs are quite sensitive to the DSHOT duty cycle.
     400             :      * Options are (ticks, percentage):
     401             :      * 20/7/14, 35/70
     402             :      * 11/4/8, 36/72
     403             :      * 8/3/6, 37/75 <-- this is the preferred duty cycle and has some support on the interwebs
     404             :      */
     405             :     // bitwidths: 8/3/6 == 37%/75%
     406             :     static constexpr uint32_t DSHOT_BIT_WIDTH_TICKS_DEFAULT = 8;
     407             :     static constexpr uint32_t DSHOT_BIT_0_TICKS_DEFAULT = 3;
     408             :     static constexpr uint32_t DSHOT_BIT_1_TICKS_DEFAULT = 6;
     409             :     // bitwidths: 11/4/8 == 36%/72%
     410             :     static constexpr uint32_t DSHOT_BIT_WIDTH_TICKS_S = 11;
     411             :     static constexpr uint32_t DSHOT_BIT_0_TICKS_S = 4;
     412             :     static constexpr uint32_t DSHOT_BIT_1_TICKS_S = 8;
     413             : 
     414             :     static uint32_t DSHOT_BIT_WIDTH_TICKS;
     415             :     static uint32_t DSHOT_BIT_0_TICKS;
     416             :     static uint32_t DSHOT_BIT_1_TICKS;
     417             : 
     418             :     // See WS2812B spec for expected pulse widths
     419             :     static constexpr uint32_t NEOP_BIT_WIDTH_TICKS = 8;
     420             :     static constexpr uint32_t NEOP_BIT_0_TICKS = 2;
     421             :     static constexpr uint32_t NEOP_BIT_1_TICKS = 6;
     422             :     // neopixel does not use pulse widths at all
     423             :     static constexpr uint32_t PROFI_BIT_0_TICKS = 7;
     424             :     static constexpr uint32_t PROFI_BIT_1_TICKS = 14;
     425             :     static constexpr uint32_t PROFI_BIT_WIDTH_TICKS = 20;
     426             : 
     427             :     // suitably long LED output period to support high LED counts
     428             :     static constexpr uint32_t LED_OUTPUT_PERIOD_US = 10000;
     429             : 
     430             : protected:
     431             : 
     432             :     // helper functions for implementation of get_output_mode_banner
     433             :     void append_to_banner(char banner_msg[], uint8_t banner_msg_len, output_mode out_mode, uint8_t low_ch, uint8_t high_ch) const;
     434             :     const char* get_output_mode_string(enum output_mode out_mode) const;
     435             : 
     436             :     uint16_t _esc_pwm_min = 1000;
     437             :     uint16_t _esc_pwm_max = 2000;
     438             : };

Generated by: LCOV version 1.14