mirror of
https://github.com/iNavFlight/inav.git
synced 2025-07-13 11:29:56 +03:00
Merge remote-tracking branch 'origin/master' into sh_mixer_profile
This commit is contained in:
commit
2b75da5a44
8 changed files with 83 additions and 115 deletions
|
@ -9,21 +9,15 @@ By default, switching between profiles requires reboot to take affect. However,
|
|||
Please note that this is an emerging / experimental capability that will require some effort by the pilot to implement.
|
||||
|
||||
## Setup for VTOL
|
||||
|
||||
- For mixer profile switching it is necessary to keep motor and servo PWM mapping consistent between Fixed-Wing (FW) and Multi-rotor (MR) profiles
|
||||
- Traditionally, FW and MR have had different enumerations to define the PWM mappings. For VTOL operation it is necessary to set the `timer_output_mode` overrides to allow a consistent enumeration and thus mapping between MR and FW modes.
|
||||
- A VTOL specific FC target was required in the early stage of the development, but thanks to `timer_output_mode` overrides, It is not needed anymore.
|
||||
- In operation, it is necessary to set the `mixer_profile` and the `pid_profile` separately and to set a [RC mode](#rc-mode-settings) to switch between them.
|
||||
|
||||
- A VTOL specific FC target or `timer_output_mode` overrides was required in the early stage of the development, But since unified mapping introduced in INAV 7.0 It is not needed anymore.
|
||||
- ~~For mixer profile switching it is necessary to keep motor and servo PWM mapping consistent between Fixed-Wing (FW) and Multi-rotor (MR) profiles~~
|
||||
- ~~Traditionally, FW and MR have had different enumerations to define the PWM mappings. For VTOL operation it is necessary to set the `timer_output_mode` overrides to allow a consistent enumeration and thus mapping between MR and FW modes.~~
|
||||
- ~~In operation, it is necessary to set the `mixer_profile` and the `pid_profile` separately and to set a [RC mode](#rc-mode-settings) to switch between them.~~
|
||||
## Configuration
|
||||
### Timer overrides
|
||||

|
||||
|
||||
Set Output mode to `AUTO`. And set all servo/motor Timer outputs to `MOTORS` or `SERVOS`. This will result in a consistent mapping between MR and FW modes.
|
||||
A Sanity check on timer overrides settings could potentially block Profile Switch for safety reasons.
|
||||
|
||||
Set the timer overrides for the outputs that you are intended to use.
|
||||
For SITL builds, is not necessary to set timer overrides.
|
||||
|
||||
Please note that there are some display issues on the configurator that will show wrong mapping on the mixer_profile which has less motor/servo compared with the another
|
||||
### Profile Switch
|
||||
|
||||
Setup the FW mode and MR mode separately in two different mixer profiles:
|
||||
|
|
|
@ -1,17 +1,28 @@
|
|||
# INAV Programming Framework
|
||||
|
||||
INAV Programming Framework (abbr. IPF) is a mechanism that allows to evaluate cenrtain flight parameters (RC channels, switches, altitude, distance, timers, other logic conditions) and use the value of evaluated expression in different places of INAV. Currently, the result of LCs can be used in:
|
||||
INAV Programming Framework (IPF) is a mechanism that allows you to to create
|
||||
custom functionality in INAV. You can choose for certain actions to be done,
|
||||
based on custom conditions you select.
|
||||
|
||||
Logic conditions can be based on things such as RC channel values, switches, altitude,
|
||||
distance, timers, etc. The conditions you create can also make use of other conditions
|
||||
you've entered previously.
|
||||
The results can be used in:
|
||||
|
||||
* [Servo mixer](Mixer.md) to activate/deactivate certain servo mix rulers
|
||||
* To activate/deactivate system overrides
|
||||
|
||||
INAV Programming Framework coinsists of:
|
||||
INAV Programming Framework consists of:
|
||||
|
||||
* Logic Conditions - each Logic Condition can be understood as a single command, a single line of code
|
||||
* Global Variables - variables that can store values from and for LogiC Conditions and servo mixer
|
||||
* Logic Conditions - each Logic Condition can be understood as a single command, a single line of code. Each logic condition consists of:
|
||||
* an operator (action), such as "plus" or "set vtx power"
|
||||
* one or two operands (nouns), which the action acts upon. Operands are often numbers, such as a channel value or the distance to home.
|
||||
* "activator" condition - optional. This condition is only active when another condition is true
|
||||
* Global Variables - variables that can store values from and for Logic Conditions and servo mixer
|
||||
* Programming PID - general purpose, user configurable PID controllers
|
||||
|
||||
IPF can be edited using INAV Configurator user interface, or via CLI
|
||||
IPF can be edited using INAV Configurator user interface, or via CLI. To use COnfigurator, click the tab labeled
|
||||
"Programming". The various options shown in Configurator are described below.
|
||||
|
||||
## Logic Conditions
|
||||
|
||||
|
@ -310,3 +321,14 @@ Steps:
|
|||
2. Scale range [0:1000] to [0:3]
|
||||
3. Increase range by `1` to have the range of [1:4]
|
||||
4. Assign LC#2 to VTX power function
|
||||
|
||||
## Common issues / questions about IPF
|
||||
|
||||
One common mistake involves setting RC channel values. To override (set) the
|
||||
value of a specific RC channel, choose "Override RC value", then for operand A
|
||||
choose *value* and enter the channel number. Choosing "get RC value" is a common mistake,
|
||||
which does something other than what you probably want.
|
||||
|
||||

|
||||
|
||||
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 17 KiB |
BIN
docs/assets/images/ipf_set_get_rc_channel.png
Normal file
BIN
docs/assets/images/ipf_set_get_rc_channel.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
|
@ -38,7 +38,7 @@ uint8_t hardwareMotorType = MOTOR_UNKNOWN;
|
|||
void detectBrushedESC(void)
|
||||
{
|
||||
for (int i = 0; i < timerHardwareCount; i++) {
|
||||
if (timerHardware[i].usageFlags & TIM_USE_MC_MOTOR) {
|
||||
if (timerHardware[i].usageFlags & TIM_USE_MOTOR) {
|
||||
IO_t MotorDetectPin = IOGetByTag(timerHardware[i].tag);
|
||||
IOInit(MotorDetectPin, OWNER_SYSTEM, RESOURCE_INPUT, 0);
|
||||
IOConfigGPIO(MotorDetectPin, IOCFG_IPU);
|
||||
|
|
|
@ -211,37 +211,22 @@ static bool checkPwmTimerConflicts(const timerHardware_t *timHw)
|
|||
}
|
||||
|
||||
static void timerHardwareOverride(timerHardware_t * timer) {
|
||||
|
||||
switch (timerOverrides(timer2id(timer->tim))->outputMode) {
|
||||
case OUTPUT_MODE_MOTORS:
|
||||
if (timer->usageFlags & (TIM_USE_MC_SERVO | TIM_USE_FW_SERVO)) {
|
||||
timer->usageFlags &= ~(TIM_USE_MC_SERVO | TIM_USE_FW_SERVO);
|
||||
timer->usageFlags |= TIM_USE_MC_MOTOR | TIM_USE_FW_MOTOR;
|
||||
if (TIM_IS_SERVO(timer->usageFlags)) {
|
||||
timer->usageFlags &= ~TIM_USE_SERVO;
|
||||
timer->usageFlags |= TIM_USE_MOTOR;
|
||||
}
|
||||
break;
|
||||
case OUTPUT_MODE_SERVOS:
|
||||
if (timer->usageFlags & (TIM_USE_MC_MOTOR|TIM_USE_FW_MOTOR)) {
|
||||
timer->usageFlags &= ~(TIM_USE_MC_MOTOR | TIM_USE_FW_MOTOR);
|
||||
timer->usageFlags |= TIM_USE_MC_SERVO | TIM_USE_FW_SERVO;
|
||||
if (TIM_IS_MOTOR(timer->usageFlags)) {
|
||||
timer->usageFlags &= ~TIM_USE_MOTOR;
|
||||
timer->usageFlags |= TIM_USE_SERVO;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool checkPwmAssignedToMotorOrServo(void)
|
||||
{
|
||||
// Check TIM_USE_FW_* and TIM_USE_MC_* is consistent, If so, return true, means the pwm mapping will remain same between FW and MC
|
||||
bool pwm_assigned_to_motor_or_servo = true;
|
||||
for (int idx = 0; idx < timerHardwareCount; idx++) {
|
||||
timerHardware_t *timHw = &timerHardware[idx];
|
||||
if (timHw->usageFlags & (TIM_USE_MC_MOTOR | TIM_USE_FW_MOTOR | TIM_USE_MC_SERVO | TIM_USE_FW_SERVO)) {
|
||||
pwm_assigned_to_motor_or_servo &= (timHw->usageFlags == TIM_USE_VTOL_SERVO) | (timHw->usageFlags == TIM_USE_VTOL_MOTOR);
|
||||
}
|
||||
}
|
||||
return pwm_assigned_to_motor_or_servo;
|
||||
}
|
||||
|
||||
|
||||
bool pwmHasMotorOnTimer(timMotorServoHardware_t * timOutputs, HAL_Timer_t *tim)
|
||||
{
|
||||
for (int i = 0; i < timOutputs->maxTimMotorCount; ++i) {
|
||||
|
@ -280,7 +265,6 @@ uint8_t pwmClaimTimer(HAL_Timer_t *tim, uint32_t usageFlags) {
|
|||
void pwmEnsureEnoughtMotors(uint8_t motorCount)
|
||||
{
|
||||
uint8_t motorOnlyOutputs = 0;
|
||||
uint8_t mcMotorOnlyOutputs = 0;
|
||||
|
||||
for (int idx = 0; idx < timerHardwareCount; idx++) {
|
||||
timerHardware_t *timHw = &timerHardware[idx];
|
||||
|
@ -291,49 +275,28 @@ void pwmEnsureEnoughtMotors(uint8_t motorCount)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (timHw->usageFlags & (TIM_USE_MC_MOTOR) && !(timHw->usageFlags & (TIM_USE_MC_SERVO))) {
|
||||
mcMotorOnlyOutputs++;
|
||||
mcMotorOnlyOutputs += pwmClaimTimer(timHw->tim, timHw->usageFlags);
|
||||
}
|
||||
if (timHw->usageFlags & (TIM_USE_FW_MOTOR) && !(timHw->usageFlags & (TIM_USE_FW_SERVO))) {
|
||||
if (TIM_IS_MOTOR_ONLY(timHw->usageFlags)) {
|
||||
motorOnlyOutputs++;
|
||||
motorOnlyOutputs += pwmClaimTimer(timHw->tim, timHw->usageFlags);
|
||||
}
|
||||
}
|
||||
|
||||
if (mixerConfig()->platformType == PLATFORM_MULTIROTOR ||
|
||||
mixerConfig()->platformType == PLATFORM_TRICOPTER) {
|
||||
for (int idx = 0; idx < timerHardwareCount; idx++) {
|
||||
timerHardware_t *timHw = &timerHardware[idx];
|
||||
|
||||
for (int idx = 0; mcMotorOnlyOutputs < motorCount && idx < timerHardwareCount; idx++) {
|
||||
timerHardware_t *timHw = &timerHardware[idx];
|
||||
|
||||
if (checkPwmTimerConflicts(timHw)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t mcFlags = timHw->usageFlags & (TIM_USE_MC_MOTOR | TIM_USE_MC_SERVO);
|
||||
|
||||
if (mcFlags && mcFlags != TIM_USE_MC_MOTOR) {
|
||||
timHw->usageFlags &= ~TIM_USE_MC_SERVO;
|
||||
timHw->usageFlags |= TIM_USE_MC_MOTOR;
|
||||
mcMotorOnlyOutputs++;
|
||||
mcMotorOnlyOutputs += pwmClaimTimer(timHw->tim, timHw->usageFlags);
|
||||
}
|
||||
if (checkPwmTimerConflicts(timHw)) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
for (int idx = 0; motorOnlyOutputs < motorCount && idx < timerHardwareCount; idx++) {
|
||||
timerHardware_t *timHw = &timerHardware[idx];
|
||||
|
||||
if (checkPwmTimerConflicts(timHw)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t fwFlags = timHw->usageFlags & (TIM_USE_FW_MOTOR | TIM_USE_FW_SERVO);
|
||||
if (fwFlags && fwFlags != TIM_USE_FW_MOTOR) {
|
||||
timHw->usageFlags &= ~TIM_USE_FW_SERVO;
|
||||
timHw->usageFlags |= TIM_USE_FW_MOTOR;
|
||||
if (TIM_IS_MOTOR(timHw->usageFlags) && !TIM_IS_MOTOR_ONLY(timHw->usageFlags)) {
|
||||
if (motorOnlyOutputs < motorCount) {
|
||||
timHw->usageFlags &= ~TIM_USE_SERVO;
|
||||
timHw->usageFlags |= TIM_USE_MOTOR;
|
||||
motorOnlyOutputs++;
|
||||
motorOnlyOutputs += pwmClaimTimer(timHw->tim, timHw->usageFlags);
|
||||
} else {
|
||||
timHw->usageFlags &= ~TIM_USE_MOTOR;
|
||||
pwmClaimTimer(timHw->tim, timHw->usageFlags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -341,6 +304,7 @@ void pwmEnsureEnoughtMotors(uint8_t motorCount)
|
|||
|
||||
void pwmBuildTimerOutputList(timMotorServoHardware_t * timOutputs, bool isMixerUsingServos)
|
||||
{
|
||||
UNUSED(isMixerUsingServos);
|
||||
timOutputs->maxTimMotorCount = 0;
|
||||
timOutputs->maxTimServoCount = 0;
|
||||
|
||||
|
@ -352,8 +316,6 @@ void pwmBuildTimerOutputList(timMotorServoHardware_t * timOutputs, bool isMixerU
|
|||
for (int idx = 0; idx < timerHardwareCount; idx++) {
|
||||
timerHardware_t *timHw = &timerHardware[idx];
|
||||
|
||||
timerHardwareOverride(timHw);
|
||||
|
||||
int type = MAP_TO_NONE;
|
||||
|
||||
// Check for known conflicts (i.e. UART, LEDSTRIP, Rangefinder and ADC)
|
||||
|
@ -362,47 +324,27 @@ void pwmBuildTimerOutputList(timMotorServoHardware_t * timOutputs, bool isMixerU
|
|||
continue;
|
||||
}
|
||||
|
||||
// Determine if timer belongs to motor/servo
|
||||
if (currentMixerConfig.platformType == PLATFORM_MULTIROTOR || currentMixerConfig.platformType == PLATFORM_TRICOPTER) {
|
||||
// Multicopter
|
||||
// Make sure first motorCount motor outputs get assigned to motor
|
||||
if (TIM_IS_MOTOR(timHw->usageFlags) && (motorIdx < motorCount)) {
|
||||
timHw->usageFlags &= ~TIM_USE_SERVO;
|
||||
pwmClaimTimer(timHw->tim, timHw->usageFlags);
|
||||
motorIdx += 1;
|
||||
}
|
||||
|
||||
// Make sure first motorCount outputs get assigned to motor
|
||||
if ((timHw->usageFlags & TIM_USE_MC_MOTOR) && (motorIdx < motorCount)) {
|
||||
timHw->usageFlags = timHw->usageFlags & ~TIM_USE_MC_SERVO;
|
||||
pwmClaimTimer(timHw->tim, timHw->usageFlags);
|
||||
motorIdx += 1;
|
||||
}
|
||||
|
||||
// We enable mapping to servos if mixer is actually using them and it does not conflict with used motors
|
||||
if (isMixerUsingServos && timHw->usageFlags & TIM_USE_MC_SERVO && !pwmHasMotorOnTimer(timOutputs, timHw->tim)) {
|
||||
type = MAP_TO_SERVO_OUTPUT;
|
||||
} else if (timHw->usageFlags & TIM_USE_MC_MOTOR && !pwmHasServoOnTimer(timOutputs, timHw->tim)) {
|
||||
type = MAP_TO_MOTOR_OUTPUT;
|
||||
}
|
||||
} else {
|
||||
// Make sure first motorCount motor outputs get assigned to motor
|
||||
if ((timHw->usageFlags & TIM_USE_FW_MOTOR) && (motorIdx < motorCount)) {
|
||||
timHw->usageFlags = timHw->usageFlags & ~TIM_USE_FW_SERVO;
|
||||
pwmClaimTimer(timHw->tim, timHw->usageFlags);
|
||||
motorIdx += 1;
|
||||
}
|
||||
|
||||
// Fixed wing or HELI (one/two motors and a lot of servos
|
||||
if (timHw->usageFlags & TIM_USE_FW_SERVO && !pwmHasMotorOnTimer(timOutputs, timHw->tim)) {
|
||||
type = MAP_TO_SERVO_OUTPUT;
|
||||
} else if (timHw->usageFlags & TIM_USE_FW_MOTOR && !pwmHasServoOnTimer(timOutputs, timHw->tim)) {
|
||||
type = MAP_TO_MOTOR_OUTPUT;
|
||||
}
|
||||
if (TIM_IS_SERVO(timHw->usageFlags) && !pwmHasMotorOnTimer(timOutputs, timHw->tim)) {
|
||||
type = MAP_TO_SERVO_OUTPUT;
|
||||
} else if (TIM_IS_MOTOR(timHw->usageFlags) && !pwmHasServoOnTimer(timOutputs, timHw->tim)) {
|
||||
type = MAP_TO_MOTOR_OUTPUT;
|
||||
}
|
||||
|
||||
switch(type) {
|
||||
case MAP_TO_MOTOR_OUTPUT:
|
||||
timHw->usageFlags &= (TIM_USE_MC_MOTOR | TIM_USE_FW_MOTOR);
|
||||
timHw->usageFlags &= TIM_USE_MOTOR;
|
||||
timOutputs->timMotors[timOutputs->maxTimMotorCount++] = timHw;
|
||||
pwmClaimTimer(timHw->tim, timHw->usageFlags);
|
||||
break;
|
||||
case MAP_TO_SERVO_OUTPUT:
|
||||
timHw->usageFlags &= (TIM_USE_MC_SERVO | TIM_USE_FW_SERVO);
|
||||
timHw->usageFlags &= TIM_USE_SERVO;
|
||||
timOutputs->timServos[timOutputs->maxTimServoCount++] = timHw;
|
||||
pwmClaimTimer(timHw->tim, timHw->usageFlags);
|
||||
break;
|
||||
|
|
|
@ -73,7 +73,6 @@ typedef struct {
|
|||
} motorProtocolProperties_t;
|
||||
|
||||
bool pwmMotorAndServoInit(void);
|
||||
bool checkPwmAssignedToMotorOrServo(void);
|
||||
const motorProtocolProperties_t * getMotorProtocolProperties(motorPwmProtocolTypes_e proto);
|
||||
pwmInitError_e getPwmInitError(void);
|
||||
const char * getPwmInitErrorMessage(void);
|
||||
|
|
|
@ -110,18 +110,29 @@ typedef enum {
|
|||
TIM_USE_ANY = 0,
|
||||
TIM_USE_PPM = (1 << 0),
|
||||
TIM_USE_PWM = (1 << 1),
|
||||
TIM_USE_MC_MOTOR = (1 << 2), // Multicopter motor output
|
||||
TIM_USE_MC_SERVO = (1 << 3), // Multicopter servo output (i.e. TRI)
|
||||
TIM_USE_MOTOR = (1 << 2), // Motor output
|
||||
TIM_USE_SERVO = (1 << 3), // Servo output
|
||||
TIM_USE_MC_CHNFW = (1 << 4), // Deprecated and not used after removal of CHANNEL_FORWARDING feature
|
||||
TIM_USE_FW_MOTOR = (1 << 5),
|
||||
TIM_USE_FW_SERVO = (1 << 6),
|
||||
TIM_USE_VTOL_SERVO = (TIM_USE_FW_SERVO | TIM_USE_MC_SERVO),
|
||||
TIM_USE_VTOL_MOTOR = (TIM_USE_FW_MOTOR | TIM_USE_MC_MOTOR),
|
||||
//TIM_USE_FW_MOTOR = (1 << 5), // We no longer differentiate mc from fw on pwm allocation
|
||||
//TIM_USE_FW_SERVO = (1 << 6),
|
||||
TIM_USE_LED = (1 << 24),
|
||||
TIM_USE_BEEPER = (1 << 25),
|
||||
} timerUsageFlag_e;
|
||||
|
||||
#define TIM_USE_OUTPUT_AUTO (TIM_USE_MC_MOTOR | TIM_USE_MC_SERVO | TIM_USE_FW_MOTOR | TIM_USE_FW_SERVO)
|
||||
|
||||
// Compability
|
||||
#define TIM_USE_MC_MOTOR TIM_USE_MOTOR
|
||||
#define TIM_USE_MC_SERVO TIM_USE_SERVO
|
||||
#define TIM_USE_FW_MOTOR TIM_USE_MOTOR
|
||||
#define TIM_USE_FW_SERVO TIM_USE_SERVO
|
||||
|
||||
#define TIM_USE_OUTPUT_AUTO (TIM_USE_MOTOR | TIM_USE_SERVO)
|
||||
|
||||
#define TIM_IS_MOTOR(flags) ((flags) & TIM_USE_MOTOR)
|
||||
#define TIM_IS_SERVO(flags) ((flags) & TIM_USE_SERVO)
|
||||
|
||||
#define TIM_IS_MOTOR_ONLY(flags) (TIM_IS_MOTOR(flags) && !TIM_IS_SERVO(flags))
|
||||
#define TIM_IS_SERVO_ONLY(flags) (!TIM_IS_MOTOR(flags) && TIM_IS_SERVO(flags))
|
||||
|
||||
enum {
|
||||
TIMER_OUTPUT_NONE = 0x00,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue