Trigger gyro SPI DMA reads in EXTI handler if supported and lock gyroTask loop to gyro to eliminate missed updates and jitter

This commit is contained in:
Steve Evans 2020-08-14 16:42:20 +01:00 committed by Michael Keller
parent 415d4db5aa
commit d5f62be013
63 changed files with 755 additions and 379 deletions

View File

@ -99,4 +99,5 @@ const char * const debugModeNames[DEBUG_COUNT] = {
"D_LPF", "D_LPF",
"VTX_TRAMP", "VTX_TRAMP",
"GHST", "GHST",
"SCHEDULER_DETERMINISM"
}; };

View File

@ -115,6 +115,7 @@ typedef enum {
DEBUG_D_LPF, DEBUG_D_LPF,
DEBUG_VTX_TRAMP, DEBUG_VTX_TRAMP,
DEBUG_GHST, DEBUG_GHST,
DEBUG_SCHEDULER_DETERMINISM,
DEBUG_COUNT DEBUG_COUNT
} debugType_e; } debugType_e;

View File

@ -4782,6 +4782,19 @@ static void cliStatus(const char *cmdName, char *cmdline)
cliPrintf(" gyro %d", pos + 1); cliPrintf(" gyro %d", pos + 1);
} }
} }
#ifdef USE_SPI
#ifdef USE_GYRO_EXTI
if (gyroActiveDev()->gyroModeSPI != GYRO_EXTI_NO_INT) {
cliPrintf(" locked");
}
if (gyroActiveDev()->gyroModeSPI == GYRO_EXTI_INT_DMA) {
cliPrintf(" dma");
}
#endif
if (spiGetExtDeviceCount(&gyroActiveDev()->dev) > 1) {
cliPrintf(" shared");
}
#endif
cliPrintLinefeed(); cliPrintLinefeed();
#if defined(USE_SENSOR_NAMES) #if defined(USE_SENSOR_NAMES)
@ -4867,7 +4880,6 @@ static void cliStatus(const char *cmdName, char *cmdline)
cliPrintLinefeed(); cliPrintLinefeed();
} }
#if defined(USE_TASK_STATISTICS)
static void cliTasks(const char *cmdName, char *cmdline) static void cliTasks(const char *cmdName, char *cmdline)
{ {
UNUSED(cmdName); UNUSED(cmdName);
@ -4923,10 +4935,14 @@ static void cliTasks(const char *cmdName, char *cmdline)
getCheckFuncInfo(&checkFuncInfo); getCheckFuncInfo(&checkFuncInfo);
cliPrintLinef("RX Check Function %19d %7d %25d", checkFuncInfo.maxExecutionTimeUs, checkFuncInfo.averageExecutionTimeUs, checkFuncInfo.totalExecutionTimeUs / 1000); cliPrintLinef("RX Check Function %19d %7d %25d", checkFuncInfo.maxExecutionTimeUs, checkFuncInfo.averageExecutionTimeUs, checkFuncInfo.totalExecutionTimeUs / 1000);
cliPrintLinef("Total (excluding SERIAL) %25d.%1d%% %4d.%1d%%", maxLoadSum/10, maxLoadSum%10, averageLoadSum/10, averageLoadSum%10); cliPrintLinef("Total (excluding SERIAL) %25d.%1d%% %4d.%1d%%", maxLoadSum/10, maxLoadSum%10, averageLoadSum/10, averageLoadSum%10);
if (debugMode == DEBUG_SCHEDULER_DETERMINISM) {
extern int32_t schedLoopStartCycles, taskGuardCycles;
cliPrintLinef("Scheduler start cycles %d guard cycles %d", schedLoopStartCycles, taskGuardCycles);
}
schedulerResetCheckFunctionMaxExecutionTime(); schedulerResetCheckFunctionMaxExecutionTime();
} }
} }
#endif
static void printVersion(const char *cmdName, bool printBoardInfo) static void printVersion(const char *cmdName, bool printBoardInfo)
{ {
@ -6584,9 +6600,7 @@ const clicmd_t cmdTable[] = {
"\treverse <servo> <source> r|n", cliServoMix), "\treverse <servo> <source> r|n", cliServoMix),
#endif #endif
CLI_COMMAND_DEF("status", "show status", NULL, cliStatus), CLI_COMMAND_DEF("status", "show status", NULL, cliStatus),
#if defined(USE_TASK_STATISTICS)
CLI_COMMAND_DEF("tasks", "show task stats", NULL, cliTasks), CLI_COMMAND_DEF("tasks", "show task stats", NULL, cliTasks),
#endif
#ifdef USE_TIMER_MGMT #ifdef USE_TIMER_MGMT
CLI_COMMAND_DEF("timer", "show/set timers", "<> | <pin> list | <pin> [af<alternate function>|none|<option(deprecated)>] | list | show", cliTimer), CLI_COMMAND_DEF("timer", "show/set timers", "<> | <pin> list | <pin> [af<alternate function>|none|<option(deprecated)>] | list | show", cliTimer),
#endif #endif
@ -6832,8 +6846,6 @@ void cliEnter(serialPort_t *serialPort)
cliWriter = bufWriterInit(cliWriteBuffer, sizeof(cliWriteBuffer), (bufWrite_t)serialWriteBufShim, serialPort); cliWriter = bufWriterInit(cliWriteBuffer, sizeof(cliWriteBuffer), (bufWrite_t)serialWriteBufShim, serialPort);
cliErrorWriter = cliWriter; cliErrorWriter = cliWriter;
schedulerSetCalulateTaskStatistics(systemConfig()->task_statistics);
#ifndef MINIMAL_CLI #ifndef MINIMAL_CLI
cliPrintLine("\r\nEntering CLI Mode, type 'exit' to return, or 'help'"); cliPrintLine("\r\nEntering CLI Mode, type 'exit' to return, or 'help'");
#else #else

View File

@ -694,7 +694,7 @@ const clivalue_t valueTable[] = {
{ "mag_align_roll", VAR_INT16 | HARDWARE_VALUE, .config.minmax = { -3600, 3600 }, PG_COMPASS_CONFIG, offsetof(compassConfig_t, mag_customAlignment.roll) }, { "mag_align_roll", VAR_INT16 | HARDWARE_VALUE, .config.minmax = { -3600, 3600 }, PG_COMPASS_CONFIG, offsetof(compassConfig_t, mag_customAlignment.roll) },
{ "mag_align_pitch", VAR_INT16 | HARDWARE_VALUE, .config.minmax = { -3600, 3600 }, PG_COMPASS_CONFIG, offsetof(compassConfig_t, mag_customAlignment.pitch) }, { "mag_align_pitch", VAR_INT16 | HARDWARE_VALUE, .config.minmax = { -3600, 3600 }, PG_COMPASS_CONFIG, offsetof(compassConfig_t, mag_customAlignment.pitch) },
{ "mag_align_yaw", VAR_INT16 | HARDWARE_VALUE, .config.minmax = { -3600, 3600 }, PG_COMPASS_CONFIG, offsetof(compassConfig_t, mag_customAlignment.yaw) }, { "mag_align_yaw", VAR_INT16 | HARDWARE_VALUE, .config.minmax = { -3600, 3600 }, PG_COMPASS_CONFIG, offsetof(compassConfig_t, mag_customAlignment.yaw) },
{ "mag_bustype", VAR_UINT8 | HARDWARE_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_BUS_TYPE }, PG_COMPASS_CONFIG, offsetof(compassConfig_t, mag_bustype) }, { "mag_bustype", VAR_UINT8 | HARDWARE_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_BUS_TYPE }, PG_COMPASS_CONFIG, offsetof(compassConfig_t, mag_busType) },
{ "mag_i2c_device", VAR_UINT8 | HARDWARE_VALUE, .config.minmaxUnsigned = { 0, I2CDEV_COUNT }, PG_COMPASS_CONFIG, offsetof(compassConfig_t, mag_i2c_device) }, { "mag_i2c_device", VAR_UINT8 | HARDWARE_VALUE, .config.minmaxUnsigned = { 0, I2CDEV_COUNT }, PG_COMPASS_CONFIG, offsetof(compassConfig_t, mag_i2c_device) },
{ "mag_i2c_address", VAR_UINT8 | HARDWARE_VALUE, .config.minmaxUnsigned = { 0, I2C_ADDR7_MAX }, PG_COMPASS_CONFIG, offsetof(compassConfig_t, mag_i2c_address) }, { "mag_i2c_address", VAR_UINT8 | HARDWARE_VALUE, .config.minmaxUnsigned = { 0, I2C_ADDR7_MAX }, PG_COMPASS_CONFIG, offsetof(compassConfig_t, mag_i2c_address) },
{ "mag_spi_device", VAR_UINT8 | HARDWARE_VALUE, .config.minmaxUnsigned = { 0, SPIDEV_COUNT }, PG_COMPASS_CONFIG, offsetof(compassConfig_t, mag_spi_device) }, { "mag_spi_device", VAR_UINT8 | HARDWARE_VALUE, .config.minmaxUnsigned = { 0, SPIDEV_COUNT }, PG_COMPASS_CONFIG, offsetof(compassConfig_t, mag_spi_device) },
@ -1469,9 +1469,7 @@ const clivalue_t valueTable[] = {
#if defined(STM32F4) || defined(STM32G4) #if defined(STM32F4) || defined(STM32G4)
{ "system_hse_mhz", VAR_UINT8 | HARDWARE_VALUE, .config.minmaxUnsigned = { 0, 30 }, PG_SYSTEM_CONFIG, offsetof(systemConfig_t, hseMhz) }, { "system_hse_mhz", VAR_UINT8 | HARDWARE_VALUE, .config.minmaxUnsigned = { 0, 30 }, PG_SYSTEM_CONFIG, offsetof(systemConfig_t, hseMhz) },
#endif #endif
#if defined(USE_TASK_STATISTICS)
{ "task_statistics", VAR_INT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_SYSTEM_CONFIG, offsetof(systemConfig_t, task_statistics) }, { "task_statistics", VAR_INT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_SYSTEM_CONFIG, offsetof(systemConfig_t, task_statistics) },
#endif
{ "debug_mode", VAR_UINT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_DEBUG }, PG_SYSTEM_CONFIG, offsetof(systemConfig_t, debug_mode) }, { "debug_mode", VAR_UINT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_DEBUG }, PG_SYSTEM_CONFIG, offsetof(systemConfig_t, debug_mode) },
{ "rate_6pos_switch", VAR_UINT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_SYSTEM_CONFIG, offsetof(systemConfig_t, rateProfile6PosSwitch) }, { "rate_6pos_switch", VAR_UINT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_SYSTEM_CONFIG, offsetof(systemConfig_t, rateProfile6PosSwitch) },
#ifdef USE_OVERCLOCK #ifdef USE_OVERCLOCK

View File

@ -44,6 +44,7 @@ typedef uint32_t timeUs_t;
#define TIMEZONE_OFFSET_MINUTES_MAX 780 // +13 hours #define TIMEZONE_OFFSET_MINUTES_MAX 780 // +13 hours
static inline timeDelta_t cmpTimeUs(timeUs_t a, timeUs_t b) { return (timeDelta_t)(a - b); } static inline timeDelta_t cmpTimeUs(timeUs_t a, timeUs_t b) { return (timeDelta_t)(a - b); }
static inline int32_t cmpTimeCycles(uint32_t a, uint32_t b) { return (int32_t)(a - b); }
#define FORMATTED_DATE_TIME_BUFSIZE 30 #define FORMATTED_DATE_TIME_BUFSIZE 30

View File

@ -25,6 +25,7 @@
#include "common/axis.h" #include "common/axis.h"
#include "common/maths.h" #include "common/maths.h"
#include "common/sensor_alignment.h" #include "common/sensor_alignment.h"
#include "common/time.h"
#include "drivers/exti.h" #include "drivers/exti.h"
#include "drivers/bus.h" #include "drivers/bus.h"
#include "drivers/sensor.h" #include "drivers/sensor.h"
@ -79,6 +80,13 @@ typedef enum {
GYRO_RATE_32_kHz, GYRO_RATE_32_kHz,
} gyroRateKHz_e; } gyroRateKHz_e;
typedef enum {
GYRO_EXTI_INIT = 0,
GYRO_EXTI_INT_DMA,
GYRO_EXTI_INT,
GYRO_EXTI_NO_INT
} gyroModeSPI_e;
typedef struct gyroDev_s { typedef struct gyroDev_s {
#if defined(SIMULATOR_BUILD) && defined(SIMULATOR_MULTITHREAD) #if defined(SIMULATOR_BUILD) && defined(SIMULATOR_MULTITHREAD)
pthread_mutex_t lock; pthread_mutex_t lock;
@ -97,7 +105,15 @@ typedef struct gyroDev_s {
mpuDetectionResult_t mpuDetectionResult; mpuDetectionResult_t mpuDetectionResult;
sensor_align_e gyroAlign; sensor_align_e gyroAlign;
gyroRateKHz_e gyroRateKHz; gyroRateKHz_e gyroRateKHz;
bool dataReady; gyroModeSPI_e gyroModeSPI;
#ifdef USE_GYRO_EXTI
uint32_t detectedEXTI;
uint32_t gyroLastEXTI;
uint32_t gyroSyncEXTI;
int32_t gyroShortPeriod;
int32_t gyroDmaMaxDuration;
#endif
volatile bool dataReady;
bool gyro_high_fsr; bool gyro_high_fsr;
uint8_t hardware_lpf; uint8_t hardware_lpf;
uint8_t hardware_32khz_lpf; uint8_t hardware_32khz_lpf;

View File

@ -67,6 +67,9 @@
#define MPU_INQUIRY_MASK 0x7E #define MPU_INQUIRY_MASK 0x7E
// Need to see at least this many interrupts during initialisation to confirm EXTI connectivity
#define GYRO_EXTI_DETECT_THRESHOLD 1000
#ifdef USE_I2C_GYRO #ifdef USE_I2C_GYRO
static void mpu6050FindRevision(gyroDev_t *gyro) static void mpu6050FindRevision(gyroDev_t *gyro)
{ {
@ -107,21 +110,60 @@ static void mpu6050FindRevision(gyroDev_t *gyro)
* Gyro interrupt service routine * Gyro interrupt service routine
*/ */
#ifdef USE_GYRO_EXTI #ifdef USE_GYRO_EXTI
#ifdef USE_SPI_GYRO
// Called in ISR context
// Gyro read has just completed
busStatus_e mpuIntcallback(uint32_t arg)
{
volatile gyroDev_t *gyro = (gyroDev_t *)arg;
int32_t gyroDmaDuration = cmpTimeCycles(getCycleCounter(), gyro->gyroLastEXTI);
if (gyroDmaDuration > gyro->gyroDmaMaxDuration) {
gyro->gyroDmaMaxDuration = gyroDmaDuration;
}
gyro->dataReady = true;
return BUS_READY;
}
static void mpuIntExtiHandler(extiCallbackRec_t *cb)
{
// Non-blocking, so this needs to be static
static busSegment_t segments[] = {
{NULL, NULL, 15, true, mpuIntcallback},
{NULL, NULL, 0, true, NULL},
};
gyroDev_t *gyro = container_of(cb, gyroDev_t, exti);
// Ideally we'd use a time to capture such information, but unfortunately the port used for EXTI interrupt does
// not have an associated timer
uint32_t nowCycles = getCycleCounter();
int32_t gyroLastPeriod = cmpTimeCycles(nowCycles, gyro->gyroLastEXTI);
// This detects the short (~79us) EXTI interval of an MPU6xxx gyro
if ((gyro->gyroShortPeriod == 0) || (gyroLastPeriod < gyro->gyroShortPeriod)) {
gyro->gyroSyncEXTI = gyro->gyroLastEXTI + gyro->gyroDmaMaxDuration;
}
gyro->gyroLastEXTI = nowCycles;
if (gyro->gyroModeSPI == GYRO_EXTI_INT_DMA) {
segments[0].txData = gyro->dev.txBuf;;
segments[0].rxData = &gyro->dev.rxBuf[1];
if (!spiIsBusy(&gyro->dev)) {
spiSequence(&gyro->dev, &segments[0]);
}
}
gyro->detectedEXTI++;
}
#else
static void mpuIntExtiHandler(extiCallbackRec_t *cb) static void mpuIntExtiHandler(extiCallbackRec_t *cb)
{ {
#ifdef DEBUG_MPU_DATA_READY_INTERRUPT
static uint32_t lastCalledAtUs = 0;
const uint32_t nowUs = micros();
debug[0] = (uint16_t)(nowUs - lastCalledAtUs);
lastCalledAtUs = nowUs;
#endif
gyroDev_t *gyro = container_of(cb, gyroDev_t, exti); gyroDev_t *gyro = container_of(cb, gyroDev_t, exti);
gyro->dataReady = true; gyro->dataReady = true;
#ifdef DEBUG_MPU_DATA_READY_INTERRUPT
const uint32_t now2Us = micros();
debug[1] = (uint16_t)(now2Us - nowUs);
#endif
} }
#endif
static void mpuIntExtiInit(gyroDev_t *gyro) static void mpuIntExtiInit(gyroDev_t *gyro)
{ {
@ -177,37 +219,128 @@ bool mpuGyroRead(gyroDev_t *gyro)
return true; return true;
} }
#ifdef USE_SPI_GYRO #ifdef USE_SPI_GYRO
bool mpuAccReadSPI(accDev_t *acc) bool mpuAccReadSPI(accDev_t *acc)
{ {
STATIC_DMA_DATA_AUTO uint8_t dataToSend[7] = {MPU_RA_ACCEL_XOUT_H | 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; switch (acc->gyro->gyroModeSPI) {
STATIC_DMA_DATA_AUTO uint8_t data[7]; case GYRO_EXTI_INT:
case GYRO_EXTI_NO_INT:
{
// Ensure any prior DMA has completed before continuing
spiWaitClaim(&acc->gyro->dev);
const bool ack = spiReadWriteBufRB(&acc->gyro->dev, dataToSend, data, 7); acc->gyro->dev.txBuf[0] = MPU_RA_ACCEL_XOUT_H | 0x80;
if (!ack) {
return false; busSegment_t segments[] = {
{NULL, NULL, 7, true, NULL},
{NULL, NULL, 0, true, NULL},
};
segments[0].txData = acc->gyro->dev.txBuf;
segments[0].rxData = &acc->gyro->dev.rxBuf[1];
spiSequence(&acc->gyro->dev, &segments[0]);
// Wait for completion
spiWait(&acc->gyro->dev);
// Fall through
FALLTHROUGH;
} }
acc->ADCRaw[X] = (int16_t)((data[1] << 8) | data[2]); case GYRO_EXTI_INT_DMA:
acc->ADCRaw[Y] = (int16_t)((data[3] << 8) | data[4]); {
acc->ADCRaw[Z] = (int16_t)((data[5] << 8) | data[6]); // If read was triggered in interrupt don't bother waiting. The worst that could happen is that we pick
// up an old value.
// This data was read from the gyro, which is the same SPI device as the acc
uint16_t *accData = (uint16_t *)acc->gyro->dev.rxBuf;
acc->ADCRaw[X] = __builtin_bswap16(accData[1]);
acc->ADCRaw[Y] = __builtin_bswap16(accData[2]);
acc->ADCRaw[Z] = __builtin_bswap16(accData[3]);
break;
}
case GYRO_EXTI_INIT:
default:
break;
}
return true; return true;
} }
bool mpuGyroReadSPI(gyroDev_t *gyro) bool mpuGyroReadSPI(gyroDev_t *gyro)
{ {
STATIC_DMA_DATA_AUTO uint8_t dataToSend[7] = {MPU_RA_GYRO_XOUT_H | 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; uint16_t *gyroData = (uint16_t *)gyro->dev.rxBuf;
STATIC_DMA_DATA_AUTO uint8_t data[7]; switch (gyro->gyroModeSPI) {
case GYRO_EXTI_INIT:
{
// Initialise the tx buffer to all 0xff
memset(gyro->dev.txBuf, 0xff, 16);
#ifdef USE_GYRO_EXTI
// Check that minimum number of interrupts have been detected
const bool ack = spiReadWriteBufRB(&gyro->dev, dataToSend, data, 7); // We need some offset from the gyro interrupts to ensure sampling after the interrupt
if (!ack) { gyro->gyroDmaMaxDuration = 5;
return false; // Using DMA for gyro access upsets the scheduler on the F4
if (gyro->detectedEXTI > GYRO_EXTI_DETECT_THRESHOLD) {
if (spiUseDMA(&gyro->dev)) {
// Indicate that the bus on which this device resides may initiate DMA transfers from interrupt context
spiSetAtomicWait(&gyro->dev);
gyro->gyroModeSPI = GYRO_EXTI_INT_DMA;
gyro->dev.callbackArg = (uint32_t)gyro;
gyro->dev.txBuf[0] = MPU_RA_ACCEL_XOUT_H | 0x80;
} else {
// Interrupts are present, but no DMA
gyro->gyroModeSPI = GYRO_EXTI_INT;
}
} else
#endif
{
gyro->gyroModeSPI = GYRO_EXTI_NO_INT;
}
break;
} }
gyro->gyroADCRaw[X] = (int16_t)((data[1] << 8) | data[2]); case GYRO_EXTI_INT:
gyro->gyroADCRaw[Y] = (int16_t)((data[3] << 8) | data[4]); case GYRO_EXTI_NO_INT:
gyro->gyroADCRaw[Z] = (int16_t)((data[5] << 8) | data[6]); {
// Ensure any prior DMA has completed before continuing
spiWaitClaim(&gyro->dev);
gyro->dev.txBuf[0] = MPU_RA_GYRO_XOUT_H | 0x80;
busSegment_t segments[] = {
{NULL, NULL, 7, true, NULL},
{NULL, NULL, 0, true, NULL},
};
segments[0].txData = gyro->dev.txBuf;
segments[0].rxData = &gyro->dev.rxBuf[1];
spiSequence(&gyro->dev, &segments[0]);
// Wait for completion
spiWait(&gyro->dev);
gyro->gyroADCRaw[X] = __builtin_bswap16(gyroData[1]);
gyro->gyroADCRaw[Y] = __builtin_bswap16(gyroData[2]);
gyro->gyroADCRaw[Z] = __builtin_bswap16(gyroData[3]);
break;
}
case GYRO_EXTI_INT_DMA:
{
// If read was triggered in interrupt don't bother waiting. The worst that could happen is that we pick
// up an old value.
gyro->gyroADCRaw[X] = __builtin_bswap16(gyroData[5]);
gyro->gyroADCRaw[Y] = __builtin_bswap16(gyroData[6]);
gyro->gyroADCRaw[Z] = __builtin_bswap16(gyroData[7]);
break;
}
default:
break;
}
return true; return true;
} }

View File

@ -24,8 +24,6 @@
#include "drivers/exti.h" #include "drivers/exti.h"
#include "drivers/sensor.h" #include "drivers/sensor.h"
//#define DEBUG_MPU_DATA_READY_INTERRUPT
#if defined(USE_GYRO_SPI_MPU6500) || defined(USE_GYRO_SPI_MPU6000) || defined(USE_GYRO_SPI_MPU9250) || defined(USE_GYRO_SPI_ICM20649) \ #if defined(USE_GYRO_SPI_MPU6500) || defined(USE_GYRO_SPI_MPU6000) || defined(USE_GYRO_SPI_MPU9250) || defined(USE_GYRO_SPI_ICM20649) \
|| defined(USE_GYRO_SPI_ICM20689) || defined(USE_GYRO_SPI_ICM20689)
#define GYRO_USES_SPI #define GYRO_USES_SPI

View File

@ -41,8 +41,6 @@
#include "accgyro_mpu.h" #include "accgyro_mpu.h"
#include "accgyro_mpu6050.h" #include "accgyro_mpu6050.h"
//#define DEBUG_MPU_DATA_READY_INTERRUPT
// MPU6050, Standard address 0x68 // MPU6050, Standard address 0x68
// MPU_INT on PB13 on rev4 Naze32 hardware // MPU_INT on PB13 on rev4 Naze32 hardware
#define MPU6050_ADDRESS 0x68 #define MPU6050_ADDRESS 0x68

View File

@ -20,6 +20,8 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "platform.h" #include "platform.h"
@ -33,6 +35,7 @@
#include "drivers/io_impl.h" #include "drivers/io_impl.h"
#include "drivers/nvic.h" #include "drivers/nvic.h"
#include "drivers/sensor.h" #include "drivers/sensor.h"
#include "drivers/system.h"
#include "drivers/time.h" #include "drivers/time.h"
// 10 MHz max SPI frequency // 10 MHz max SPI frequency
@ -123,6 +126,9 @@ typedef enum {
BMI270_VAL_FIFO_WTM_1 = 0x00, // FIFO watermark MSB BMI270_VAL_FIFO_WTM_1 = 0x00, // FIFO watermark MSB
} bmi270ConfigValues_e; } bmi270ConfigValues_e;
// Need to see at least this many interrupts during initialisation to confirm EXTI connectivity
#define GYRO_EXTI_DETECT_THRESHOLD 1000
// BMI270 register reads are 16bits with the first byte a "dummy" value 0 // BMI270 register reads are 16bits with the first byte a "dummy" value 0
// that must be ignored. The result is in the second byte. // that must be ignored. The result is in the second byte.
static uint8_t bmi270RegisterRead(const extDevice_t *dev, bmi270Register_e registerId) static uint8_t bmi270RegisterRead(const extDevice_t *dev, bmi270Register_e registerId)
@ -246,11 +252,52 @@ static void bmi270Config(gyroDev_t *gyro)
extiCallbackRec_t bmi270IntCallbackRec; extiCallbackRec_t bmi270IntCallbackRec;
#if defined(USE_GYRO_EXTI) && defined(USE_MPU_DATA_READY_SIGNAL) /*
* Gyro interrupt service routine
*/
#ifdef USE_GYRO_EXTI
// Called in ISR context
// Gyro read has just completed
busStatus_e bmi270Intcallback(uint32_t arg)
{
volatile gyroDev_t *gyro = (gyroDev_t *)arg;
int32_t gyroDmaDuration = cmpTimeCycles(getCycleCounter(), gyro->gyroLastEXTI);
if (gyroDmaDuration > gyro->gyroDmaMaxDuration) {
gyro->gyroDmaMaxDuration = gyroDmaDuration;
}
gyro->dataReady = true;
return BUS_READY;
}
void bmi270ExtiHandler(extiCallbackRec_t *cb) void bmi270ExtiHandler(extiCallbackRec_t *cb)
{ {
// Non-blocking, so this needs to be static
static busSegment_t segments[] = {
{NULL, NULL, 14, true, bmi270Intcallback},
{NULL, NULL, 0, true, NULL},
};
gyroDev_t *gyro = container_of(cb, gyroDev_t, exti); gyroDev_t *gyro = container_of(cb, gyroDev_t, exti);
gyro->dataReady = true; // Ideally we'd use a time to capture such information, but unfortunately the port used for EXTI interrupt does
// not have an associated timer
uint32_t nowCycles = getCycleCounter();
gyro->gyroSyncEXTI = gyro->gyroLastEXTI + gyro->gyroDmaMaxDuration;
gyro->gyroLastEXTI = nowCycles;
if (gyro->gyroModeSPI == GYRO_EXTI_INT_DMA) {
segments[0].txData = gyro->dev.txBuf;
segments[0].rxData = gyro->dev.rxBuf;
if (!spiIsBusy(&gyro->dev)) {
spiSequence(&gyro->dev, &segments[0]);
}
}
gyro->detectedEXTI++;
} }
static void bmi270IntExtiInit(gyroDev_t *gyro) static void bmi270IntExtiInit(gyroDev_t *gyro)
@ -266,56 +313,135 @@ static void bmi270IntExtiInit(gyroDev_t *gyro)
EXTIConfig(mpuIntIO, &gyro->exti, NVIC_PRIO_MPU_INT_EXTI, IOCFG_IN_FLOATING, BETAFLIGHT_EXTI_TRIGGER_RISING); EXTIConfig(mpuIntIO, &gyro->exti, NVIC_PRIO_MPU_INT_EXTI, IOCFG_IN_FLOATING, BETAFLIGHT_EXTI_TRIGGER_RISING);
EXTIEnable(mpuIntIO, true); EXTIEnable(mpuIntIO, true);
} }
#else
void bmi270ExtiHandler(extiCallbackRec_t *cb)
{
gyroDev_t *gyro = container_of(cb, gyroDev_t, exti);
gyro->dataReady = true;
}
#endif #endif
static bool bmi270AccRead(accDev_t *acc) static bool bmi270AccRead(accDev_t *acc)
{ {
enum { switch (acc->gyro->gyroModeSPI) {
IDX_REG = 0, case GYRO_EXTI_INT:
IDX_SKIP, case GYRO_EXTI_NO_INT:
IDX_ACCEL_XOUT_L, {
IDX_ACCEL_XOUT_H, // Ensure any prior DMA has completed before continuing
IDX_ACCEL_YOUT_L, spiWaitClaim(&acc->gyro->dev);
IDX_ACCEL_YOUT_H,
IDX_ACCEL_ZOUT_L,
IDX_ACCEL_ZOUT_H,
BUFFER_SIZE,
};
STATIC_DMA_DATA_AUTO uint8_t bmi270_rx_buf[BUFFER_SIZE]; acc->gyro->dev.txBuf[0] = BMI270_REG_ACC_DATA_X_LSB | 0x80;
STATIC_DMA_DATA_AUTO uint8_t bmi270_tx_buf[BUFFER_SIZE] = {BMI270_REG_ACC_DATA_X_LSB | 0x80, 0, 0, 0, 0, 0, 0, 0};
spiReadWriteBuf(&acc->gyro->dev, (uint8_t *)bmi270_tx_buf, bmi270_rx_buf, BUFFER_SIZE); // receive response busSegment_t segments[] = {
{NULL, NULL, 8, true, NULL},
{NULL, NULL, 0, true, NULL},
};
segments[0].txData = acc->gyro->dev.txBuf;
segments[0].rxData = acc->gyro->dev.rxBuf;
acc->ADCRaw[X] = (int16_t)((bmi270_rx_buf[IDX_ACCEL_XOUT_H] << 8) | bmi270_rx_buf[IDX_ACCEL_XOUT_L]); spiSequence(&acc->gyro->dev, &segments[0]);
acc->ADCRaw[Y] = (int16_t)((bmi270_rx_buf[IDX_ACCEL_YOUT_H] << 8) | bmi270_rx_buf[IDX_ACCEL_YOUT_L]);
acc->ADCRaw[Z] = (int16_t)((bmi270_rx_buf[IDX_ACCEL_ZOUT_H] << 8) | bmi270_rx_buf[IDX_ACCEL_ZOUT_L]); // Wait for completion
spiWait(&acc->gyro->dev);
// Fall through
FALLTHROUGH;
}
case GYRO_EXTI_INT_DMA:
{
// If read was triggered in interrupt don't bother waiting. The worst that could happen is that we pick
// up an old value.
// This data was read from the gyro, which is the same SPI device as the acc
uint16_t *accData = (uint16_t *)acc->gyro->dev.rxBuf;
acc->ADCRaw[X] = accData[1];
acc->ADCRaw[Y] = accData[2];
acc->ADCRaw[Z] = accData[3];
break;
}
case GYRO_EXTI_INIT:
default:
break;
}
return true; return true;
} }
static bool bmi270GyroReadRegister(gyroDev_t *gyro) static bool bmi270GyroReadRegister(gyroDev_t *gyro)
{ {
enum { uint16_t *gyroData = (uint16_t *)gyro->dev.rxBuf;
IDX_REG = 0, switch (gyro->gyroModeSPI) {
IDX_SKIP, case GYRO_EXTI_INIT:
IDX_GYRO_XOUT_L, {
IDX_GYRO_XOUT_H, // Initialise the tx buffer to all 0x00
IDX_GYRO_YOUT_L, memset(gyro->dev.txBuf, 0x00, 14);
IDX_GYRO_YOUT_H, #ifdef USE_GYRO_EXTI
IDX_GYRO_ZOUT_L, // Check that minimum number of interrupts have been detected
IDX_GYRO_ZOUT_H,
BUFFER_SIZE,
};
STATIC_DMA_DATA_AUTO uint8_t bmi270_rx_buf[BUFFER_SIZE]; // We need some offset from the gyro interrupts to ensure sampling after the interrupt
STATIC_DMA_DATA_AUTO uint8_t bmi270_tx_buf[BUFFER_SIZE] = {BMI270_REG_GYR_DATA_X_LSB | 0x80, 0, 0, 0, 0, 0, 0, 0}; gyro->gyroDmaMaxDuration = 5;
// Using DMA for gyro access upsets the scheduler on the F4
if (gyro->detectedEXTI > GYRO_EXTI_DETECT_THRESHOLD) {
if (spiUseDMA(&gyro->dev)) {
// Indicate that the bus on which this device resides may initiate DMA transfers from interrupt context
spiSetAtomicWait(&gyro->dev);
gyro->gyroModeSPI = GYRO_EXTI_INT_DMA;
gyro->dev.callbackArg = (uint32_t)gyro;
gyro->dev.txBuf[0] = BMI270_REG_ACC_DATA_X_LSB | 0x80;
} else {
// Interrupts are present, but no DMA
gyro->gyroModeSPI = GYRO_EXTI_INT;
}
} else
#endif
{
gyro->gyroModeSPI = GYRO_EXTI_NO_INT;
}
break;
}
spiReadWriteBuf(&gyro->dev, (uint8_t *)bmi270_tx_buf, bmi270_rx_buf, BUFFER_SIZE); // receive response case GYRO_EXTI_INT:
case GYRO_EXTI_NO_INT:
{
// Ensure any prior DMA has completed before continuing
spiWaitClaim(&gyro->dev);
gyro->gyroADCRaw[X] = (int16_t)((bmi270_rx_buf[IDX_GYRO_XOUT_H] << 8) | bmi270_rx_buf[IDX_GYRO_XOUT_L]); gyro->dev.txBuf[0] = BMI270_REG_GYR_DATA_X_LSB | 0x80;
gyro->gyroADCRaw[Y] = (int16_t)((bmi270_rx_buf[IDX_GYRO_YOUT_H] << 8) | bmi270_rx_buf[IDX_GYRO_YOUT_L]);
gyro->gyroADCRaw[Z] = (int16_t)((bmi270_rx_buf[IDX_GYRO_ZOUT_H] << 8) | bmi270_rx_buf[IDX_GYRO_ZOUT_L]); busSegment_t segments[] = {
{NULL, NULL, 8, true, NULL},
{NULL, NULL, 0, true, NULL},
};
segments[0].txData = gyro->dev.txBuf;
segments[0].rxData = gyro->dev.rxBuf;
spiSequence(&gyro->dev, &segments[0]);
// Wait for completion
spiWait(&gyro->dev);
gyro->gyroADCRaw[X] = gyroData[1];
gyro->gyroADCRaw[Y] = gyroData[2];
gyro->gyroADCRaw[Z] = gyroData[3];
break;
}
case GYRO_EXTI_INT_DMA:
{
// If read was triggered in interrupt don't bother waiting. The worst that could happen is that we pick
// up an old value.
gyro->gyroADCRaw[X] = gyroData[4];
gyro->gyroADCRaw[Y] = gyroData[5];
gyro->gyroADCRaw[Z] = gyroData[6];
break;
}
default:
break;
}
return true; return true;
} }
@ -401,7 +527,7 @@ static void bmi270SpiGyroInit(gyroDev_t *gyro)
{ {
bmi270Config(gyro); bmi270Config(gyro);
#if defined(USE_GYRO_EXTI) && defined(USE_MPU_DATA_READY_SIGNAL) #if defined(USE_GYRO_EXTI)
bmi270IntExtiInit(gyro); bmi270IntExtiInit(gyro);
#endif #endif
} }

View File

@ -52,6 +52,8 @@ static void mpu6000AccAndGyroInit(gyroDev_t *gyro);
// 20 MHz max SPI frequency // 20 MHz max SPI frequency
#define MPU6000_MAX_SPI_CLK_HZ 20000000 #define MPU6000_MAX_SPI_CLK_HZ 20000000
#define MPU6000_SHORT_THRESHOLD 82 // Any interrupt interval less than this will be recognised as the short interval of ~79us
// Bits // Bits
#define BIT_SLEEP 0x40 #define BIT_SLEEP 0x40
#define BIT_H_RESET 0x80 #define BIT_H_RESET 0x80
@ -235,7 +237,9 @@ bool mpu6000SpiGyroDetect(gyroDev_t *gyro)
gyro->initFn = mpu6000SpiGyroInit; gyro->initFn = mpu6000SpiGyroInit;
gyro->readFn = mpuGyroReadSPI; gyro->readFn = mpuGyroReadSPI;
gyro->scale = GYRO_SCALE_2000DPS; gyro->scale = GYRO_SCALE_2000DPS;
#ifdef USE_GYRO_EXTI
gyro->gyroShortPeriod = clockMicrosToCycles(MPU6000_SHORT_THRESHOLD);
#endif
return true; return true;
} }

View File

@ -53,7 +53,7 @@ FAST_DATA_ZERO_INIT bool useDshotTelemetry = false;
FAST_DATA_ZERO_INIT loadDmaBufferFn *loadDmaBuffer; FAST_DATA_ZERO_INIT loadDmaBufferFn *loadDmaBuffer;
FAST_CODE uint8_t loadDmaBufferDshot(uint32_t *dmaBuffer, int stride, uint16_t packet) FAST_CODE_NOINLINE uint8_t loadDmaBufferDshot(uint32_t *dmaBuffer, int stride, uint16_t packet)
{ {
int i; int i;
for (i = 0; i < 16; i++) { for (i = 0; i < 16; i++) {
@ -66,7 +66,7 @@ FAST_CODE uint8_t loadDmaBufferDshot(uint32_t *dmaBuffer, int stride, uint16_t p
return DSHOT_DMA_BUFFER_SIZE; return DSHOT_DMA_BUFFER_SIZE;
} }
FAST_CODE uint8_t loadDmaBufferProshot(uint32_t *dmaBuffer, int stride, uint16_t packet) FAST_CODE_NOINLINE uint8_t loadDmaBufferProshot(uint32_t *dmaBuffer, int stride, uint16_t packet)
{ {
int i; int i;
for (i = 0; i < 4; i++) { for (i = 0; i < 4; i++) {

View File

@ -44,6 +44,8 @@
#include "light_ws2811strip.h" #include "light_ws2811strip.h"
#include "scheduler/scheduler.h"
#ifdef USE_LEDSTRIP_CACHE_MGMT #ifdef USE_LEDSTRIP_CACHE_MGMT
// WS2811_DMA_BUFFER_SIZE is multiples of uint32_t // WS2811_DMA_BUFFER_SIZE is multiples of uint32_t
// Number of bytes required for buffer // Number of bytes required for buffer
@ -179,6 +181,7 @@ void ws2811UpdateStrip(ledStripFormatRGB_e ledFormat)
{ {
// don't wait - risk of infinite block, just get an update next time round // don't wait - risk of infinite block, just get an update next time round
if (!ws2811Initialised || ws2811LedDataTransferInProgress) { if (!ws2811Initialised || ws2811LedDataTransferInProgress) {
ignoreTaskStateTime();
return; return;
} }

View File

@ -70,7 +70,7 @@
#define NVIC_PRIO_USB NVIC_BUILD_PRIORITY(2, 0) #define NVIC_PRIO_USB NVIC_BUILD_PRIORITY(2, 0)
#define NVIC_PRIO_USB_WUP NVIC_BUILD_PRIORITY(1, 0) #define NVIC_PRIO_USB_WUP NVIC_BUILD_PRIORITY(1, 0)
#define NVIC_PRIO_SONAR_ECHO NVIC_BUILD_PRIORITY(0x0f, 0x0f) #define NVIC_PRIO_SONAR_ECHO NVIC_BUILD_PRIORITY(0x0f, 0x0f)
#define NVIC_PRIO_MPU_DATA_READY NVIC_BUILD_PRIORITY(0x0f, 0x0f) #define NVIC_PRIO_MPU_DATA_READY NVIC_BUILD_PRIORITY(0, 1)
#define NVIC_PRIO_MAG_DATA_READY NVIC_BUILD_PRIORITY(0x0f, 0x0f) #define NVIC_PRIO_MAG_DATA_READY NVIC_BUILD_PRIORITY(0x0f, 0x0f)
#define NVIC_PRIO_CALLBACK NVIC_BUILD_PRIORITY(0x0f, 0x0f) #define NVIC_PRIO_CALLBACK NVIC_BUILD_PRIORITY(0x0f, 0x0f)
#define NVIC_PRIO_MAX7456_DMA NVIC_BUILD_PRIORITY(3, 0) #define NVIC_PRIO_MAX7456_DMA NVIC_BUILD_PRIORITY(3, 0)

View File

@ -68,7 +68,7 @@ void cycleCounterInit(void)
#if defined(DWT_LAR_UNLOCK_VALUE) #if defined(DWT_LAR_UNLOCK_VALUE)
#if defined(STM32F7) || defined(STM32H7) #if defined(STM32F7) || defined(STM32H7)
DWT->LAR = DWT_LAR_UNLOCK_VALUE; ITM->LAR = DWT_LAR_UNLOCK_VALUE;
#elif defined(STM32F3) || defined(STM32F4) #elif defined(STM32F3) || defined(STM32F4)
// Note: DWT_Type does not contain LAR member. // Note: DWT_Type does not contain LAR member.
#define DWT_LAR #define DWT_LAR
@ -146,7 +146,7 @@ uint32_t micros(void)
return (ms * 1000) + (usTicks * 1000 - cycle_cnt) / usTicks; return (ms * 1000) + (usTicks * 1000 - cycle_cnt) / usTicks;
} }
inline uint32_t getCycleCounter(void) uint32_t getCycleCounter(void)
{ {
return DWT->CYCCNT; return DWT->CYCCNT;
} }
@ -156,6 +156,17 @@ uint32_t clockCyclesToMicros(uint32_t clockCycles)
return clockCycles / usTicks; return clockCycles / usTicks;
} }
// Note that this conversion is signed as this is used for periods rather than absolute timestamps
int32_t clockCyclesTo10thMicros(int32_t clockCycles)
{
return 10 * clockCycles / (int32_t)usTicks;
}
uint32_t clockMicrosToCycles(uint32_t micros)
{
return micros * usTicks;
}
// Return system uptime in milliseconds (rollover in 49 days) // Return system uptime in milliseconds (rollover in 49 days)
uint32_t millis(void) uint32_t millis(void)
{ {

View File

@ -66,6 +66,8 @@ void systemResetToBootloader(bootloaderRequestType_e requestType);
bool isMPUSoftReset(void); bool isMPUSoftReset(void);
void cycleCounterInit(void); void cycleCounterInit(void);
uint32_t clockCyclesToMicros(uint32_t clockCycles); uint32_t clockCyclesToMicros(uint32_t clockCycles);
int32_t clockCyclesTo10thMicros(int32_t clockCycles);
uint32_t clockMicrosToCycles(uint32_t micros);
uint32_t getCycleCounter(void); uint32_t getCycleCounter(void);
#if defined(STM32H7) || defined(STM32G4) #if defined(STM32H7) || defined(STM32G4)
void systemCheckResetReason(void); void systemCheckResetReason(void);

View File

@ -81,6 +81,8 @@
#include "rx/rx.h" #include "rx/rx.h"
#include "scheduler/scheduler.h"
#include "sensors/acceleration.h" #include "sensors/acceleration.h"
#include "sensors/adcinternal.h" #include "sensors/adcinternal.h"
#include "sensors/barometer.h" #include "sensors/barometer.h"
@ -91,8 +93,6 @@
#include "sensors/sensors.h" #include "sensors/sensors.h"
#include "sensors/rangefinder.h" #include "sensors/rangefinder.h"
#include "scheduler/scheduler.h"
#include "telemetry/telemetry.h" #include "telemetry/telemetry.h"
#include "telemetry/crsf.h" #include "telemetry/crsf.h"
@ -170,9 +170,9 @@ bool taskUpdateRxMainInProgress()
static void taskUpdateRxMain(timeUs_t currentTimeUs) static void taskUpdateRxMain(timeUs_t currentTimeUs)
{ {
// Where we are using a state machine call ignoreTaskTime() for all states bar one // Where we are using a state machine call ignoreTaskStateTime() for all states bar one
if (rxState != MODES) { if (rxState != MODES) {
ignoreTaskTime(); ignoreTaskStateTime();
} }
switch (rxState) { switch (rxState) {
@ -418,7 +418,6 @@ void tasksInit(void)
#endif #endif
} }
#if defined(USE_TASK_STATISTICS)
#define DEFINE_TASK(taskNameParam, subTaskNameParam, checkFuncParam, taskFuncParam, desiredPeriodParam, staticPriorityParam) { \ #define DEFINE_TASK(taskNameParam, subTaskNameParam, checkFuncParam, taskFuncParam, desiredPeriodParam, staticPriorityParam) { \
.taskName = taskNameParam, \ .taskName = taskNameParam, \
.subTaskName = subTaskNameParam, \ .subTaskName = subTaskNameParam, \
@ -427,15 +426,6 @@ void tasksInit(void)
.desiredPeriodUs = desiredPeriodParam, \ .desiredPeriodUs = desiredPeriodParam, \
.staticPriority = staticPriorityParam \ .staticPriority = staticPriorityParam \
} }
#else
#define DEFINE_TASK(taskNameParam, subTaskNameParam, checkFuncParam, taskFuncParam, desiredPeriodParam, staticPriorityParam) { \
.checkFunc = checkFuncParam, \
.taskFunc = taskFuncParam, \
.desiredPeriodUs = desiredPeriodParam, \
.staticPriority = staticPriorityParam \
}
#endif
task_t tasks[TASK_COUNT] = { task_t tasks[TASK_COUNT] = {
[TASK_SYSTEM] = DEFINE_TASK("SYSTEM", "LOAD", NULL, taskSystemLoad, TASK_PERIOD_HZ(10), TASK_PRIORITY_MEDIUM_HIGH), [TASK_SYSTEM] = DEFINE_TASK("SYSTEM", "LOAD", NULL, taskSystemLoad, TASK_PERIOD_HZ(10), TASK_PRIORITY_MEDIUM_HIGH),

View File

@ -45,6 +45,8 @@
#include "io/gps.h" #include "io/gps.h"
#include "scheduler/scheduler.h"
#include "sensors/acceleration.h" #include "sensors/acceleration.h"
#include "sensors/barometer.h" #include "sensors/barometer.h"
#include "sensors/compass.h" #include "sensors/compass.h"
@ -591,6 +593,7 @@ void imuUpdateAttitude(timeUs_t currentTimeUs)
acc.accADC[X] = 0; acc.accADC[X] = 0;
acc.accADC[Y] = 0; acc.accADC[Y] = 0;
acc.accADC[Z] = 0; acc.accADC[Z] = 0;
ignoreTaskStateTime();
} }
} }
#endif // USE_ACC #endif // USE_ACC

View File

@ -543,7 +543,6 @@ static void showSensorsPage(void)
} }
#if defined(USE_TASK_STATISTICS)
static void showTasksPage(void) static void showTasksPage(void)
{ {
uint8_t rowIndex = PAGE_TITLE_LINE_COUNT; uint8_t rowIndex = PAGE_TITLE_LINE_COUNT;
@ -568,7 +567,6 @@ static void showTasksPage(void)
} }
} }
} }
#endif
#ifdef USE_BLACKBOX #ifdef USE_BLACKBOX
static void showBBPage(void) static void showBBPage(void)
@ -624,9 +622,7 @@ static const pageEntry_t pages[PAGE_COUNT] = {
{ PAGE_RX, "RX", showRxPage, PAGE_FLAGS_NONE }, { PAGE_RX, "RX", showRxPage, PAGE_FLAGS_NONE },
{ PAGE_BATTERY, "BATTERY", showBatteryPage, PAGE_FLAGS_NONE }, { PAGE_BATTERY, "BATTERY", showBatteryPage, PAGE_FLAGS_NONE },
{ PAGE_SENSORS, "SENSORS", showSensorsPage, PAGE_FLAGS_NONE }, { PAGE_SENSORS, "SENSORS", showSensorsPage, PAGE_FLAGS_NONE },
#if defined(USE_TASK_STATISTICS)
{ PAGE_TASKS, "TASKS", showTasksPage, PAGE_FLAGS_NONE }, { PAGE_TASKS, "TASKS", showTasksPage, PAGE_FLAGS_NONE },
#endif
#ifdef USE_BLACKBOX #ifdef USE_BLACKBOX
{ PAGE_BB, "BLACK BOX", showBBPage, PAGE_FLAGS_NONE }, { PAGE_BB, "BLACK BOX", showBBPage, PAGE_FLAGS_NONE },
#endif #endif

View File

@ -44,9 +44,7 @@ typedef enum {
PAGE_RX, PAGE_RX,
PAGE_PROFILE, PAGE_PROFILE,
PAGE_RPROF, PAGE_RPROF,
#if defined(USE_TASK_STATISTICS)
PAGE_TASKS, PAGE_TASKS,
#endif
#ifdef USE_GPS #ifdef USE_GPS
PAGE_GPS, PAGE_GPS,
#endif #endif

View File

@ -755,14 +755,14 @@ void gpsUpdate(timeUs_t currentTimeUs)
updateGPSRescueState(); updateGPSRescueState();
} }
#endif #endif
// Call ignoreTaskTime() unless this took appreciable time // Call ignoreTaskShortExecTime() unless this took appreciable time
// Note that this will mess up the rate/Hz display under tasks, but the code // Note that this will mess up the rate/Hz display under tasks, but the code
// takes widely varying time to complete // takes widely varying time to complete
endTimeUs = micros(); endTimeUs = micros();
if ((endTimeUs - currentTimeUs) > maxTimeUs) { if ((endTimeUs - currentTimeUs) > maxTimeUs) {
maxTimeUs = endTimeUs - currentTimeUs; maxTimeUs = endTimeUs - currentTimeUs;
} else { } else {
ignoreTaskTime(); ignoreTaskShortExecTime();
// Decay max time // Decay max time
maxTimeUs--; maxTimeUs--;
} }

View File

@ -1066,8 +1066,8 @@ static void applyStatusProfile(timeUs_t now) {
} }
if (!timActive) { if (!timActive) {
// Call ignoreTaskTime() unless data is being processed // Call ignoreTaskShortExecTime() unless data is being processed
ignoreTaskTime(); ignoreTaskShortExecTime();
return; // no change this update, keep old state return; // no change this update, keep old state
} }
@ -1253,8 +1253,8 @@ void ledStripUpdate(timeUs_t currentTimeUs)
#endif #endif
if (!isWS2811LedStripReady()) { if (!isWS2811LedStripReady()) {
// Call ignoreTaskTime() unless data is being processed // Call ignoreTaskShortExecTime() unless data is being processed
ignoreTaskTime(); ignoreTaskShortExecTime();
return; return;
} }
@ -1282,8 +1282,8 @@ void ledStripUpdate(timeUs_t currentTimeUs)
break; break;
} }
} else { } else {
// Call ignoreTaskTime() unless data is being processed // Call ignoreTaskShortExecTime() unless data is being processed
ignoreTaskTime(); ignoreTaskShortExecTime();
} }
} }

View File

@ -87,6 +87,8 @@
#include "rx/crsf.h" #include "rx/crsf.h"
#include "rx/rx.h" #include "rx/rx.h"
#include "scheduler/scheduler.h"
#include "sensors/acceleration.h" #include "sensors/acceleration.h"
#include "sensors/battery.h" #include "sensors/battery.h"
#include "sensors/esc_sensor.h" #include "sensors/esc_sensor.h"
@ -1049,6 +1051,8 @@ void osdUpdate(timeUs_t currentTimeUs)
// Frsky osd need a display redraw after search for MAX7456 devices // Frsky osd need a display redraw after search for MAX7456 devices
if (osdDisplayPortDeviceType == OSD_DISPLAYPORT_DEVICE_FRSKYOSD) { if (osdDisplayPortDeviceType == OSD_DISPLAYPORT_DEVICE_FRSKYOSD) {
displayRedraw(osdDisplayPort); displayRedraw(osdDisplayPort);
} else {
ignoreTaskShortExecTime();
} }
return; return;
} }
@ -1062,6 +1066,7 @@ void osdUpdate(timeUs_t currentTimeUs)
// don't touch buffers if DMA transaction is in progress // don't touch buffers if DMA transaction is in progress
if (displayIsTransferInProgress(osdDisplayPort)) { if (displayIsTransferInProgress(osdDisplayPort)) {
ignoreTaskShortExecTime();
return; return;
} }
@ -1069,6 +1074,7 @@ void osdUpdate(timeUs_t currentTimeUs)
static uint32_t idlecounter = 0; static uint32_t idlecounter = 0;
if (!ARMING_FLAG(ARMED)) { if (!ARMING_FLAG(ARMED)) {
if (idlecounter++ % 4 != 0) { if (idlecounter++ % 4 != 0) {
ignoreTaskShortExecTime();
return; return;
} }
} }
@ -1091,6 +1097,7 @@ void osdUpdate(timeUs_t currentTimeUs)
if (doDrawScreen) { if (doDrawScreen) {
displayDrawScreen(osdDisplayPort); displayDrawScreen(osdDisplayPort);
} }
ignoreTaskShortExecTime();
} }
++counter; ++counter;
} }

View File

@ -26,6 +26,8 @@
#include "platform.h" #include "platform.h"
#include "drivers/accgyro/accgyro.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "build/debug.h" #include "build/debug.h"
@ -34,14 +36,15 @@
#include "common/utils.h" #include "common/utils.h"
#include "drivers/time.h" #include "drivers/time.h"
#include "drivers/accgyro/accgyro.h"
#include "drivers/system.h"
#include "fc/core.h" #include "fc/core.h"
#include "fc/tasks.h" #include "fc/tasks.h"
#include "scheduler.h" #include "scheduler.h"
#define TASK_AVERAGE_EXECUTE_FALLBACK_US 30 // Default task average time if USE_TASK_STATISTICS is not defined #include "sensors/gyro_init.h"
#define TASK_AVERAGE_EXECUTE_PADDING_US 5 // Add a little padding to the average execution time
// DEBUG_SCHEDULER, timings for: // DEBUG_SCHEDULER, timings for:
// 0 - gyroUpdate() // 0 - gyroUpdate()
@ -49,21 +52,48 @@
// 2 - time spent in scheduler // 2 - time spent in scheduler
// 3 - time spent executing check function // 3 - time spent executing check function
// DEBUG_SCHEDULER_DETERMINISM, requires USE_LATE_TASK_STATISTICS to be defined
// 0 - Gyro task start cycle time in 10th of a us
// 1 - ID of late task
// 2 - Amount task is late in 10th of a us
// 3 - Gyro lock skew in clock cycles
extern task_t tasks[];
static FAST_DATA_ZERO_INIT task_t *currentTask = NULL; static FAST_DATA_ZERO_INIT task_t *currentTask = NULL;
static FAST_DATA_ZERO_INIT bool ignoreCurrentTaskTime; static FAST_DATA_ZERO_INIT bool ignoreCurrentTaskExecRate;
static FAST_DATA_ZERO_INIT bool ignoreCurrentTaskExecTime;
// More than this many cycles overdue will be considered late
#define DEBUG_SCHEDULER_DETERMINISM_THRESHOLD 10
int32_t schedLoopStartCycles;
static int32_t schedLoopStartMinCycles;
static int32_t schedLoopStartMaxCycles;
static uint32_t schedLoopStartDeltaDownCycles;
static uint32_t schedLoopStartDeltaUpCycles;
int32_t taskGuardCycles;
static int32_t taskGuardMinCycles;
static int32_t taskGuardMaxCycles;
static uint32_t taskGuardDeltaDownCycles;
static uint32_t taskGuardDeltaUpCycles;
static FAST_DATA_ZERO_INIT uint32_t totalWaitingTasks; static FAST_DATA_ZERO_INIT uint32_t totalWaitingTasks;
static FAST_DATA_ZERO_INIT uint32_t totalWaitingTasksSamples; static FAST_DATA_ZERO_INIT uint32_t totalWaitingTasksSamples;
static FAST_DATA_ZERO_INIT bool calculateTaskStatistics;
FAST_DATA_ZERO_INIT uint16_t averageSystemLoadPercent = 0; FAST_DATA_ZERO_INIT uint16_t averageSystemLoadPercent = 0;
static FAST_DATA_ZERO_INIT int taskQueuePos = 0; static FAST_DATA_ZERO_INIT int taskQueuePos = 0;
STATIC_UNIT_TESTED FAST_DATA_ZERO_INIT int taskQueueSize = 0; STATIC_UNIT_TESTED FAST_DATA_ZERO_INIT int taskQueueSize = 0;
static FAST_DATA int periodCalculationBasisOffset = offsetof(task_t, lastExecutedAtUs); static bool optimizeSchedRate;
static FAST_DATA_ZERO_INIT bool gyroEnabled; static FAST_DATA_ZERO_INIT bool gyroEnabled;
static int32_t desiredPeriodCycles;
static uint32_t lastTargetCycles;
// No need for a linked list for the queue, since items are only inserted at startup // No need for a linked list for the queue, since items are only inserted at startup
STATIC_UNIT_TESTED FAST_DATA_ZERO_INIT task_t* taskQueueArray[TASK_COUNT + 1]; // extra item for NULL pointer at end of queue STATIC_UNIT_TESTED FAST_DATA_ZERO_INIT task_t* taskQueueArray[TASK_COUNT + 1]; // extra item for NULL pointer at end of queue
@ -145,7 +175,6 @@ void taskSystemLoad(timeUs_t currentTimeUs)
#endif #endif
} }
#if defined(USE_TASK_STATISTICS)
timeUs_t checkFuncMaxExecutionTimeUs; timeUs_t checkFuncMaxExecutionTimeUs;
timeUs_t checkFuncTotalExecutionTimeUs; timeUs_t checkFuncTotalExecutionTimeUs;
timeUs_t checkFuncMovingSumExecutionTimeUs; timeUs_t checkFuncMovingSumExecutionTimeUs;
@ -158,14 +187,12 @@ void getCheckFuncInfo(cfCheckFuncInfo_t *checkFuncInfo)
checkFuncInfo->averageExecutionTimeUs = checkFuncMovingSumExecutionTimeUs / TASK_STATS_MOVING_SUM_COUNT; checkFuncInfo->averageExecutionTimeUs = checkFuncMovingSumExecutionTimeUs / TASK_STATS_MOVING_SUM_COUNT;
checkFuncInfo->averageDeltaTimeUs = checkFuncMovingSumDeltaTimeUs / TASK_STATS_MOVING_SUM_COUNT; checkFuncInfo->averageDeltaTimeUs = checkFuncMovingSumDeltaTimeUs / TASK_STATS_MOVING_SUM_COUNT;
} }
#endif
void getTaskInfo(taskId_e taskId, taskInfo_t * taskInfo) void getTaskInfo(taskId_e taskId, taskInfo_t * taskInfo)
{ {
taskInfo->isEnabled = queueContains(getTask(taskId)); taskInfo->isEnabled = queueContains(getTask(taskId));
taskInfo->desiredPeriodUs = getTask(taskId)->desiredPeriodUs; taskInfo->desiredPeriodUs = getTask(taskId)->desiredPeriodUs;
taskInfo->staticPriority = getTask(taskId)->staticPriority; taskInfo->staticPriority = getTask(taskId)->staticPriority;
#if defined(USE_TASK_STATISTICS)
taskInfo->taskName = getTask(taskId)->taskName; taskInfo->taskName = getTask(taskId)->taskName;
taskInfo->subTaskName = getTask(taskId)->subTaskName; taskInfo->subTaskName = getTask(taskId)->subTaskName;
taskInfo->maxExecutionTimeUs = getTask(taskId)->maxExecutionTimeUs; taskInfo->maxExecutionTimeUs = getTask(taskId)->maxExecutionTimeUs;
@ -179,7 +206,6 @@ void getTaskInfo(taskId_e taskId, taskInfo_t * taskInfo)
taskInfo->runCount = getTask(taskId)->runCount; taskInfo->runCount = getTask(taskId)->runCount;
taskInfo->execTime = getTask(taskId)->execTime; taskInfo->execTime = getTask(taskId)->execTime;
#endif #endif
#endif
} }
void rescheduleTask(taskId_e taskId, timeDelta_t newPeriodUs) void rescheduleTask(taskId_e taskId, timeDelta_t newPeriodUs)
@ -191,6 +217,11 @@ void rescheduleTask(taskId_e taskId, timeDelta_t newPeriodUs)
task_t *task = getTask(taskId); task_t *task = getTask(taskId);
task->desiredPeriodUs = MAX(SCHEDULER_DELAY_LIMIT, newPeriodUs); // Limit delay to 100us (10 kHz) to prevent scheduler clogging task->desiredPeriodUs = MAX(SCHEDULER_DELAY_LIMIT, newPeriodUs); // Limit delay to 100us (10 kHz) to prevent scheduler clogging
} }
// Catch the case where the gyro loop is adjusted
if (taskId == TASK_GYRO) {
desiredPeriodCycles = (int32_t)clockMicrosToCycles((uint32_t)getTask(TASK_GYRO)->desiredPeriodUs);
}
} }
void setTaskEnabled(taskId_e taskId, bool enabled) void setTaskEnabled(taskId_e taskId, bool enabled)
@ -216,20 +247,22 @@ timeDelta_t getTaskDeltaTimeUs(taskId_e taskId)
} }
} }
// Called by tasks executing what are known to be short states // Called by tasks executing what are known to be short states. Tasks should call this routine in all states
void ignoreTaskTime() // except the one which takes the longest to execute.
void ignoreTaskStateTime()
{ {
ignoreCurrentTaskTime = true; ignoreCurrentTaskExecRate = true;
ignoreCurrentTaskExecTime = true;
} }
void schedulerSetCalulateTaskStatistics(bool calculateTaskStatisticsToUse) // Called by tasks without state machines executing in what is known to be a shorter time than peak
void ignoreTaskShortExecTime()
{ {
calculateTaskStatistics = calculateTaskStatisticsToUse; ignoreCurrentTaskExecTime = true;
} }
void schedulerResetTaskStatistics(taskId_e taskId) void schedulerResetTaskStatistics(taskId_e taskId)
{ {
#if defined(USE_TASK_STATISTICS)
if (taskId == TASK_SELF) { if (taskId == TASK_SELF) {
currentTask->movingSumExecutionTimeUs = 0; currentTask->movingSumExecutionTimeUs = 0;
currentTask->movingSumDeltaTimeUs = 0; currentTask->movingSumDeltaTimeUs = 0;
@ -241,14 +274,10 @@ void schedulerResetTaskStatistics(taskId_e taskId)
getTask(taskId)->totalExecutionTimeUs = 0; getTask(taskId)->totalExecutionTimeUs = 0;
getTask(taskId)->maxExecutionTimeUs = 0; getTask(taskId)->maxExecutionTimeUs = 0;
} }
#else
UNUSED(taskId);
#endif
} }
void schedulerResetTaskMaxExecutionTime(taskId_e taskId) void schedulerResetTaskMaxExecutionTime(taskId_e taskId)
{ {
#if defined(USE_TASK_STATISTICS)
if (taskId == TASK_SELF) { if (taskId == TASK_SELF) {
currentTask->maxExecutionTimeUs = 0; currentTask->maxExecutionTimeUs = 0;
} else if (taskId < TASK_COUNT) { } else if (taskId < TASK_COUNT) {
@ -259,37 +288,46 @@ void schedulerResetTaskMaxExecutionTime(taskId_e taskId)
task->runCount = 0; task->runCount = 0;
#endif #endif
} }
#else
UNUSED(taskId);
#endif
} }
#if defined(USE_TASK_STATISTICS)
void schedulerResetCheckFunctionMaxExecutionTime(void) void schedulerResetCheckFunctionMaxExecutionTime(void)
{ {
checkFuncMaxExecutionTimeUs = 0; checkFuncMaxExecutionTimeUs = 0;
} }
#endif
void schedulerInit(void) void schedulerInit(void)
{ {
calculateTaskStatistics = true;
queueClear(); queueClear();
queueAdd(getTask(TASK_SYSTEM)); queueAdd(getTask(TASK_SYSTEM));
schedLoopStartMinCycles = clockMicrosToCycles(SCHED_START_LOOP_MIN_US);
schedLoopStartMaxCycles = clockMicrosToCycles(SCHED_START_LOOP_MAX_US);
schedLoopStartCycles = schedLoopStartMinCycles;
schedLoopStartDeltaDownCycles = clockMicrosToCycles(1) / SCHED_START_LOOP_DOWN_STEP;
schedLoopStartDeltaUpCycles = clockMicrosToCycles(1) / SCHED_START_LOOP_UP_STEP;
taskGuardMinCycles = clockMicrosToCycles(TASK_GUARD_MARGIN_MIN_US);
taskGuardMaxCycles = clockMicrosToCycles(TASK_GUARD_MARGIN_MAX_US);
taskGuardCycles = taskGuardMinCycles;
taskGuardDeltaDownCycles = clockMicrosToCycles(1) / TASK_GUARD_MARGIN_DOWN_STEP;
taskGuardDeltaUpCycles = clockMicrosToCycles(1) / TASK_GUARD_MARGIN_UP_STEP;
desiredPeriodCycles = (int32_t)clockMicrosToCycles((uint32_t)getTask(TASK_GYRO)->desiredPeriodUs);
lastTargetCycles = getCycleCounter();
} }
void schedulerOptimizeRate(bool optimizeRate) void schedulerOptimizeRate(bool optimizeRate)
{ {
periodCalculationBasisOffset = optimizeRate ? offsetof(task_t, lastDesiredAt) : offsetof(task_t, lastExecutedAtUs); optimizeSchedRate = optimizeRate;
} }
inline static timeUs_t getPeriodCalculationBasis(const task_t* task) inline static timeUs_t getPeriodCalculationBasis(const task_t* task)
{ {
if (task->staticPriority == TASK_PRIORITY_REALTIME) { if ((task->staticPriority == TASK_PRIORITY_REALTIME) && (optimizeSchedRate)) {
return *(timeUs_t*)((uint8_t*)task + periodCalculationBasisOffset); return task->lastDesiredAt;
} else {
return task->lastExecutedAtUs;
} }
return task->lastExecutedAtUs;
} }
FAST_CODE timeUs_t schedulerExecuteTask(task_t *selectedTask, timeUs_t currentTimeUs) FAST_CODE timeUs_t schedulerExecuteTask(task_t *selectedTask, timeUs_t currentTimeUs)
@ -298,38 +336,33 @@ FAST_CODE timeUs_t schedulerExecuteTask(task_t *selectedTask, timeUs_t currentTi
if (selectedTask) { if (selectedTask) {
currentTask = selectedTask; currentTask = selectedTask;
ignoreCurrentTaskTime = false; ignoreCurrentTaskExecRate = false;
#if defined(USE_TASK_STATISTICS) ignoreCurrentTaskExecTime = false;
float period = currentTimeUs - selectedTask->lastExecutedAtUs; float period = currentTimeUs - selectedTask->lastExecutedAtUs;
#endif
selectedTask->lastExecutedAtUs = currentTimeUs; selectedTask->lastExecutedAtUs = currentTimeUs;
selectedTask->lastDesiredAt += (cmpTimeUs(currentTimeUs, selectedTask->lastDesiredAt) / selectedTask->desiredPeriodUs) * selectedTask->desiredPeriodUs; selectedTask->lastDesiredAt += (cmpTimeUs(currentTimeUs, selectedTask->lastDesiredAt) / selectedTask->desiredPeriodUs) * selectedTask->desiredPeriodUs;
selectedTask->dynamicPriority = 0; selectedTask->dynamicPriority = 0;
// Execute task // Execute task
#if defined(USE_TASK_STATISTICS) const timeUs_t currentTimeBeforeTaskCallUs = micros();
if (calculateTaskStatistics) { selectedTask->taskFunc(currentTimeBeforeTaskCallUs);
const timeUs_t currentTimeBeforeTaskCallUs = micros(); taskExecutionTimeUs = micros() - currentTimeBeforeTaskCallUs;
selectedTask->taskFunc(currentTimeBeforeTaskCallUs); if (!ignoreCurrentTaskExecRate) {
taskExecutionTimeUs = micros() - currentTimeBeforeTaskCallUs; // Record task execution rate and max execution time
if (!ignoreCurrentTaskTime) { selectedTask->taskLatestDeltaTimeUs = cmpTimeUs(currentTimeUs, selectedTask->lastStatsAtUs);
selectedTask->taskLatestDeltaTimeUs = cmpTimeUs(currentTimeUs, selectedTask->lastStatsAtUs); selectedTask->lastStatsAtUs = currentTimeUs;
selectedTask->lastStatsAtUs = currentTimeUs; selectedTask->maxExecutionTimeUs = MAX(selectedTask->maxExecutionTimeUs, taskExecutionTimeUs);
selectedTask->movingSumExecutionTimeUs += taskExecutionTimeUs - selectedTask->movingSumExecutionTimeUs / TASK_STATS_MOVING_SUM_COUNT; }
selectedTask->movingSumDeltaTimeUs += selectedTask->taskLatestDeltaTimeUs - selectedTask->movingSumDeltaTimeUs / TASK_STATS_MOVING_SUM_COUNT; if (!ignoreCurrentTaskExecTime) {
selectedTask->maxExecutionTimeUs = MAX(selectedTask->maxExecutionTimeUs, taskExecutionTimeUs); // Update estimate of expected task duration
selectedTask->movingSumExecutionTimeUs += taskExecutionTimeUs - selectedTask->movingSumExecutionTimeUs / TASK_STATS_MOVING_SUM_COUNT;
selectedTask->movingSumDeltaTimeUs += selectedTask->taskLatestDeltaTimeUs - selectedTask->movingSumDeltaTimeUs / TASK_STATS_MOVING_SUM_COUNT;
} }
selectedTask->totalExecutionTimeUs += taskExecutionTimeUs; // time consumed by scheduler + task selectedTask->totalExecutionTimeUs += taskExecutionTimeUs; // time consumed by scheduler + task
selectedTask->movingAverageCycleTimeUs += 0.05f * (period - selectedTask->movingAverageCycleTimeUs); selectedTask->movingAverageCycleTimeUs += 0.05f * (period - selectedTask->movingAverageCycleTimeUs);
#if defined(USE_LATE_TASK_STATISTICS) #if defined(USE_LATE_TASK_STATISTICS)
selectedTask->runCount++; selectedTask->runCount++;
#endif #endif
} else
#endif
{
selectedTask->taskLatestDeltaTimeUs = cmpTimeUs(currentTimeUs, selectedTask->lastExecutedAtUs);
selectedTask->taskFunc(currentTimeUs);
}
} }
return taskExecutionTimeUs; return taskExecutionTimeUs;
@ -350,79 +383,147 @@ static void readSchedulerLocals(task_t *selectedTask, uint8_t selectedTaskDynami
FAST_CODE void scheduler(void) FAST_CODE void scheduler(void)
{ {
#if defined(USE_LATE_TASK_STATISTICS)
static task_t *lastTask;
#endif
// Cache currentTime // Cache currentTime
const timeUs_t schedulerStartTimeUs = micros(); const timeUs_t schedulerStartTimeUs = micros();
timeUs_t currentTimeUs = schedulerStartTimeUs; timeUs_t currentTimeUs;
uint32_t nowCycles;
timeUs_t taskExecutionTimeUs = 0; timeUs_t taskExecutionTimeUs = 0;
task_t *selectedTask = NULL; task_t *selectedTask = NULL;
uint16_t selectedTaskDynamicPriority = 0; uint16_t selectedTaskDynamicPriority = 0;
uint16_t waitingTasks = 0; uint16_t waitingTasks = 0;
bool realtimeTaskRan = false; uint32_t nextTargetCycles = 0;
timeDelta_t gyroTaskDelayUs = 0; int32_t schedLoopRemainingCycles;
if (gyroEnabled) { if (gyroEnabled) {
// Realtime gyro/filtering/PID tasks get complete priority // Realtime gyro/filtering/PID tasks get complete priority
task_t *gyroTask = getTask(TASK_GYRO); task_t *gyroTask = getTask(TASK_GYRO);
const timeUs_t gyroExecuteTimeUs = getPeriodCalculationBasis(gyroTask) + gyroTask->desiredPeriodUs; nowCycles = getCycleCounter();
gyroTaskDelayUs = cmpTimeUs(gyroExecuteTimeUs, currentTimeUs); // time until the next expected gyro sample #if defined(UNIT_TEST)
if (gyroTaskDelayUs <= 0) { lastTargetCycles = clockMicrosToCycles(gyroTask->lastExecutedAtUs);
taskExecutionTimeUs = schedulerExecuteTask(gyroTask, currentTimeUs); #endif
nextTargetCycles = lastTargetCycles + desiredPeriodCycles;
schedLoopRemainingCycles = cmpTimeCycles(nextTargetCycles, nowCycles);
if (schedLoopRemainingCycles < -desiredPeriodCycles) {
/* A task has so grossly overrun that at entire gyro cycle has been skipped
* This is most likely to occur when connected to the configurator via USB as the serial
* task is non-deterministic
* Recover as best we can, advancing scheduling by a whole number of cycles
*/
nextTargetCycles += desiredPeriodCycles * (1 + (schedLoopRemainingCycles / -desiredPeriodCycles));
schedLoopRemainingCycles = cmpTimeCycles(nextTargetCycles, nowCycles);
}
// Tune out the time lost between completing the last task execution and re-entering the scheduler
if ((schedLoopRemainingCycles < schedLoopStartMinCycles) &&
(schedLoopStartCycles < schedLoopStartMaxCycles)) {
schedLoopStartCycles += schedLoopStartDeltaUpCycles;
}
// Once close to the timing boundary, poll for it's arrival
if (schedLoopRemainingCycles < schedLoopStartCycles) {
// Record the interval between scheduling the gyro task
static int32_t lastRealtimeStartCycles = 0;
if (schedLoopStartCycles > schedLoopStartMinCycles) {
schedLoopStartCycles -= schedLoopStartDeltaDownCycles;
}
#if !defined(UNIT_TEST)
while (schedLoopRemainingCycles > 0) {
nowCycles = getCycleCounter();
schedLoopRemainingCycles = cmpTimeCycles(nextTargetCycles, nowCycles);
}
#endif
DEBUG_SET(DEBUG_SCHEDULER_DETERMINISM, 0, clockCyclesTo10thMicros(cmpTimeCycles(nowCycles, lastRealtimeStartCycles)));
lastRealtimeStartCycles = nowCycles;
currentTimeUs = clockCyclesToMicros(nowCycles);
taskExecutionTimeUs += schedulerExecuteTask(gyroTask, currentTimeUs);
if (gyroFilterReady()) { if (gyroFilterReady()) {
taskExecutionTimeUs += schedulerExecuteTask(getTask(TASK_FILTER), currentTimeUs); taskExecutionTimeUs += schedulerExecuteTask(getTask(TASK_FILTER), currentTimeUs);
} }
if (pidLoopReady()) { if (pidLoopReady()) {
taskExecutionTimeUs += schedulerExecuteTask(getTask(TASK_PID), currentTimeUs); taskExecutionTimeUs += schedulerExecuteTask(getTask(TASK_PID), currentTimeUs);
} }
currentTimeUs = micros();
realtimeTaskRan = true;
#if defined(USE_LATE_TASK_STATISTICS) lastTargetCycles = nextTargetCycles;
// Late, so make a note of the offending task
if (gyroTaskDelayUs < -1) { #ifdef USE_GYRO_EXTI
if (lastTask) { gyroDev_t *gyro = gyroActiveDev();
lastTask->lateCount++;
// Bring the scheduler into lock with the gyro
if (gyro->gyroModeSPI != GYRO_EXTI_NO_INT) {
// Track the actual gyro rate over given number of cycle times and set the expected timebase
static uint32_t terminalGyroRateCount = 0;
static int32_t sampleRateStartCycles;
if ((terminalGyroRateCount == 0)) {
terminalGyroRateCount = gyro->detectedEXTI + GYRO_RATE_COUNT;
sampleRateStartCycles = nowCycles;
}
if (gyro->detectedEXTI >= terminalGyroRateCount) {
// Calculate the number of clock cycles on average between gyro interrupts
uint32_t sampleCycles = nowCycles - sampleRateStartCycles;
desiredPeriodCycles = sampleCycles / GYRO_RATE_COUNT;
sampleRateStartCycles = nowCycles;
terminalGyroRateCount += GYRO_RATE_COUNT;
}
// Track the actual gyro rate over given number of cycle times and remove skew
static uint32_t terminalGyroLockCount = 0;
static int32_t accGyroSkew = 0;
int32_t gyroSkew = cmpTimeCycles(nextTargetCycles, gyro->gyroSyncEXTI) % desiredPeriodCycles;
if (gyroSkew > (desiredPeriodCycles / 2)) {
gyroSkew -= desiredPeriodCycles;
}
accGyroSkew += gyroSkew;
if ((terminalGyroLockCount == 0)) {
terminalGyroLockCount = gyro->detectedEXTI + GYRO_LOCK_COUNT;
}
if (gyro->detectedEXTI >= terminalGyroLockCount) {
terminalGyroLockCount += GYRO_LOCK_COUNT;
// Move the desired start time of the gyroTask
lastTargetCycles -= (accGyroSkew/GYRO_LOCK_COUNT);
DEBUG_SET(DEBUG_SCHEDULER_DETERMINISM, 3, clockCyclesTo10thMicros(accGyroSkew/GYRO_LOCK_COUNT));
accGyroSkew = 0;
} }
} }
lastTask = NULL;
#endif #endif
} }
} }
if (!gyroEnabled || realtimeTaskRan || (gyroTaskDelayUs > GYRO_TASK_GUARD_INTERVAL_US)) { nowCycles = getCycleCounter();
// The task to be invoked schedLoopRemainingCycles = cmpTimeCycles(nextTargetCycles, nowCycles);
if (!gyroEnabled || (schedLoopRemainingCycles > (int32_t)clockMicrosToCycles(CHECK_GUARD_MARGIN_US))) {
currentTimeUs = micros();
// Update task dynamic priorities // Update task dynamic priorities
for (task_t *task = queueFirst(); task != NULL; task = queueNext()) { for (task_t *task = queueFirst(); task != NULL; task = queueNext()) {
if (task->staticPriority != TASK_PRIORITY_REALTIME) { if (task->staticPriority != TASK_PRIORITY_REALTIME) {
// Task has checkFunc - event driven // Task has checkFunc - event driven
if (task->checkFunc) { if (task->checkFunc) {
#if defined(SCHEDULER_DEBUG)
const timeUs_t currentTimeBeforeCheckFuncCallUs = micros();
#else
const timeUs_t currentTimeBeforeCheckFuncCallUs = currentTimeUs;
#endif
// Increase priority for event driven tasks // Increase priority for event driven tasks
if (task->dynamicPriority > 0) { if (task->dynamicPriority > 0) {
task->taskAgeCycles = 1 + ((currentTimeUs - task->lastSignaledAtUs) / task->desiredPeriodUs); task->taskAgeCycles = 1 + (cmpTimeUs(currentTimeUs, task->lastSignaledAtUs) / task->desiredPeriodUs);
task->dynamicPriority = 1 + task->staticPriority * task->taskAgeCycles; task->dynamicPriority = 1 + task->staticPriority * task->taskAgeCycles;
waitingTasks++; waitingTasks++;
} else if (task->checkFunc(currentTimeBeforeCheckFuncCallUs, cmpTimeUs(currentTimeBeforeCheckFuncCallUs, task->lastExecutedAtUs))) { } else if (task->checkFunc(currentTimeUs, cmpTimeUs(currentTimeUs, task->lastExecutedAtUs))) {
#if defined(SCHEDULER_DEBUG) const uint32_t checkFuncExecutionTimeUs = cmpTimeUs(micros(), currentTimeUs);
DEBUG_SET(DEBUG_SCHEDULER, 3, micros() - currentTimeBeforeCheckFuncCallUs); DEBUG_SET(DEBUG_SCHEDULER, 3, checkFuncExecutionTimeUs);
#endif checkFuncMovingSumExecutionTimeUs += checkFuncExecutionTimeUs - checkFuncMovingSumExecutionTimeUs / TASK_STATS_MOVING_SUM_COUNT;
#if defined(USE_TASK_STATISTICS) checkFuncMovingSumDeltaTimeUs += task->taskLatestDeltaTimeUs - checkFuncMovingSumDeltaTimeUs / TASK_STATS_MOVING_SUM_COUNT;
if (calculateTaskStatistics) { checkFuncTotalExecutionTimeUs += checkFuncExecutionTimeUs; // time consumed by scheduler + task
const uint32_t checkFuncExecutionTimeUs = micros() - currentTimeBeforeCheckFuncCallUs; checkFuncMaxExecutionTimeUs = MAX(checkFuncMaxExecutionTimeUs, checkFuncExecutionTimeUs);
checkFuncMovingSumExecutionTimeUs += checkFuncExecutionTimeUs - checkFuncMovingSumExecutionTimeUs / TASK_STATS_MOVING_SUM_COUNT; task->lastSignaledAtUs = currentTimeUs;
checkFuncMovingSumDeltaTimeUs += task->taskLatestDeltaTimeUs - checkFuncMovingSumDeltaTimeUs / TASK_STATS_MOVING_SUM_COUNT;
checkFuncTotalExecutionTimeUs += checkFuncExecutionTimeUs; // time consumed by scheduler + task
checkFuncMaxExecutionTimeUs = MAX(checkFuncMaxExecutionTimeUs, checkFuncExecutionTimeUs);
}
#endif
task->lastSignaledAtUs = currentTimeBeforeCheckFuncCallUs;
task->taskAgeCycles = 1; task->taskAgeCycles = 1;
task->dynamicPriority = 1 + task->staticPriority; task->dynamicPriority = 1 + task->staticPriority;
waitingTasks++; waitingTasks++;
@ -432,7 +533,7 @@ FAST_CODE void scheduler(void)
} else { } else {
// Task is time-driven, dynamicPriority is last execution age (measured in desiredPeriods) // Task is time-driven, dynamicPriority is last execution age (measured in desiredPeriods)
// Task age is calculated from last execution // Task age is calculated from last execution
task->taskAgeCycles = ((currentTimeUs - getPeriodCalculationBasis(task)) / task->desiredPeriodUs); task->taskAgeCycles = (cmpTimeUs(currentTimeUs, getPeriodCalculationBasis(task)) / task->desiredPeriodUs);
if (task->taskAgeCycles > 0) { if (task->taskAgeCycles > 0) {
task->dynamicPriority = 1 + task->staticPriority * task->taskAgeCycles; task->dynamicPriority = 1 + task->staticPriority * task->taskAgeCycles;
waitingTasks++; waitingTasks++;
@ -450,35 +551,46 @@ FAST_CODE void scheduler(void)
totalWaitingTasks += waitingTasks; totalWaitingTasks += waitingTasks;
if (selectedTask) { if (selectedTask) {
timeDelta_t taskRequiredTimeUs = TASK_AVERAGE_EXECUTE_FALLBACK_US; // default average time if task statistics are not available timeDelta_t taskRequiredTimeUs = selectedTask->movingSumExecutionTimeUs / TASK_STATS_MOVING_SUM_COUNT;
#if defined(USE_TASK_STATISTICS)
if (calculateTaskStatistics) {
taskRequiredTimeUs = selectedTask->movingSumExecutionTimeUs / TASK_STATS_MOVING_SUM_COUNT + TASK_AVERAGE_EXECUTE_PADDING_US;
#if defined(USE_LATE_TASK_STATISTICS) #if defined(USE_LATE_TASK_STATISTICS)
selectedTask->execTime = taskRequiredTimeUs; selectedTask->execTime = taskRequiredTimeUs;
#endif #endif
} int32_t taskRequiredTimeCycles = (int32_t)clockMicrosToCycles((uint32_t)taskRequiredTimeUs);
#endif
// Add in the time spent so far in check functions and the scheduler logic
taskRequiredTimeUs += cmpTimeUs(micros(), currentTimeUs);
if (!gyroEnabled || realtimeTaskRan || (taskRequiredTimeUs < gyroTaskDelayUs)) {
taskExecutionTimeUs += schedulerExecuteTask(selectedTask, currentTimeUs);
} else {
selectedTask = NULL;
}
nowCycles = getCycleCounter();
schedLoopRemainingCycles = cmpTimeCycles(nextTargetCycles, nowCycles);
// Allow a little extra time
taskRequiredTimeCycles += taskGuardCycles;
if (!gyroEnabled || (taskRequiredTimeCycles < schedLoopRemainingCycles)) {
taskExecutionTimeUs += schedulerExecuteTask(selectedTask, currentTimeUs);
nowCycles = getCycleCounter();
schedLoopRemainingCycles = cmpTimeCycles(nextTargetCycles, nowCycles);
if (schedLoopRemainingCycles < taskGuardMinCycles) {
if (taskGuardCycles < taskGuardMaxCycles) {
taskGuardCycles += taskGuardDeltaUpCycles;
}
} else if (taskGuardCycles > taskGuardMinCycles) {
taskGuardCycles -= taskGuardDeltaDownCycles;
}
#if defined(USE_LATE_TASK_STATISTICS) #if defined(USE_LATE_TASK_STATISTICS)
lastTask = selectedTask; if (schedLoopRemainingCycles < DEBUG_SCHEDULER_DETERMINISM_THRESHOLD) {
#endif DEBUG_SET(DEBUG_SCHEDULER_DETERMINISM, 1, selectedTask - tasks);
DEBUG_SET(DEBUG_SCHEDULER_DETERMINISM, 2, clockCyclesTo10thMicros(schedLoopRemainingCycles));
selectedTask->lateCount++ ;
}
#endif // USE_LATE_TASK_STATISTICS
} else if (selectedTask->taskAgeCycles > TASK_AGE_EXPEDITE_COUNT) {
// If a task has been unable to run, then reduce it's recorded estimated run time to ensure
// it's ultimate scheduling
selectedTask->movingSumExecutionTimeUs *= TASK_AGE_EXPEDITE_SCALE;
}
} }
} }
#if defined(SCHEDULER_DEBUG)
DEBUG_SET(DEBUG_SCHEDULER, 2, micros() - schedulerStartTimeUs - taskExecutionTimeUs); // time spent in scheduler DEBUG_SET(DEBUG_SCHEDULER, 2, micros() - schedulerStartTimeUs - taskExecutionTimeUs); // time spent in scheduler
#else
UNUSED(taskExecutionTimeUs);
#endif
#if defined(UNIT_TEST) #if defined(UNIT_TEST)
readSchedulerLocals(selectedTask, selectedTaskDynamicPriority, waitingTasks); readSchedulerLocals(selectedTask, selectedTaskDynamicPriority, waitingTasks);
@ -494,3 +606,9 @@ uint16_t getAverageSystemLoadPercent(void)
{ {
return averageSystemLoadPercent; return averageSystemLoadPercent;
} }
float schedulerGetCycleTimeMultipler(void)
{
return (float)clockMicrosToCycles(getTask(TASK_GYRO)->desiredPeriodUs) / desiredPeriodCycles;
}

View File

@ -27,14 +27,29 @@
#define TASK_PERIOD_MS(ms) ((ms) * 1000) #define TASK_PERIOD_MS(ms) ((ms) * 1000)
#define TASK_PERIOD_US(us) (us) #define TASK_PERIOD_US(us) (us)
#define GYRO_TASK_GUARD_INTERVAL_US 10 // Don't run any other tasks if gyro task will be run soon
#if defined(USE_TASK_STATISTICS)
#define TASK_STATS_MOVING_SUM_COUNT 32 #define TASK_STATS_MOVING_SUM_COUNT 32
#endif
#define LOAD_PERCENTAGE_ONE 100 #define LOAD_PERCENTAGE_ONE 100
#define SCHED_START_LOOP_MIN_US 4 // Wait at start of scheduler loop if gyroTask is nearly due
#define SCHED_START_LOOP_MAX_US 12
#define SCHED_START_LOOP_DOWN_STEP 50 // Fraction of a us to reduce start loop wait
#define SCHED_START_LOOP_UP_STEP 1 // Fraction of a us to increase start loop wait
#define TASK_GUARD_MARGIN_MIN_US 2 // Add an amount to the estimate of a task duration
#define TASK_GUARD_MARGIN_MAX_US 5
#define TASK_GUARD_MARGIN_DOWN_STEP 50 // Fraction of a us to reduce task guard margin
#define TASK_GUARD_MARGIN_UP_STEP 1 // Fraction of a us to increase task guard margin
#define CHECK_GUARD_MARGIN_US 2 // Add a margin to the amount of time allowed for a check function to run
#define TASK_AGE_EXPEDITE_COUNT 1 // Make aged tasks more schedulable
#define TASK_AGE_EXPEDITE_SCALE 0.9 // By scaling their expected execution time
// Gyro interrupt counts over which to measure loop time and skew
#define GYRO_RATE_COUNT 32000
#define GYRO_LOCK_COUNT 400
typedef enum { typedef enum {
TASK_PRIORITY_REALTIME = -1, // Task will be run outside the scheduler logic TASK_PRIORITY_REALTIME = -1, // Task will be run outside the scheduler logic
TASK_PRIORITY_IDLE = 0, // Disables dynamic scheduling, task is executed only if no other task is active this cycle TASK_PRIORITY_IDLE = 0, // Disables dynamic scheduling, task is executed only if no other task is active this cycle
@ -164,10 +179,8 @@ typedef enum {
typedef struct { typedef struct {
// Configuration // Configuration
#if defined(USE_TASK_STATISTICS)
const char * taskName; const char * taskName;
const char * subTaskName; const char * subTaskName;
#endif
bool (*checkFunc)(timeUs_t currentTimeUs, timeDelta_t currentDeltaTimeUs); bool (*checkFunc)(timeUs_t currentTimeUs, timeDelta_t currentDeltaTimeUs);
void (*taskFunc)(timeUs_t currentTimeUs); void (*taskFunc)(timeUs_t currentTimeUs);
timeDelta_t desiredPeriodUs; // target period of execution timeDelta_t desiredPeriodUs; // target period of execution
@ -181,7 +194,6 @@ typedef struct {
timeUs_t lastSignaledAtUs; // time of invocation event for event-driven tasks timeUs_t lastSignaledAtUs; // time of invocation event for event-driven tasks
timeUs_t lastDesiredAt; // time of last desired execution timeUs_t lastDesiredAt; // time of last desired execution
#if defined(USE_TASK_STATISTICS)
// Statistics // Statistics
float movingAverageCycleTimeUs; float movingAverageCycleTimeUs;
timeUs_t movingSumExecutionTimeUs; // moving sum over 32 samples timeUs_t movingSumExecutionTimeUs; // moving sum over 32 samples
@ -194,7 +206,6 @@ typedef struct {
uint32_t lateCount; uint32_t lateCount;
timeUs_t execTime; timeUs_t execTime;
#endif #endif
#endif
} task_t; } task_t;
void getCheckFuncInfo(cfCheckFuncInfo_t *checkFuncInfo); void getCheckFuncInfo(cfCheckFuncInfo_t *checkFuncInfo);
@ -202,8 +213,8 @@ void getTaskInfo(taskId_e taskId, taskInfo_t *taskInfo);
void rescheduleTask(taskId_e taskId, timeDelta_t newPeriodUs); void rescheduleTask(taskId_e taskId, timeDelta_t newPeriodUs);
void setTaskEnabled(taskId_e taskId, bool newEnabledState); void setTaskEnabled(taskId_e taskId, bool newEnabledState);
timeDelta_t getTaskDeltaTimeUs(taskId_e taskId); timeDelta_t getTaskDeltaTimeUs(taskId_e taskId);
void ignoreTaskTime(); void ignoreTaskStateTime();
void schedulerSetCalulateTaskStatistics(bool calculateTaskStatistics); void ignoreTaskShortExecTime();
void schedulerResetTaskStatistics(taskId_e taskId); void schedulerResetTaskStatistics(taskId_e taskId);
void schedulerResetTaskMaxExecutionTime(taskId_e taskId); void schedulerResetTaskMaxExecutionTime(taskId_e taskId);
void schedulerResetCheckFunctionMaxExecutionTime(void); void schedulerResetCheckFunctionMaxExecutionTime(void);
@ -215,3 +226,4 @@ void taskSystemLoad(timeUs_t currentTimeUs);
void schedulerOptimizeRate(bool optimizeRate); void schedulerOptimizeRate(bool optimizeRate);
void schedulerEnableGyro(void); void schedulerEnableGyro(void);
uint16_t getAverageSystemLoadPercent(void); uint16_t getAverageSystemLoadPercent(void);
float schedulerGetCycleTimeMultipler(void);

View File

@ -389,7 +389,13 @@ uint32_t baroUpdate(void)
// Tell the scheduler to ignore how long this task takes unless the pressure is being read // Tell the scheduler to ignore how long this task takes unless the pressure is being read
// as that takes the longest // as that takes the longest
if (state != BAROMETER_NEEDS_PRESSURE_READ) { if (state != BAROMETER_NEEDS_PRESSURE_READ) {
ignoreTaskTime(); ignoreTaskStateTime();
}
if (busBusy(&baro.dev.dev, NULL)) {
// If the bus is busy, simply return to have another go later
ignoreTaskStateTime();
return sleepTime;
} }
switch (state) { switch (state) {
@ -422,7 +428,7 @@ uint32_t baroUpdate(void)
if (baro.dev.read_up(&baro.dev)) { if (baro.dev.read_up(&baro.dev)) {
state = BAROMETER_NEEDS_PRESSURE_SAMPLE; state = BAROMETER_NEEDS_PRESSURE_SAMPLE;
} else { } else {
ignoreTaskTime(); ignoreTaskStateTime();
} }
break; break;

View File

@ -81,26 +81,26 @@ void pgResetFn_compassConfig(compassConfig_t *compassConfig)
// 3. Slave I2C device on SPI gyro // 3. Slave I2C device on SPI gyro
#if defined(USE_SPI) && (defined(USE_MAG_SPI_HMC5883) || defined(USE_MAG_SPI_AK8963)) #if defined(USE_SPI) && (defined(USE_MAG_SPI_HMC5883) || defined(USE_MAG_SPI_AK8963))
compassConfig->mag_bustype = BUS_TYPE_SPI; compassConfig->mag_busType = BUS_TYPE_SPI;
compassConfig->mag_spi_device = SPI_DEV_TO_CFG(spiDeviceByInstance(MAG_SPI_INSTANCE)); compassConfig->mag_spi_device = SPI_DEV_TO_CFG(spiDeviceByInstance(MAG_SPI_INSTANCE));
compassConfig->mag_spi_csn = IO_TAG(MAG_CS_PIN); compassConfig->mag_spi_csn = IO_TAG(MAG_CS_PIN);
compassConfig->mag_i2c_device = I2C_DEV_TO_CFG(I2CINVALID); compassConfig->mag_i2c_device = I2C_DEV_TO_CFG(I2CINVALID);
compassConfig->mag_i2c_address = 0; compassConfig->mag_i2c_address = 0;
#elif defined(USE_MAG_HMC5883) || defined(USE_MAG_QMC5883) || defined(USE_MAG_AK8975) || (defined(USE_MAG_AK8963) && !(defined(USE_GYRO_SPI_MPU6500) || defined(USE_GYRO_SPI_MPU9250))) #elif defined(USE_MAG_HMC5883) || defined(USE_MAG_QMC5883) || defined(USE_MAG_AK8975) || (defined(USE_MAG_AK8963) && !(defined(USE_GYRO_SPI_MPU6500) || defined(USE_GYRO_SPI_MPU9250)))
compassConfig->mag_bustype = BUS_TYPE_I2C; compassConfig->mag_busType = BUS_TYPE_I2C;
compassConfig->mag_i2c_device = I2C_DEV_TO_CFG(MAG_I2C_INSTANCE); compassConfig->mag_i2c_device = I2C_DEV_TO_CFG(MAG_I2C_INSTANCE);
compassConfig->mag_i2c_address = 0; compassConfig->mag_i2c_address = 0;
compassConfig->mag_spi_device = SPI_DEV_TO_CFG(SPIINVALID); compassConfig->mag_spi_device = SPI_DEV_TO_CFG(SPIINVALID);
compassConfig->mag_spi_csn = IO_TAG_NONE; compassConfig->mag_spi_csn = IO_TAG_NONE;
#elif defined(USE_MAG_AK8963) && (defined(USE_GYRO_SPI_MPU6500) || defined(USE_GYRO_SPI_MPU9250)) #elif defined(USE_MAG_AK8963) && (defined(USE_GYRO_SPI_MPU6500) || defined(USE_GYRO_SPI_MPU9250))
compassConfig->mag_bustype = BUS_TYPE_MPU_SLAVE; compassConfig->mag_busType = BUS_TYPE_MPU_SLAVE;
compassConfig->mag_i2c_device = I2C_DEV_TO_CFG(I2CINVALID); compassConfig->mag_i2c_device = I2C_DEV_TO_CFG(I2CINVALID);
compassConfig->mag_i2c_address = 0; compassConfig->mag_i2c_address = 0;
compassConfig->mag_spi_device = SPI_DEV_TO_CFG(SPIINVALID); compassConfig->mag_spi_device = SPI_DEV_TO_CFG(SPIINVALID);
compassConfig->mag_spi_csn = IO_TAG_NONE; compassConfig->mag_spi_csn = IO_TAG_NONE;
#else #else
compassConfig->mag_hardware = MAG_NONE; compassConfig->mag_hardware = MAG_NONE;
compassConfig->mag_bustype = BUS_TYPE_NONE; compassConfig->mag_busType = BUS_TYPE_NONE;
compassConfig->mag_i2c_device = I2C_DEV_TO_CFG(I2CINVALID); compassConfig->mag_i2c_device = I2C_DEV_TO_CFG(I2CINVALID);
compassConfig->mag_i2c_address = 0; compassConfig->mag_i2c_address = 0;
compassConfig->mag_spi_device = SPI_DEV_TO_CFG(SPIINVALID); compassConfig->mag_spi_device = SPI_DEV_TO_CFG(SPIINVALID);
@ -115,7 +115,7 @@ static uint8_t magInit = 0;
void compassPreInit(void) void compassPreInit(void)
{ {
#ifdef USE_SPI #ifdef USE_SPI
if (compassConfig()->mag_bustype == BUS_TYPE_SPI) { if (compassConfig()->mag_busType == BUS_TYPE_SPI) {
spiPreinitRegister(compassConfig()->mag_spi_csn, IOCFG_IPU, 1); spiPreinitRegister(compassConfig()->mag_spi_csn, IOCFG_IPU, 1);
} }
#endif #endif
@ -136,7 +136,7 @@ bool compassDetect(magDev_t *magDev, uint8_t *alignment)
magDev->magIntExtiTag = compassConfig()->interruptTag; magDev->magIntExtiTag = compassConfig()->interruptTag;
#endif #endif
switch (compassConfig()->mag_bustype) { switch (compassConfig()->mag_busType) {
#ifdef USE_I2C #ifdef USE_I2C
case BUS_TYPE_I2C: case BUS_TYPE_I2C:
i2cBusSetInstance(dev, compassConfig()->mag_i2c_device); i2cBusSetInstance(dev, compassConfig()->mag_i2c_device);

View File

@ -49,7 +49,7 @@ extern mag_t mag;
typedef struct compassConfig_s { typedef struct compassConfig_s {
uint8_t mag_alignment; // mag alignment uint8_t mag_alignment; // mag alignment
uint8_t mag_hardware; // Which mag hardware to use on boards with more than one device uint8_t mag_hardware; // Which mag hardware to use on boards with more than one device
uint8_t mag_bustype; uint8_t mag_busType;
uint8_t mag_i2c_device; uint8_t mag_i2c_device;
uint8_t mag_i2c_address; uint8_t mag_i2c_address;
uint8_t mag_spi_device; uint8_t mag_spi_device;

View File

@ -78,6 +78,11 @@
#define ACTIVE_GYRO (&gyro.gyroSensor1) #define ACTIVE_GYRO (&gyro.gyroSensor1)
#endif #endif
// The gyro buffer is split 50/50, the first half for the transmit buffer, the second half for the receive buffer
// This buffer is large enough for the gyros currently supported in accgyro_mpu.c but should be reviewed id other
// gyro types are supported with SPI DMA.
#define GYRO_BUF_SIZE 32
static gyroDetectionFlags_t gyroDetectionFlags = GYRO_NONE_MASK; static gyroDetectionFlags_t gyroDetectionFlags = GYRO_NONE_MASK;
static uint16_t calculateNyquistAdjustedNotchHz(uint16_t notchHz, uint16_t notchCutoffHz) static uint16_t calculateNyquistAdjustedNotchHz(uint16_t notchHz, uint16_t notchCutoffHz)
@ -635,6 +640,11 @@ bool gyroInit(void)
} }
if (gyro.gyroToUse == GYRO_CONFIG_USE_GYRO_2 || gyro.gyroToUse == GYRO_CONFIG_USE_GYRO_BOTH) { if (gyro.gyroToUse == GYRO_CONFIG_USE_GYRO_2 || gyro.gyroToUse == GYRO_CONFIG_USE_GYRO_BOTH) {
static DMA_DATA uint8_t gyroBuf2[GYRO_BUF_SIZE];
// SPI DMA buffer required per device
gyro.gyroSensor2.gyroDev.dev.txBuf = gyroBuf2;
gyro.gyroSensor2.gyroDev.dev.rxBuf = &gyroBuf2[GYRO_BUF_SIZE / 2];
gyroInitSensor(&gyro.gyroSensor2, gyroDeviceConfig(1)); gyroInitSensor(&gyro.gyroSensor2, gyroDeviceConfig(1));
gyro.gyroHasOverflowProtection = gyro.gyroHasOverflowProtection && gyro.gyroSensor2.gyroDev.gyroHasOverflowProtection; gyro.gyroHasOverflowProtection = gyro.gyroHasOverflowProtection && gyro.gyroSensor2.gyroDev.gyroHasOverflowProtection;
detectedSensors[SENSOR_INDEX_GYRO] = gyro.gyroSensor2.gyroDev.gyroHardware; detectedSensors[SENSOR_INDEX_GYRO] = gyro.gyroSensor2.gyroDev.gyroHardware;
@ -646,6 +656,10 @@ bool gyroInit(void)
} }
if (gyro.gyroToUse == GYRO_CONFIG_USE_GYRO_1 || gyro.gyroToUse == GYRO_CONFIG_USE_GYRO_BOTH) { if (gyro.gyroToUse == GYRO_CONFIG_USE_GYRO_1 || gyro.gyroToUse == GYRO_CONFIG_USE_GYRO_BOTH) {
static DMA_DATA uint8_t gyroBuf1[GYRO_BUF_SIZE];
// SPI DMA buffer required per device
gyro.gyroSensor1.gyroDev.dev.txBuf = gyroBuf1;
gyro.gyroSensor1.gyroDev.dev.rxBuf = &gyroBuf1[GYRO_BUF_SIZE / 2];
gyroInitSensor(&gyro.gyroSensor1, gyroDeviceConfig(0)); gyroInitSensor(&gyro.gyroSensor1, gyroDeviceConfig(0));
gyro.gyroHasOverflowProtection = gyro.gyroHasOverflowProtection && gyro.gyroSensor1.gyroDev.gyroHasOverflowProtection; gyro.gyroHasOverflowProtection = gyro.gyroHasOverflowProtection && gyro.gyroSensor1.gyroDev.gyroHasOverflowProtection;
detectedSensors[SENSOR_INDEX_GYRO] = gyro.gyroSensor1.gyroDev.gyroHardware; detectedSensors[SENSOR_INDEX_GYRO] = gyro.gyroSensor1.gyroDev.gyroHardware;

View File

@ -32,7 +32,6 @@
#define USE_EXTI #define USE_EXTI
#define MAG_INT_EXTI PC14 #define MAG_INT_EXTI PC14
#define USE_MPU_DATA_READY_SIGNAL #define USE_MPU_DATA_READY_SIGNAL
//#define DEBUG_MPU_DATA_READY_INTERRUPT
#define USE_GYRO #define USE_GYRO
#define USE_GYRO_MPU6050 #define USE_GYRO_MPU6050

View File

@ -41,7 +41,6 @@
#define BEEPER_PIN PA5 #define BEEPER_PIN PA5
#define USE_EXTI #define USE_EXTI
//#define DEBUG_MPU_DATA_READY_INTERRUPT
#define USE_MPU_DATA_READY_SIGNAL #define USE_MPU_DATA_READY_SIGNAL
// Using MPU6050 for the moment. // Using MPU6050 for the moment.

View File

@ -40,7 +40,6 @@
#define USE_EXTI #define USE_EXTI
#define USE_GYRO_EXTI #define USE_GYRO_EXTI
#define GYRO_1_EXTI_PIN PC14 #define GYRO_1_EXTI_PIN PC14
//#define DEBUG_MPU_DATA_READY_INTERRUPT
#define USE_MPU_DATA_READY_SIGNAL #define USE_MPU_DATA_READY_SIGNAL
#define ENSURE_MPU_DATA_READY_IS_LOW #define ENSURE_MPU_DATA_READY_IS_LOW

View File

@ -38,7 +38,6 @@
#define USE_EXTI #define USE_EXTI
#define USE_GYRO_EXTI #define USE_GYRO_EXTI
#define GYRO_1_EXTI_PIN PC14 #define GYRO_1_EXTI_PIN PC14
//#define DEBUG_MPU_DATA_READY_INTERRUPT
#define USE_MPU_DATA_READY_SIGNAL #define USE_MPU_DATA_READY_SIGNAL
#define ENSURE_MPU_DATA_READY_IS_LOW #define ENSURE_MPU_DATA_READY_IS_LOW

View File

@ -36,7 +36,6 @@
#define USE_EXTI #define USE_EXTI
#define USE_GYRO_EXTI #define USE_GYRO_EXTI
#define GYRO_1_EXTI_PIN PA8 #define GYRO_1_EXTI_PIN PA8
//#define DEBUG_MPU_DATA_READY_INTERRUPT
#define USE_MPU_DATA_READY_SIGNAL #define USE_MPU_DATA_READY_SIGNAL
#define ENSURE_MPU_DATA_READY_IS_LOW #define ENSURE_MPU_DATA_READY_IS_LOW

View File

@ -47,7 +47,6 @@
#define USE_GYRO_EXTI #define USE_GYRO_EXTI
#define GYRO_1_EXTI_PIN PC5 #define GYRO_1_EXTI_PIN PC5
#define USE_MPU_DATA_READY_SIGNAL #define USE_MPU_DATA_READY_SIGNAL
//#define DEBUG_MPU_DATA_READY_INTERRUPT
#define GYRO_1_CS_PIN PC4 #define GYRO_1_CS_PIN PC4
#define GYRO_1_SPI_INSTANCE SPI1 #define GYRO_1_SPI_INSTANCE SPI1

View File

@ -33,8 +33,6 @@
#define USE_GYRO_EXTI #define USE_GYRO_EXTI
#define GYRO_1_EXTI_PIN PA3 #define GYRO_1_EXTI_PIN PA3
#define USE_MPU_DATA_READY_SIGNAL #define USE_MPU_DATA_READY_SIGNAL
//#define DEBUG_MPU_DATA_READY_INTERRUPT
#define USE_SPI #define USE_SPI
#define USE_SPI_DEVICE_1 #define USE_SPI_DEVICE_1
#define USE_SPI_DEVICE_2 #define USE_SPI_DEVICE_2

View File

@ -134,7 +134,7 @@
#define USE_RX_FRSKY_SPI_X #define USE_RX_FRSKY_SPI_X
#define USE_RX_SFHSS_SPI #define USE_RX_SFHSS_SPI
#define USE_RX_REDPINE_SPI #define USE_RX_REDPINE_SPI
#define DEFAULT_RX_FEATURE FEATURE_RX_SPI #define DEFAULT_RX_FEATURE FEATURE_RX_SPI
#define RX_SPI_DEFAULT_PROTOCOL RX_SPI_FRSKY_X #define RX_SPI_DEFAULT_PROTOCOL RX_SPI_FRSKY_X
#define USE_RX_FRSKY_SPI_TELEMETRY #define USE_RX_FRSKY_SPI_TELEMETRY

View File

@ -115,7 +115,6 @@
#define USE_EXTI #define USE_EXTI
#define USE_GYRO_EXTI #define USE_GYRO_EXTI
#define GYRO_1_EXTI_PIN PC13 #define GYRO_1_EXTI_PIN PC13
//#define DEBUG_MPU_DATA_READY_INTERRUPT
#define USE_MPU_DATA_READY_SIGNAL #define USE_MPU_DATA_READY_SIGNAL
#define ENSURE_MPU_DATA_READY_IS_LOW #define ENSURE_MPU_DATA_READY_IS_LOW

View File

@ -33,7 +33,6 @@
#define USE_EXTI #define USE_EXTI
#define USE_GYRO_EXTI #define USE_GYRO_EXTI
#define GYRO_1_EXTI_PIN PB5 #define GYRO_1_EXTI_PIN PB5
//#define DEBUG_MPU_DATA_READY_INTERRUPT
#define USE_MPU_DATA_READY_SIGNAL #define USE_MPU_DATA_READY_SIGNAL
#define ENSURE_MPU_DATA_READY_IS_LOW #define ENSURE_MPU_DATA_READY_IS_LOW

View File

@ -54,7 +54,6 @@
#define USE_EXTI #define USE_EXTI
#define USE_GYRO_EXTI #define USE_GYRO_EXTI
#define GYRO_1_EXTI_PIN PC4 #define GYRO_1_EXTI_PIN PC4
//#define DEBUG_MPU_DATA_READY_INTERRUPT
#define USE_MPU_DATA_READY_SIGNAL #define USE_MPU_DATA_READY_SIGNAL
#define ENSURE_MPU_DATA_READY_IS_LOW #define ENSURE_MPU_DATA_READY_IS_LOW
@ -110,7 +109,7 @@
#define UART1_RX_PIN PB7 #define UART1_RX_PIN PB7
#else #else
#define UART1_RX_PIN PA10 #define UART1_RX_PIN PA10
#endif #endif
#define UART1_TX_PIN PA9 #define UART1_TX_PIN PA9
#define USE_UART4 #define USE_UART4

View File

@ -48,7 +48,6 @@
#define USE_EXTI #define USE_EXTI
#define USE_GYRO_EXTI #define USE_GYRO_EXTI
#define GYRO_1_EXTI_PIN PC4 #define GYRO_1_EXTI_PIN PC4
//#define DEBUG_MPU_DATA_READY_INTERRUPT
#define USE_MPU_DATA_READY_SIGNAL #define USE_MPU_DATA_READY_SIGNAL
#define ENSURE_MPU_DATA_READY_IS_LOW #define ENSURE_MPU_DATA_READY_IS_LOW

View File

@ -32,10 +32,10 @@ const timerHardware_t timerHardware[USABLE_TIMER_CHANNEL_COUNT] = {
DEF_TIM(TIM8, CH4, PC9, TIM_USE_PPM, 0, 0 ), // PPM IN DEF_TIM(TIM8, CH4, PC9, TIM_USE_PPM, 0, 0 ), // PPM IN
DEF_TIM(TIM2, CH4, PA3, TIM_USE_MOTOR, 0, 1 ), // S1_OUT - DMA1_ST6_CH3 DEF_TIM(TIM2, CH4, PA3, TIM_USE_MOTOR, 0, 1 ), // S1_OUT - DMA1_ST6_CH3
DEF_TIM(TIM3, CH3, PB0, TIM_USE_MOTOR, 0, 0 ), // S2_OUT - DMA1_ST7_CH5 DEF_TIM(TIM3, CH3, PB0, TIM_USE_MOTOR, 0, 0 ), // S2_OUT - DMA1_ST7_CH5
DEF_TIM(TIM1, CH3N, PB1, TIM_USE_MOTOR, 0, 0 ), // S3_OUT - DMA2_ST6_CH0 DEF_TIM(TIM3, CH4, PB1, TIM_USE_MOTOR, 0, 0 ), // S3_OUT - DMA1_ST2_CH5
DEF_TIM(TIM2, CH3, PA2, TIM_USE_MOTOR, 0, 0 ), // S4_OUT - DMA1_ST1_CH3 DEF_TIM(TIM2, CH3, PA2, TIM_USE_MOTOR, 0, 0 ), // S4_OUT - DMA1_ST1_CH3
DEF_TIM(TIM5, CH1, PA0, TIM_USE_LED, 0, 0 ), // LED_STRIP - DMA1_ST2_CH6 DEF_TIM(TIM5, CH1, PA0, TIM_USE_LED, 0, 0 ), // LED_STRIP - DMA1_ST2_CH6
DEF_TIM(TIM11, CH1, PB9, TIM_USE_CAMERA_CONTROL, 0, 0), // CAM_CTL DEF_TIM(TIM11, CH1, PB9, TIM_USE_CAMERA_CONTROL, 0, 0), // CAM_CTL
}; };

View File

@ -20,6 +20,8 @@
#pragma once #pragma once
#define USE_LATE_TASK_STATISTICS
#ifdef FURYF4OSD #ifdef FURYF4OSD
#define TARGET_BOARD_IDENTIFIER "FY4O" #define TARGET_BOARD_IDENTIFIER "FY4O"
#define USBD_PRODUCT_STRING "FuryF4OSD" #define USBD_PRODUCT_STRING "FuryF4OSD"
@ -158,4 +160,4 @@
#define TARGET_IO_PORTD (BIT(2)) #define TARGET_IO_PORTD (BIT(2))
#define USABLE_TIMER_CHANNEL_COUNT 7 #define USABLE_TIMER_CHANNEL_COUNT 7
#define USED_TIMERS ( TIM_N(1) | TIM_N(2) | TIM_N(3) | TIM_N(5) | TIM_N(8) | TIM_N(11) ) #define USED_TIMERS ( TIM_N(2) | TIM_N(3) | TIM_N(5) | TIM_N(8) | TIM_N(11) )

View File

@ -56,7 +56,6 @@
#define USE_EXTI #define USE_EXTI
#define USE_GYRO_EXTI #define USE_GYRO_EXTI
#define GYRO_1_EXTI_PIN PC5 #define GYRO_1_EXTI_PIN PC5
//#define DEBUG_MPU_DATA_READY_INTERRUPT
#define USE_MPU_DATA_READY_SIGNAL #define USE_MPU_DATA_READY_SIGNAL
#define ENSURE_MPU_DATA_READY_IS_LOW #define ENSURE_MPU_DATA_READY_IS_LOW
@ -73,7 +72,7 @@
#if defined(FLYWOOF405) #if defined(FLYWOOF405)
//------MPU6000 //------MPU6000
#define USE_GYRO_SPI_MPU6000 #define USE_GYRO_SPI_MPU6000
#define USE_ACC_SPI_MPU6000 #define USE_ACC_SPI_MPU6000
#endif #endif
#if defined(KAKUTEF4V2) || defined(FLYWOOF405) // There is invertor on RXD3(PB11), so PB10/PB11 can't be used as I2C2. #if defined(KAKUTEF4V2) || defined(FLYWOOF405) // There is invertor on RXD3(PB11), so PB10/PB11 can't be used as I2C2.
@ -154,8 +153,8 @@
#define USE_PINIOBOX #define USE_PINIOBOX
#else #else
#define ESCSERIAL_TIMER_TX_PIN PC7 // (HARDARE=0,PPM) #define ESCSERIAL_TIMER_TX_PIN PC7 // (HARDARE=0,PPM)
#endif #endif
#define USE_SPI #define USE_SPI
#define USE_SPI_DEVICE_1 //ICM20689 #define USE_SPI_DEVICE_1 //ICM20689
#define SPI1_NSS_PIN PC4 #define SPI1_NSS_PIN PC4

View File

@ -48,7 +48,6 @@
#define USE_EXTI #define USE_EXTI
#define USE_GYRO_EXTI #define USE_GYRO_EXTI
#define GYRO_1_EXTI_PIN PA5 #define GYRO_1_EXTI_PIN PA5
//#define DEBUG_MPU_DATA_READY_INTERRUPT
#define USE_MPU_DATA_READY_SIGNAL #define USE_MPU_DATA_READY_SIGNAL
#define ENSURE_MPU_DATA_READY_IS_LOW #define ENSURE_MPU_DATA_READY_IS_LOW

View File

@ -115,7 +115,7 @@
#define USE_ADC #define USE_ADC
#define ADC_INSTANCE ADC3 #define ADC_INSTANCE ADC3
#define ADC3_DMA_OPT 0 #define ADC3_DMA_OPT 1
#define VBAT_ADC_PIN PC1 #define VBAT_ADC_PIN PC1
#define RSSI_ADC_PIN PC2 #define RSSI_ADC_PIN PC2

View File

@ -35,9 +35,7 @@
#define USE_EXTI #define USE_EXTI
#define MAG_INT_EXTI PC14 #define MAG_INT_EXTI PC14
//#define DEBUG_MPU_DATA_READY_INTERRUPT
#define USE_MPU_DATA_READY_SIGNAL #define USE_MPU_DATA_READY_SIGNAL
//#define DEBUG_MAG_DATA_READY_INTERRUPT
#define USE_MAG_DATA_READY_SIGNAL #define USE_MAG_DATA_READY_SIGNAL
// SPI2 // SPI2

View File

@ -40,7 +40,6 @@
#define USE_GYRO_EXTI #define USE_GYRO_EXTI
#define GYRO_1_EXTI_PIN PB2 #define GYRO_1_EXTI_PIN PB2
#define USE_MPU_DATA_READY_SIGNAL #define USE_MPU_DATA_READY_SIGNAL
//#define DEBUG_MPU_DATA_READY_INTERRUPT
#define GYRO_1_CS_PIN PC4 #define GYRO_1_CS_PIN PC4
#define GYRO_1_SPI_INSTANCE SPI1 #define GYRO_1_SPI_INSTANCE SPI1

View File

@ -20,6 +20,8 @@
#pragma once #pragma once
#define USE_LATE_TASK_STATISTICS
#if defined(AIRBOTF4) #if defined(AIRBOTF4)
#define TARGET_BOARD_IDENTIFIER "AIR4" #define TARGET_BOARD_IDENTIFIER "AIR4"
#define USBD_PRODUCT_STRING "AirbotF4" #define USBD_PRODUCT_STRING "AirbotF4"

View File

@ -19,6 +19,9 @@
*/ */
#pragma once #pragma once
#define USE_LATE_TASK_STATISTICS
#define TARGET_BOARD_IDENTIFIER "REVN" #define TARGET_BOARD_IDENTIFIER "REVN"
#define USBD_PRODUCT_STRING "Revo Nano" #define USBD_PRODUCT_STRING "Revo Nano"

View File

@ -334,6 +334,25 @@ uint32_t millis(void) {
return millis64() & 0xFFFFFFFF; return millis64() & 0xFFFFFFFF;
} }
uint32_t clockCyclesToMicros(uint32_t clockCycles)
{
return clockCycles;
}
int32_t clockCyclesTo10thMicros(int32_t clockCycles)
{
return clockCycles;
}
uint32_t clockMicrosToCycles(uint32_t micros)
{
return micros;
}
uint32_t getCycleCounter(void)
{
return (uint32_t) (micros64() & 0xFFFFFFFF);
}
void microsleep(uint32_t usec) { void microsleep(uint32_t usec) {
struct timespec ts; struct timespec ts;
ts.tv_sec = 0; ts.tv_sec = 0;

View File

@ -40,7 +40,6 @@
#define USE_EXTI #define USE_EXTI
#define USE_GYRO_EXTI #define USE_GYRO_EXTI
#define GYRO_1_EXTI_PIN PC5 #define GYRO_1_EXTI_PIN PC5
//#define DEBUG_MPU_DATA_READY_INTERRUPT
#define USE_MPU_DATA_READY_SIGNAL #define USE_MPU_DATA_READY_SIGNAL
#define ENSURE_MPU_DATA_READY_IS_LOW #define ENSURE_MPU_DATA_READY_IS_LOW

View File

@ -43,7 +43,6 @@
#define USE_GYRO_EXTI #define USE_GYRO_EXTI
#define GYRO_1_EXTI_PIN PC4 #define GYRO_1_EXTI_PIN PC4
#define USE_MPU_DATA_READY_SIGNAL #define USE_MPU_DATA_READY_SIGNAL
//#define DEBUG_MPU_DATA_READY_INTERRUPT
#define GYRO_1_CS_PIN PC4 #define GYRO_1_CS_PIN PC4
#define GYRO_1_SPI_INSTANCE SPI1 #define GYRO_1_SPI_INSTANCE SPI1

View File

@ -69,7 +69,6 @@
#define GYRO_1_EXTI_PIN PC4 #define GYRO_1_EXTI_PIN PC4
#endif #endif
#define USE_MPU_DATA_READY_SIGNAL #define USE_MPU_DATA_READY_SIGNAL
//#define DEBUG_MPU_DATA_READY_INTERRUPT
#if defined(PIRXF4) #if defined(PIRXF4)
#define GYRO_1_CS_PIN PC4 #define GYRO_1_CS_PIN PC4

View File

@ -28,8 +28,6 @@
// -Wpadded can be turned on to check padding of structs // -Wpadded can be turned on to check padding of structs
//#pragma GCC diagnostic warning "-Wpadded" //#pragma GCC diagnostic warning "-Wpadded"
//#define SCHEDULER_DEBUG // define this to use scheduler debug[] values. Undefined by default for performance reasons
#ifdef STM32F1 #ifdef STM32F1
#define MINIMAL_CLI #define MINIMAL_CLI
// Using RX DMA disables the use of receive callbacks // Using RX DMA disables the use of receive callbacks
@ -92,6 +90,7 @@
#define USE_TIMER_MGMT #define USE_TIMER_MGMT
#define USE_PERSISTENT_OBJECTS #define USE_PERSISTENT_OBJECTS
#define USE_CUSTOM_DEFAULTS_ADDRESS #define USE_CUSTOM_DEFAULTS_ADDRESS
#define USE_LATE_TASK_STATISTICS
#endif // STM32F7 #endif // STM32F7
#ifdef STM32H7 #ifdef STM32H7
@ -133,6 +132,7 @@
#define USE_MCO #define USE_MCO
#define USE_DMA_SPEC #define USE_DMA_SPEC
#define USE_TIMER_MGMT #define USE_TIMER_MGMT
#define USE_LATE_TASK_STATISTICS
#endif #endif
#if defined(STM32F4) || defined(STM32F7) || defined(STM32H7) || defined(STM32G4) #if defined(STM32F4) || defined(STM32F7) || defined(STM32H7) || defined(STM32G4)
@ -231,7 +231,6 @@ extern uint8_t _dmaram_end__;
#define USE_CLI #define USE_CLI
#define USE_SERIAL_PASSTHROUGH #define USE_SERIAL_PASSTHROUGH
#define USE_TASK_STATISTICS
#define USE_GYRO_REGISTER_DUMP // Adds gyroregisters command to cli to dump configured register values #define USE_GYRO_REGISTER_DUMP // Adds gyroregisters command to cli to dump configured register values
#define USE_IMU_CALC #define USE_IMU_CALC
#define USE_PPM #define USE_PPM

View File

@ -202,6 +202,10 @@ uint16_t batteryWarningVoltage;
uint8_t useHottAlarmSoundPeriod (void) { return 0; } uint8_t useHottAlarmSoundPeriod (void) { return 0; }
const uint32_t baudRates[] = {0, 9600, 19200, 38400, 57600, 115200, 230400, 250000, 400000}; // see baudRate_e const uint32_t baudRates[] = {0, 9600, 19200, 38400, 57600, 115200, 230400, 250000, 400000}; // see baudRate_e
uint8_t debugMode;
int32_t schedLoopStartCycles;
int32_t taskGuardCycles;
uint32_t micros(void) {return 0;} uint32_t micros(void) {return 0;}
int32_t getAmperage(void) { int32_t getAmperage(void) {
@ -324,7 +328,6 @@ uint8_t serialRead(serialPort_t *){return 0;}
void bufWriterAppend(bufWriter_t *, uint8_t ch){ printf("%c", ch); } void bufWriterAppend(bufWriter_t *, uint8_t ch){ printf("%c", ch); }
void serialWriteBufShim(void *, const uint8_t *, int) {} void serialWriteBufShim(void *, const uint8_t *, int) {}
bufWriter_t *bufWriterInit(uint8_t *, int, bufWrite_t, void *) {return NULL;} bufWriter_t *bufWriterInit(uint8_t *, int, bufWrite_t, void *) {return NULL;}
void schedulerSetCalulateTaskStatistics(bool) {}
void setArmingDisabled(armingDisableFlags_e) {} void setArmingDisabled(armingDisableFlags_e) {}
void waitForSerialPortToFinishTransmitting(serialPort_t *) {} void waitForSerialPortToFinishTransmitting(serialPort_t *) {}

View File

@ -250,4 +250,6 @@ void mixerSetThrottleAngleCorrection(int) {};
bool gpsRescueIsRunning(void) { return false; } bool gpsRescueIsRunning(void) { return false; }
bool isFixedWing(void) { return false; } bool isFixedWing(void) { return false; }
void pinioBoxTaskControl(void) {} void pinioBoxTaskControl(void) {}
void ignoreTaskShortExecTime(void) {}
void ignoreTaskStateTime(void) {}
} }

View File

@ -395,7 +395,7 @@ bool isFlipOverAfterCrashActive(void) { return false; }
void ws2811LedStripEnable(void) { } void ws2811LedStripEnable(void) { }
void setUsedLedCount(unsigned) { }; void setUsedLedCount(unsigned) { }
void pinioBoxTaskControl(void) {} void pinioBoxTaskControl(void) {}
void ignoreTaskTime(void) {} void ignoreTaskShortExecTime(void) {}
} }

View File

@ -559,4 +559,7 @@ extern "C" {
float getMotorOutputLow(void) { return 1000.0; } float getMotorOutputLow(void) { return 1000.0; }
float getMotorOutputHigh(void) { return 2047.0; } float getMotorOutputHigh(void) { return 2047.0; }
void ignoreTaskShortExecTime(void) {}
void ignoreTaskStateTime(void) {}
} }

View File

@ -1319,4 +1319,6 @@ extern "C" {
bool isUpright(void) { return true; } bool isUpright(void) { return true; }
float getMotorOutputLow(void) { return 1000.0; } float getMotorOutputLow(void) { return 1000.0; }
float getMotorOutputHigh(void) { return 2047.0; } float getMotorOutputHigh(void) { return 2047.0; }
void ignoreTaskShortExecTime(void) {}
void ignoreTaskStateTime(void) {}
} }

View File

@ -51,10 +51,18 @@ extern "C" {
bool taskPidRan = false; bool taskPidRan = false;
bool taskFilterReady = false; bool taskFilterReady = false;
bool taskPidReady = false; bool taskPidReady = false;
uint8_t activePidLoopDenom = 1;
int16_t debug[1];
uint8_t debugMode = 0;
// set up micros() to simulate time // set up micros() to simulate time
uint32_t simulatedTime = 0; uint32_t simulatedTime = 0;
uint32_t micros(void) { return simulatedTime; } uint32_t micros(void) { return simulatedTime; }
uint32_t clockCyclesToMicros(uint32_t x) { return x/10;}
int32_t clockCyclesTo10thMicros(int32_t x) { return x;}
uint32_t clockMicrosToCycles(uint32_t x) { return x*10;}
uint32_t getCycleCounter(void) {return simulatedTime * 10;}
// set up tasks to take a simulated representative time to execute // set up tasks to take a simulated representative time to execute
bool gyroFilterReady(void) { return taskFilterReady; } bool gyroFilterReady(void) { return taskFilterReady; }
@ -463,6 +471,7 @@ TEST(SchedulerUnittest, TestGyroTask)
// run the scheduler // run the scheduler
scheduler(); scheduler();
// the gyro task indicator should be true and the TASK_FILTER and TASK_PID indicators should be false // the gyro task indicator should be true and the TASK_FILTER and TASK_PID indicators should be false
EXPECT_TRUE(taskGyroRan); EXPECT_TRUE(taskGyroRan);
EXPECT_FALSE(taskFilterRan); EXPECT_FALSE(taskFilterRan);
@ -507,102 +516,3 @@ TEST(SchedulerUnittest, TestGyroTask)
EXPECT_EQ(static_cast<task_t*>(0), unittest_scheduler_selectedTask); EXPECT_EQ(static_cast<task_t*>(0), unittest_scheduler_selectedTask);
} }
// Test the scheduling logic that prevents other tasks from running if they
// might interfere with the timing of the next gyro task.
TEST(SchedulerUnittest, TestGyroLookahead)
{
static const uint32_t startTime = 4000;
// enable task statistics
schedulerSetCalulateTaskStatistics(true);
// disable scheduler optimize rate
schedulerOptimizeRate(false);
// enable the gyro
schedulerEnableGyro();
// disable all tasks except TASK_GYRO, TASK_ACCEL
for (int taskId = 0; taskId < TASK_COUNT; ++taskId) {
setTaskEnabled(static_cast<taskId_e>(taskId), false);
}
setTaskEnabled(TASK_GYRO, true);
setTaskEnabled(TASK_ACCEL, true);
#if defined(USE_TASK_STATISTICS)
// set the average run time for TASK_ACCEL
tasks[TASK_ACCEL].movingSumExecutionTimeUs = TEST_UPDATE_ACCEL_TIME * TASK_STATS_MOVING_SUM_COUNT;
#endif
/* Test that another task will run if there's plenty of time till the next gyro sample time */
// set it up so TASK_GYRO just ran and TASK_ACCEL is ready to run
simulatedTime = startTime;
tasks[TASK_GYRO].lastExecutedAtUs = simulatedTime;
tasks[TASK_ACCEL].lastExecutedAtUs = simulatedTime - TASK_PERIOD_HZ(1000);
// reset the flags
resetGyroTaskTestFlags();
// run the scheduler
scheduler();
// the gyro, filter and PID task indicators should be false
EXPECT_FALSE(taskGyroRan);
EXPECT_FALSE(taskFilterRan);
EXPECT_FALSE(taskPidRan);
// TASK_ACCEL should have run
EXPECT_EQ(&tasks[TASK_ACCEL], unittest_scheduler_selectedTask);
/* Test that another task won't run if the time till the gyro task is less than the guard interval */
// set it up so TASK_GYRO will run soon and TASK_ACCEL is ready to run
simulatedTime = startTime;
tasks[TASK_GYRO].lastExecutedAtUs = simulatedTime - TASK_PERIOD_HZ(TEST_GYRO_SAMPLE_HZ) + GYRO_TASK_GUARD_INTERVAL_US / 2;
tasks[TASK_ACCEL].lastExecutedAtUs = simulatedTime - TASK_PERIOD_HZ(1000);
// reset the flags
resetGyroTaskTestFlags();
// run the scheduler
scheduler();
// the gyro, filter and PID task indicators should be false
EXPECT_FALSE(taskGyroRan);
EXPECT_FALSE(taskFilterRan);
EXPECT_FALSE(taskPidRan);
// TASK_ACCEL should not have run
EXPECT_EQ(static_cast<task_t*>(0), unittest_scheduler_selectedTask);
/* Test that another task won't run if the time till the gyro task is less than the average task interval */
// set it up so TASK_GYRO will run soon and TASK_ACCEL is ready to run
simulatedTime = startTime;
tasks[TASK_GYRO].lastExecutedAtUs = simulatedTime - TASK_PERIOD_HZ(TEST_GYRO_SAMPLE_HZ) + TEST_UPDATE_ACCEL_TIME / 2;
tasks[TASK_ACCEL].lastExecutedAtUs = simulatedTime - TASK_PERIOD_HZ(1000);
// reset the flags
resetGyroTaskTestFlags();
// run the scheduler
scheduler();
// the gyro, filter and PID task indicators should be false
EXPECT_FALSE(taskGyroRan);
EXPECT_FALSE(taskFilterRan);
EXPECT_FALSE(taskPidRan);
// TASK_ACCEL should not have run
EXPECT_EQ(static_cast<task_t*>(0), unittest_scheduler_selectedTask);
/* Test that another task will run if the gyro task gets executed */
// set it up so TASK_GYRO will run now and TASK_ACCEL is ready to run
simulatedTime = startTime;
tasks[TASK_GYRO].lastExecutedAtUs = simulatedTime - TASK_PERIOD_HZ(TEST_GYRO_SAMPLE_HZ);
tasks[TASK_ACCEL].lastExecutedAtUs = simulatedTime - TASK_PERIOD_HZ(1000);
// reset the flags
resetGyroTaskTestFlags();
// make the TASK_FILTER and TASK_PID ready to run
taskFilterReady = true;
taskPidReady = true;
// run the scheduler
scheduler();
// TASK_GYRO, TASK_FILTER, and TASK_PID should all run
EXPECT_TRUE(taskGyroRan);
EXPECT_TRUE(taskFilterRan);
EXPECT_TRUE(taskPidRan);
// TASK_ACCEL should have run
EXPECT_EQ(&tasks[TASK_ACCEL], unittest_scheduler_selectedTask);
}

View File

@ -20,6 +20,7 @@
#define SCHEDULER_DELAY_LIMIT 1 #define SCHEDULER_DELAY_LIMIT 1
#define TASK_GYROPID_DESIRED_PERIOD 100 #define TASK_GYROPID_DESIRED_PERIOD 100
#define DMA_DATA
#define DMA_DATA_ZERO_INIT #define DMA_DATA_ZERO_INIT
#define USE_ACC #define USE_ACC
@ -63,7 +64,6 @@
#define USE_UART5 #define USE_UART5
#define USE_SOFTSERIAL1 #define USE_SOFTSERIAL1
#define USE_SOFTSERIAL2 #define USE_SOFTSERIAL2
#define USE_TASK_STATISTICS
#define SERIAL_PORT_COUNT 8 #define SERIAL_PORT_COUNT 8

View File

@ -32,6 +32,8 @@ extern "C" {
extern "C" { extern "C" {
void updateLEDDMABuffer(ledStripFormatRGB_e ledFormat, rgbColor24bpp_t *color, unsigned ledIndex); void updateLEDDMABuffer(ledStripFormatRGB_e ledFormat, rgbColor24bpp_t *color, unsigned ledIndex);
void ignoreTaskShortExecTime(void) {}
void ignoreTaskStateTime(void) {}
} }
TEST(WS2812, updateDMABuffer) { TEST(WS2812, updateDMABuffer) {
@ -89,3 +91,4 @@ bool ws2811LedStripHardwareInit(ioTag_t ioTag) {
void ws2811LedStripDMAEnable(void) {} void ws2811LedStripDMAEnable(void) {}
} }