LCOV - code coverage report
Current view: top level - AP_Motors - AP_Motors_Class.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 129 182 70.9 %
Date: 2026-09-09 11:02:08 Functions: 20 21 95.2 %

          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             : #include "AP_Motors_Class.h"
      17             : #include <AP_HAL/AP_HAL.h>
      18             : #include <SRV_Channel/SRV_Channel.h>
      19             : #include <GCS_MAVLink/GCS.h>
      20             : #include <AP_Notify/AP_Notify.h>
      21             : #include <AP_Logger/AP_Logger.h>
      22             : #include <cstdint>
      23             : 
      24             : #define AP_MOTORS_SLEW_FILTER_CUTOFF 50.0f
      25             : 
      26             : extern const AP_HAL::HAL& hal;
      27             : 
      28             : // singleton instance
      29             : AP_Motors *AP_Motors::_singleton;
      30             : 
      31             : // Constructor
      32        1285 : AP_Motors::AP_Motors(uint16_t speed_hz) :
      33        1285 :     _speed_hz(speed_hz),
      34        1285 :     _throttle_filter(),
      35        1285 :     _throttle_slew(),
      36        1285 :     _throttle_slew_filter(),
      37        1285 :     _spool_desired(DesiredSpoolState::SHUT_DOWN),
      38        2570 :     _spool_state(SpoolState::SHUT_DOWN)
      39             : {
      40        1285 :     _singleton = this;
      41             : 
      42             :     // setup throttle filtering
      43        1285 :     _throttle_filter.set_cutoff_frequency(0.0f);
      44        1285 :     _throttle_filter.reset(0.0f);
      45             : 
      46        1285 :     _throttle_slew_filter.set_cutoff_frequency(AP_MOTORS_SLEW_FILTER_CUTOFF);
      47        1285 :     _throttle_slew_filter.reset(0.0f);
      48             : 
      49             :     // setup throttle slew detector
      50        1285 :     _throttle_slew.reset();
      51             : 
      52             :     // init limit flags
      53        1285 :     limit.set_all(true);
      54        1285 :     _thrust_boost = false;
      55        1285 :     _thrust_balanced = true;
      56        1285 : };
      57             : 
      58        3477 : void AP_Motors::get_frame_and_type_string(char *buffer, uint8_t buflen) const
      59             : {
      60        3477 :     const char *frame_str = get_frame_string();
      61        3477 :     const char *type_str = get_type_string();
      62        3477 :     if (type_str != nullptr && strlen(type_str)
      63             : #if AP_SCRIPTING_ENABLED
      64        3201 :         && custom_frame_string == nullptr
      65             : #endif
      66             :     ) {
      67        3201 :         hal.util->snprintf(buffer, buflen, "Frame: %s/%s", frame_str, type_str);
      68             :     } else {
      69         276 :         hal.util->snprintf(buffer, buflen, "Frame: %s", frame_str);
      70             :     }
      71        3477 : }
      72             : 
      73        1668 : void AP_Motors::armed(bool arm)
      74             : {
      75        1668 :     if (_armed != arm) {
      76        1668 :         _armed = arm;
      77        1668 :         AP_Notify::flags.armed = arm;
      78        1668 :         if (!arm) {
      79         828 :             save_params_on_disarm();
      80             :         }
      81             :     }
      82        1668 : };
      83             : 
      84             : // pilot input in the -1 ~ +1 range for roll, pitch and yaw. 0~1 range for throttle
      85     3748409 : void AP_Motors::set_radio_passthrough(float roll_input, float pitch_input, float throttle_input, float yaw_input)
      86             : {
      87     3748409 :     _roll_radio_passthrough = roll_input;
      88     3748409 :     _pitch_radio_passthrough = pitch_input;
      89     3748409 :     _throttle_radio_passthrough = throttle_input;
      90     3748409 :     _yaw_radio_passthrough = yaw_input;
      91     3748409 : }
      92             : 
      93             : /*
      94             :   write to an output channel
      95             :  */
      96   155585226 : void AP_Motors::rc_write(uint8_t chan, uint16_t pwm)
      97             : {
      98   155585226 :     SRV_Channel::Function function = SRV_Channels::get_motor_function(chan);
      99   155585226 :     if ((1U<<chan) & _motor_pwm_scaled.mask) {
     100             :         // note that PWM_MIN/MAX has been forced to 1000/2000
     101           0 :         SRV_Channels::set_output_scaled(function, float(pwm) - _motor_pwm_scaled.offset);
     102             :     } else {
     103   155585226 :         SRV_Channels::set_output_pwm(function, pwm);
     104             :     }
     105   155585226 : }
     106             : 
     107             : /*
     108             :   write to an output channel for an angle actuator
     109             :  */
     110     4559042 : void AP_Motors::rc_write_angle(uint8_t chan, int16_t angle_cd)
     111             : {
     112     4559042 :     SRV_Channel::Function function = SRV_Channels::get_motor_function(chan);
     113     4559042 :     SRV_Channels::set_output_scaled(function, angle_cd);
     114     4559042 : }
     115             : 
     116             : /*
     117             :   set frequency of a set of channels
     118             :  */
     119        2565 : void AP_Motors::rc_set_freq(uint32_t motor_mask, uint16_t freq_hz)
     120             : {
     121        2565 :     if (freq_hz > 50) {
     122        2565 :         _motor_fast_mask |= motor_mask;
     123             :     }
     124             : 
     125        2565 :     const uint32_t mask = motor_mask_to_srv_channel_mask(motor_mask);
     126        2565 :     hal.rcout->set_freq(mask, freq_hz);
     127        2565 :     hal.rcout->set_dshot_esc_type(SRV_Channels::get_dshot_esc_type());
     128             : 
     129        2565 :     const PWMType type = _pwm_type;
     130        2565 :     switch (type) {
     131           0 :     case PWMType::ONESHOT:
     132           0 :         if (freq_hz > 50 && mask != 0) {
     133           0 :             hal.rcout->set_output_mode(mask, AP_HAL::RCOutput::MODE_PWM_ONESHOT);
     134             :         }
     135           0 :         break;
     136           0 :     case PWMType::ONESHOT125:
     137           0 :         if (freq_hz > 50 && mask != 0) {
     138           0 :             hal.rcout->set_output_mode(mask, AP_HAL::RCOutput::MODE_PWM_ONESHOT125);
     139             :         }
     140           0 :         break;
     141           0 :     case PWMType::BRUSHED:
     142           0 :         hal.rcout->set_output_mode(mask, AP_HAL::RCOutput::MODE_PWM_BRUSHED);
     143           0 :         break;
     144           0 :     case PWMType::DSHOT150:
     145           0 :         hal.rcout->set_output_mode(mask, AP_HAL::RCOutput::MODE_PWM_DSHOT150);
     146           0 :         break;
     147           0 :     case PWMType::DSHOT300:
     148           0 :         hal.rcout->set_output_mode(mask, AP_HAL::RCOutput::MODE_PWM_DSHOT300);
     149           0 :         break;
     150           0 :     case PWMType::DSHOT600:
     151           0 :         hal.rcout->set_output_mode(mask, AP_HAL::RCOutput::MODE_PWM_DSHOT600);
     152           0 :         break;
     153           0 :     case PWMType::DSHOT1200:
     154           0 :         hal.rcout->set_output_mode(mask, AP_HAL::RCOutput::MODE_PWM_DSHOT1200);
     155           0 :         break;
     156           0 :     case PWMType::PWM_RANGE:
     157             :     case PWMType::PWM_ANGLE:
     158             :         /*
     159             :           this is a motor output type for multirotors which honours
     160             :           the SERVOn_MIN/MAX (and TRIM for angle) values per channel
     161             : 
     162             :           Motor PWM min and max are hard coded to 1000 to 2000.
     163             :           Range type offsets by 1000 to get 0 to 1000.
     164             :           Angle type offsets by 1500 to get -500 to 500.
     165             :          */
     166             : 
     167           0 :         if (type == PWMType::PWM_RANGE) {
     168           0 :             _motor_pwm_scaled.offset = 1000.0;
     169             :         } else {
     170             :             // PWMType::PWM_ANGLE
     171           0 :             _motor_pwm_scaled.offset = 1500.0;
     172             :         }
     173             : 
     174           0 :         _motor_pwm_scaled.mask |= motor_mask;
     175           0 :         for (uint8_t i=0; i<16; i++) {
     176           0 :             if ((1U<<i) & motor_mask) {
     177           0 :                 if (type == PWMType::PWM_RANGE) {
     178           0 :                     SRV_Channels::set_range(SRV_Channels::get_motor_function(i), 1000);
     179             :                 } else {
     180             :                     // PWMType::PWM_ANGLE
     181           0 :                     SRV_Channels::set_angle(SRV_Channels::get_motor_function(i), 500);
     182             :                 }
     183             :             }
     184             :         }
     185           0 :         hal.rcout->set_output_mode(mask, AP_HAL::RCOutput::MODE_PWM_NORMAL);
     186           0 :         break;
     187        2565 :     default:
     188        2565 :         hal.rcout->set_output_mode(mask, AP_HAL::RCOutput::MODE_PWM_NORMAL);
     189        2565 :         break;
     190             :     }
     191        2565 : }
     192             : 
     193             : /*
     194             :   map an internal motor mask to real motor mask, accounting for
     195             :   SERVOn_FUNCTION mappings, and allowing for multiple outputs per
     196             :   motor number
     197             :  */
     198       35513 : uint32_t AP_Motors::motor_mask_to_srv_channel_mask(uint32_t mask) const
     199             : {
     200       35513 :     uint32_t mask2 = 0;
     201     1171929 :     for (uint8_t i = 0; i < 32; i++) {
     202     1136416 :         uint32_t bit = 1UL << i;
     203     1136416 :         if (mask & bit) {
     204      152680 :             SRV_Channel::Function function = SRV_Channels::get_motor_function(i);
     205      152680 :             mask2 |= SRV_Channels::get_output_channel_mask(function);
     206             :         }
     207             :     }
     208       35513 :     return mask2;
     209             : }
     210             : 
     211             : /*
     212             :   add a motor, setting up default output function as needed
     213             :  */
     214        5270 : void AP_Motors::add_motor_num(int8_t motor_num)
     215             : {
     216             :     // ensure valid motor number is provided
     217        5270 :     if (motor_num >= 0 && motor_num < AP_MOTORS_MAX_NUM_MOTORS) {
     218        5270 :         SRV_Channel::Function function = SRV_Channels::get_motor_function(motor_num);
     219        5270 :         SRV_Channels::set_aux_channel_default(function, motor_num);
     220             :     }
     221        5270 : }
     222             : 
     223             : #if AP_SCRIPTING_ENABLED
     224           0 : void AP_Motors::set_external_limits(bool roll, bool pitch, bool yaw, bool throttle_lower, bool throttle_upper)
     225             : {
     226           0 :     external_limits.roll = roll;
     227           0 :     external_limits.pitch = pitch;
     228           0 :     external_limits.yaw = yaw;
     229           0 :     external_limits.throttle_lower = throttle_lower;
     230           0 :     external_limits.throttle_upper = throttle_upper;
     231           0 : }
     232             : #endif
     233             : 
     234             : // returns true if the configured PWM type is digital and should have fixed endpoints
     235        1199 : bool AP_Motors::is_digital_pwm_type() const
     236             : {
     237        1199 :     switch ((PWMType)_pwm_type) {
     238           0 :     case PWMType::DSHOT150:
     239             :     case PWMType::DSHOT300:
     240             :     case PWMType::DSHOT600:
     241             :     case PWMType::DSHOT1200:
     242           0 :         return true;
     243        1199 :     case PWMType::NORMAL:
     244             :     case PWMType::ONESHOT:
     245             :     case PWMType::ONESHOT125:
     246             :     case PWMType::BRUSHED:
     247             :     case PWMType::PWM_RANGE:
     248             :     case PWMType::PWM_ANGLE:
     249        1199 :         break;
     250             :     }
     251        1199 :     return false;
     252             : }
     253             : 
     254             : // return string corresponding to frame_class
     255        3544 : const char* AP_Motors::get_frame_string() const
     256             : {
     257             : #if AP_SCRIPTING_ENABLED
     258        3544 :     if (custom_frame_string != nullptr) {
     259           5 :         return custom_frame_string;
     260             :     }
     261             : #endif
     262        3539 :     return _get_frame_string();
     263             : }
     264             : 
     265             : #if AP_SCRIPTING_ENABLED
     266             : // set custom frame string
     267           4 : void AP_Motors::set_frame_string(const char * str) {
     268           4 :     if (custom_frame_string != nullptr) {
     269           0 :         return;
     270             :     }
     271           4 :     const size_t len = strlen(str)+1;
     272           4 :     custom_frame_string = NEW_NOTHROW char[len];
     273           4 :     if (custom_frame_string != nullptr) {
     274           4 :         strncpy(custom_frame_string, str, len);
     275             :     }
     276             : }
     277             : #endif
     278             : 
     279             : #if HAL_LOGGING_ENABLED
     280    30412448 : void AP_Motors::Log_Write_SPOL() {
     281             : 
     282    30412448 :     const bool spool_state_changed{_spool_state != _logged_spool_state};
     283    30412448 :     const bool des_spool_state_changed{_spool_desired != _logged_spool_desired};
     284             : 
     285             :     // Log only changes.
     286    30412448 :     if (!spool_state_changed && !des_spool_state_changed) {
     287    30408808 :         return;
     288             :     }
     289             : 
     290        3640 :     _logged_spool_state = _spool_state;
     291        3640 :     _logged_spool_desired = _spool_desired;
     292             : 
     293        3640 :     const struct log_SPOL pkt {
     294             :         LOG_PACKET_HEADER_INIT(LOG_SPOL_MSG),
     295        3640 :         time_us         : AP_HAL::micros64(),
     296        3640 :         spool_state     : (uint8_t)_spool_state,
     297        3640 :         des_spool_state : (uint8_t)_spool_desired
     298        3640 :     };
     299        3640 :     AP::logger().WriteBlock(&pkt, sizeof(pkt));
     300             : 
     301             : }
     302             : #endif
     303             : 
     304             : // output_test_seq - spin a motor at the pwm value specified
     305             : //  motor_seq is the motor's sequence number from 1 to the number of motors on the frame
     306             : //  pwm value is an actual pwm value that will be output, normally in the range of 1000 ~ 2000
     307             : //  return true if output was successful, false if not possible
     308       11185 : bool AP_Motors::output_test_seq(uint8_t motor_seq, int16_t pwm)
     309             : {
     310       11185 :     if (armed() && _interlock) {
     311       11185 :         _output_test_seq(motor_seq, pwm);
     312       11185 :         return true;
     313             :     }
     314           0 :     output_min();
     315           0 :     return false;
     316             : }
     317             : 
     318      102945 : bool AP_Motors::arming_checks(size_t buflen, char *buffer) const
     319             : {
     320      102945 :     if (!initialised_ok()) {
     321          56 :         hal.util->snprintf(buffer, buflen, "Check frame class and type");
     322          56 :         return false;
     323             :     }
     324             : 
     325      102889 :     return true;
     326             : }
     327             : 
     328           8 : bool AP_Motors::motor_test_checks(size_t buflen, char *buffer) const
     329             : {
     330             :     // Must pass base class arming checks (the function above)
     331             :     // Do not run frame specific arming checks as motor test is less strict
     332             :     // For example not all the outputs have to be assigned
     333           8 :     if (!AP_Motors::arming_checks(buflen, buffer)) {
     334           0 :         return false;
     335             :     }
     336             : 
     337             :     // check if safety switch has been pushed
     338           8 :     if (hal.util->safety_switch_state() == AP_HAL::Util::SAFETY_DISARMED) {
     339           0 :         hal.util->snprintf(buffer, buflen, "Safety switch");
     340           0 :         return false;
     341             :     }
     342             : 
     343             :     // check E-Stop is not active
     344           8 :     if (SRV_Channels::get_emergency_stop()) {
     345           0 :         hal.util->snprintf(buffer, buflen, "Motor Emergency Stopped");
     346           0 :         return false;
     347             :     }
     348             : 
     349           8 :     return true;
     350             : }
     351             : 
     352             : // set all limits
     353    38284631 : void AP_Motors::AP_Motors_limit::set_all(bool flag)
     354             : {
     355    38284631 :     set_rpy(flag);
     356    38284631 :     set_throttle(flag);
     357    38284631 : }
     358             : 
     359             : // set limits for roll pitch yaw
     360    52007708 : void AP_Motors::AP_Motors_limit::set_rpy(bool flag)
     361             : {
     362    52007708 :     roll = flag;
     363    52007708 :     pitch = flag;
     364    52007708 :     yaw = flag;
     365    52007708 : }
     366             : 
     367             : // set limits for throttle upper and lower
     368    41523461 : void AP_Motors::AP_Motors_limit::set_throttle(bool flag)
     369             : {
     370    41523461 :     throttle_lower = flag;
     371    41523461 :     throttle_upper = flag;
     372    41523461 : }
     373             : 
     374             : namespace AP {
     375      436492 :     AP_Motors *motors()
     376             :     {
     377      436492 :         return AP_Motors::get_singleton();
     378             :     }
     379             : }

Generated by: LCOV version 1.14