/* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #ifdef HAL_PERIPH_ENABLE_RC_OUT #include "AP_Periph.h" #if AP_SIM_ENABLED #include #endif // magic value from UAVCAN driver packet // dsdl/uavcan/equipment/esc/1030.RawCommand.uavcan // Raw ESC command normalized into [-8192, 8191] #define UAVCAN_ESC_MAX_VALUE 8191 #define SERVO_OUT_RCIN_MAX 32 // note that we allow for more than is in the enum #define SERVO_OUT_MOTOR_MAX 12 // SRV_Channel::k_motor1 ... SRV_Channel::k_motor8, SRV_Channel::k_motor9 ... SRV_Channel::k_motor12 extern const AP_HAL::HAL &hal; void AP_Periph_FW::rcout_init() { #if AP_PERIPH_SAFETY_SWITCH_ENABLED // start up with safety enabled. This disables the pwm output until we receive an packet from the rempte system hal.rcout->force_safety_on(); #else hal.rcout->force_safety_off(); #endif #if HAL_WITH_ESC_TELEM && !HAL_GCS_ENABLED if (g.esc_telem_port >= 0) { serial_manager.set_protocol_and_baud(g.esc_telem_port, AP_SerialManager::SerialProtocol_ESCTelemetry, 115200); } #endif #if HAL_PWM_COUNT > 0 for (uint8_t i=0; iset_dshot_esc_type(SRV_Channels::get_dshot_esc_type()); #endif // run PWM ESCs at configured rate hal.rcout->set_freq(esc_mask, g.esc_rate.get()); // setup ESCs with the desired PWM type, allowing for DShot AP::srv().init(esc_mask, (AP_HAL::RCOutput::output_mode)g.esc_pwm_type.get()); // run DShot at 1kHz hal.rcout->set_dshot_rate(SRV_Channels::get_dshot_rate(), 400); #if HAL_WITH_ESC_TELEM esc_telem_update_period_ms = 1000 / constrain_int32(g.esc_telem_rate.get(), 1, 1000); #endif } void AP_Periph_FW::rcout_init_1Hz() { // this runs at 1Hz to allow for run-time param changes AP::srv().enable_aux_servos(); for (uint8_t i=0; i 0 const SRV_Channel::Aux_servo_function_t function = SRV_Channel::Aux_servo_function_t(SRV_Channel::k_rcin1 + actuator_id - 1); SRV_Channels::set_output_norm(function, command_value); rcout_has_new_data_to_update = true; #if AP_SIM_ENABLED sim_update_actuator(actuator_id); #endif #endif } void AP_Periph_FW::rcout_srv_PWM(uint8_t actuator_id, const float command_value) { #if HAL_PWM_COUNT > 0 const SRV_Channel::Aux_servo_function_t function = SRV_Channel::Aux_servo_function_t(SRV_Channel::k_rcin1 + actuator_id - 1); SRV_Channels::set_output_pwm(function, uint16_t(command_value+0.5)); rcout_has_new_data_to_update = true; #if AP_SIM_ENABLED sim_update_actuator(actuator_id); #endif #endif } void AP_Periph_FW::rcout_handle_safety_state(uint8_t safety_state) { if (safety_state == 255) { hal.rcout->force_safety_off(); } else { hal.rcout->force_safety_on(); } rcout_has_new_data_to_update = true; } void AP_Periph_FW::rcout_update() { uint32_t now_ms = AP_HAL::millis(); const uint16_t esc_timeout_ms = g.esc_command_timeout_ms >= 0 ? g.esc_command_timeout_ms : 0; // Don't allow negative timeouts! const bool has_esc_rawcommand_timed_out = esc_timeout_ms != 0 && ((now_ms - last_esc_raw_command_ms) >= esc_timeout_ms); if (last_esc_num_channels > 0 && has_esc_rawcommand_timed_out) { // If we've seen ESCs previously, and a timeout has occurred, then zero the outputs int16_t esc_output[last_esc_num_channels]; memset(esc_output, 0, sizeof(esc_output)); rcout_esc(esc_output, last_esc_num_channels); // register that the output has been changed rcout_has_new_data_to_update = true; } if (!rcout_has_new_data_to_update) { return; } rcout_has_new_data_to_update = false; auto &srv = AP::srv(); SRV_Channels::calc_pwm(); srv.cork(); SRV_Channels::output_ch_all(); srv.push(); #if HAL_WITH_ESC_TELEM if (now_ms - last_esc_telem_update_ms >= esc_telem_update_period_ms) { last_esc_telem_update_ms = now_ms; esc_telem_update(); } #if AP_EXTENDED_ESC_TELEM_ENABLED esc_telem_extended_update(now_ms); #endif #endif } #if AP_SIM_ENABLED /* update simulation of servos, sending actuator status */ void AP_Periph_FW::sim_update_actuator(uint8_t actuator_id) { sim_actuator.mask |= 1U << actuator_id; // send status at 10Hz const uint32_t period_ms = 100; const uint32_t now_ms = AP_HAL::millis(); if (now_ms - sim_actuator.last_send_ms < period_ms) { return; } sim_actuator.last_send_ms = now_ms; for (uint8_t i=0; i