|
|
@ -56,8 +56,10 @@ PWMOut::PWMOut(int instance, uint8_t output_base) : |
|
|
|
_cycle_perf(perf_alloc(PC_ELAPSED, MODULE_NAME": cycle")), |
|
|
|
_cycle_perf(perf_alloc(PC_ELAPSED, MODULE_NAME": cycle")), |
|
|
|
_interval_perf(perf_alloc(PC_INTERVAL, MODULE_NAME": interval")) |
|
|
|
_interval_perf(perf_alloc(PC_INTERVAL, MODULE_NAME": interval")) |
|
|
|
{ |
|
|
|
{ |
|
|
|
_mixing_output.setAllMinValues(PWM_DEFAULT_MIN); |
|
|
|
if (!_mixing_output.useDynamicMixing()) { |
|
|
|
_mixing_output.setAllMaxValues(PWM_DEFAULT_MAX); |
|
|
|
_mixing_output.setAllMinValues(PWM_DEFAULT_MIN); |
|
|
|
|
|
|
|
_mixing_output.setAllMaxValues(PWM_DEFAULT_MAX); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
PWMOut::~PWMOut() |
|
|
|
PWMOut::~PWMOut() |
|
|
@ -92,7 +94,13 @@ int PWMOut::init() |
|
|
|
|
|
|
|
|
|
|
|
_mixing_output.setDriverInstance(_class_instance); |
|
|
|
_mixing_output.setDriverInstance(_class_instance); |
|
|
|
|
|
|
|
|
|
|
|
_num_outputs = math::min(FMU_MAX_ACTUATORS - (int)_output_base, MAX_PER_INSTANCE); |
|
|
|
if (_mixing_output.useDynamicMixing()) { |
|
|
|
|
|
|
|
_num_outputs = FMU_MAX_ACTUATORS; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
_num_outputs = math::min(FMU_MAX_ACTUATORS - (int)_output_base, MAX_PER_INSTANCE); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
_pwm_mask = ((1u << _num_outputs) - 1) << _output_base; |
|
|
|
_pwm_mask = ((1u << _num_outputs) - 1) << _output_base; |
|
|
|
_mixing_output.setMaxNumOutputs(_num_outputs); |
|
|
|
_mixing_output.setMaxNumOutputs(_num_outputs); |
|
|
|
|
|
|
|
|
|
|
@ -132,6 +140,10 @@ int PWMOut::init() |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
int PWMOut::set_pwm_rate(unsigned rate_map, unsigned default_rate, unsigned alt_rate) |
|
|
|
int PWMOut::set_pwm_rate(unsigned rate_map, unsigned default_rate, unsigned alt_rate) |
|
|
|
{ |
|
|
|
{ |
|
|
|
|
|
|
|
if (_mixing_output.useDynamicMixing()) { |
|
|
|
|
|
|
|
return -EINVAL; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
PX4_DEBUG("pwm_out%u set_pwm_rate %x %u %u", _instance, rate_map, default_rate, alt_rate); |
|
|
|
PX4_DEBUG("pwm_out%u set_pwm_rate %x %u %u", _instance, rate_map, default_rate, alt_rate); |
|
|
|
|
|
|
|
|
|
|
|
for (unsigned pass = 0; pass < 2; pass++) { |
|
|
|
for (unsigned pass = 0; pass < 2; pass++) { |
|
|
@ -264,6 +276,11 @@ int PWMOut::task_spawn(int argc, char *argv[]) |
|
|
|
return PX4_ERROR; |
|
|
|
return PX4_ERROR; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// only start one instance with dynamic mixing
|
|
|
|
|
|
|
|
if (dev->_mixing_output.useDynamicMixing()) { |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} else { |
|
|
|
} else { |
|
|
|
PX4_ERR("alloc failed"); |
|
|
|
PX4_ERR("alloc failed"); |
|
|
|
} |
|
|
|
} |
|
|
@ -283,44 +300,110 @@ bool PWMOut::update_pwm_out_state(bool on) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (on && !_pwm_initialized && _pwm_mask != 0) { |
|
|
|
if (on && !_pwm_initialized && _pwm_mask != 0) { |
|
|
|
|
|
|
|
|
|
|
|
// Collect all PWM masks from all instances
|
|
|
|
if (_mixing_output.useDynamicMixing()) { |
|
|
|
uint32_t pwm_mask_new = 0; |
|
|
|
|
|
|
|
// Collect the PWM alt rate channels across all instances
|
|
|
|
for (int timer = 0; timer < MAX_IO_TIMERS; ++timer) { |
|
|
|
uint32_t pwm_alt_rate_channels_new = 0; |
|
|
|
_timer_rates[timer] = -1; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint32_t channels = io_timer_get_group(timer); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (channels == 0) { |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
char param_name[17]; |
|
|
|
|
|
|
|
snprintf(param_name, sizeof(param_name), "%s_TIM%u", _mixing_output.paramPrefix(), timer); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int32_t tim_config = 0; |
|
|
|
|
|
|
|
param_t handle = param_find(param_name); |
|
|
|
|
|
|
|
param_get(handle, &tim_config); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (tim_config > 0) { |
|
|
|
|
|
|
|
_timer_rates[timer] = tim_config; |
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < PWM_OUT_MAX_INSTANCES; i++) { |
|
|
|
} else if (tim_config == -1) { // OneShot
|
|
|
|
if (_objects[i].load()) { |
|
|
|
_timer_rates[timer] = 0; |
|
|
|
|
|
|
|
|
|
|
|
pwm_mask_new |= _objects[i].load()->get_pwm_mask(); |
|
|
|
} else { |
|
|
|
pwm_alt_rate_channels_new |= _objects[i].load()->get_alt_rate_channels(); |
|
|
|
_pwm_mask &= ~channels; // don't use for pwm
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int ret = up_pwm_servo_init(_pwm_mask); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (ret < 0) { |
|
|
|
|
|
|
|
PX4_ERR("up_pwm_servo_init failed (%i)", ret); |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_pwm_mask = ret; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// set the timer rates
|
|
|
|
|
|
|
|
for (int timer = 0; timer < MAX_IO_TIMERS; ++timer) { |
|
|
|
|
|
|
|
uint32_t channels = _pwm_mask & up_pwm_servo_get_rate_group(timer); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (channels == 0) { |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ret = up_pwm_servo_set_rate_group_update(timer, _timer_rates[timer]); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (ret != 0) { |
|
|
|
|
|
|
|
PX4_ERR("up_pwm_servo_set_rate_group_update failed for timer %i, rate %i (%i)", timer, _timer_rates[timer], ret); |
|
|
|
|
|
|
|
_timer_rates[timer] = -1; |
|
|
|
|
|
|
|
_pwm_mask &= ~channels; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_pwm_initialized = true; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// disable unused functions
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < _num_outputs; ++i) { |
|
|
|
|
|
|
|
if (((1 << i) & _pwm_mask) == 0) { |
|
|
|
|
|
|
|
_mixing_output.disableFunction(i); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Initialize the PWM output state for all instances
|
|
|
|
} else { |
|
|
|
// this is re-done once per instance, but harmless
|
|
|
|
// Collect all PWM masks from all instances
|
|
|
|
int ret = up_pwm_servo_init(pwm_mask_new); |
|
|
|
uint32_t pwm_mask_new = 0; |
|
|
|
|
|
|
|
// Collect the PWM alt rate channels across all instances
|
|
|
|
|
|
|
|
uint32_t pwm_alt_rate_channels_new = 0; |
|
|
|
|
|
|
|
|
|
|
|
if (ret >= 0) { |
|
|
|
|
|
|
|
for (int i = 0; i < PWM_OUT_MAX_INSTANCES; i++) { |
|
|
|
for (int i = 0; i < PWM_OUT_MAX_INSTANCES; i++) { |
|
|
|
if (_objects[i].load()) { |
|
|
|
if (_objects[i].load()) { |
|
|
|
_objects[i].load()->set_pwm_mask(_objects[i].load()->get_pwm_mask() & ret); |
|
|
|
|
|
|
|
|
|
|
|
pwm_mask_new |= _objects[i].load()->get_pwm_mask(); |
|
|
|
|
|
|
|
pwm_alt_rate_channels_new |= _objects[i].load()->get_alt_rate_channels(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Set rate is not affecting non-masked channels, so can be called
|
|
|
|
// Initialize the PWM output state for all instances
|
|
|
|
// individually
|
|
|
|
// this is re-done once per instance, but harmless
|
|
|
|
set_pwm_rate(get_alt_rate_channels(), get_default_rate(), get_alt_rate()); |
|
|
|
int ret = up_pwm_servo_init(pwm_mask_new); |
|
|
|
|
|
|
|
|
|
|
|
_pwm_initialized = true; |
|
|
|
if (ret >= 0) { |
|
|
|
_all_instances_ready.fetch_add(1); |
|
|
|
for (int i = 0; i < PWM_OUT_MAX_INSTANCES; i++) { |
|
|
|
|
|
|
|
if (_objects[i].load()) { |
|
|
|
|
|
|
|
_objects[i].load()->set_pwm_mask(_objects[i].load()->get_pwm_mask() & ret); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} else { |
|
|
|
// Set rate is not affecting non-masked channels, so can be called
|
|
|
|
PX4_ERR("up_pwm_servo_init failed (%i)", ret); |
|
|
|
// individually
|
|
|
|
|
|
|
|
set_pwm_rate(get_alt_rate_channels(), get_default_rate(), get_alt_rate()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_pwm_initialized = true; |
|
|
|
|
|
|
|
_all_instances_ready.fetch_add(1); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
PX4_ERR("up_pwm_servo_init failed (%i)", ret); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
up_pwm_servo_arm(on, _pwm_mask); |
|
|
|
up_pwm_servo_arm(on, _pwm_mask); |
|
|
|
return _all_instances_ready.load() == PWM_OUT_MAX_INSTANCES; |
|
|
|
return _all_instances_ready.load() == PWM_OUT_MAX_INSTANCES || _mixing_output.useDynamicMixing(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
bool PWMOut::updateOutputs(bool stop_motors, uint16_t outputs[MAX_ACTUATORS], |
|
|
|
bool PWMOut::updateOutputs(bool stop_motors, uint16_t outputs[MAX_ACTUATORS], |
|
|
@ -332,7 +415,12 @@ bool PWMOut::updateOutputs(bool stop_motors, uint16_t outputs[MAX_ACTUATORS], |
|
|
|
|
|
|
|
|
|
|
|
/* output to the servos */ |
|
|
|
/* output to the servos */ |
|
|
|
if (_pwm_initialized) { |
|
|
|
if (_pwm_initialized) { |
|
|
|
for (size_t i = 0; i < math::min(_num_outputs, num_outputs); i++) { |
|
|
|
for (size_t i = 0; i < num_outputs; i++) { |
|
|
|
|
|
|
|
if (!_mixing_output.isFunctionSet(i)) { |
|
|
|
|
|
|
|
// do not run any signal on disabled channels
|
|
|
|
|
|
|
|
outputs[i] = 0; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (_pwm_mask & (1 << (i + _output_base))) { |
|
|
|
if (_pwm_mask & (1 << (i + _output_base))) { |
|
|
|
up_pwm_servo_set(_output_base + i, outputs[i]); |
|
|
|
up_pwm_servo_set(_output_base + i, outputs[i]); |
|
|
|
} |
|
|
|
} |
|
|
@ -362,13 +450,16 @@ void PWMOut::Run() |
|
|
|
perf_begin(_cycle_perf); |
|
|
|
perf_begin(_cycle_perf); |
|
|
|
perf_count(_interval_perf); |
|
|
|
perf_count(_interval_perf); |
|
|
|
|
|
|
|
|
|
|
|
// push backup schedule
|
|
|
|
if (!_mixing_output.useDynamicMixing()) { |
|
|
|
ScheduleDelayed(_backup_schedule_interval_us); |
|
|
|
// push backup schedule
|
|
|
|
|
|
|
|
ScheduleDelayed(_backup_schedule_interval_us); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
_mixing_output.update(); |
|
|
|
_mixing_output.update(); |
|
|
|
|
|
|
|
|
|
|
|
/* update PWM status if armed or if disarmed PWM values are set */ |
|
|
|
/* update PWM status if armed or if disarmed PWM values are set */ |
|
|
|
bool pwm_on = _mixing_output.armed().armed || (_num_disarmed_set > 0) || _mixing_output.armed().in_esc_calibration_mode; |
|
|
|
bool pwm_on = _mixing_output.armed().armed || (_num_disarmed_set > 0) || _mixing_output.useDynamicMixing() |
|
|
|
|
|
|
|
|| _mixing_output.armed().in_esc_calibration_mode; |
|
|
|
|
|
|
|
|
|
|
|
if (_pwm_on != pwm_on) { |
|
|
|
if (_pwm_on != pwm_on) { |
|
|
|
|
|
|
|
|
|
|
@ -384,10 +475,12 @@ void PWMOut::Run() |
|
|
|
_parameter_update_sub.copy(&pupdate); |
|
|
|
_parameter_update_sub.copy(&pupdate); |
|
|
|
|
|
|
|
|
|
|
|
// update parameters from storage
|
|
|
|
// update parameters from storage
|
|
|
|
// update_params(); // do not update PWM params for now (was interfering with VTOL PWM settings)
|
|
|
|
if (_mixing_output.useDynamicMixing()) { // do not update PWM params for now (was interfering with VTOL PWM settings)
|
|
|
|
|
|
|
|
update_params(); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (_pwm_initialized && _current_update_rate == 0) { |
|
|
|
if (_pwm_initialized && _current_update_rate == 0 && !_mixing_output.useDynamicMixing()) { |
|
|
|
update_current_rate(); |
|
|
|
update_current_rate(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -401,6 +494,10 @@ void PWMOut::update_params() |
|
|
|
{ |
|
|
|
{ |
|
|
|
updateParams(); |
|
|
|
updateParams(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (_mixing_output.useDynamicMixing()) { |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
int32_t pwm_min_default = PWM_DEFAULT_MIN; |
|
|
|
int32_t pwm_min_default = PWM_DEFAULT_MIN; |
|
|
|
int32_t pwm_max_default = PWM_DEFAULT_MAX; |
|
|
|
int32_t pwm_max_default = PWM_DEFAULT_MAX; |
|
|
|
int32_t pwm_disarmed_default = 0; |
|
|
|
int32_t pwm_disarmed_default = 0; |
|
|
@ -636,7 +733,7 @@ int PWMOut::pwm_ioctl(file *filp, int cmd, unsigned long arg) |
|
|
|
struct pwm_output_values *pwm = (struct pwm_output_values *)arg; |
|
|
|
struct pwm_output_values *pwm = (struct pwm_output_values *)arg; |
|
|
|
|
|
|
|
|
|
|
|
/* discard if too many values are sent */ |
|
|
|
/* discard if too many values are sent */ |
|
|
|
if (pwm->channel_count > FMU_MAX_ACTUATORS) { |
|
|
|
if (pwm->channel_count > FMU_MAX_ACTUATORS || _mixing_output.useDynamicMixing()) { |
|
|
|
ret = -EINVAL; |
|
|
|
ret = -EINVAL; |
|
|
|
break; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
@ -681,7 +778,7 @@ int PWMOut::pwm_ioctl(file *filp, int cmd, unsigned long arg) |
|
|
|
struct pwm_output_values *pwm = (struct pwm_output_values *)arg; |
|
|
|
struct pwm_output_values *pwm = (struct pwm_output_values *)arg; |
|
|
|
|
|
|
|
|
|
|
|
/* discard if too many values are sent */ |
|
|
|
/* discard if too many values are sent */ |
|
|
|
if (pwm->channel_count > FMU_MAX_ACTUATORS) { |
|
|
|
if (pwm->channel_count > FMU_MAX_ACTUATORS || _mixing_output.useDynamicMixing()) { |
|
|
|
ret = -EINVAL; |
|
|
|
ret = -EINVAL; |
|
|
|
break; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
@ -736,7 +833,7 @@ int PWMOut::pwm_ioctl(file *filp, int cmd, unsigned long arg) |
|
|
|
struct pwm_output_values *pwm = (struct pwm_output_values *)arg; |
|
|
|
struct pwm_output_values *pwm = (struct pwm_output_values *)arg; |
|
|
|
|
|
|
|
|
|
|
|
/* discard if too many values are sent */ |
|
|
|
/* discard if too many values are sent */ |
|
|
|
if (pwm->channel_count > FMU_MAX_ACTUATORS) { |
|
|
|
if (pwm->channel_count > FMU_MAX_ACTUATORS || _mixing_output.useDynamicMixing()) { |
|
|
|
ret = -EINVAL; |
|
|
|
ret = -EINVAL; |
|
|
|
break; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
@ -781,7 +878,7 @@ int PWMOut::pwm_ioctl(file *filp, int cmd, unsigned long arg) |
|
|
|
struct pwm_output_values *pwm = (struct pwm_output_values *)arg; |
|
|
|
struct pwm_output_values *pwm = (struct pwm_output_values *)arg; |
|
|
|
|
|
|
|
|
|
|
|
/* discard if too many values are sent */ |
|
|
|
/* discard if too many values are sent */ |
|
|
|
if (pwm->channel_count > FMU_MAX_ACTUATORS) { |
|
|
|
if (pwm->channel_count > FMU_MAX_ACTUATORS || _mixing_output.useDynamicMixing()) { |
|
|
|
ret = -EINVAL; |
|
|
|
ret = -EINVAL; |
|
|
|
break; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
@ -1223,6 +1320,27 @@ int PWMOut::print_status() |
|
|
|
perf_print_counter(_interval_perf); |
|
|
|
perf_print_counter(_interval_perf); |
|
|
|
_mixing_output.printStatus(); |
|
|
|
_mixing_output.printStatus(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (_mixing_output.useDynamicMixing() && _pwm_initialized) { |
|
|
|
|
|
|
|
for (int timer = 0; timer < MAX_IO_TIMERS; ++timer) { |
|
|
|
|
|
|
|
if (_timer_rates[timer] >= 0) { |
|
|
|
|
|
|
|
PX4_INFO_RAW("Timer %i: rate: %3i", timer, _timer_rates[timer]); |
|
|
|
|
|
|
|
uint32_t channels = _pwm_mask & up_pwm_servo_get_rate_group(timer); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (channels > 0) { |
|
|
|
|
|
|
|
PX4_INFO_RAW(" channels: "); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (uint32_t channel = 0; channel < _num_outputs; ++channel) { |
|
|
|
|
|
|
|
if ((1 << channel) & channels) { |
|
|
|
|
|
|
|
PX4_INFO_RAW("%" PRIu32 " ", channel); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PX4_INFO_RAW("\n"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return 0; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|