/* (c) 2017 night_ghost@ykoctpa.ru */ #pragma GCC optimize ("O2") #include "AP_HAL_F4Light.h" #include "RCOutput.h" #include #include <4way/serial_4way.h> #include "GPIO.h" using namespace F4Light; // only one! //#define DEBUG_PWM 5 // motor 6 //#define DEBUG_INT 5 #define F4Light_OUT_CHANNELS 6 // motor's channels enabled by default #ifndef SERVO_PIN_5 #define SERVO_PIN_5 ((uint8_t)-1) #endif #ifndef SERVO_PIN_6 #define SERVO_PIN_6 ((uint8_t)-1) #endif #ifndef SERVO_PIN_7 #define SERVO_PIN_7 ((uint8_t)-1) #endif #ifndef SERVO_PIN_8 #define SERVO_PIN_8 ((uint8_t)-1) #endif #ifndef SERVO_PIN_9 #define SERVO_PIN_9 ((uint8_t)-1) #endif #ifndef SERVO_PIN_10 #define SERVO_PIN_10 ((uint8_t)-1) #endif #ifndef SERVO_PIN_11 #define SERVO_PIN_11 ((uint8_t)-1) #endif // #if FRAME_CONFIG == QUAD_FRAME // this is only QUAD layouts // ArduCopter static const uint8_t output_channels_arducopter[]= { // pin assignment SERVO_PIN_1, //Timer3/3 - 1 SERVO_PIN_2, //Timer3/4 - 2 SERVO_PIN_3, //Timer2/3 - 3 SERVO_PIN_4, //Timer2/2 - 4 SERVO_PIN_5, SERVO_PIN_6, // servo channels on input port SERVO_PIN_7, // PB15 CH2_IN - PPM2 SERVO_PIN_8, // PC6 CH3_IN UART6 SERVO_PIN_9, // PC7 CH4_IN UART6 SERVO_PIN_10, // PC8 CH5_IN i2c SERVO_PIN_11, // PC9 CH6_IN i2c }; // mimics foreign layouts // OpenPilot static const uint8_t output_channels_openpilot[]= { // pin assignment SERVO_PIN_2, //Timer3/4 - 2 SERVO_PIN_4, //Timer2/2 - 4 SERVO_PIN_1, //Timer3/3 - 1 SERVO_PIN_3, //Timer2/3 - 3 SERVO_PIN_5, //Timer2/1 SERVO_PIN_6, //Timer2/0 // servo channels on input port SERVO_PIN_7, // PB15 CH2_IN - PPM2 SERVO_PIN_8, // PC6 CH3_IN UART6 SERVO_PIN_9, // PC7 CH4_IN UART6 SERVO_PIN_10, // PC8 CH5_IN i2c SERVO_PIN_11, // PC9 CH6_IN i2c }; // Cleanflight static const uint8_t output_channels_cleanflight[]= { // pin assignment SERVO_PIN_2, //Timer3/4 - 2 SERVO_PIN_3, //Timer2/3 - 3 SERVO_PIN_4, //Timer2/2 - 4 SERVO_PIN_1, //Timer3/3 - 1 SERVO_PIN_5, //Timer2/1 SERVO_PIN_6, //Timer2/0 // servo channels on input port SERVO_PIN_7, // PB15 CH2_IN - PPM2 SERVO_PIN_8, // PC6 CH3_IN UART6 SERVO_PIN_9, // PC7 CH4_IN UART6 SERVO_PIN_10, // PC8 CH5_IN i2c SERVO_PIN_11, // PC9 CH6_IN i2c }; // Arducopter, shifted 2 pins right to use up to 2 servos static const uint8_t output_channels_servo[]= { // pin assignment SERVO_PIN_3, //Timer2/3 - 1 SERVO_PIN_4, //Timer2/2 - 2 SERVO_PIN_5, //Timer2/1 - 3 SERVO_PIN_6, //Timer2/0 - 4 SERVO_PIN_1, //Timer3/3 servo1 SERVO_PIN_2, //Timer3/4 servo2 // servo channels on input port SERVO_PIN_7, // PB15 CH2_IN - PPM2 SERVO_PIN_8, // PC6 CH3_IN UART6 SERVO_PIN_9, // PC7 CH4_IN UART6 SERVO_PIN_10, // PC8 CH5_IN i2c SERVO_PIN_11, // PC9 CH6_IN i2c }; static const uint8_t * const revo_motor_map[]={ output_channels_arducopter, output_channels_servo, output_channels_openpilot, output_channels_cleanflight, }; // #endif static const uint8_t *output_channels = output_channels_openpilot; // current pin assignment enum BOARD_PWM_MODES RCOutput::_mode = BOARD_PWM_NORMAL; bool RCOutput::_once_mode = false; uint16_t RCOutput::_period[F4Light_MAX_OUTPUT_CHANNELS] IN_CCM; uint16_t RCOutput::_freq[F4Light_MAX_OUTPUT_CHANNELS] IN_CCM; uint8_t RCOutput::_initialized[F4Light_MAX_OUTPUT_CHANNELS] IN_CCM; uint16_t RCOutput::_enabled_channels=0; bool RCOutput::_sbus_enabled=0; bool RCOutput::_corked=0; uint8_t RCOutput::_used_channels=0; uint8_t RCOutput::_servo_mask=0; uint32_t RCOutput::_timer2_preload; uint16_t RCOutput::_timer3_preload; uint8_t RCOutput::_pwm_type=0; const timer_dev* RCOutput::out_timers[16] IN_CCM; uint8_t RCOutput::num_out_timers IN_CCM; #define PWM_TIMER_KHZ 2000 // 1000 in cleanflight #define ONESHOT125_TIMER_KHZ 8000 // 8000 in cleanflight #define ONESHOT42_TIMER_KHZ 28000 // 24000 in cleanflight #define PWM_BRUSHED_TIMER_KHZ 16000 // 8000 in cleanflight #define _BV(bit) (1U << (bit)) void RCOutput::init() { memset(&_period[0], 0, sizeof(_period)); memset(&_initialized[0], 0, sizeof(_initialized)); _used_channels=0; } void RCOutput::do_4way_if(AP_HAL::UARTDriver* uart) { esc4wayInit(output_channels, F4Light_OUT_CHANNELS); esc4wayProcess(uart); } void RCOutput::lateInit(){ // 2nd stage with loaded parameters uint8_t map = hal_param_helper->_motor_layout; _servo_mask = hal_param_helper->_servo_mask; _pwm_type = hal_param_helper->_pwm_type; if(map >= ARRAY_SIZE(revo_motor_map)) return; // don't initialize if parameter is wrong output_channels = revo_motor_map[map]; InitPWM(); } void RCOutput::InitPWM() { for(uint8_t i = 0; i < F4Light_MAX_OUTPUT_CHANNELS; i++) { _freq[i] = 50; } fill_timers(); _set_output_mode(MODE_PWM_NORMAL); // init timers } // not from _freq to take channel dependency uint16_t RCOutput::get_freq(uint8_t ch) { if(ch >= F4Light_MAX_OUTPUT_CHANNELS) return 0; const timer_dev *dev = PIN_MAP[output_channels[ch]].timer_device; /* transform to period by inverse of _time_period(icr) */ return (uint16_t)(dev->state->freq / timer_get_reload(dev)); } // fill array of used timers void RCOutput::fill_timers(){ memset(out_timers, 0, sizeof(out_timers)); // clear it first num_out_timers=0; for (uint16_t ch = 0; ch < F4Light_OUT_CHANNELS; ch++) { if (!(_enabled_channels & _BV(ch))) continue; // not enabled const timer_dev *tim = PIN_MAP[output_channels[ch]].timer_device; bool add=true; for(uint8_t i =0; i 50Hz switch(_mode){ case BOARD_PWM_NORMAL: default: // output uses timers 2 & 3 so let init them for PWM mode period = ((PWM_TIMER_KHZ*1000UL) / 50); // 50Hz by default - will be corrected late per-channel in init_channel() freq = PWM_TIMER_KHZ; // 2MHz 0.5us ticks - for 50..490Hz PWM break; case BOARD_PWM_ONESHOT: // same as PWM but with manual restarting // output uses timers 2 & 3 so let init them for PWM mode period = (uint32_t)-1; // max possible freq = PWM_TIMER_KHZ; // 2MHz 0.5us ticks - for 50..490Hz PWM _once_mode=true; break; case BOARD_PWM_ONESHOT125: period = (uint32_t)-1; // max possible freq = ONESHOT125_TIMER_KHZ; // 16MHz 62.5ns ticks - for 125uS..490Hz OneShot125 // at 16Mhz, period with 65536 will be 244Hz so even 16-bit timer will never overflows at 500Hz loop _once_mode=true; break; case BOARD_PWM_PWM125: // like Oneshot125 but PWM so not once period = (uint32_t)-1; // max possible freq = ONESHOT125_TIMER_KHZ; // 16MHz 62.5ns ticks - for 125uS..490Hz OneShot125 // at 16Mhz, period with 65536 will be 244Hz so even 16-bit timer will never overflows at 500Hz loop break; case BOARD_PWM_ONESHOT42: period = (uint32_t)-1; // max possible freq = ONESHOT42_TIMER_KHZ; // 16MHz 62.5ns ticks - for 125uS..490Hz OneShot125 // at 28Mhz, period with 65536 will be 427Hz so even 16-bit timer should not overflows at 500Hz loop, but very close to _once_mode=true; break; case BOARD_PWM_BRUSHED: // dev period freq, kHz period = 1000; freq = PWM_BRUSHED_TIMER_KHZ; // 16MHz - 0..1 in 1000 steps break; } #if 1 // correct code should init all timers used for outputs for (uint16_t ch = 0; ch < F4Light_OUT_CHANNELS; ch++) { const timer_dev *tim = PIN_MAP[output_channels[ch]].timer_device; tim->state->update=false; // reset flag first } for (uint16_t ch = 0; ch < F4Light_OUT_CHANNELS; ch++) { if (!(_enabled_channels & _BV(ch))) continue; // not enabled if(_freq[ch]>50){ const timer_dev *tim = PIN_MAP[output_channels[ch]].timer_device; tim->state->update=true; // set flag for update for needed timer } } for (uint16_t ch = 0; ch < F4Light_OUT_CHANNELS; ch++) { uint8_t pin = output_channels[ch]; const timer_dev *tim = PIN_MAP[pin].timer_device; if(tim->state->update) { configTimeBase(tim, period, freq); tim->state->update = false; // only once if(_mode == BOARD_PWM_BRUSHED) tim->state->freq_scale=1; } } #else // raw and dirty way // dev period freq, kHz configTimeBase(TIMER2, period, freq); // 16MHz 62.5ns ticks - for 125uS..490Hz OneShot125 configTimeBase(TIMER3, period, freq); // 16MHz 62.5ns ticks if(_mode == BOARD_PWM_BRUSHED) { TIMER2->state->freq_scale=1; TIMER3->state->freq_scale=1; } #endif init_channels(); #if 1 for(uint8_t i =0; istate->freq_scale=1; } bool RCOutput::is_servo_enabled(uint8_t ch){ if(ch>=F4Light_OUT_CHANNELS){ // servos uint8_t sn = ch - F4Light_OUT_CHANNELS; if(!(_servo_mask & (1< [125;250] // so frequency of timers should be 8 times more - 16MHz, but timers on 84MHz can give only 16.8MHz // channels 1&2, 3&4&5&6 can has a different rates void RCOutput::set_freq(uint32_t chmask, uint16_t freq_hz) { uint32_t mask=1; uint16_t freq = freq_hz; for(uint8_t i=0; i< F4Light_MAX_OUTPUT_CHANNELS; i++) { // кто последний тот и папа if(chmask & mask) { if(!(_enabled_channels & mask) ) return; // not enabled // for true one-shot if(_once_mode && freq_hz>50) continue; // no frequency in OneShoot modes _freq[i] = freq_hz; if(_once_mode && freq_hz>50) freq = freq_hz / 2; // divide frequency by 2 in OneShoot modes const uint8_t pin = output_channels[i]; const timer_dev *dev = PIN_MAP[pin].timer_device; timer_set_reload(dev, _timer_period(freq, dev)); } mask <<= 1; } } void RCOutput::init_channel(uint8_t ch){ if(ch>=F4Light_MAX_OUTPUT_CHANNELS) return; uint8_t pin = output_channels[ch]; if (pin >= BOARD_NR_GPIO_PINS) return; const stm32_pin_info &p = PIN_MAP[pin]; const timer_dev *dev = p.timer_device; timer_set_mode( dev, p.timer_channel, TIMER_PWM); uint16_t freq = _freq[ch]; if(_once_mode && freq>50) freq/=2; timer_set_reload(dev, _timer_period(freq, dev)); if(_once_mode) { timer_set_compare(dev, p.timer_channel, 0); // to prevent outputs in case of timer overflow } } void RCOutput::init_channels(){ for (uint16_t ch = 0; ch < F4Light_OUT_CHANNELS; ch++) { init_channel(ch); } } /* constrain pwm to be between min and max pulsewidth */ static inline uint16_t constrain_period(uint16_t p) { if (p > RC_INPUT_MAX_PULSEWIDTH) return RC_INPUT_MAX_PULSEWIDTH; if (p < RC_INPUT_MIN_PULSEWIDTH) return RC_INPUT_MIN_PULSEWIDTH; return p; } void RCOutput::set_pwm(uint8_t ch, uint16_t pwm){ if(ch>=F4Light_MAX_OUTPUT_CHANNELS) return; if (!(_enabled_channels & _BV(ch))) return; // not enabled if(!is_servo_enabled(ch)) return; // disabled servo uint8_t pin = output_channels[ch]; if (pin >= BOARD_NR_GPIO_PINS) return; switch(_mode){ case BOARD_PWM_BRUSHED: pwm -= 1000; // move from 1000..2000 to 0..1000 break; case BOARD_PWM_ONESHOT42: // works at single freq case BOARD_PWM_ONESHOT125: break; default: pwm <<= 1; // frequency of timers 2MHz break; } const stm32_pin_info &p = PIN_MAP[pin]; const timer_dev *dev = p.timer_device; pwm *= dev->state->freq_scale; // take into account the inaccuracy of setting the timer frequency for small prescalers timer_set_compare(dev, p.timer_channel, pwm); } void RCOutput::write(uint8_t ch, uint16_t period_us) { if(ch>=F4Light_MAX_OUTPUT_CHANNELS) return; if(_used_channels=F4Light_MAX_OUTPUT_CHANNELS) return; for (int i = 0; i < len; i++) { write(i + ch, period_us[i]); } } uint16_t RCOutput::read(uint8_t ch) { if(ch>=F4Light_MAX_OUTPUT_CHANNELS) return RC_INPUT_MIN_PULSEWIDTH; return _period[ch]; } void RCOutput::read(uint16_t* period_us, uint8_t len) {// here we don't need to limit channel count - all unsupported will be read as RC_INPUT_MIN_PULSEWIDT for (int i = 0; i < len; i++) { period_us[i] = read(i); } } void RCOutput::enable_ch(uint8_t ch) { if (ch >= F4Light_MAX_OUTPUT_CHANNELS) return; if(_enabled_channels & (1U<= F4Light_MAX_OUTPUT_CHANNELS) { return; } if(!is_servo_enabled(ch)) return; // disabled servo _enabled_channels &= ~(1U<= BOARD_NR_GPIO_PINS) return; GPIO::_pinMode(pin, OUTPUT); GPIO::_write(pin, 0); fill_timers(); // re-calculate list of used timers } void RCOutput::push() { #ifdef DEBUG_PWM uint8_t spin = output_channels[DEBUG_PWM]; // motor 6 as strobe GPIO::_pinMode(spin, OUTPUT); GPIO::_write(spin, 1); #endif for (uint16_t ch = 0; ch < _used_channels; ch++) { set_pwm(ch, _period[ch]); } if(_once_mode){ // generate timer's update on ALL used pins, but only once per timer #if 1 for(uint8_t i =0; istate->update=false; // reset flag first } for (uint16_t ch = 0; ch < F4Light_OUT_CHANNELS; ch++) { if (!(_enabled_channels & _BV(ch))) continue; // not enabled const timer_dev *tim = PIN_MAP[output_channels[ch]].timer_device; tim->state->update=true; // set flag for update for needed timer } for (uint16_t ch = 0; ch < F4Light_OUT_CHANNELS; ch++) { const timer_dev *tim = PIN_MAP[output_channels[ch]].timer_device; if(tim->state->update) { timer_generate_update(tim); tim->state->update = false; // only once } } #endif } _corked = false; #ifdef DEBUG_PWM GPIO::_write(spin, 0); #endif }