1
0
Fork 0
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:
shota 2023-09-23 01:14:28 +09:00
commit 2b75da5a44
8 changed files with 83 additions and 115 deletions

View file

@ -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. Please note that this is an emerging / experimental capability that will require some effort by the pilot to implement.
## Setup for VTOL ## Setup for VTOL
- 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 - ~~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. - ~~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.~~
- 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 ## Configuration
### Timer overrides ### Timer overrides
![Alt text](Screenshots/timer_outputs.png) Set the timer overrides for the outputs that you are intended to use.
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.
For SITL builds, is not necessary to set timer overrides. 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 ### Profile Switch
Setup the FW mode and MR mode separately in two different mixer profiles: Setup the FW mode and MR mode separately in two different mixer profiles:

View file

@ -1,17 +1,28 @@
# INAV Programming Framework # 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 * [Servo mixer](Mixer.md) to activate/deactivate certain servo mix rulers
* To activate/deactivate system overrides * 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 * Logic Conditions - each Logic Condition can be understood as a single command, a single line of code. Each logic condition consists of:
* Global Variables - variables that can store values from and for LogiC Conditions and servo mixer * 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 * 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 ## Logic Conditions
@ -310,3 +321,14 @@ Steps:
2. Scale range [0:1000] to [0:3] 2. Scale range [0:1000] to [0:3]
3. Increase range by `1` to have the range of [1:4] 3. Increase range by `1` to have the range of [1:4]
4. Assign LC#2 to VTX power function 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.
![screenshot of override an RC channel with a value](./assets/images/ipf_set_get_rc_channel.png)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View file

@ -38,7 +38,7 @@ uint8_t hardwareMotorType = MOTOR_UNKNOWN;
void detectBrushedESC(void) void detectBrushedESC(void)
{ {
for (int i = 0; i < timerHardwareCount; i++) { 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); IO_t MotorDetectPin = IOGetByTag(timerHardware[i].tag);
IOInit(MotorDetectPin, OWNER_SYSTEM, RESOURCE_INPUT, 0); IOInit(MotorDetectPin, OWNER_SYSTEM, RESOURCE_INPUT, 0);
IOConfigGPIO(MotorDetectPin, IOCFG_IPU); IOConfigGPIO(MotorDetectPin, IOCFG_IPU);

View file

@ -211,37 +211,22 @@ static bool checkPwmTimerConflicts(const timerHardware_t *timHw)
} }
static void timerHardwareOverride(timerHardware_t * timer) { static void timerHardwareOverride(timerHardware_t * timer) {
switch (timerOverrides(timer2id(timer->tim))->outputMode) { switch (timerOverrides(timer2id(timer->tim))->outputMode) {
case OUTPUT_MODE_MOTORS: case OUTPUT_MODE_MOTORS:
if (timer->usageFlags & (TIM_USE_MC_SERVO | TIM_USE_FW_SERVO)) { if (TIM_IS_SERVO(timer->usageFlags)) {
timer->usageFlags &= ~(TIM_USE_MC_SERVO | TIM_USE_FW_SERVO); timer->usageFlags &= ~TIM_USE_SERVO;
timer->usageFlags |= TIM_USE_MC_MOTOR | TIM_USE_FW_MOTOR; timer->usageFlags |= TIM_USE_MOTOR;
} }
break; break;
case OUTPUT_MODE_SERVOS: case OUTPUT_MODE_SERVOS:
if (timer->usageFlags & (TIM_USE_MC_MOTOR|TIM_USE_FW_MOTOR)) { if (TIM_IS_MOTOR(timer->usageFlags)) {
timer->usageFlags &= ~(TIM_USE_MC_MOTOR | TIM_USE_FW_MOTOR); timer->usageFlags &= ~TIM_USE_MOTOR;
timer->usageFlags |= TIM_USE_MC_SERVO | TIM_USE_FW_SERVO; timer->usageFlags |= TIM_USE_SERVO;
} }
break; 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) bool pwmHasMotorOnTimer(timMotorServoHardware_t * timOutputs, HAL_Timer_t *tim)
{ {
for (int i = 0; i < timOutputs->maxTimMotorCount; ++i) { 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) void pwmEnsureEnoughtMotors(uint8_t motorCount)
{ {
uint8_t motorOnlyOutputs = 0; uint8_t motorOnlyOutputs = 0;
uint8_t mcMotorOnlyOutputs = 0;
for (int idx = 0; idx < timerHardwareCount; idx++) { for (int idx = 0; idx < timerHardwareCount; idx++) {
timerHardware_t *timHw = &timerHardware[idx]; timerHardware_t *timHw = &timerHardware[idx];
@ -291,49 +275,28 @@ void pwmEnsureEnoughtMotors(uint8_t motorCount)
continue; continue;
} }
if (timHw->usageFlags & (TIM_USE_MC_MOTOR) && !(timHw->usageFlags & (TIM_USE_MC_SERVO))) { if (TIM_IS_MOTOR_ONLY(timHw->usageFlags)) {
mcMotorOnlyOutputs++;
mcMotorOnlyOutputs += pwmClaimTimer(timHw->tim, timHw->usageFlags);
}
if (timHw->usageFlags & (TIM_USE_FW_MOTOR) && !(timHw->usageFlags & (TIM_USE_FW_SERVO))) {
motorOnlyOutputs++; motorOnlyOutputs++;
motorOnlyOutputs += pwmClaimTimer(timHw->tim, timHw->usageFlags); motorOnlyOutputs += pwmClaimTimer(timHw->tim, timHw->usageFlags);
} }
} }
if (mixerConfig()->platformType == PLATFORM_MULTIROTOR || for (int idx = 0; idx < timerHardwareCount; idx++) {
mixerConfig()->platformType == PLATFORM_TRICOPTER) { timerHardware_t *timHw = &timerHardware[idx];
for (int idx = 0; mcMotorOnlyOutputs < motorCount && idx < timerHardwareCount; idx++) { if (checkPwmTimerConflicts(timHw)) {
timerHardware_t *timHw = &timerHardware[idx]; continue;
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);
}
} }
} else {
for (int idx = 0; motorOnlyOutputs < motorCount && idx < timerHardwareCount; idx++) {
timerHardware_t *timHw = &timerHardware[idx];
if (checkPwmTimerConflicts(timHw)) { if (TIM_IS_MOTOR(timHw->usageFlags) && !TIM_IS_MOTOR_ONLY(timHw->usageFlags)) {
continue; if (motorOnlyOutputs < motorCount) {
} timHw->usageFlags &= ~TIM_USE_SERVO;
timHw->usageFlags |= TIM_USE_MOTOR;
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;
motorOnlyOutputs++; motorOnlyOutputs++;
motorOnlyOutputs += pwmClaimTimer(timHw->tim, timHw->usageFlags); 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) void pwmBuildTimerOutputList(timMotorServoHardware_t * timOutputs, bool isMixerUsingServos)
{ {
UNUSED(isMixerUsingServos);
timOutputs->maxTimMotorCount = 0; timOutputs->maxTimMotorCount = 0;
timOutputs->maxTimServoCount = 0; timOutputs->maxTimServoCount = 0;
@ -352,8 +316,6 @@ void pwmBuildTimerOutputList(timMotorServoHardware_t * timOutputs, bool isMixerU
for (int idx = 0; idx < timerHardwareCount; idx++) { for (int idx = 0; idx < timerHardwareCount; idx++) {
timerHardware_t *timHw = &timerHardware[idx]; timerHardware_t *timHw = &timerHardware[idx];
timerHardwareOverride(timHw);
int type = MAP_TO_NONE; int type = MAP_TO_NONE;
// Check for known conflicts (i.e. UART, LEDSTRIP, Rangefinder and ADC) // Check for known conflicts (i.e. UART, LEDSTRIP, Rangefinder and ADC)
@ -362,47 +324,27 @@ void pwmBuildTimerOutputList(timMotorServoHardware_t * timOutputs, bool isMixerU
continue; continue;
} }
// Determine if timer belongs to motor/servo // Make sure first motorCount motor outputs get assigned to motor
if (currentMixerConfig.platformType == PLATFORM_MULTIROTOR || currentMixerConfig.platformType == PLATFORM_TRICOPTER) { if (TIM_IS_MOTOR(timHw->usageFlags) && (motorIdx < motorCount)) {
// Multicopter timHw->usageFlags &= ~TIM_USE_SERVO;
pwmClaimTimer(timHw->tim, timHw->usageFlags);
motorIdx += 1;
}
// Make sure first motorCount outputs get assigned to motor if (TIM_IS_SERVO(timHw->usageFlags) && !pwmHasMotorOnTimer(timOutputs, timHw->tim)) {
if ((timHw->usageFlags & TIM_USE_MC_MOTOR) && (motorIdx < motorCount)) { type = MAP_TO_SERVO_OUTPUT;
timHw->usageFlags = timHw->usageFlags & ~TIM_USE_MC_SERVO; } else if (TIM_IS_MOTOR(timHw->usageFlags) && !pwmHasServoOnTimer(timOutputs, timHw->tim)) {
pwmClaimTimer(timHw->tim, timHw->usageFlags); type = MAP_TO_MOTOR_OUTPUT;
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;
}
} }
switch(type) { switch(type) {
case MAP_TO_MOTOR_OUTPUT: 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; timOutputs->timMotors[timOutputs->maxTimMotorCount++] = timHw;
pwmClaimTimer(timHw->tim, timHw->usageFlags); pwmClaimTimer(timHw->tim, timHw->usageFlags);
break; break;
case MAP_TO_SERVO_OUTPUT: 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; timOutputs->timServos[timOutputs->maxTimServoCount++] = timHw;
pwmClaimTimer(timHw->tim, timHw->usageFlags); pwmClaimTimer(timHw->tim, timHw->usageFlags);
break; break;

View file

@ -73,7 +73,6 @@ typedef struct {
} motorProtocolProperties_t; } motorProtocolProperties_t;
bool pwmMotorAndServoInit(void); bool pwmMotorAndServoInit(void);
bool checkPwmAssignedToMotorOrServo(void);
const motorProtocolProperties_t * getMotorProtocolProperties(motorPwmProtocolTypes_e proto); const motorProtocolProperties_t * getMotorProtocolProperties(motorPwmProtocolTypes_e proto);
pwmInitError_e getPwmInitError(void); pwmInitError_e getPwmInitError(void);
const char * getPwmInitErrorMessage(void); const char * getPwmInitErrorMessage(void);

View file

@ -110,18 +110,29 @@ typedef enum {
TIM_USE_ANY = 0, TIM_USE_ANY = 0,
TIM_USE_PPM = (1 << 0), TIM_USE_PPM = (1 << 0),
TIM_USE_PWM = (1 << 1), TIM_USE_PWM = (1 << 1),
TIM_USE_MC_MOTOR = (1 << 2), // Multicopter motor output TIM_USE_MOTOR = (1 << 2), // Motor output
TIM_USE_MC_SERVO = (1 << 3), // Multicopter servo output (i.e. TRI) 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_MC_CHNFW = (1 << 4), // Deprecated and not used after removal of CHANNEL_FORWARDING feature
TIM_USE_FW_MOTOR = (1 << 5), //TIM_USE_FW_MOTOR = (1 << 5), // We no longer differentiate mc from fw on pwm allocation
TIM_USE_FW_SERVO = (1 << 6), //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_LED = (1 << 24), TIM_USE_LED = (1 << 24),
TIM_USE_BEEPER = (1 << 25), TIM_USE_BEEPER = (1 << 25),
} timerUsageFlag_e; } 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 { enum {
TIMER_OUTPUT_NONE = 0x00, TIMER_OUTPUT_NONE = 0x00,