2018-01-05 02:19:51 -04:00
|
|
|
/*
|
|
|
|
* This file 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 file 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 <http://www.gnu.org/licenses/>.
|
|
|
|
*
|
|
|
|
* Code by Andrew Tridgell and Siddharth Bharat Purohit
|
|
|
|
*/
|
|
|
|
#include "RCOutput.h"
|
|
|
|
#include <AP_Math/AP_Math.h>
|
2018-01-05 04:55:01 -04:00
|
|
|
#include <AP_BoardConfig/AP_BoardConfig.h>
|
2018-01-05 02:19:51 -04:00
|
|
|
|
2018-03-01 20:46:30 -04:00
|
|
|
#if HAL_USE_PWM == TRUE
|
|
|
|
|
2018-01-05 02:19:51 -04:00
|
|
|
using namespace ChibiOS;
|
|
|
|
|
|
|
|
extern const AP_HAL::HAL& hal;
|
|
|
|
|
|
|
|
#if HAL_WITH_IO_MCU
|
|
|
|
#include <AP_IOMCU/AP_IOMCU.h>
|
|
|
|
extern AP_IOMCU iomcu;
|
|
|
|
#endif
|
|
|
|
|
2018-01-13 00:02:05 -04:00
|
|
|
struct RCOutput::pwm_group RCOutput::pwm_group_list[] = { HAL_PWM_GROUPS };
|
2018-01-05 02:19:51 -04:00
|
|
|
|
2018-01-11 21:49:33 -04:00
|
|
|
#define NUM_GROUPS ARRAY_SIZE_SIMPLE(pwm_group_list)
|
2018-01-07 19:40:23 -04:00
|
|
|
|
2018-01-12 22:33:48 -04:00
|
|
|
#define CHAN_DISABLED 255
|
|
|
|
|
2018-01-13 00:02:05 -04:00
|
|
|
void RCOutput::init()
|
2018-01-05 02:19:51 -04:00
|
|
|
{
|
2018-01-07 19:40:23 -04:00
|
|
|
for (uint8_t i = 0; i < NUM_GROUPS; i++ ) {
|
2018-01-05 02:19:51 -04:00
|
|
|
//Start Pwm groups
|
|
|
|
pwmStart(pwm_group_list[i].pwm_drv, &pwm_group_list[i].pwm_cfg);
|
2018-01-12 04:29:16 -04:00
|
|
|
for (uint8_t j = 0; j < 4; j++ ) {
|
2018-01-12 22:33:48 -04:00
|
|
|
if (pwm_group_list[i].chan[j] != CHAN_DISABLED) {
|
2018-01-12 04:29:16 -04:00
|
|
|
total_channels = MAX(total_channels, pwm_group_list[i].chan[j]+1);
|
|
|
|
}
|
|
|
|
}
|
2018-01-05 02:19:51 -04:00
|
|
|
}
|
|
|
|
#if HAL_WITH_IO_MCU
|
2018-01-05 04:55:01 -04:00
|
|
|
if (AP_BoardConfig::io_enabled()) {
|
|
|
|
iomcu.init();
|
|
|
|
// with IOMCU the local channels start at 8
|
|
|
|
chan_offset = 8;
|
2018-01-12 04:29:16 -04:00
|
|
|
total_channels += chan_offset;
|
2018-01-05 04:55:01 -04:00
|
|
|
}
|
2018-01-05 02:19:51 -04:00
|
|
|
#endif
|
2018-01-17 06:25:02 -04:00
|
|
|
chMtxObjectInit(&trigger_mutex);
|
2018-01-05 02:19:51 -04:00
|
|
|
}
|
|
|
|
|
2018-01-13 00:02:05 -04:00
|
|
|
void RCOutput::set_freq(uint32_t chmask, uint16_t freq_hz)
|
2018-01-05 02:19:51 -04:00
|
|
|
{
|
|
|
|
//check if the request spans accross any of the channel groups
|
|
|
|
uint8_t update_mask = 0;
|
|
|
|
// greater than 400 doesn't give enough room at higher periods for
|
|
|
|
// the down pulse
|
|
|
|
if (freq_hz > 400 && _output_mode != MODE_PWM_BRUSHED) {
|
|
|
|
freq_hz = 400;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if HAL_WITH_IO_MCU
|
2018-01-05 04:55:01 -04:00
|
|
|
if (AP_BoardConfig::io_enabled()) {
|
|
|
|
iomcu.set_freq(chmask, freq_hz);
|
|
|
|
}
|
2018-01-05 02:19:51 -04:00
|
|
|
#endif
|
|
|
|
|
|
|
|
chmask >>= chan_offset;
|
|
|
|
if (chmask == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-01-07 19:40:23 -04:00
|
|
|
for (uint8_t i = 0; i < NUM_GROUPS; i++ ) {
|
2018-01-12 04:29:16 -04:00
|
|
|
uint16_t grp_ch_mask = 0;
|
|
|
|
for (uint8_t j=0; j<4; j++) {
|
2018-01-12 22:33:48 -04:00
|
|
|
if (pwm_group_list[i].chan[j] != CHAN_DISABLED) {
|
2018-01-12 04:29:16 -04:00
|
|
|
grp_ch_mask |= (1U<<pwm_group_list[i].chan[j]);
|
|
|
|
}
|
|
|
|
}
|
2018-01-12 17:13:00 -04:00
|
|
|
if ((grp_ch_mask & chmask) != 0) {
|
|
|
|
/*
|
|
|
|
we enable the new frequency on all groups that have one
|
|
|
|
of the requested channels. This means we may enable high
|
|
|
|
speed on some channels that aren't requested, but that
|
|
|
|
is needed in order to fly a vehicle such a a hex
|
|
|
|
multicopter properly
|
|
|
|
*/
|
2018-01-05 02:19:51 -04:00
|
|
|
update_mask |= grp_ch_mask;
|
2018-01-12 23:53:29 -04:00
|
|
|
uint16_t freq_set = freq_hz;
|
2018-02-02 02:20:57 -04:00
|
|
|
uint32_t old_clock = pwm_group_list[i].pwm_cfg.frequency;
|
|
|
|
|
2018-01-12 23:53:29 -04:00
|
|
|
if (freq_set > 400 && pwm_group_list[i].pwm_cfg.frequency == 1000000) {
|
|
|
|
// need to change to an 8MHz clock
|
|
|
|
pwm_group_list[i].pwm_cfg.frequency = 8000000;
|
|
|
|
} else if (freq_set <= 400 && pwm_group_list[i].pwm_cfg.frequency == 8000000) {
|
|
|
|
// need to change to an 1MHz clock
|
|
|
|
pwm_group_list[i].pwm_cfg.frequency = 1000000;
|
|
|
|
}
|
2018-02-02 02:20:57 -04:00
|
|
|
|
|
|
|
// check if the frequency is possible, and keep halving
|
|
|
|
// down to 1MHz until it is OK with the hardware timer we
|
|
|
|
// are using. If we don't do this we'll hit an assert in
|
|
|
|
// the ChibiOS PWM driver on some timers
|
|
|
|
PWMDriver *pwmp = pwm_group_list[i].pwm_drv;
|
|
|
|
uint32_t psc = (pwmp->clock / pwmp->config->frequency) - 1;
|
|
|
|
while ((psc > 0xFFFF || ((psc + 1) * pwmp->config->frequency) != pwmp->clock) &&
|
|
|
|
pwm_group_list[i].pwm_cfg.frequency > 1000000) {
|
|
|
|
pwm_group_list[i].pwm_cfg.frequency /= 2;
|
|
|
|
psc = (pwmp->clock / pwmp->config->frequency) - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (old_clock != pwm_group_list[i].pwm_cfg.frequency) {
|
|
|
|
// we need to stop and start to setup the new clock
|
2018-01-12 23:53:29 -04:00
|
|
|
pwmStop(pwm_group_list[i].pwm_drv);
|
|
|
|
pwmStart(pwm_group_list[i].pwm_drv, &pwm_group_list[i].pwm_cfg);
|
|
|
|
}
|
2018-01-05 02:19:51 -04:00
|
|
|
pwmChangePeriod(pwm_group_list[i].pwm_drv,
|
2018-01-12 23:53:29 -04:00
|
|
|
pwm_group_list[i].pwm_cfg.frequency/freq_set);
|
2018-01-05 02:19:51 -04:00
|
|
|
}
|
|
|
|
}
|
2018-01-12 23:53:29 -04:00
|
|
|
if (freq_hz > 50) {
|
|
|
|
fast_channel_mask |= update_mask;
|
|
|
|
}
|
2018-01-05 02:19:51 -04:00
|
|
|
if (chmask != update_mask) {
|
|
|
|
hal.console->printf("RCOutput: Failed to set PWM frequency req %x set %x\n", (unsigned)chmask, (unsigned)update_mask);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-12 23:53:29 -04:00
|
|
|
/*
|
|
|
|
set default output rate
|
|
|
|
*/
|
2018-01-13 00:02:05 -04:00
|
|
|
void RCOutput::set_default_rate(uint16_t freq_hz)
|
2018-01-12 23:53:29 -04:00
|
|
|
{
|
|
|
|
#if HAL_WITH_IO_MCU
|
|
|
|
if (AP_BoardConfig::io_enabled()) {
|
|
|
|
iomcu.set_default_rate(freq_hz);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
for (uint8_t i = 0; i < NUM_GROUPS; i++ ) {
|
|
|
|
uint16_t grp_ch_mask = 0;
|
|
|
|
for (uint8_t j=0; j<4; j++) {
|
|
|
|
if (pwm_group_list[i].chan[j] != CHAN_DISABLED) {
|
|
|
|
grp_ch_mask |= (1U<<pwm_group_list[i].chan[j]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (grp_ch_mask & fast_channel_mask) {
|
|
|
|
// don't change fast channels
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
pwmChangePeriod(pwm_group_list[i].pwm_drv,
|
|
|
|
pwm_group_list[i].pwm_cfg.frequency/freq_hz);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-13 00:02:05 -04:00
|
|
|
uint16_t RCOutput::get_freq(uint8_t chan)
|
2018-01-05 02:19:51 -04:00
|
|
|
{
|
2018-01-12 04:29:16 -04:00
|
|
|
if (chan >= total_channels) {
|
|
|
|
return 0;
|
|
|
|
}
|
2018-01-05 02:19:51 -04:00
|
|
|
#if HAL_WITH_IO_MCU
|
|
|
|
if (chan < chan_offset) {
|
|
|
|
return iomcu.get_freq(chan);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
chan -= chan_offset;
|
|
|
|
|
2018-01-07 19:40:23 -04:00
|
|
|
for (uint8_t i = 0; i < NUM_GROUPS; i++ ) {
|
2018-01-05 02:19:51 -04:00
|
|
|
for (uint8_t j = 0; j < 4; j++) {
|
|
|
|
if (pwm_group_list[i].chan[j] == chan) {
|
|
|
|
return pwm_group_list[i].pwm_drv->config->frequency / pwm_group_list[i].pwm_drv->period;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// assume 50Hz default
|
|
|
|
return 50;
|
|
|
|
}
|
|
|
|
|
2018-01-13 00:02:05 -04:00
|
|
|
void RCOutput::enable_ch(uint8_t chan)
|
2018-01-05 02:19:51 -04:00
|
|
|
{
|
2018-01-12 04:29:16 -04:00
|
|
|
if (chan >= total_channels) {
|
|
|
|
return;
|
|
|
|
}
|
2018-01-05 02:19:51 -04:00
|
|
|
if (chan < chan_offset) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
chan -= chan_offset;
|
|
|
|
|
2018-01-07 19:40:23 -04:00
|
|
|
for (uint8_t i = 0; i < NUM_GROUPS; i++ ) {
|
2018-01-05 02:19:51 -04:00
|
|
|
for (uint8_t j = 0; j < 4; j++) {
|
|
|
|
if ((pwm_group_list[i].chan[j] == chan) && !(en_mask & 1<<chan)) {
|
|
|
|
en_mask |= 1<<chan;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-13 00:02:05 -04:00
|
|
|
void RCOutput::disable_ch(uint8_t chan)
|
2018-01-05 02:19:51 -04:00
|
|
|
{
|
2018-01-12 04:29:16 -04:00
|
|
|
if (chan >= total_channels) {
|
|
|
|
return;
|
|
|
|
}
|
2018-01-05 02:19:51 -04:00
|
|
|
if (chan < chan_offset) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
chan -= chan_offset;
|
|
|
|
|
2018-01-07 19:40:23 -04:00
|
|
|
for (uint8_t i = 0; i < NUM_GROUPS; i++ ) {
|
2018-01-05 02:19:51 -04:00
|
|
|
for (uint8_t j = 0; j < 4; j++) {
|
|
|
|
if (pwm_group_list[i].chan[j] == chan) {
|
|
|
|
pwmDisableChannel(pwm_group_list[i].pwm_drv, j);
|
|
|
|
en_mask &= ~(1<<chan);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-13 00:02:05 -04:00
|
|
|
void RCOutput::write(uint8_t chan, uint16_t period_us)
|
2018-01-05 02:19:51 -04:00
|
|
|
{
|
2018-01-12 04:29:16 -04:00
|
|
|
if (chan >= total_channels) {
|
|
|
|
return;
|
|
|
|
}
|
2018-01-05 02:19:51 -04:00
|
|
|
last_sent[chan] = period_us;
|
|
|
|
|
|
|
|
#if HAL_WITH_IO_MCU
|
|
|
|
// handle IO MCU channels
|
2018-01-05 04:55:01 -04:00
|
|
|
if (AP_BoardConfig::io_enabled()) {
|
|
|
|
iomcu.write_channel(chan, period_us);
|
|
|
|
}
|
2018-01-05 02:19:51 -04:00
|
|
|
#endif
|
|
|
|
if (chan < chan_offset) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
chan -= chan_offset;
|
|
|
|
|
|
|
|
period[chan] = period_us;
|
|
|
|
num_channels = MAX(chan+1, num_channels);
|
|
|
|
if (!corked) {
|
|
|
|
push_local();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
push values to local channels from period[] array
|
|
|
|
*/
|
2018-01-13 00:02:05 -04:00
|
|
|
void RCOutput::push_local(void)
|
2018-01-05 02:19:51 -04:00
|
|
|
{
|
|
|
|
if (num_channels == 0) {
|
|
|
|
return;
|
|
|
|
}
|
2018-01-06 05:42:47 -04:00
|
|
|
uint16_t outmask = (1U<<num_channels)-1;
|
|
|
|
outmask &= en_mask;
|
2018-01-16 03:58:58 -04:00
|
|
|
|
|
|
|
uint16_t widest_pulse = 0;
|
|
|
|
uint8_t need_trigger = 0;
|
2018-01-06 05:42:47 -04:00
|
|
|
|
2018-01-07 19:40:23 -04:00
|
|
|
for (uint8_t i = 0; i < NUM_GROUPS; i++ ) {
|
2018-01-05 02:19:51 -04:00
|
|
|
for (uint8_t j = 0; j < 4; j++) {
|
|
|
|
uint8_t chan = pwm_group_list[i].chan[j];
|
2018-01-12 22:33:48 -04:00
|
|
|
if (chan == CHAN_DISABLED) {
|
|
|
|
continue;
|
|
|
|
}
|
2018-01-05 02:19:51 -04:00
|
|
|
if (outmask & (1UL<<chan)) {
|
|
|
|
uint32_t period_us = period[chan];
|
2018-01-12 17:13:00 -04:00
|
|
|
if(_output_mode == MODE_PWM_BRUSHED && (fast_channel_mask & (1UL<<chan))) {
|
|
|
|
// note that we only use brushed signals on fast
|
|
|
|
// channels. This allows for ordinary PWM on
|
|
|
|
// servos attached to a brushed vehicle
|
2018-01-05 02:19:51 -04:00
|
|
|
if (period_us <= _esc_pwm_min) {
|
|
|
|
period_us = 0;
|
|
|
|
} else if (period_us >= _esc_pwm_max) {
|
|
|
|
period_us = PWM_FRACTION_TO_WIDTH(pwm_group_list[i].pwm_drv, 1, 1);
|
|
|
|
} else {
|
|
|
|
period_us = PWM_FRACTION_TO_WIDTH(pwm_group_list[i].pwm_drv,\
|
|
|
|
(_esc_pwm_max - _esc_pwm_min), (period_us - _esc_pwm_min));
|
|
|
|
}
|
|
|
|
pwmEnableChannel(pwm_group_list[i].pwm_drv, j, period_us);
|
|
|
|
} else {
|
2018-01-12 04:29:16 -04:00
|
|
|
uint32_t width = (pwm_group_list[i].pwm_cfg.frequency/1000000) * period_us;
|
|
|
|
pwmEnableChannel(pwm_group_list[i].pwm_drv, j, width);
|
2018-01-05 02:19:51 -04:00
|
|
|
}
|
2018-01-16 03:58:58 -04:00
|
|
|
if (period_us > widest_pulse) {
|
|
|
|
widest_pulse = period_us;
|
|
|
|
}
|
|
|
|
need_trigger |= (1U<<i);
|
2018-01-05 02:19:51 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-01-16 03:58:58 -04:00
|
|
|
|
2018-01-17 06:25:02 -04:00
|
|
|
if (widest_pulse > 2300) {
|
|
|
|
widest_pulse = 2300;
|
|
|
|
}
|
|
|
|
trigger_widest_pulse = widest_pulse;
|
|
|
|
|
|
|
|
trigger_groups = need_trigger;
|
|
|
|
|
|
|
|
if (trigger_groups && _output_mode == MODE_PWM_ONESHOT) {
|
|
|
|
trigger_oneshot();
|
2018-01-16 03:58:58 -04:00
|
|
|
}
|
2018-01-05 02:19:51 -04:00
|
|
|
}
|
|
|
|
|
2018-01-13 00:02:05 -04:00
|
|
|
uint16_t RCOutput::read(uint8_t chan)
|
2018-01-05 02:19:51 -04:00
|
|
|
{
|
2018-01-12 04:29:16 -04:00
|
|
|
if (chan >= total_channels) {
|
|
|
|
return 0;
|
|
|
|
}
|
2018-01-05 02:19:51 -04:00
|
|
|
#if HAL_WITH_IO_MCU
|
|
|
|
if (chan < chan_offset) {
|
|
|
|
return iomcu.read_channel(chan);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
chan -= chan_offset;
|
|
|
|
return period[chan];
|
|
|
|
}
|
|
|
|
|
2018-01-13 00:02:05 -04:00
|
|
|
void RCOutput::read(uint16_t* period_us, uint8_t len)
|
2018-01-05 02:19:51 -04:00
|
|
|
{
|
2018-01-12 04:29:16 -04:00
|
|
|
if (len > total_channels) {
|
|
|
|
len = total_channels;
|
|
|
|
}
|
2018-01-05 02:19:51 -04:00
|
|
|
#if HAL_WITH_IO_MCU
|
|
|
|
for (uint8_t i=0; i<MIN(len, chan_offset); i++) {
|
|
|
|
period_us[i] = iomcu.read_channel(i);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (len <= chan_offset) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
len -= chan_offset;
|
|
|
|
period_us += chan_offset;
|
|
|
|
|
|
|
|
memcpy(period_us, period, len*sizeof(uint16_t));
|
|
|
|
}
|
|
|
|
|
2018-01-13 00:02:05 -04:00
|
|
|
uint16_t RCOutput::read_last_sent(uint8_t chan)
|
2018-01-05 02:19:51 -04:00
|
|
|
{
|
2018-01-12 04:29:16 -04:00
|
|
|
if (chan >= total_channels) {
|
|
|
|
return 0;
|
|
|
|
}
|
2018-01-05 02:19:51 -04:00
|
|
|
return last_sent[chan];
|
|
|
|
}
|
|
|
|
|
2018-01-13 00:02:05 -04:00
|
|
|
void RCOutput::read_last_sent(uint16_t* period_us, uint8_t len)
|
2018-01-05 02:19:51 -04:00
|
|
|
{
|
2018-01-12 04:29:16 -04:00
|
|
|
if (len > total_channels) {
|
|
|
|
len = total_channels;
|
|
|
|
}
|
2018-01-05 02:19:51 -04:00
|
|
|
for (uint8_t i=0; i<len; i++) {
|
|
|
|
period_us[i] = read_last_sent(i);
|
|
|
|
}
|
|
|
|
}
|
2018-01-17 06:25:02 -04:00
|
|
|
|
2018-01-05 02:19:51 -04:00
|
|
|
/*
|
|
|
|
setup output mode
|
|
|
|
*/
|
2018-01-13 00:02:05 -04:00
|
|
|
void RCOutput::set_output_mode(enum output_mode mode)
|
2018-01-05 02:19:51 -04:00
|
|
|
{
|
|
|
|
_output_mode = mode;
|
|
|
|
if (_output_mode == MODE_PWM_BRUSHED) {
|
|
|
|
// force zero output initially
|
|
|
|
for (uint8_t i=chan_offset; i<chan_offset+num_channels; i++) {
|
|
|
|
write(i, 0);
|
|
|
|
}
|
|
|
|
}
|
2018-01-16 03:58:58 -04:00
|
|
|
if (_output_mode == MODE_PWM_ONESHOT) {
|
|
|
|
// for oneshot we force 1Hz output and then trigger on each loop
|
|
|
|
for (uint8_t i=0; i< NUM_GROUPS; i++) {
|
|
|
|
pwmChangePeriod(pwm_group_list[i].pwm_drv, pwm_group_list[i].pwm_cfg.frequency);
|
|
|
|
}
|
|
|
|
#if HAL_WITH_IO_MCU
|
|
|
|
if (AP_BoardConfig::io_enabled()) {
|
|
|
|
return iomcu.set_oneshot_mode();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
2018-01-05 02:19:51 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
force the safety switch on, disabling PWM output from the IO board
|
|
|
|
*/
|
2018-01-13 00:02:05 -04:00
|
|
|
bool RCOutput::force_safety_on(void)
|
2018-01-05 02:19:51 -04:00
|
|
|
{
|
|
|
|
#if HAL_WITH_IO_MCU
|
2018-01-05 04:55:01 -04:00
|
|
|
if (AP_BoardConfig::io_enabled()) {
|
|
|
|
return iomcu.force_safety_on();
|
|
|
|
}
|
2018-01-05 02:19:51 -04:00
|
|
|
#endif
|
2018-01-05 04:55:01 -04:00
|
|
|
return false;
|
2018-01-05 02:19:51 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
force the safety switch off, enabling PWM output from the IO board
|
|
|
|
*/
|
2018-01-13 00:02:05 -04:00
|
|
|
void RCOutput::force_safety_off(void)
|
2018-01-05 02:19:51 -04:00
|
|
|
{
|
|
|
|
#if HAL_WITH_IO_MCU
|
2018-01-05 04:55:01 -04:00
|
|
|
if (AP_BoardConfig::io_enabled()) {
|
|
|
|
iomcu.force_safety_off();
|
|
|
|
}
|
2018-01-05 02:19:51 -04:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
start corking output
|
|
|
|
*/
|
2018-01-13 00:02:05 -04:00
|
|
|
void RCOutput::cork(void)
|
2018-01-05 02:19:51 -04:00
|
|
|
{
|
|
|
|
corked = true;
|
|
|
|
#if HAL_WITH_IO_MCU
|
2018-01-05 04:55:01 -04:00
|
|
|
if (AP_BoardConfig::io_enabled()) {
|
|
|
|
iomcu.cork();
|
|
|
|
}
|
2018-01-05 02:19:51 -04:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
stop corking output
|
|
|
|
*/
|
2018-01-13 00:02:05 -04:00
|
|
|
void RCOutput::push(void)
|
2018-01-05 02:19:51 -04:00
|
|
|
{
|
|
|
|
corked = false;
|
|
|
|
push_local();
|
|
|
|
#if HAL_WITH_IO_MCU
|
2018-01-05 04:55:01 -04:00
|
|
|
if (AP_BoardConfig::io_enabled()) {
|
|
|
|
iomcu.push();
|
|
|
|
}
|
2018-01-05 02:19:51 -04:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
enable sbus output
|
|
|
|
*/
|
2018-01-13 00:02:05 -04:00
|
|
|
bool RCOutput::enable_px4io_sbus_out(uint16_t rate_hz)
|
2018-01-05 02:19:51 -04:00
|
|
|
{
|
|
|
|
#if HAL_WITH_IO_MCU
|
2018-01-05 04:55:01 -04:00
|
|
|
if (AP_BoardConfig::io_enabled()) {
|
|
|
|
return iomcu.enable_sbus_out(rate_hz);
|
|
|
|
}
|
2018-01-05 02:19:51 -04:00
|
|
|
#endif
|
2018-01-05 04:55:01 -04:00
|
|
|
return false;
|
2018-01-05 02:19:51 -04:00
|
|
|
}
|
2018-01-17 06:25:02 -04:00
|
|
|
|
|
|
|
/*
|
|
|
|
trigger output groups for oneshot mode
|
|
|
|
*/
|
|
|
|
void RCOutput::trigger_oneshot(void)
|
|
|
|
{
|
|
|
|
if (!chMtxTryLock(&trigger_mutex)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
uint64_t now = AP_HAL::micros64();
|
|
|
|
if (now < min_pulse_trigger_us) {
|
|
|
|
// guarantee minimum pulse separation
|
|
|
|
hal.scheduler->delay_microseconds(min_pulse_trigger_us - now);
|
|
|
|
}
|
|
|
|
osalSysLock();
|
|
|
|
for (uint8_t i = 0; i < NUM_GROUPS; i++ ) {
|
|
|
|
if (trigger_groups & (1U<<i)) {
|
|
|
|
// this triggers pulse output for a channel group
|
|
|
|
pwm_group_list[i].pwm_drv->tim->EGR = STM32_TIM_EGR_UG;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
osalSysUnlock();
|
|
|
|
/*
|
|
|
|
calculate time that we are allowed to trigger next pulse
|
|
|
|
to guarantee at least a 50us gap between pulses
|
|
|
|
*/
|
|
|
|
min_pulse_trigger_us = AP_HAL::micros64() + trigger_widest_pulse + 50;
|
|
|
|
chMtxUnlock(&trigger_mutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
periodic timer. The only need for a periodic timer is in oneshot
|
|
|
|
mode where we want to sustain a minimum output rate for when the
|
|
|
|
main loop is busy doing something like gyro calibration
|
|
|
|
|
|
|
|
A mininum output rate helps with some oneshot ESCs
|
|
|
|
*/
|
|
|
|
void RCOutput::timer_tick(void)
|
|
|
|
{
|
|
|
|
if (_output_mode != MODE_PWM_ONESHOT ||
|
|
|
|
trigger_groups == 0 ||
|
|
|
|
min_pulse_trigger_us == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
uint64_t now = AP_HAL::micros64();
|
|
|
|
if (now - min_pulse_trigger_us > 10000) {
|
|
|
|
// trigger at a minimum of 100Hz
|
|
|
|
trigger_oneshot();
|
|
|
|
}
|
|
|
|
}
|
2018-03-01 20:46:30 -04:00
|
|
|
|
|
|
|
#endif // HAL_USE_PWM
|