Skip to content

Commit

Permalink
Merge branch 'master' into abo_throttle_set_refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
breadoven authored Sep 18, 2023
2 parents 2f7f4d8 + f3bd99b commit 8b21195
Show file tree
Hide file tree
Showing 33 changed files with 793 additions and 149 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,5 @@ README.pdf
# local changes only
make/local.mk
launch.json
.vscode/tasks.json
.vscode/c_cpp_properties.json
61 changes: 61 additions & 0 deletions docs/Cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ While connected to the CLI, all Logical Switches are temporarily disabled (5.1.0
| `status` | Show status. Error codes can be looked up [here](https://github.com/iNavFlight/inav/wiki/%22Something%22-is-disabled----Reasons) |
| `tasks` | Show task stats |
| `temp_sensor` | List or configure temperature sensor(s). See [temperature sensors documentation](Temperature-sensors.md) for more information. |
| `timer_output_mode` | Override automatic timer / pwm function allocation. [Additional Information](#timer_outout_mode)|
| `version` | Show version |
| `wp` | List or configure waypoints. See the [navigation documentation](Navigation.md#cli-command-wp-to-manage-waypoints). |

Expand Down Expand Up @@ -170,6 +171,66 @@ serial 0 -4

`serial` can also be used without any argument to print the current configuration of all the serial ports.

### `timer_output_mode`

Since INAV 7, the firmware can dynamically allocate servo and motor outputs. This removes the need for bespoke targets for special cases (e.g. `MATEKF405` and `MATEKF405_SERVOS6`).

#### Syntax

```
timer_output_mode [timer [function]]
```
where:
* Without parameters, lists the current timers and modes
* With just a `timer` lists the mode for that timer
* With both `timer` and `function`, sets the function for that timers

Note:

* `timer` identifies the timer **index** (from 0); thus is one less than the corresponding `TIMn` definition in a target's `target.c`.
* The function is one of `AUTO` (the default), `MOTORS` or `SERVOS`.

Motors are allocated first, hence having a servo before a motor may require use of `timer_output_mode`.

#### Example

The original `MATEKF405` target defined a multi-rotor (MR) servo on output S1. The later `MATEKF405_SERVOS6` target defined (for MR) S1 as a motor and S6 as a servo. This was more logical, but annoying for anyone who had a legacy `MATEKF405` tricopter with the servo on S1.

#### Solution

There is now a single `MATEKF405` target. The `target.c` sets the relevant outputs as:

```
DEF_TIM(TIM3, CH1, PC6, TIM_USE_OUTPUT_AUTO, 0, 0), // S1
DEF_TIM(TIM8, CH2, PC7, TIM_USE_OUTPUT_AUTO, 0, 1), // S2 UP(2,1)
DEF_TIM(TIM8, CH3, PC8, TIM_USE_OUTPUT_AUTO, 0, 1), // S3 UP(2,1)
DEF_TIM(TIM8, CH4, PC9, TIM_USE_OUTPUT_AUTO, 0, 0), // S4 UP(2,1)
DEF_TIM(TIM2, CH1, PA15, TIM_USE_MC_MOTOR | TIM_USE_LED, 0, 0), // S5 UP(1,7)
DEF_TIM(TIM1, CH1, PA8, TIM_USE_OUTPUT_AUTO, 0, 0), // S6 UP(2,5)
DEF_TIM(TIM4, CH3, PB8, TIM_USE_OUTPUT_AUTO, 0, 0), // S7 D(1,7)!S5 UP(2,6)
```

Using the "motors first" allocation, the servo would end up on S6, which in the legacy "tricopter servo on S1" case is not desired.

Forcing the S1 output (`TIM3`) to servo is achieved by:

```
timer_output_mode 2 SERVOS
```

with resulting `resource` output:

```
C06: SERVO4 OUT
C07: MOTOR1 OUT
C08: MOTOR2 OUT
C09: MOTOR3 OUT
```

Note that the `timer` **index** in the `timer_output_mode` line is one less than the mnemonic in `target.c`, `timer` of 2 for `TIM3`.

Note that the usual caveat that one should not share a timer with both a motor and a servo still apply.

## Flash chip management

For targets that have a flash data chip, typically used for blackbox logs, the following additional comamnds are provided.
Expand Down
19 changes: 17 additions & 2 deletions docs/ESC and servo outputs.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,23 @@ While motors are usually ordered sequentially, here is no standard output layout

## Modifying output mapping

INAV 5 allows the limited output type mapping by allowing to change the function of *ALL* outputs at the same time. It can be done with the `output_mode` CLI setting. Allowed values:
### Modifying all outputs at the same time

Since INAV 5, it has been possible to force *ALL* outputs to be `MOTORS` or `SERVOS`.

Traditional ESCs usually can be controlled via a servo output, but would require calibration.

This can be done with the `output_mode` CLI setting. Allowed values:

* `AUTO` assigns outputs according to the default mapping
* `SERVOS` assigns all outputs to servos
* `MOTORS` assigns all outputs to motors
* `MOTORS` assigns all outputs to motors

### Modifying only some outputs

INAV 7 introduced extra functionality that let you force only some outputs to be either *MOTORS* or *SERVOS*, with some restrictions dictated by the hardware.

The mains restrictions is that outputs need to be associated with timers, which are usually shared between multiple outputs. Two outputs on the same timer need to have the same function.

The easiest way to modify outputs, is to use the Mixer tab in the Configurator, as it will clearly show you which timer is used by all outputs, but you can also use `timer_output_mode` on the cli.
This can be used in conjunction to the previous method, in that cass all outputs will follow `output_mode` and `timer_output_mode` overrides are applied after that.
2 changes: 1 addition & 1 deletion docs/Programming Framework.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ The flight mode operands return `true` when the mode is active. These are modes
| 7 | HORIZON | `true` when you are in the **Horizon** flight mode. |
| 8 | AIR | `true` when you the **Airmode** flight mode modifier is active. |
| 9 | USER1 | `true` when the **USER 1** mode is active. |
| 10 | USER2 | `true` when the **USER 21** mode is active. |
| 10 | USER2 | `true` when the **USER 2** mode is active. |
| 11 | COURSE_HOLD | `true` when you are in the **Course Hold** flight mode. |
| 12 | USER3 | `true` when the **USER 3** mode is active. |
| 13 | USER4 | `true` when the **USER 4** mode is active. |
Expand Down
2 changes: 1 addition & 1 deletion docs/Settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -4838,7 +4838,7 @@ Selection of pitot hardware.

| Default | Min | Max |
| --- | --- | --- |
| NONE | | |
| VIRTUAL | | |

---

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Install Git, Make, gcc and Ruby
- `sudo apt-get install git make cmake ruby`

Install python and python-yaml to allow updates to settings.md
- `sudo apt-get install python3 python-yaml`
- `sudo apt-get install python3`

### CMAKE and Ubuntu 18_04

Expand Down
3 changes: 2 additions & 1 deletion src/main/config/parameter_group_ids.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,8 @@
#define PG_UNUSED_1 1029
#define PG_POWER_LIMITS_CONFIG 1030
#define PG_OSD_COMMON_CONFIG 1031
#define PG_INAV_END 1031
#define PG_TIMER_OVERRIDE_CONFIG 1032
#define PG_INAV_END 1032

// OSD configuration (subject to change)
//#define PG_OSD_FONT_CONFIG 2047
Expand Down
160 changes: 136 additions & 24 deletions src/main/drivers/pwm_mapping.c
Original file line number Diff line number Diff line change
Expand Up @@ -212,27 +212,128 @@ static bool checkPwmTimerConflicts(const timerHardware_t *timHw)

static void timerHardwareOverride(timerHardware_t * timer) {
if (mixerConfig()->outputMode == OUTPUT_MODE_SERVOS) {

//Motors are rewritten as servos
if (timer->usageFlags & TIM_USE_MC_MOTOR) {
timer->usageFlags = timer->usageFlags & ~TIM_USE_MC_MOTOR;
timer->usageFlags = timer->usageFlags | TIM_USE_MC_SERVO;
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 (timer->usageFlags & TIM_USE_FW_MOTOR) {
timer->usageFlags = timer->usageFlags & ~TIM_USE_FW_MOTOR;
timer->usageFlags = timer->usageFlags | TIM_USE_FW_SERVO;
}

} else if (mixerConfig()->outputMode == OUTPUT_MODE_MOTORS) {

// Servos are rewritten as motors
if (timer->usageFlags & TIM_USE_MC_SERVO) {
timer->usageFlags = timer->usageFlags & ~TIM_USE_MC_SERVO;
timer->usageFlags = timer->usageFlags | TIM_USE_MC_MOTOR;
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;
}
}

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;
}
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;
}
break;
}
}

bool pwmHasMotorOnTimer(timMotorServoHardware_t * timOutputs, HAL_Timer_t *tim)
{
for (int i = 0; i < timOutputs->maxTimMotorCount; ++i) {
if (timOutputs->timMotors[i]->tim == tim) {
return true;
}
}

return false;
}

bool pwmHasServoOnTimer(timMotorServoHardware_t * timOutputs, HAL_Timer_t *tim)
{
for (int i = 0; i < timOutputs->maxTimServoCount; ++i) {
if (timOutputs->timServos[i]->tim == tim) {
return true;
}
}

return false;
}

uint8_t pwmClaimTimer(HAL_Timer_t *tim, uint32_t usageFlags) {
uint8_t changed = 0;
for (int idx = 0; idx < timerHardwareCount; idx++) {
timerHardware_t *timHw = &timerHardware[idx];
if (timHw->tim == tim && timHw->usageFlags != usageFlags) {
timHw->usageFlags = usageFlags;
changed++;
}
}

return changed;
}

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];

timerHardwareOverride(timHw);

if (checkPwmTimerConflicts(timHw)) {
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))) {
motorOnlyOutputs++;
motorOnlyOutputs += pwmClaimTimer(timHw->tim, timHw->usageFlags);
}
}

if (mixerConfig()->platformType == PLATFORM_MULTIROTOR ||
mixerConfig()->platformType == PLATFORM_TRICOPTER) {

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 (timer->usageFlags & TIM_USE_FW_SERVO) {
timer->usageFlags = timer->usageFlags & ~TIM_USE_FW_SERVO;
timer->usageFlags = timer->usageFlags | TIM_USE_FW_MOTOR;
} 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;
motorOnlyOutputs++;
motorOnlyOutputs += pwmClaimTimer(timHw->tim, timHw->usageFlags);
}
}
}
}
Expand All @@ -245,8 +346,9 @@ void pwmBuildTimerOutputList(timMotorServoHardware_t * timOutputs, bool isMixerU
uint8_t motorCount = getMotorCount();
uint8_t motorIdx = 0;

for (int idx = 0; idx < timerHardwareCount; idx++) {
pwmEnsureEnoughtMotors(motorCount);

for (int idx = 0; idx < timerHardwareCount; idx++) {
timerHardware_t *timHw = &timerHardware[idx];

timerHardwareOverride(timHw);
Expand All @@ -266,32 +368,42 @@ void pwmBuildTimerOutputList(timMotorServoHardware_t * timOutputs, bool isMixerU
// 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
if (isMixerUsingServos && timHw->usageFlags & TIM_USE_MC_SERVO) {
// 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) {
} 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) {
if (timHw->usageFlags & TIM_USE_FW_SERVO && !pwmHasMotorOnTimer(timOutputs, timHw->tim)) {
type = MAP_TO_SERVO_OUTPUT;
}
else if (timHw->usageFlags & TIM_USE_FW_MOTOR) {
} else if (timHw->usageFlags & TIM_USE_FW_MOTOR && !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);
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);
timOutputs->timServos[timOutputs->maxTimServoCount++] = timHw;
pwmClaimTimer(timHw->tim, timHw->usageFlags);
break;
default:
break;
Expand Down
14 changes: 12 additions & 2 deletions src/main/drivers/timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,18 @@

timHardwareContext_t * timerCtx[HARDWARE_TIMER_DEFINITION_COUNT];


uint8_t timer2id(const HAL_Timer_t *tim)
{
for (int i = 0; i < HARDWARE_TIMER_DEFINITION_COUNT; ++i) {
if (timerDefinitions[i].tim == tim) return i;
}

return (uint8_t)-1;
}

#if defined(AT32F43x)
uint8_t lookupTimerIndex(const tmr_type *tim)
uint8_t lookupTimerIndex(const HAL_Timer_t *tim)
{
int i;

Expand All @@ -54,7 +64,7 @@ uint8_t lookupTimerIndex(const tmr_type *tim)
}
#else
// return index of timer in timer table. Lowest timer has index 0
uint8_t lookupTimerIndex(const TIM_TypeDef *tim)
uint8_t lookupTimerIndex(const HAL_Timer_t *tim)
{
int i;

Expand Down
Loading

0 comments on commit 8b21195

Please sign in to comment.