mirror of
https://github.com/betaflight/betaflight.git
synced 2025-07-23 16:25:31 +03:00
Merge pull request #1860 from martinbudden/bf_pg0
Parameter groups EEPROM migration
This commit is contained in:
commit
e72528f020
17 changed files with 932 additions and 378 deletions
1
Makefile
1
Makefile
|
@ -561,6 +561,7 @@ COMMON_SRC = \
|
|||
config/config_eeprom.c \
|
||||
config/feature.c \
|
||||
config/parameter_group.c \
|
||||
config/config_streamer.c \
|
||||
drivers/adc.c \
|
||||
drivers/buf_writer.c \
|
||||
drivers/bus_i2c_soft.c \
|
||||
|
|
|
@ -18,294 +18,285 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
#include "drivers/system.h"
|
||||
|
||||
#include "config/config_master.h"
|
||||
|
||||
#include "build/build_config.h"
|
||||
|
||||
#include "common/maths.h"
|
||||
|
||||
#include "config/config_eeprom.h"
|
||||
#include "config/config_streamer.h"
|
||||
#include "config/config_master.h"
|
||||
#include "config/parameter_group.h"
|
||||
|
||||
#if !defined(FLASH_SIZE)
|
||||
#error "Flash size not defined for target. (specify in KB)"
|
||||
#endif
|
||||
#include "drivers/system.h"
|
||||
|
||||
#include "fc/config.h"
|
||||
|
||||
#ifndef FLASH_PAGE_SIZE
|
||||
#ifdef STM32F303xC
|
||||
#define FLASH_PAGE_SIZE ((uint16_t)0x800)
|
||||
#endif
|
||||
// declare a dummy PG, since scanEEPROM assumes there is at least one PG
|
||||
// !!TODO remove once first PG has been created out of masterConfg
|
||||
typedef struct dummpConfig_s {
|
||||
uint8_t dummy;
|
||||
} dummyConfig_t;
|
||||
PG_DECLARE(dummyConfig_t, dummyConfig);
|
||||
#define PG_DUMMY_CONFIG 1
|
||||
PG_REGISTER(dummyConfig_t, dummyConfig, PG_DUMMY_CONFIG, 0);
|
||||
|
||||
#ifdef STM32F10X_MD
|
||||
#define FLASH_PAGE_SIZE ((uint16_t)0x400)
|
||||
#endif
|
||||
extern uint8_t __config_start; // configured via linker script when building binaries.
|
||||
extern uint8_t __config_end;
|
||||
|
||||
#ifdef STM32F10X_HD
|
||||
#define FLASH_PAGE_SIZE ((uint16_t)0x800)
|
||||
#endif
|
||||
static uint16_t eepromConfigSize;
|
||||
|
||||
#if defined(STM32F745xx)
|
||||
#define FLASH_PAGE_SIZE ((uint32_t)0x40000)
|
||||
#endif
|
||||
typedef enum {
|
||||
CR_CLASSICATION_SYSTEM = 0,
|
||||
CR_CLASSICATION_PROFILE1 = 1,
|
||||
CR_CLASSICATION_PROFILE2 = 2,
|
||||
CR_CLASSICATION_PROFILE3 = 3,
|
||||
CR_CLASSICATION_PROFILE_LAST = CR_CLASSICATION_PROFILE3,
|
||||
} configRecordFlags_e;
|
||||
|
||||
#if defined(STM32F746xx)
|
||||
#define FLASH_PAGE_SIZE ((uint32_t)0x40000)
|
||||
#endif
|
||||
#define CR_CLASSIFICATION_MASK (0x3)
|
||||
|
||||
#if defined(STM32F722xx)
|
||||
#define FLASH_PAGE_SIZE ((uint32_t)0x20000)
|
||||
#endif
|
||||
// Header for the saved copy.
|
||||
typedef struct {
|
||||
uint8_t format;
|
||||
} PG_PACKED configHeader_t;
|
||||
|
||||
#if defined(STM32F40_41xxx)
|
||||
#define FLASH_PAGE_SIZE ((uint32_t)0x20000)
|
||||
#endif
|
||||
// Header for each stored PG.
|
||||
typedef struct {
|
||||
// split up.
|
||||
uint16_t size;
|
||||
pgn_t pgn;
|
||||
uint8_t version;
|
||||
|
||||
#if defined (STM32F411xE)
|
||||
#define FLASH_PAGE_SIZE ((uint32_t)0x20000)
|
||||
#endif
|
||||
// lower 2 bits used to indicate system or profile number, see CR_CLASSIFICATION_MASK
|
||||
uint8_t flags;
|
||||
|
||||
#endif
|
||||
uint8_t pg[];
|
||||
} PG_PACKED configRecord_t;
|
||||
|
||||
#if !defined(FLASH_SIZE) && !defined(FLASH_PAGE_COUNT)
|
||||
#ifdef STM32F10X_MD
|
||||
#define FLASH_PAGE_COUNT 128
|
||||
#endif
|
||||
|
||||
#ifdef STM32F10X_HD
|
||||
#define FLASH_PAGE_COUNT 128
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(FLASH_SIZE)
|
||||
#if defined(STM32F40_41xxx)
|
||||
#define FLASH_PAGE_COUNT 4
|
||||
#elif defined (STM32F411xE)
|
||||
#define FLASH_PAGE_COUNT 3
|
||||
#elif defined (STM32F722xx)
|
||||
#define FLASH_PAGE_COUNT 3
|
||||
#elif defined (STM32F745xx)
|
||||
#define FLASH_PAGE_COUNT 4
|
||||
#elif defined (STM32F746xx)
|
||||
#define FLASH_PAGE_COUNT 4
|
||||
#else
|
||||
#define FLASH_PAGE_COUNT ((FLASH_SIZE * 0x400) / FLASH_PAGE_SIZE)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(FLASH_PAGE_SIZE)
|
||||
#error "Flash page size not defined for target."
|
||||
#endif
|
||||
|
||||
#if !defined(FLASH_PAGE_COUNT)
|
||||
#error "Flash page count not defined for target."
|
||||
#endif
|
||||
|
||||
#if FLASH_SIZE <= 128
|
||||
#define FLASH_TO_RESERVE_FOR_CONFIG 0x800
|
||||
#else
|
||||
#define FLASH_TO_RESERVE_FOR_CONFIG 0x1000
|
||||
#endif
|
||||
|
||||
// use the last flash pages for storage
|
||||
#ifdef CUSTOM_FLASH_MEMORY_ADDRESS
|
||||
size_t custom_flash_memory_address = 0;
|
||||
#define CONFIG_START_FLASH_ADDRESS (custom_flash_memory_address)
|
||||
#else
|
||||
// use the last flash pages for storage
|
||||
#ifndef CONFIG_START_FLASH_ADDRESS
|
||||
#define CONFIG_START_FLASH_ADDRESS (0x08000000 + (uint32_t)((FLASH_PAGE_SIZE * FLASH_PAGE_COUNT) - FLASH_TO_RESERVE_FOR_CONFIG))
|
||||
#endif
|
||||
#endif
|
||||
// Footer for the saved copy.
|
||||
typedef struct {
|
||||
uint16_t terminator;
|
||||
} PG_PACKED configFooter_t;
|
||||
// checksum is appended just after footer. It is not included in footer to make checksum calculation consistent
|
||||
|
||||
// Used to check the compiler packing at build time.
|
||||
typedef struct {
|
||||
uint8_t byte;
|
||||
uint32_t word;
|
||||
} PG_PACKED packingTest_t;
|
||||
|
||||
void initEEPROM(void)
|
||||
{
|
||||
// Verify that this architecture packs as expected.
|
||||
BUILD_BUG_ON(offsetof(packingTest_t, byte) != 0);
|
||||
BUILD_BUG_ON(offsetof(packingTest_t, word) != 1);
|
||||
BUILD_BUG_ON(sizeof(packingTest_t) != 5);
|
||||
|
||||
BUILD_BUG_ON(sizeof(configHeader_t) != 1);
|
||||
BUILD_BUG_ON(sizeof(configFooter_t) != 2);
|
||||
BUILD_BUG_ON(sizeof(configRecord_t) != 6);
|
||||
}
|
||||
|
||||
static uint8_t calculateChecksum(const uint8_t *data, uint32_t length)
|
||||
static uint8_t updateChecksum(uint8_t chk, const void *data, uint32_t length)
|
||||
{
|
||||
uint8_t checksum = 0;
|
||||
const uint8_t *byteOffset;
|
||||
const uint8_t *p = (const uint8_t *)data;
|
||||
const uint8_t *pend = p + length;
|
||||
|
||||
for (byteOffset = data; byteOffset < (data + length); byteOffset++)
|
||||
checksum ^= *byteOffset;
|
||||
return checksum;
|
||||
for (; p != pend; p++) {
|
||||
chk ^= *p;
|
||||
}
|
||||
return chk;
|
||||
}
|
||||
|
||||
// Scan the EEPROM config. Returns true if the config is valid.
|
||||
bool isEEPROMContentValid(void)
|
||||
{
|
||||
const master_t *temp = (const master_t *) CONFIG_START_FLASH_ADDRESS;
|
||||
uint8_t checksum = 0;
|
||||
uint8_t chk = 0;
|
||||
const uint8_t *p = &__config_start;
|
||||
const configHeader_t *header = (const configHeader_t *)p;
|
||||
|
||||
// check version number
|
||||
if (EEPROM_CONF_VERSION != temp->version)
|
||||
if (header->format != EEPROM_CONF_VERSION) {
|
||||
return false;
|
||||
}
|
||||
chk = updateChecksum(chk, header, sizeof(*header));
|
||||
p += sizeof(*header);
|
||||
// include the transitional masterConfig record
|
||||
chk = updateChecksum(chk, p, sizeof(masterConfig));
|
||||
p += sizeof(masterConfig);
|
||||
|
||||
// check size and magic numbers
|
||||
if (temp->size != sizeof(master_t) || temp->magic_be != 0xBE || temp->magic_ef != 0xEF)
|
||||
return false;
|
||||
for (;;) {
|
||||
const configRecord_t *record = (const configRecord_t *)p;
|
||||
|
||||
if (strncasecmp(temp->boardIdentifier, TARGET_BOARD_IDENTIFIER, sizeof(TARGET_BOARD_IDENTIFIER)))
|
||||
return false;
|
||||
if (record->size == 0) {
|
||||
// Found the end. Stop scanning.
|
||||
break;
|
||||
}
|
||||
if (p + record->size >= &__config_end
|
||||
|| record->size < sizeof(*record)) {
|
||||
// Too big or too small.
|
||||
return false;
|
||||
}
|
||||
|
||||
// verify integrity of temporary copy
|
||||
checksum = calculateChecksum((const uint8_t *) temp, sizeof(master_t));
|
||||
if (checksum != 0)
|
||||
return false;
|
||||
chk = updateChecksum(chk, p, record->size);
|
||||
|
||||
// looks good, let's roll!
|
||||
p += record->size;
|
||||
}
|
||||
|
||||
const configFooter_t *footer = (const configFooter_t *)p;
|
||||
chk = updateChecksum(chk, footer, sizeof(*footer));
|
||||
p += sizeof(*footer);
|
||||
chk = ~chk;
|
||||
const uint8_t checkSum = *p;
|
||||
p += sizeof(checkSum);
|
||||
eepromConfigSize = p - &__config_start;
|
||||
return chk == checkSum;
|
||||
}
|
||||
|
||||
uint16_t getEEPROMConfigSize(void)
|
||||
{
|
||||
return eepromConfigSize;
|
||||
}
|
||||
|
||||
// find config record for reg + classification (profile info) in EEPROM
|
||||
// return NULL when record is not found
|
||||
// this function assumes that EEPROM content is valid
|
||||
static const configRecord_t *findEEPROM(const pgRegistry_t *reg, configRecordFlags_e classification)
|
||||
{
|
||||
const uint8_t *p = &__config_start;
|
||||
p += sizeof(configHeader_t); // skip header
|
||||
p += sizeof(master_t); // skip the transitional master_t record
|
||||
while (true) {
|
||||
const configRecord_t *record = (const configRecord_t *)p;
|
||||
if (record->size == 0
|
||||
|| p + record->size >= &__config_end
|
||||
|| record->size < sizeof(*record))
|
||||
break;
|
||||
if (pgN(reg) == record->pgn
|
||||
&& (record->flags & CR_CLASSIFICATION_MASK) == classification)
|
||||
return record;
|
||||
p += record->size;
|
||||
}
|
||||
// record not found
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Initialize all PG records from EEPROM.
|
||||
// This functions processes all PGs sequentially, scanning EEPROM for each one. This is suboptimal,
|
||||
// but each PG is loaded/initialized exactly once and in defined order.
|
||||
bool loadEEPROM(void)
|
||||
{
|
||||
// read in the transitional masterConfig record
|
||||
const uint8_t *p = &__config_start;
|
||||
p += sizeof(configHeader_t); // skip header
|
||||
masterConfig = *(master_t*)p;
|
||||
|
||||
PG_FOREACH(reg) {
|
||||
configRecordFlags_e cls_start, cls_end;
|
||||
if (pgIsSystem(reg)) {
|
||||
cls_start = CR_CLASSICATION_SYSTEM;
|
||||
cls_end = CR_CLASSICATION_SYSTEM;
|
||||
} else {
|
||||
cls_start = CR_CLASSICATION_PROFILE1;
|
||||
cls_end = CR_CLASSICATION_PROFILE_LAST;
|
||||
}
|
||||
for (configRecordFlags_e cls = cls_start; cls <= cls_end; cls++) {
|
||||
int profileIndex = cls - cls_start;
|
||||
const configRecord_t *rec = findEEPROM(reg, cls);
|
||||
if (rec) {
|
||||
// config from EEPROM is available, use it to initialize PG. pgLoad will handle version mismatch
|
||||
pgLoad(reg, profileIndex, rec->pg, rec->size - offsetof(configRecord_t, pg), rec->version);
|
||||
} else {
|
||||
pgReset(reg, profileIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(STM32F7)
|
||||
|
||||
// FIXME: HAL for now this will only work for F4/F7 as flash layout is different
|
||||
void writeEEPROM(void)
|
||||
static bool writeSettingsToEEPROM(void)
|
||||
{
|
||||
// Generate compile time error if the config does not fit in the reserved area of flash.
|
||||
BUILD_BUG_ON(sizeof(master_t) > FLASH_TO_RESERVE_FOR_CONFIG);
|
||||
config_streamer_t streamer;
|
||||
config_streamer_init(&streamer);
|
||||
|
||||
HAL_StatusTypeDef status;
|
||||
uint32_t wordOffset;
|
||||
int8_t attemptsRemaining = 3;
|
||||
config_streamer_start(&streamer, (uintptr_t)&__config_start, &__config_end - &__config_start);
|
||||
uint8_t chk = 0;
|
||||
|
||||
suspendRxSignal();
|
||||
configHeader_t header = {
|
||||
.format = EEPROM_CONF_VERSION,
|
||||
};
|
||||
|
||||
// prepare checksum/version constants
|
||||
masterConfig.version = EEPROM_CONF_VERSION;
|
||||
masterConfig.size = sizeof(master_t);
|
||||
masterConfig.magic_be = 0xBE;
|
||||
masterConfig.magic_ef = 0xEF;
|
||||
masterConfig.chk = 0; // erase checksum before recalculating
|
||||
masterConfig.chk = calculateChecksum((const uint8_t *) &masterConfig, sizeof(master_t));
|
||||
config_streamer_write(&streamer, (uint8_t *)&header, sizeof(header));
|
||||
chk = updateChecksum(chk, (uint8_t *)&header, sizeof(header));
|
||||
// write the transitional masterConfig record
|
||||
config_streamer_write(&streamer, (uint8_t *)&masterConfig, sizeof(masterConfig));
|
||||
chk = updateChecksum(chk, (uint8_t *)&masterConfig, sizeof(masterConfig));
|
||||
PG_FOREACH(reg) {
|
||||
const uint16_t regSize = pgSize(reg);
|
||||
configRecord_t record = {
|
||||
.size = sizeof(configRecord_t) + regSize,
|
||||
.pgn = pgN(reg),
|
||||
.version = pgVersion(reg),
|
||||
.flags = 0
|
||||
};
|
||||
|
||||
// write it
|
||||
/* Unlock the Flash to enable the flash control register access *************/
|
||||
HAL_FLASH_Unlock();
|
||||
while (attemptsRemaining--)
|
||||
{
|
||||
/* Fill EraseInit structure*/
|
||||
FLASH_EraseInitTypeDef EraseInitStruct = {0};
|
||||
EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS;
|
||||
EraseInitStruct.VoltageRange = FLASH_VOLTAGE_RANGE_3; // 2.7-3.6V
|
||||
EraseInitStruct.Sector = (FLASH_SECTOR_TOTAL-1);
|
||||
EraseInitStruct.NbSectors = 1;
|
||||
uint32_t SECTORError;
|
||||
status = HAL_FLASHEx_Erase(&EraseInitStruct, &SECTORError);
|
||||
if (status != HAL_OK)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (wordOffset = 0; wordOffset < sizeof(master_t); wordOffset += 4)
|
||||
{
|
||||
status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, CONFIG_START_FLASH_ADDRESS + wordOffset, *(uint32_t *) ((char *) &masterConfig + wordOffset));
|
||||
if(status != HAL_OK)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (pgIsSystem(reg)) {
|
||||
// write the only instance
|
||||
record.flags |= CR_CLASSICATION_SYSTEM;
|
||||
config_streamer_write(&streamer, (uint8_t *)&record, sizeof(record));
|
||||
chk = updateChecksum(chk, (uint8_t *)&record, sizeof(record));
|
||||
config_streamer_write(&streamer, reg->address, regSize);
|
||||
chk = updateChecksum(chk, reg->address, regSize);
|
||||
} else {
|
||||
// write one instance for each profile
|
||||
for (uint8_t profileIndex = 0; profileIndex < MAX_PROFILE_COUNT; profileIndex++) {
|
||||
record.flags = 0;
|
||||
|
||||
record.flags |= ((profileIndex + 1) & CR_CLASSIFICATION_MASK);
|
||||
config_streamer_write(&streamer, (uint8_t *)&record, sizeof(record));
|
||||
chk = updateChecksum(chk, (uint8_t *)&record, sizeof(record));
|
||||
const uint8_t *address = reg->address + (regSize * profileIndex);
|
||||
config_streamer_write(&streamer, address, regSize);
|
||||
chk = updateChecksum(chk, address, regSize);
|
||||
}
|
||||
}
|
||||
if (status == HAL_OK) {
|
||||
break;
|
||||
}
|
||||
|
||||
configFooter_t footer = {
|
||||
.terminator = 0,
|
||||
};
|
||||
|
||||
config_streamer_write(&streamer, (uint8_t *)&footer, sizeof(footer));
|
||||
chk = updateChecksum(chk, (uint8_t *)&footer, sizeof(footer));
|
||||
|
||||
// append checksum now
|
||||
chk = ~chk;
|
||||
config_streamer_write(&streamer, &chk, sizeof(chk));
|
||||
|
||||
config_streamer_flush(&streamer);
|
||||
|
||||
bool success = config_streamer_finish(&streamer) == 0;
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void writeConfigToEEPROM(void)
|
||||
{
|
||||
bool success = false;
|
||||
// write it
|
||||
for (int attempt = 0; attempt < 3 && !success; attempt++) {
|
||||
if (writeSettingsToEEPROM()) {
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
HAL_FLASH_Lock();
|
||||
|
||||
if (success && isEEPROMContentValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Flash write failed - just die now
|
||||
if (status != HAL_OK || !isEEPROMContentValid()) {
|
||||
failureMode(FAILURE_FLASH_WRITE_FAILED);
|
||||
}
|
||||
|
||||
resumeRxSignal();
|
||||
}
|
||||
#else
|
||||
void writeEEPROM(void)
|
||||
{
|
||||
// Generate compile time error if the config does not fit in the reserved area of flash.
|
||||
BUILD_BUG_ON(sizeof(master_t) > FLASH_TO_RESERVE_FOR_CONFIG);
|
||||
|
||||
FLASH_Status status = 0;
|
||||
uint32_t wordOffset;
|
||||
int8_t attemptsRemaining = 3;
|
||||
|
||||
suspendRxSignal();
|
||||
|
||||
// prepare checksum/version constants
|
||||
masterConfig.version = EEPROM_CONF_VERSION;
|
||||
masterConfig.size = sizeof(master_t);
|
||||
masterConfig.magic_be = 0xBE;
|
||||
masterConfig.magic_ef = 0xEF;
|
||||
masterConfig.chk = 0; // erase checksum before recalculating
|
||||
masterConfig.chk = calculateChecksum((const uint8_t *) &masterConfig, sizeof(master_t));
|
||||
|
||||
// write it
|
||||
FLASH_Unlock();
|
||||
while (attemptsRemaining--) {
|
||||
#if defined(STM32F4)
|
||||
FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR);
|
||||
#elif defined(STM32F303)
|
||||
FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPERR);
|
||||
#elif defined(STM32F10X)
|
||||
FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);
|
||||
#endif
|
||||
for (wordOffset = 0; wordOffset < sizeof(master_t); wordOffset += 4) {
|
||||
if (wordOffset % FLASH_PAGE_SIZE == 0) {
|
||||
#if defined(STM32F40_41xxx)
|
||||
status = FLASH_EraseSector(FLASH_Sector_8, VoltageRange_3); //0x08080000 to 0x080A0000
|
||||
#elif defined (STM32F411xE)
|
||||
status = FLASH_EraseSector(FLASH_Sector_7, VoltageRange_3); //0x08060000 to 0x08080000
|
||||
#else
|
||||
status = FLASH_ErasePage(CONFIG_START_FLASH_ADDRESS + wordOffset);
|
||||
#endif
|
||||
if (status != FLASH_COMPLETE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
status = FLASH_ProgramWord(CONFIG_START_FLASH_ADDRESS + wordOffset,
|
||||
*(uint32_t *) ((char *) &masterConfig + wordOffset));
|
||||
if (status != FLASH_COMPLETE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (status == FLASH_COMPLETE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
FLASH_Lock();
|
||||
|
||||
// Flash write failed - just die now
|
||||
if (status != FLASH_COMPLETE || !isEEPROMContentValid()) {
|
||||
failureMode(FAILURE_FLASH_WRITE_FAILED);
|
||||
}
|
||||
|
||||
resumeRxSignal();
|
||||
}
|
||||
#endif
|
||||
|
||||
void readEEPROM(void)
|
||||
{
|
||||
// Sanity check
|
||||
if (!isEEPROMContentValid())
|
||||
failureMode(FAILURE_INVALID_EEPROM_CONTENTS);
|
||||
|
||||
suspendRxSignal();
|
||||
|
||||
// Read flash
|
||||
memcpy(&masterConfig, (char *) CONFIG_START_FLASH_ADDRESS, sizeof(master_t));
|
||||
|
||||
if (masterConfig.current_profile_index > MAX_PROFILE_COUNT - 1) // sanity check
|
||||
masterConfig.current_profile_index = 0;
|
||||
|
||||
setProfile(masterConfig.current_profile_index);
|
||||
|
||||
validateAndFixConfig();
|
||||
activateConfig();
|
||||
|
||||
resumeRxSignal();
|
||||
failureMode(FAILURE_FLASH_WRITE_FAILED);
|
||||
}
|
||||
|
|
|
@ -17,9 +17,12 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define EEPROM_CONF_VERSION 154
|
||||
|
||||
void initEEPROM(void);
|
||||
void writeEEPROM();
|
||||
void readEEPROM(void);
|
||||
bool isEEPROMContentValid(void);
|
||||
bool loadEEPROM(void);
|
||||
void writeConfigToEEPROM(void);
|
||||
uint16_t getEEPROMConfigSize(void);
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "common/axis.h"
|
||||
#include "fc/config.h"
|
||||
#include "fc/rc_controls.h"
|
||||
#include "flight/pid.h"
|
||||
|
|
40
src/main/config/config_reset.h
Normal file
40
src/main/config/config_reset.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* This file is part of Cleanflight.
|
||||
*
|
||||
* Cleanflight is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Cleanflight is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef __UNIQL
|
||||
# define __UNIQL_CONCAT2(x,y) x ## y
|
||||
# define __UNIQL_CONCAT(x,y) __UNIQL_CONCAT2(x,y)
|
||||
# define __UNIQL(x) __UNIQL_CONCAT(x,__LINE__)
|
||||
#endif
|
||||
|
||||
// overwrite _name with data passed as arguments. This version forces GCC to really copy data
|
||||
// It is not possible to use multiple RESET_CONFIGs on single line (__UNIQL limitation)
|
||||
#define RESET_CONFIG(_type, _name, ...) \
|
||||
static const _type __UNIQL(_reset_template_) = { \
|
||||
__VA_ARGS__ \
|
||||
}; \
|
||||
memcpy((_name), &__UNIQL(_reset_template_), sizeof(*(_name))); \
|
||||
/**/
|
||||
|
||||
// overwrite _name with data passed as arguments. GCC is allowed to set structure field-by-field
|
||||
#define RESET_CONFIG_2(_type, _name, ...) \
|
||||
*(_name) = (_type) { \
|
||||
__VA_ARGS__ \
|
||||
}; \
|
||||
/**/
|
263
src/main/config/config_streamer.c
Normal file
263
src/main/config/config_streamer.c
Normal file
|
@ -0,0 +1,263 @@
|
|||
/*
|
||||
* This file is part of Cleanflight.
|
||||
*
|
||||
* Cleanflight is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Cleanflight is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
#include "drivers/system.h"
|
||||
|
||||
#include "config/config_streamer.h"
|
||||
|
||||
extern uint8_t __config_start; // configured via linker script when building binaries.
|
||||
extern uint8_t __config_end;
|
||||
|
||||
#if !defined(FLASH_PAGE_SIZE)
|
||||
// F1
|
||||
# if defined(STM32F10X_MD)
|
||||
# define FLASH_PAGE_SIZE (0x400)
|
||||
# elif defined(STM32F10X_HD)
|
||||
# define FLASH_PAGE_SIZE (0x800)
|
||||
// F3
|
||||
# elif defined(STM32F303xC)
|
||||
# define FLASH_PAGE_SIZE (0x800)
|
||||
// F4
|
||||
# elif defined(STM32F40_41xxx)
|
||||
# define FLASH_PAGE_SIZE ((uint32_t)0x20000)
|
||||
# elif defined (STM32F411xE)
|
||||
# define FLASH_PAGE_SIZE ((uint32_t)0x20000)
|
||||
# elif defined(STM32F427_437xx)
|
||||
# define FLASH_PAGE_SIZE ((uint32_t)0x20000) // 128K sectors
|
||||
// F7
|
||||
#elif defined(STM32F722xx)
|
||||
# define FLASH_PAGE_SIZE ((uint32_t)0x20000)
|
||||
# elif defined(STM32F745xx)
|
||||
# define FLASH_PAGE_SIZE ((uint32_t)0x40000)
|
||||
# elif defined(STM32F746xx)
|
||||
# define FLASH_PAGE_SIZE ((uint32_t)0x40000)
|
||||
# elif defined(UNIT_TEST)
|
||||
# define FLASH_PAGE_SIZE (0x400)
|
||||
# else
|
||||
# error "Flash page size not defined for target."
|
||||
# endif
|
||||
#endif
|
||||
|
||||
void config_streamer_init(config_streamer_t *c)
|
||||
{
|
||||
memset(c, 0, sizeof(*c));
|
||||
}
|
||||
|
||||
void config_streamer_start(config_streamer_t *c, uintptr_t base, int size)
|
||||
{
|
||||
// base must start at FLASH_PAGE_SIZE boundary
|
||||
c->address = base;
|
||||
c->size = size;
|
||||
if (!c->unlocked) {
|
||||
#if defined(STM32F7)
|
||||
HAL_FLASH_Unlock();
|
||||
#else
|
||||
FLASH_Unlock();
|
||||
#endif
|
||||
c->unlocked = true;
|
||||
}
|
||||
|
||||
#if defined(STM32F10X)
|
||||
FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);
|
||||
#elif defined(STM32F303)
|
||||
FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPERR);
|
||||
#elif defined(STM32F4)
|
||||
FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR);
|
||||
#elif defined(STM32F7)
|
||||
// NOP
|
||||
#elif defined(UNIT_TEST)
|
||||
// NOP
|
||||
#else
|
||||
# error "Unsupported CPU"
|
||||
#endif
|
||||
c->err = 0;
|
||||
}
|
||||
|
||||
#if defined(STM32F7)
|
||||
/*
|
||||
Sector 0 0x08000000 - 0x08007FFF 32 Kbytes
|
||||
Sector 1 0x08008000 - 0x0800FFFF 32 Kbytes
|
||||
Sector 2 0x08010000 - 0x08017FFF 32 Kbytes
|
||||
Sector 3 0x08018000 - 0x0801FFFF 32 Kbytes
|
||||
Sector 4 0x08020000 - 0x0803FFFF 128 Kbytes
|
||||
Sector 5 0x08040000 - 0x0807FFFF 256 Kbytes
|
||||
Sector 6 0x08080000 - 0x080BFFFF 256 Kbytes
|
||||
Sector 7 0x080C0000 - 0x080FFFFF 256 Kbytes
|
||||
*/
|
||||
|
||||
static uint32_t getFLASHSectorForEEPROM(void)
|
||||
{
|
||||
if ((uint32_t)&__config_start <= 0x08007FFF)
|
||||
return FLASH_SECTOR_0;
|
||||
if ((uint32_t)&__config_start <= 0x0800FFFF)
|
||||
return FLASH_SECTOR_1;
|
||||
if ((uint32_t)&__config_start <= 0x08017FFF)
|
||||
return FLASH_SECTOR_2;
|
||||
if ((uint32_t)&__config_start <= 0x0801FFFF)
|
||||
return FLASH_SECTOR_3;
|
||||
if ((uint32_t)&__config_start <= 0x0803FFFF)
|
||||
return FLASH_SECTOR_4;
|
||||
if ((uint32_t)&__config_start <= 0x0807FFFF)
|
||||
return FLASH_SECTOR_5;
|
||||
if ((uint32_t)&__config_start <= 0x080BFFFF)
|
||||
return FLASH_SECTOR_6;
|
||||
if ((uint32_t)&__config_start <= 0x080FFFFF)
|
||||
return FLASH_SECTOR_7;
|
||||
|
||||
// Not good
|
||||
while (1) {
|
||||
failureMode(FAILURE_FLASH_WRITE_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined(STM32F4)
|
||||
/*
|
||||
Sector 0 0x08000000 - 0x08003FFF 16 Kbytes
|
||||
Sector 1 0x08004000 - 0x08007FFF 16 Kbytes
|
||||
Sector 2 0x08008000 - 0x0800BFFF 16 Kbytes
|
||||
Sector 3 0x0800C000 - 0x0800FFFF 16 Kbytes
|
||||
Sector 4 0x08010000 - 0x0801FFFF 64 Kbytes
|
||||
Sector 5 0x08020000 - 0x0803FFFF 128 Kbytes
|
||||
Sector 6 0x08040000 - 0x0805FFFF 128 Kbytes
|
||||
Sector 7 0x08060000 - 0x0807FFFF 128 Kbytes
|
||||
Sector 8 0x08080000 - 0x0809FFFF 128 Kbytes
|
||||
Sector 9 0x080A0000 - 0x080BFFFF 128 Kbytes
|
||||
Sector 10 0x080C0000 - 0x080DFFFF 128 Kbytes
|
||||
Sector 11 0x080E0000 - 0x080FFFFF 128 Kbytes
|
||||
*/
|
||||
|
||||
static uint32_t getFLASHSectorForEEPROM(void)
|
||||
{
|
||||
if ((uint32_t)&__config_start <= 0x08003FFF)
|
||||
return FLASH_Sector_0;
|
||||
if ((uint32_t)&__config_start <= 0x08007FFF)
|
||||
return FLASH_Sector_1;
|
||||
if ((uint32_t)&__config_start <= 0x0800BFFF)
|
||||
return FLASH_Sector_2;
|
||||
if ((uint32_t)&__config_start <= 0x0800FFFF)
|
||||
return FLASH_Sector_3;
|
||||
if ((uint32_t)&__config_start <= 0x0801FFFF)
|
||||
return FLASH_Sector_4;
|
||||
if ((uint32_t)&__config_start <= 0x0803FFFF)
|
||||
return FLASH_Sector_5;
|
||||
if ((uint32_t)&__config_start <= 0x0805FFFF)
|
||||
return FLASH_Sector_6;
|
||||
if ((uint32_t)&__config_start <= 0x0807FFFF)
|
||||
return FLASH_Sector_7;
|
||||
if ((uint32_t)&__config_start <= 0x0809FFFF)
|
||||
return FLASH_Sector_8;
|
||||
if ((uint32_t)&__config_start <= 0x080DFFFF)
|
||||
return FLASH_Sector_9;
|
||||
if ((uint32_t)&__config_start <= 0x080BFFFF)
|
||||
return FLASH_Sector_10;
|
||||
if ((uint32_t)&__config_start <= 0x080FFFFF)
|
||||
return FLASH_Sector_11;
|
||||
|
||||
// Not good
|
||||
while (1) {
|
||||
failureMode(FAILURE_FLASH_WRITE_FAILED);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static int write_word(config_streamer_t *c, uint32_t value)
|
||||
{
|
||||
if (c->err != 0) {
|
||||
return c->err;
|
||||
}
|
||||
#if defined(STM32F7)
|
||||
if (c->address % FLASH_PAGE_SIZE == 0) {
|
||||
FLASH_EraseInitTypeDef EraseInitStruct = {
|
||||
.TypeErase = FLASH_TYPEERASE_SECTORS,
|
||||
.VoltageRange = FLASH_VOLTAGE_RANGE_3, // 2.7-3.6V
|
||||
.NbSectors = 1
|
||||
};
|
||||
EraseInitStruct.Sector = getFLASHSectorForEEPROM();
|
||||
uint32_t SECTORError;
|
||||
const HAL_StatusTypeDef status = HAL_FLASHEx_Erase(&EraseInitStruct, &SECTORError);
|
||||
if (status != HAL_OK){
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
const HAL_StatusTypeDef status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, c->address, value);
|
||||
if (status != HAL_OK) {
|
||||
return -2;
|
||||
}
|
||||
#else
|
||||
if (c->address % FLASH_PAGE_SIZE == 0) {
|
||||
#if defined(STM32F4)
|
||||
const FLASH_Status status = FLASH_EraseSector(getFLASHSectorForEEPROM(), VoltageRange_3); //0x08080000 to 0x080A0000
|
||||
#else
|
||||
const FLASH_Status status = FLASH_ErasePage(c->address);
|
||||
#endif
|
||||
if (status != FLASH_COMPLETE) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
const FLASH_Status status = FLASH_ProgramWord(c->address, value);
|
||||
if (status != FLASH_COMPLETE) {
|
||||
return -2;
|
||||
}
|
||||
#endif
|
||||
c->address += sizeof(value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_streamer_write(config_streamer_t *c, const uint8_t *p, uint32_t size)
|
||||
{
|
||||
for (const uint8_t *pat = p; pat != (uint8_t*)p + size; pat++) {
|
||||
c->buffer.b[c->at++] = *pat;
|
||||
|
||||
if (c->at == sizeof(c->buffer)) {
|
||||
c->err = write_word(c, c->buffer.w);
|
||||
c->at = 0;
|
||||
}
|
||||
}
|
||||
return c->err;
|
||||
}
|
||||
|
||||
int config_streamer_status(config_streamer_t *c)
|
||||
{
|
||||
return c->err;
|
||||
}
|
||||
|
||||
int config_streamer_flush(config_streamer_t *c)
|
||||
{
|
||||
if (c->at != 0) {
|
||||
memset(c->buffer.b + c->at, 0, sizeof(c->buffer) - c->at);
|
||||
c->err = write_word(c, c->buffer.w);
|
||||
c->at = 0;
|
||||
}
|
||||
return c-> err;
|
||||
}
|
||||
|
||||
int config_streamer_finish(config_streamer_t *c)
|
||||
{
|
||||
if (c->unlocked) {
|
||||
#if defined(STM32F7)
|
||||
HAL_FLASH_Lock();
|
||||
#else
|
||||
FLASH_Lock();
|
||||
#endif
|
||||
c->unlocked = false;
|
||||
}
|
||||
return c->err;
|
||||
}
|
45
src/main/config/config_streamer.h
Normal file
45
src/main/config/config_streamer.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* This file is part of Cleanflight.
|
||||
*
|
||||
* Cleanflight is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Cleanflight is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// Streams data out to the EEPROM, padding to the write size as
|
||||
// needed, and updating the checksum as it goes.
|
||||
|
||||
typedef struct config_streamer_s {
|
||||
uintptr_t address;
|
||||
int size;
|
||||
union {
|
||||
uint8_t b[4];
|
||||
uint32_t w;
|
||||
} buffer;
|
||||
int at;
|
||||
int err;
|
||||
bool unlocked;
|
||||
} config_streamer_t;
|
||||
|
||||
void config_streamer_init(config_streamer_t *c);
|
||||
|
||||
void config_streamer_start(config_streamer_t *c, uintptr_t base, int size);
|
||||
int config_streamer_write(config_streamer_t *c, const uint8_t *p, uint32_t size);
|
||||
int config_streamer_flush(config_streamer_t *c);
|
||||
|
||||
int config_streamer_finish(config_streamer_t *c);
|
||||
int config_streamer_status(config_streamer_t *c);
|
|
@ -20,6 +20,8 @@
|
|||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "build/build_config.h"
|
||||
|
||||
typedef uint16_t pgn_t;
|
||||
|
||||
// parameter group registry flags
|
||||
|
@ -54,6 +56,7 @@ static inline uint16_t pgN(const pgRegistry_t* reg) {return reg->pgn & PGR_PGN_M
|
|||
static inline uint8_t pgVersion(const pgRegistry_t* reg) {return reg->pgn >> 12;}
|
||||
static inline uint16_t pgSize(const pgRegistry_t* reg) {return reg->size & PGR_SIZE_MASK;}
|
||||
static inline uint16_t pgIsSystem(const pgRegistry_t* reg) {return (reg->size & PGR_SIZE_PROFILE_FLAG) == 0;}
|
||||
static inline uint16_t pgIsProfile(const pgRegistry_t* reg) {return (reg->size & PGR_SIZE_PROFILE_FLAG) == PGR_SIZE_PROFILE_FLAG;}
|
||||
|
||||
#define PG_PACKED __attribute__((packed))
|
||||
|
||||
|
@ -100,22 +103,25 @@ extern const uint8_t __pg_resetdata_end[];
|
|||
// Declare system config
|
||||
#define PG_DECLARE(_type, _name) \
|
||||
extern _type _name ## _System; \
|
||||
static inline _type* _name(void) { return &_name ## _System; } \
|
||||
static inline const _type* _name(void) { return &_name ## _System; }\
|
||||
static inline _type* _name ## Mutable(void) { return &_name ## _System; }\
|
||||
struct _dummy \
|
||||
/**/
|
||||
|
||||
// Declare system config array
|
||||
#define PG_DECLARE_ARR(_type, _size, _name) \
|
||||
#define PG_DECLARE_ARRAY(_type, _size, _name) \
|
||||
extern _type _name ## _SystemArray[_size]; \
|
||||
static inline _type* _name(int _index) { return &_name ## _SystemArray[_index]; } \
|
||||
static inline _type (* _name ## _arr(void))[_size] { return &_name ## _SystemArray; } \
|
||||
static inline const _type* _name(int _index) { return &_name ## _SystemArray[_index]; } \
|
||||
static inline _type* _name ## Mutable(int _index) { return &_name ## _SystemArray[_index]; } \
|
||||
static inline _type (* _name ## _array(void))[_size] { return &_name ## _SystemArray; } \
|
||||
struct _dummy \
|
||||
/**/
|
||||
|
||||
// Declare profile config
|
||||
#define PG_DECLARE_PROFILE(_type, _name) \
|
||||
extern _type *_name ## _ProfileCurrent; \
|
||||
static inline _type* _name(void) { return _name ## _ProfileCurrent; } \
|
||||
static inline const _type* _name(void) { return _name ## _ProfileCurrent; } \
|
||||
static inline _type* _name ## Mutable(void) { return _name ## _ProfileCurrent; } \
|
||||
struct _dummy \
|
||||
/**/
|
||||
|
||||
|
@ -148,7 +154,7 @@ extern const uint8_t __pg_resetdata_end[];
|
|||
/**/
|
||||
|
||||
// Register system config array
|
||||
#define PG_REGISTER_ARR_I(_type, _size, _name, _pgn, _version, _reset) \
|
||||
#define PG_REGISTER_ARRAY_I(_type, _size, _name, _pgn, _version, _reset) \
|
||||
_type _name ## _SystemArray[_size]; \
|
||||
extern const pgRegistry_t _name ##_Registry; \
|
||||
const pgRegistry_t _name ## _Registry PG_REGISTER_ATTRIBUTES = { \
|
||||
|
@ -160,20 +166,20 @@ extern const uint8_t __pg_resetdata_end[];
|
|||
} \
|
||||
/**/
|
||||
|
||||
#define PG_REGISTER_ARR(_type, _size, _name, _pgn, _version) \
|
||||
PG_REGISTER_ARR_I(_type, _size, _name, _pgn, _version, .reset = {.ptr = 0}) \
|
||||
#define PG_REGISTER_ARRAY(_type, _size, _name, _pgn, _version) \
|
||||
PG_REGISTER_ARRAY_I(_type, _size, _name, _pgn, _version, .reset = {.ptr = 0}) \
|
||||
/**/
|
||||
|
||||
#define PG_REGISTER_ARR_WITH_RESET_FN(_type, _size, _name, _pgn, _version) \
|
||||
#define PG_REGISTER_ARRAY_WITH_RESET_FN(_type, _size, _name, _pgn, _version) \
|
||||
extern void pgResetFn_ ## _name(_type *); \
|
||||
PG_REGISTER_ARR_I(_type, _size, _name, _pgn, _version, .reset = {.fn = (pgResetFunc*)&pgResetFn_ ## _name}) \
|
||||
PG_REGISTER_ARRAY_I(_type, _size, _name, _pgn, _version, .reset = {.fn = (pgResetFunc*)&pgResetFn_ ## _name}) \
|
||||
/**/
|
||||
|
||||
#if 0
|
||||
// ARRAY reset mechanism is not implemented yet, only few places in code would benefit from it - See pgResetInstance
|
||||
#define PG_REGISTER_ARR_WITH_RESET_TEMPLATE(_type, _size, _name, _pgn, _version) \
|
||||
#define PG_REGISTER_ARRAY_WITH_RESET_TEMPLATE(_type, _size, _name, _pgn, _version) \
|
||||
extern const _type pgResetTemplate_ ## _name; \
|
||||
PG_REGISTER_ARR_I(_type, _size, _name, _pgn, _version, .reset = {.ptr = (void*)&pgResetTemplate_ ## _name}) \
|
||||
PG_REGISTER_ARRAY_I(_type, _size, _name, _pgn, _version, .reset = {.ptr = (void*)&pgResetTemplate_ ## _name}) \
|
||||
/**/
|
||||
#endif
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
*/
|
||||
|
||||
// FC configuration
|
||||
#define PG_FAILSAFE_CONFIG 1 // strruct OK
|
||||
#define PG_FAILSAFE_CONFIG 1 // struct OK
|
||||
#define PG_BOARD_ALIGNMENT 2 // struct OK
|
||||
#define PG_GIMBAL_CONFIG 3 // struct OK
|
||||
#define PG_MOTOR_MIXER 4 // two structs mixerConfig_t servoMixerConfig_t
|
||||
|
|
|
@ -17,6 +17,9 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
void systemInit(void);
|
||||
void delayMicroseconds(uint32_t us);
|
||||
void delay(uint32_t ms);
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <math.h>
|
||||
#include <ctype.h>
|
||||
|
||||
//#define USE_PARAMETER_GROUPS
|
||||
#include "platform.h"
|
||||
|
||||
// FIXME remove this for targets that don't need a CLI. Perhaps use a no-op macro when USE_CLI is not enabled
|
||||
|
@ -48,6 +49,9 @@ uint8_t cliMode = 0;
|
|||
#include "config/config_master.h"
|
||||
#include "config/feature.h"
|
||||
|
||||
#include "config/parameter_group.h"
|
||||
#include "config/parameter_group_ids.h"
|
||||
|
||||
#include "drivers/accgyro.h"
|
||||
#include "drivers/buf_writer.h"
|
||||
#include "drivers/bus_i2c.h"
|
||||
|
@ -113,16 +117,6 @@ static uint8_t cliWriteBuffer[sizeof(*cliWriter) + 128];
|
|||
static char cliBuffer[48];
|
||||
static uint32_t bufferIndex = 0;
|
||||
|
||||
typedef enum {
|
||||
DUMP_MASTER = (1 << 0),
|
||||
DUMP_PROFILE = (1 << 1),
|
||||
DUMP_RATES = (1 << 2),
|
||||
DUMP_ALL = (1 << 3),
|
||||
DO_DIFF = (1 << 4),
|
||||
SHOW_DEFAULTS = (1 << 5),
|
||||
HIDE_UNUSED = (1 << 6)
|
||||
} dumpFlags_e;
|
||||
|
||||
static const char* const emptyName = "-";
|
||||
|
||||
#ifndef USE_QUAD_MIXER_ONLY
|
||||
|
@ -448,21 +442,21 @@ static const lookupTableEntry_t lookupTables[] = {
|
|||
#define VALUE_MODE_OFFSET 6
|
||||
|
||||
typedef enum {
|
||||
// value type
|
||||
// value type, bits 0-3
|
||||
VAR_UINT8 = (0 << VALUE_TYPE_OFFSET),
|
||||
VAR_INT8 = (1 << VALUE_TYPE_OFFSET),
|
||||
VAR_UINT16 = (2 << VALUE_TYPE_OFFSET),
|
||||
VAR_INT16 = (3 << VALUE_TYPE_OFFSET),
|
||||
//VAR_UINT32 = (4 << VALUE_TYPE_OFFSET),
|
||||
VAR_FLOAT = (5 << VALUE_TYPE_OFFSET),
|
||||
VAR_FLOAT = (5 << VALUE_TYPE_OFFSET), // 0x05
|
||||
|
||||
// value section
|
||||
// value section, bits 4-5
|
||||
MASTER_VALUE = (0 << VALUE_SECTION_OFFSET),
|
||||
PROFILE_VALUE = (1 << VALUE_SECTION_OFFSET),
|
||||
PROFILE_RATE_VALUE = (2 << VALUE_SECTION_OFFSET),
|
||||
PROFILE_RATE_VALUE = (2 << VALUE_SECTION_OFFSET), // 0x20
|
||||
// value mode
|
||||
MODE_DIRECT = (0 << VALUE_MODE_OFFSET),
|
||||
MODE_LOOKUP = (1 << VALUE_MODE_OFFSET)
|
||||
MODE_DIRECT = (0 << VALUE_MODE_OFFSET), // 0x40
|
||||
MODE_LOOKUP = (1 << VALUE_MODE_OFFSET) // 0x80
|
||||
} cliValueFlag_e;
|
||||
|
||||
#define VALUE_TYPE_MASK (0x0F)
|
||||
|
@ -483,6 +477,22 @@ typedef union {
|
|||
cliMinMaxConfig_t minmax;
|
||||
} cliValueConfig_t;
|
||||
|
||||
#ifdef USE_PARAMETER_GROUPS
|
||||
typedef struct {
|
||||
const char *name;
|
||||
const uint8_t type; // see cliValueFlag_e
|
||||
const cliValueConfig_t config;
|
||||
|
||||
pgn_t pgn;
|
||||
uint16_t offset;
|
||||
} __attribute__((packed)) clivalue_t;
|
||||
|
||||
static const clivalue_t valueTable[] = {
|
||||
{ "dummy", VAR_UINT8 | MASTER_VALUE, .config.minmax = { 0, 255 }, 0, 0 }
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
const uint8_t type; // see cliValueFlag_e
|
||||
|
@ -490,7 +500,7 @@ typedef struct {
|
|||
const cliValueConfig_t config;
|
||||
} clivalue_t;
|
||||
|
||||
const clivalue_t valueTable[] = {
|
||||
static const clivalue_t valueTable[] = {
|
||||
#ifndef SKIP_TASK_STATISTICS
|
||||
{ "task_statistics", VAR_INT8 | MASTER_VALUE | MODE_LOOKUP, &masterConfig.task_statistics, .config.lookup = { TABLE_OFF_ON } },
|
||||
#endif
|
||||
|
@ -808,8 +818,7 @@ const clivalue_t valueTable[] = {
|
|||
{ "displayport_max7456_row_adjust", VAR_INT8 | MASTER_VALUE, &displayPortProfileMax7456()->rowAdjust, .config.minmax = { -3, 0 } },
|
||||
#endif
|
||||
};
|
||||
|
||||
#define VALUE_COUNT (sizeof(valueTable) / sizeof(clivalue_t))
|
||||
#endif
|
||||
|
||||
static void cliPrint(const char *str)
|
||||
{
|
||||
|
@ -835,6 +844,16 @@ static void cliPutp(void *p, char ch)
|
|||
bufWriterAppend(p, ch);
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
DUMP_MASTER = (1 << 0),
|
||||
DUMP_PROFILE = (1 << 1),
|
||||
DUMP_RATES = (1 << 2),
|
||||
DUMP_ALL = (1 << 3),
|
||||
DO_DIFF = (1 << 4),
|
||||
SHOW_DEFAULTS = (1 << 5),
|
||||
HIDE_UNUSED = (1 << 6)
|
||||
} dumpFlags_e;
|
||||
|
||||
static bool cliDumpPrintf(uint8_t dumpMask, bool equalsDefault, const char *format, ...)
|
||||
{
|
||||
if (!((dumpMask & DO_DIFF) && equalsDefault)) {
|
||||
|
@ -928,6 +947,122 @@ static void printValuePointer(const clivalue_t *var, void *valuePointer, uint32_
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef USE_PARAMETER_GROUPS
|
||||
|
||||
static bool valuePtrEqualsDefault(uint8_t type, const void *ptr, const void *ptrDefault)
|
||||
{
|
||||
bool result = false;
|
||||
switch (type & VALUE_TYPE_MASK) {
|
||||
case VAR_UINT8:
|
||||
result = *(uint8_t *)ptr == *(uint8_t *)ptrDefault;
|
||||
break;
|
||||
|
||||
case VAR_INT8:
|
||||
result = *(int8_t *)ptr == *(int8_t *)ptrDefault;
|
||||
break;
|
||||
|
||||
case VAR_UINT16:
|
||||
result = *(uint16_t *)ptr == *(uint16_t *)ptrDefault;
|
||||
break;
|
||||
|
||||
case VAR_INT16:
|
||||
result = *(int16_t *)ptr == *(int16_t *)ptrDefault;
|
||||
break;
|
||||
|
||||
/* not currently used
|
||||
case VAR_UINT32:
|
||||
result = *(uint32_t *)ptr == *(uint32_t *)ptrDefault;
|
||||
break;*/
|
||||
|
||||
case VAR_FLOAT:
|
||||
result = *(float *)ptr == *(float *)ptrDefault;
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
typedef struct cliCurrentAndDefaultConfig_s {
|
||||
const void *currentConfig; // the copy
|
||||
const void *defaultConfig; // the PG value as set by default
|
||||
} cliCurrentAndDefaultConfig_t;
|
||||
|
||||
static const cliCurrentAndDefaultConfig_t *getCurrentAndDefaultConfigs(pgn_t pgn)
|
||||
{
|
||||
static cliCurrentAndDefaultConfig_t ret;
|
||||
|
||||
switch (pgn) {
|
||||
default:
|
||||
ret.currentConfig = NULL;
|
||||
ret.defaultConfig = NULL;
|
||||
break;
|
||||
}
|
||||
return &ret;
|
||||
}
|
||||
|
||||
static uint16_t getValueOffset(const clivalue_t *value)
|
||||
{
|
||||
switch (value->type & VALUE_SECTION_MASK) {
|
||||
case MASTER_VALUE:
|
||||
case PROFILE_VALUE:
|
||||
return value->offset;
|
||||
case PROFILE_RATE_VALUE:
|
||||
return value->offset + sizeof(controlRateConfig_t) * getCurrentControlRateProfile();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *getValuePointer(const clivalue_t *value)
|
||||
{
|
||||
const pgRegistry_t* rec = pgFind(value->pgn);
|
||||
|
||||
switch (value->type & VALUE_SECTION_MASK) {
|
||||
case MASTER_VALUE:
|
||||
case PROFILE_VALUE:
|
||||
return rec->address + value->offset;
|
||||
case PROFILE_RATE_VALUE:
|
||||
return rec->address + value->offset + sizeof(controlRateConfig_t) * getCurrentControlRateProfile();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void dumpPgValue(const clivalue_t *value, uint8_t dumpMask)
|
||||
{
|
||||
const char *format = "set %s = ";
|
||||
const cliCurrentAndDefaultConfig_t *config = getCurrentAndDefaultConfigs(value->pgn);
|
||||
if (config->currentConfig == NULL || config->defaultConfig == NULL) {
|
||||
// has not been set up properly
|
||||
cliPrintf("VALUE %s ERROR\r\n", value->name);
|
||||
return;
|
||||
}
|
||||
const int valueOffset = getValueOffset(value);
|
||||
switch (dumpMask & (DO_DIFF | SHOW_DEFAULTS)) {
|
||||
case DO_DIFF:
|
||||
if (valuePtrEqualsDefault(value->type, (uint8_t*)config->currentConfig + valueOffset, (uint8_t*)config->defaultConfig + valueOffset)) {
|
||||
break;
|
||||
}
|
||||
// drop through, since not equal to default
|
||||
case 0:
|
||||
case SHOW_DEFAULTS:
|
||||
cliPrintf(format, value->name);
|
||||
printValuePointer(value, (uint8_t*)config->currentConfig + valueOffset, 0);
|
||||
cliPrint("\r\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void dumpAllValues(uint16_t valueSection, uint8_t dumpMask)
|
||||
{
|
||||
for (uint32_t i = 0; i < ARRAYLEN(valueTable); i++) {
|
||||
const clivalue_t *value = &valueTable[i];
|
||||
bufWriterFlush(cliWriter);
|
||||
if ((value->type & VALUE_SECTION_MASK) == valueSection) {
|
||||
dumpPgValue(value, dumpMask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void *getValuePointer(const clivalue_t *value)
|
||||
{
|
||||
void *ptr = value->ptr;
|
||||
|
@ -942,6 +1077,7 @@ void *getValuePointer(const clivalue_t *value)
|
|||
|
||||
return ptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void *getDefaultPointer(void *valuePointer, const master_t *defaultConfig)
|
||||
{
|
||||
|
@ -1003,7 +1139,7 @@ static void cliPrintVarDefault(const clivalue_t *var, uint32_t full, const maste
|
|||
static void dumpValues(uint16_t valueSection, uint8_t dumpMask, const master_t *defaultConfig)
|
||||
{
|
||||
const clivalue_t *value;
|
||||
for (uint32_t i = 0; i < VALUE_COUNT; i++) {
|
||||
for (uint32_t i = 0; i < ARRAYLEN(valueTable); i++) {
|
||||
value = &valueTable[i];
|
||||
|
||||
if ((value->type & VALUE_SECTION_MASK) != valueSection) {
|
||||
|
@ -1050,13 +1186,7 @@ typedef union {
|
|||
|
||||
static void cliSetVar(const clivalue_t *var, const int_float_value_t value)
|
||||
{
|
||||
void *ptr = var->ptr;
|
||||
if ((var->type & VALUE_SECTION_MASK) == PROFILE_VALUE) {
|
||||
ptr = ((uint8_t *)ptr) + (sizeof(profile_t) * masterConfig.current_profile_index);
|
||||
}
|
||||
if ((var->type & VALUE_SECTION_MASK) == PROFILE_RATE_VALUE) {
|
||||
ptr = ((uint8_t *)ptr) + (sizeof(profile_t) * masterConfig.current_profile_index) + (sizeof(controlRateConfig_t) * getCurrentControlRateProfile());
|
||||
}
|
||||
void *ptr = getValuePointer(var);
|
||||
|
||||
switch (var->type & VALUE_TYPE_MASK) {
|
||||
case VAR_UINT8:
|
||||
|
@ -1105,9 +1235,9 @@ static void cliShowArgumentRangeError(char *name, int min, int max)
|
|||
cliPrintf("%s must be between %d and %d\r\n", name, min, max);
|
||||
}
|
||||
|
||||
static char *nextArg(char *currentArg)
|
||||
static const char *nextArg(const char *currentArg)
|
||||
{
|
||||
char *ptr = strchr(currentArg, ' ');
|
||||
const char *ptr = strchr(currentArg, ' ');
|
||||
while (ptr && *ptr == ' ') {
|
||||
ptr++;
|
||||
}
|
||||
|
@ -1115,14 +1245,12 @@ static char *nextArg(char *currentArg)
|
|||
return ptr;
|
||||
}
|
||||
|
||||
static char *processChannelRangeArgs(char *ptr, channelRange_t *range, uint8_t *validArgumentCount)
|
||||
static const char *processChannelRangeArgs(const char *ptr, channelRange_t *range, uint8_t *validArgumentCount)
|
||||
{
|
||||
int val;
|
||||
|
||||
for (uint32_t argIndex = 0; argIndex < 2; argIndex++) {
|
||||
ptr = nextArg(ptr);
|
||||
if (ptr) {
|
||||
val = atoi(ptr);
|
||||
int val = atoi(ptr);
|
||||
val = CHANNEL_VALUE_TO_STEP(val);
|
||||
if (val >= MIN_MODE_RANGE_STEP && val <= MAX_MODE_RANGE_STEP) {
|
||||
if (argIndex == 0) {
|
||||
|
@ -1141,10 +1269,10 @@ static char *processChannelRangeArgs(char *ptr, channelRange_t *range, uint8_t *
|
|||
// Check if a string's length is zero
|
||||
static bool isEmpty(const char *string)
|
||||
{
|
||||
return *string == '\0';
|
||||
return (string == NULL || *string == '\0') ? true : false;
|
||||
}
|
||||
|
||||
static void printRxFail(uint8_t dumpMask, const rxConfig_t *rxConfig, const rxConfig_t *defaultRxConfig)
|
||||
static void printRxFailsafe(uint8_t dumpMask, const rxConfig_t *rxConfig, const rxConfig_t *defaultRxConfig)
|
||||
{
|
||||
// print out rxConfig failsafe settings
|
||||
for (uint32_t channel = 0; channel < MAX_SUPPORTED_RC_CHANNEL_COUNT; channel++) {
|
||||
|
@ -1183,7 +1311,7 @@ static void printRxFail(uint8_t dumpMask, const rxConfig_t *rxConfig, const rxCo
|
|||
}
|
||||
}
|
||||
|
||||
static void cliRxFail(char *cmdline)
|
||||
static void cliRxFailsafe(char *cmdline)
|
||||
{
|
||||
uint8_t channel;
|
||||
char buf[3];
|
||||
|
@ -1191,10 +1319,10 @@ static void cliRxFail(char *cmdline)
|
|||
if (isEmpty(cmdline)) {
|
||||
// print out rxConfig failsafe settings
|
||||
for (channel = 0; channel < MAX_SUPPORTED_RC_CHANNEL_COUNT; channel++) {
|
||||
cliRxFail(itoa(channel, buf, 10));
|
||||
cliRxFailsafe(itoa(channel, buf, 10));
|
||||
}
|
||||
} else {
|
||||
char *ptr = cmdline;
|
||||
const char *ptr = cmdline;
|
||||
channel = atoi(ptr++);
|
||||
if ((channel < MAX_SUPPORTED_RC_CHANNEL_COUNT)) {
|
||||
|
||||
|
@ -1301,7 +1429,7 @@ static void printAux(uint8_t dumpMask, const modeActivationProfile_t *modeActiva
|
|||
static void cliAux(char *cmdline)
|
||||
{
|
||||
int i, val = 0;
|
||||
char *ptr;
|
||||
const char *ptr;
|
||||
|
||||
if (isEmpty(cmdline)) {
|
||||
printAux(DUMP_MASTER, modeActivationProfile(), NULL);
|
||||
|
@ -1375,13 +1503,9 @@ static void printSerial(uint8_t dumpMask, const serialConfig_t *serialConfig, co
|
|||
|
||||
static void cliSerial(char *cmdline)
|
||||
{
|
||||
int i, val;
|
||||
char *ptr;
|
||||
|
||||
if (isEmpty(cmdline)) {
|
||||
printSerial(DUMP_MASTER, serialConfig(), NULL);
|
||||
|
||||
return;
|
||||
return;
|
||||
}
|
||||
serialPortConfig_t portConfig;
|
||||
memset(&portConfig, 0 , sizeof(portConfig));
|
||||
|
@ -1390,9 +1514,9 @@ static void cliSerial(char *cmdline)
|
|||
|
||||
uint8_t validArgumentCount = 0;
|
||||
|
||||
ptr = cmdline;
|
||||
const char *ptr = cmdline;
|
||||
|
||||
val = atoi(ptr++);
|
||||
int val = atoi(ptr++);
|
||||
currentConfig = serialFindPortConfiguration(val);
|
||||
if (currentConfig) {
|
||||
portConfig.identifier = val;
|
||||
|
@ -1406,7 +1530,7 @@ static void cliSerial(char *cmdline)
|
|||
validArgumentCount++;
|
||||
}
|
||||
|
||||
for (i = 0; i < 4; i ++) {
|
||||
for (int i = 0; i < 4; i ++) {
|
||||
ptr = nextArg(ptr);
|
||||
if (!ptr) {
|
||||
break;
|
||||
|
@ -1455,7 +1579,6 @@ static void cliSerial(char *cmdline)
|
|||
}
|
||||
|
||||
memcpy(currentConfig, &portConfig, sizeof(portConfig));
|
||||
|
||||
}
|
||||
|
||||
#ifndef SKIP_SERIAL_PASSTHROUGH
|
||||
|
@ -1574,7 +1697,7 @@ static void printAdjustmentRange(uint8_t dumpMask, const adjustmentProfile_t *ad
|
|||
static void cliAdjustmentRange(char *cmdline)
|
||||
{
|
||||
int i, val = 0;
|
||||
char *ptr;
|
||||
const char *ptr;
|
||||
|
||||
if (isEmpty(cmdline)) {
|
||||
printAdjustmentRange(DUMP_MASTER, adjustmentProfile(), NULL);
|
||||
|
@ -1678,7 +1801,7 @@ static void cliMotorMix(char *cmdline)
|
|||
#else
|
||||
int check = 0;
|
||||
uint8_t len;
|
||||
char *ptr;
|
||||
const char *ptr;
|
||||
|
||||
if (isEmpty(cmdline)) {
|
||||
printMotorMix(DUMP_MASTER, customMotorMixer(0), NULL);
|
||||
|
@ -1766,7 +1889,7 @@ static void printRxRange(uint8_t dumpMask, const rxConfig_t *rxConfig, const rxC
|
|||
static void cliRxRange(char *cmdline)
|
||||
{
|
||||
int i, validArgumentCount = 0;
|
||||
char *ptr;
|
||||
const char *ptr;
|
||||
|
||||
if (isEmpty(cmdline)) {
|
||||
printRxRange(DUMP_MASTER, rxConfig(), NULL);
|
||||
|
@ -1828,7 +1951,7 @@ static void printLed(uint8_t dumpMask, const ledConfig_t *ledConfigs, const ledC
|
|||
static void cliLed(char *cmdline)
|
||||
{
|
||||
int i;
|
||||
char *ptr;
|
||||
const char *ptr;
|
||||
|
||||
if (isEmpty(cmdline)) {
|
||||
printLed(DUMP_MASTER, ledStripConfig()->ledConfigs, NULL);
|
||||
|
@ -1866,7 +1989,7 @@ static void printColor(uint8_t dumpMask, const hsvColor_t *colors, const hsvColo
|
|||
static void cliColor(char *cmdline)
|
||||
{
|
||||
int i;
|
||||
char *ptr;
|
||||
const char *ptr;
|
||||
|
||||
if (isEmpty(cmdline)) {
|
||||
printColor(DUMP_MASTER, ledStripConfig()->colors, NULL);
|
||||
|
@ -2144,10 +2267,8 @@ static void printServoMix(uint8_t dumpMask, const master_t *defaultConfig)
|
|||
|
||||
static void cliServoMix(char *cmdline)
|
||||
{
|
||||
uint8_t len;
|
||||
char *ptr;
|
||||
int args[8], check = 0;
|
||||
len = strlen(cmdline);
|
||||
int len = strlen(cmdline);
|
||||
|
||||
if (len == 0) {
|
||||
printServoMix(DUMP_MASTER, NULL);
|
||||
|
@ -2158,7 +2279,7 @@ static void cliServoMix(char *cmdline)
|
|||
servoProfile()->servoConf[i].reversedSources = 0;
|
||||
}
|
||||
} else if (strncasecmp(cmdline, "load", 4) == 0) {
|
||||
ptr = nextArg(cmdline);
|
||||
const char *ptr = nextArg(cmdline);
|
||||
if (ptr) {
|
||||
len = strlen(ptr);
|
||||
for (uint32_t i = 0; ; i++) {
|
||||
|
@ -2176,7 +2297,7 @@ static void cliServoMix(char *cmdline)
|
|||
}
|
||||
} else if (strncasecmp(cmdline, "reverse", 7) == 0) {
|
||||
enum {SERVO = 0, INPUT, REVERSE, ARGS_COUNT};
|
||||
ptr = strchr(cmdline, ' ');
|
||||
char *ptr = strchr(cmdline, ' ');
|
||||
|
||||
len = strlen(ptr);
|
||||
if (len == 0) {
|
||||
|
@ -2218,7 +2339,7 @@ static void cliServoMix(char *cmdline)
|
|||
cliServoMix("reverse");
|
||||
} else {
|
||||
enum {RULE = 0, TARGET, INPUT, RATE, SPEED, MIN, MAX, BOX, ARGS_COUNT};
|
||||
ptr = strtok(cmdline, " ");
|
||||
char *ptr = strtok(cmdline, " ");
|
||||
while (ptr != NULL && check < ARGS_COUNT) {
|
||||
args[check++] = atoi(ptr);
|
||||
ptr = strtok(NULL, " ");
|
||||
|
@ -2392,7 +2513,7 @@ static void cliFlashRead(char *cmdline)
|
|||
|
||||
uint8_t buffer[32];
|
||||
|
||||
char *nextArg = strchr(cmdline, ' ');
|
||||
const char *nextArg = strchr(cmdline, ' ');
|
||||
|
||||
if (!nextArg) {
|
||||
cliShowParseError();
|
||||
|
@ -2461,7 +2582,7 @@ static void printVtx(uint8_t dumpMask, const master_t *defaultConfig)
|
|||
static void cliVtx(char *cmdline)
|
||||
{
|
||||
int i, val = 0;
|
||||
char *ptr;
|
||||
const char *ptr;
|
||||
|
||||
if (isEmpty(cmdline)) {
|
||||
printVtx(DUMP_MASTER, NULL);
|
||||
|
@ -3032,7 +3153,13 @@ static void cliDumpProfile(uint8_t profileIndex, uint8_t dumpMask, const master_
|
|||
cliPrintHashLine("profile");
|
||||
cliProfile("");
|
||||
cliPrint("\r\n");
|
||||
#ifdef USE_PARAMETER_GROUPS
|
||||
(void)(defaultConfig);
|
||||
dumpAllValues(PROFILE_VALUE, dumpMask);
|
||||
dumpAllValues(PROFILE_RATE_VALUE, dumpMask);
|
||||
#else
|
||||
dumpValues(PROFILE_VALUE, dumpMask, defaultConfig);
|
||||
#endif
|
||||
cliRateProfile("");
|
||||
}
|
||||
|
||||
|
@ -3072,7 +3199,7 @@ static void cliGet(char *cmdline)
|
|||
const clivalue_t *val;
|
||||
int matchedCommands = 0;
|
||||
|
||||
for (uint32_t i = 0; i < VALUE_COUNT; i++) {
|
||||
for (uint32_t i = 0; i < ARRAYLEN(valueTable); i++) {
|
||||
if (strstr(valueTable[i].name, cmdline)) {
|
||||
val = &valueTable[i];
|
||||
cliPrintf("%s = ", valueTable[i].name);
|
||||
|
@ -3103,7 +3230,7 @@ static void cliSet(char *cmdline)
|
|||
|
||||
if (len == 0 || (len == 1 && cmdline[0] == '*')) {
|
||||
cliPrint("Current settings: \r\n");
|
||||
for (uint32_t i = 0; i < VALUE_COUNT; i++) {
|
||||
for (uint32_t i = 0; i < ARRAYLEN(valueTable); i++) {
|
||||
val = &valueTable[i];
|
||||
cliPrintf("%s = ", valueTable[i].name);
|
||||
cliPrintVar(val, len); // when len is 1 (when * is passed as argument), it will print min/max values as well, for gui
|
||||
|
@ -3124,7 +3251,7 @@ static void cliSet(char *cmdline)
|
|||
eqptr++;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < VALUE_COUNT; i++) {
|
||||
for (uint32_t i = 0; i < ARRAYLEN(valueTable); i++) {
|
||||
val = &valueTable[i];
|
||||
// ensure exact match when setting to prevent setting variables with shorter names
|
||||
if (strncasecmp(cmdline, valueTable[i].name, strlen(valueTable[i].name)) == 0 && variableNameLength == strlen(valueTable[i].name)) {
|
||||
|
@ -3540,6 +3667,18 @@ static void cliResource(char *cmdline)
|
|||
}
|
||||
#endif /* USE_RESOURCE_MGMT */
|
||||
|
||||
#ifdef USE_PARAMETER_GROUPS
|
||||
static void backupConfigs(void)
|
||||
{
|
||||
// make copies of configs to do differencing
|
||||
|
||||
}
|
||||
|
||||
static void restoreConfigs(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
static void printConfig(char *cmdline, bool doDiff)
|
||||
{
|
||||
uint8_t dumpMask = DUMP_MASTER;
|
||||
|
@ -3556,13 +3695,21 @@ static void printConfig(char *cmdline, bool doDiff)
|
|||
options = cmdline;
|
||||
}
|
||||
|
||||
static master_t defaultConfig;
|
||||
if (doDiff) {
|
||||
dumpMask = dumpMask | DO_DIFF;
|
||||
}
|
||||
|
||||
static master_t defaultConfig;
|
||||
createDefaultConfig(&defaultConfig);
|
||||
|
||||
#ifdef USE_PARAMETER_GROUPS
|
||||
backupConfigs();
|
||||
// reset all configs to defaults to do differencing
|
||||
resetConfigs();
|
||||
#if defined(TARGET_CONFIG)
|
||||
targetConfiguration(&defaultConfig);
|
||||
#endif
|
||||
#endif
|
||||
if (checkCommand(options, "showdefaults")) {
|
||||
dumpMask = dumpMask | SHOW_DEFAULTS; // add default values as comments for changed values
|
||||
}
|
||||
|
@ -3646,7 +3793,7 @@ static void printConfig(char *cmdline, bool doDiff)
|
|||
#endif
|
||||
|
||||
cliPrintHashLine("rxfail");
|
||||
printRxFail(dumpMask, rxConfig(), &defaultConfig.rxConfig);
|
||||
printRxFailsafe(dumpMask, rxConfig(), &defaultConfig.rxConfig);
|
||||
|
||||
cliPrintHashLine("master");
|
||||
dumpValues(MASTER_VALUE, dumpMask, &defaultConfig);
|
||||
|
@ -3685,6 +3832,10 @@ static void printConfig(char *cmdline, bool doDiff)
|
|||
if (dumpMask & DUMP_RATES) {
|
||||
cliDumpRateProfile(currentProfile->activeRateProfile, dumpMask, &defaultConfig);
|
||||
}
|
||||
#ifdef USE_PARAMETER_GROUPS
|
||||
// restore configs from copies
|
||||
restoreConfigs();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void cliDump(char *cmdline)
|
||||
|
@ -3782,7 +3933,7 @@ const clicmd_t cmdTable[] = {
|
|||
#if defined(USE_RESOURCE_MGMT)
|
||||
CLI_COMMAND_DEF("resource", "show/set resources", NULL, cliResource),
|
||||
#endif
|
||||
CLI_COMMAND_DEF("rxfail", "show/set rx failsafe settings", NULL, cliRxFail),
|
||||
CLI_COMMAND_DEF("rxfail", "show/set rx failsafe settings", NULL, cliRxFailsafe),
|
||||
CLI_COMMAND_DEF("rxrange", "configure rx channel ranges", NULL, cliRxRange),
|
||||
CLI_COMMAND_DEF("save", "save and reboot", NULL, cliSave),
|
||||
#ifdef USE_SDCARD
|
||||
|
@ -3811,13 +3962,11 @@ const clicmd_t cmdTable[] = {
|
|||
CLI_COMMAND_DEF("vtx", "vtx channels on switch", NULL, cliVtx),
|
||||
#endif
|
||||
};
|
||||
#define CMD_COUNT (sizeof(cmdTable) / sizeof(clicmd_t))
|
||||
|
||||
static void cliHelp(char *cmdline)
|
||||
{
|
||||
UNUSED(cmdline);
|
||||
|
||||
for (uint32_t i = 0; i < CMD_COUNT; i++) {
|
||||
for (uint32_t i = 0; i < ARRAYLEN(cmdTable); i++) {
|
||||
cliPrint(cmdTable[i].name);
|
||||
#ifndef SKIP_CLI_COMMAND_HELP
|
||||
if (cmdTable[i].description) {
|
||||
|
@ -3846,7 +3995,7 @@ void cliProcess(void)
|
|||
// do tab completion
|
||||
const clicmd_t *cmd, *pstart = NULL, *pend = NULL;
|
||||
uint32_t i = bufferIndex;
|
||||
for (cmd = cmdTable; cmd < cmdTable + CMD_COUNT; cmd++) {
|
||||
for (cmd = cmdTable; cmd < cmdTable + ARRAYLEN(cmdTable); cmd++) {
|
||||
if (bufferIndex && (strncasecmp(cliBuffer, cmd->name, bufferIndex) != 0))
|
||||
continue;
|
||||
if (!pstart)
|
||||
|
@ -3907,12 +4056,12 @@ void cliProcess(void)
|
|||
|
||||
const clicmd_t *cmd;
|
||||
char *options;
|
||||
for (cmd = cmdTable; cmd < cmdTable + CMD_COUNT; cmd++) {
|
||||
for (cmd = cmdTable; cmd < cmdTable + ARRAYLEN(cmdTable); cmd++) {
|
||||
if ((options = checkCommand(cliBuffer, cmd->name))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(cmd < cmdTable + CMD_COUNT)
|
||||
if(cmd < cmdTable + ARRAYLEN(cmdTable))
|
||||
cmd->func(options);
|
||||
else
|
||||
cliPrint("Unknown command, try 'help'");
|
||||
|
|
|
@ -29,12 +29,18 @@
|
|||
|
||||
#include "cms/cms.h"
|
||||
|
||||
#include "common/color.h"
|
||||
#include "common/axis.h"
|
||||
#include "common/maths.h"
|
||||
#include "common/color.h"
|
||||
#include "common/filter.h"
|
||||
#include "common/maths.h"
|
||||
|
||||
#include "config/config_eeprom.h"
|
||||
#include "config/config_master.h"
|
||||
#include "config/config_profile.h"
|
||||
#include "config/feature.h"
|
||||
#include "config/parameter_group.h"
|
||||
#include "config/parameter_group_ids.h"
|
||||
|
||||
#include "drivers/sensor.h"
|
||||
#include "drivers/accgyro.h"
|
||||
#include "drivers/compass.h"
|
||||
#include "drivers/io.h"
|
||||
|
@ -45,6 +51,7 @@
|
|||
#include "drivers/rx_pwm.h"
|
||||
#include "drivers/rx_spi.h"
|
||||
#include "drivers/sdcard.h"
|
||||
#include "drivers/sensor.h"
|
||||
#include "drivers/serial.h"
|
||||
#include "drivers/sound_beeper.h"
|
||||
#include "drivers/system.h"
|
||||
|
@ -56,42 +63,37 @@
|
|||
#include "fc/rc_curves.h"
|
||||
#include "fc/runtime_config.h"
|
||||
|
||||
#include "sensors/sensors.h"
|
||||
#include "sensors/gyro.h"
|
||||
#include "sensors/compass.h"
|
||||
#include "sensors/acceleration.h"
|
||||
#include "sensors/barometer.h"
|
||||
#include "sensors/battery.h"
|
||||
#include "sensors/boardalignment.h"
|
||||
#include "flight/altitudehold.h"
|
||||
#include "flight/failsafe.h"
|
||||
#include "flight/imu.h"
|
||||
#include "flight/mixer.h"
|
||||
#include "flight/navigation.h"
|
||||
#include "flight/pid.h"
|
||||
#include "flight/servos.h"
|
||||
|
||||
#include "io/beeper.h"
|
||||
#include "io/serial.h"
|
||||
#include "io/gimbal.h"
|
||||
#include "io/motors.h"
|
||||
#include "io/servos.h"
|
||||
#include "io/ledstrip.h"
|
||||
#include "io/gps.h"
|
||||
#include "io/ledstrip.h"
|
||||
#include "io/motors.h"
|
||||
#include "io/osd.h"
|
||||
#include "io/serial.h"
|
||||
#include "io/servos.h"
|
||||
#include "io/vtx.h"
|
||||
|
||||
#include "rx/rx.h"
|
||||
#include "rx/rx_spi.h"
|
||||
|
||||
#include "sensors/acceleration.h"
|
||||
#include "sensors/barometer.h"
|
||||
#include "sensors/battery.h"
|
||||
#include "sensors/boardalignment.h"
|
||||
#include "sensors/compass.h"
|
||||
#include "sensors/gyro.h"
|
||||
#include "sensors/sensors.h"
|
||||
|
||||
#include "telemetry/telemetry.h"
|
||||
|
||||
#include "flight/mixer.h"
|
||||
#include "flight/servos.h"
|
||||
#include "flight/pid.h"
|
||||
#include "flight/imu.h"
|
||||
#include "flight/failsafe.h"
|
||||
#include "flight/altitudehold.h"
|
||||
#include "flight/navigation.h"
|
||||
|
||||
#include "config/config_eeprom.h"
|
||||
#include "config/config_profile.h"
|
||||
#include "config/config_master.h"
|
||||
#include "config/feature.h"
|
||||
|
||||
#ifndef DEFAULT_RX_FEATURE
|
||||
#define DEFAULT_RX_FEATURE FEATURE_RX_PARALLEL_PWM
|
||||
#endif
|
||||
|
@ -549,7 +551,7 @@ uint8_t getCurrentProfile(void)
|
|||
return masterConfig.current_profile_index;
|
||||
}
|
||||
|
||||
void setProfile(uint8_t profileIndex)
|
||||
static void setProfile(uint8_t profileIndex)
|
||||
{
|
||||
currentProfile = &masterConfig.profile[profileIndex];
|
||||
currentControlRateProfileIndex = currentProfile->activeRateProfile;
|
||||
|
@ -871,11 +873,14 @@ void createDefaultConfig(master_t *config)
|
|||
}
|
||||
}
|
||||
|
||||
static void resetConf(void)
|
||||
void resetConfigs(void)
|
||||
{
|
||||
createDefaultConfig(&masterConfig);
|
||||
pgResetAll(MAX_PROFILE_COUNT);
|
||||
pgActivateProfile(0);
|
||||
|
||||
setProfile(0);
|
||||
setControlRateProfile(0);
|
||||
|
||||
#ifdef LED_STRIP
|
||||
reevaluateLedConfig();
|
||||
|
@ -1125,21 +1130,53 @@ void validateAndFixGyroConfig(void)
|
|||
}
|
||||
}
|
||||
|
||||
void readEEPROM(void)
|
||||
{
|
||||
suspendRxSignal();
|
||||
|
||||
// Sanity check, read flash
|
||||
if (!loadEEPROM()) {
|
||||
failureMode(FAILURE_INVALID_EEPROM_CONTENTS);
|
||||
}
|
||||
|
||||
// pgActivateProfile(getCurrentProfile());
|
||||
// setControlRateProfile(rateProfileSelection()->defaultRateProfileIndex);
|
||||
|
||||
if (masterConfig.current_profile_index > MAX_PROFILE_COUNT - 1) {// sanity check
|
||||
masterConfig.current_profile_index = 0;
|
||||
}
|
||||
|
||||
setProfile(masterConfig.current_profile_index);
|
||||
|
||||
validateAndFixConfig();
|
||||
activateConfig();
|
||||
|
||||
resumeRxSignal();
|
||||
}
|
||||
|
||||
void writeEEPROM(void)
|
||||
{
|
||||
suspendRxSignal();
|
||||
|
||||
writeConfigToEEPROM();
|
||||
|
||||
resumeRxSignal();
|
||||
}
|
||||
|
||||
void resetEEPROM(void)
|
||||
{
|
||||
resetConfigs();
|
||||
writeEEPROM();
|
||||
}
|
||||
|
||||
void ensureEEPROMContainsValidData(void)
|
||||
{
|
||||
if (isEEPROMContentValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
resetEEPROM();
|
||||
}
|
||||
|
||||
void resetEEPROM(void)
|
||||
{
|
||||
resetConf();
|
||||
writeEEPROM();
|
||||
}
|
||||
|
||||
void saveConfigAndNotify(void)
|
||||
{
|
||||
writeEEPROM();
|
||||
|
|
|
@ -17,8 +17,11 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "config/parameter_group.h"
|
||||
|
||||
#if FLASH_SIZE <= 128
|
||||
#define MAX_PROFILE_COUNT 2
|
||||
#else
|
||||
|
@ -69,7 +72,10 @@ void setPreferredBeeperOffMask(uint32_t mask);
|
|||
|
||||
void copyCurrentProfileToProfileSlot(uint8_t profileSlotIndex);
|
||||
|
||||
void initEEPROM(void);
|
||||
void resetEEPROM(void);
|
||||
void readEEPROM(void);
|
||||
void writeEEPROM();
|
||||
void ensureEEPROMContainsValidData(void);
|
||||
|
||||
void saveConfigAndNotify(void);
|
||||
|
@ -79,14 +85,16 @@ void activateConfig(void);
|
|||
|
||||
uint8_t getCurrentProfile(void);
|
||||
void changeProfile(uint8_t profileIndex);
|
||||
void setProfile(uint8_t profileIndex);
|
||||
struct profile_s;
|
||||
void resetProfile(struct profile_s *profile);
|
||||
|
||||
uint8_t getCurrentControlRateProfile(void);
|
||||
void changeControlRateProfile(uint8_t profileIndex);
|
||||
bool canSoftwareSerialBeUsed(void);
|
||||
|
||||
uint16_t getCurrentMinthrottle(void);
|
||||
struct master_s;
|
||||
|
||||
void resetConfigs(void);
|
||||
struct master_s;
|
||||
void targetConfiguration(struct master_s *config);
|
||||
void targetValidateConfiguration(struct master_s *config);
|
||||
|
|
|
@ -105,7 +105,6 @@
|
|||
#endif
|
||||
|
||||
extern uint16_t cycleTime; // FIXME dependency on mw.c
|
||||
extern void resetProfile(profile_t *profile);
|
||||
|
||||
static const char * const flightControllerIdentifier = BETAFLIGHT_IDENTIFIER; // 4 UPPER CASE alpha numeric characters that identify the flight controller.
|
||||
static const char * const boardIdentifier = TARGET_BOARD_IDENTIFIER;
|
||||
|
|
|
@ -101,8 +101,7 @@
|
|||
#define ACC_MPU6500_ALIGN CW0_DEG
|
||||
|
||||
#define BARO
|
||||
#define USE_BARO_MS5611
|
||||
#define USE_BARO_BMP085
|
||||
#define USE_BARO_MS5611 // needed for Flip32 board
|
||||
#define USE_BARO_BMP280
|
||||
|
||||
/*
|
||||
|
|
|
@ -81,6 +81,19 @@ SECTIONS
|
|||
KEEP (*(SORT(.fini_array.*)))
|
||||
PROVIDE_HIDDEN (__fini_array_end = .);
|
||||
} >FLASH
|
||||
.pg_registry :
|
||||
{
|
||||
PROVIDE_HIDDEN (__pg_registry_start = .);
|
||||
KEEP (*(.pg_registry))
|
||||
KEEP (*(SORT(.pg_registry.*)))
|
||||
PROVIDE_HIDDEN (__pg_registry_end = .);
|
||||
} >FLASH
|
||||
.pg_resetdata :
|
||||
{
|
||||
PROVIDE_HIDDEN (__pg_resetdata_start = .);
|
||||
KEEP (*(.pg_resetdata))
|
||||
PROVIDE_HIDDEN (__pg_resetdata_end = .);
|
||||
} >FLASH
|
||||
|
||||
/* used by the startup to initialize data */
|
||||
_sidata = .;
|
||||
|
|
|
@ -22,10 +22,8 @@
|
|||
#include <limits.h>
|
||||
|
||||
extern "C" {
|
||||
#include "build/debug.h"
|
||||
|
||||
#include <platform.h>
|
||||
|
||||
#include "build/debug.h"
|
||||
#include "config/parameter_group.h"
|
||||
#include "config/parameter_group_ids.h"
|
||||
|
||||
|
@ -49,7 +47,7 @@ PG_RESET_TEMPLATE(motorConfig_t, motorConfig,
|
|||
|
||||
TEST(ParameterGroupsfTest, Test_pgResetAll)
|
||||
{
|
||||
memset(motorConfig(), 0, sizeof(motorConfig_t));
|
||||
memset(motorConfigMutable(), 0, sizeof(motorConfig_t));
|
||||
pgResetAll(0);
|
||||
EXPECT_EQ(1150, motorConfig()->minthrottle);
|
||||
EXPECT_EQ(1850, motorConfig()->maxthrottle);
|
||||
|
@ -59,7 +57,7 @@ TEST(ParameterGroupsfTest, Test_pgResetAll)
|
|||
|
||||
TEST(ParameterGroupsfTest, Test_pgFind)
|
||||
{
|
||||
memset(motorConfig(), 0, sizeof(motorConfig_t));
|
||||
memset(motorConfigMutable(), 0, sizeof(motorConfig_t));
|
||||
const pgRegistry_t *pgRegistry = pgFind(PG_MOTOR_CONFIG);
|
||||
pgResetCurrent(pgRegistry);
|
||||
EXPECT_EQ(1150, motorConfig()->minthrottle);
|
||||
|
@ -69,7 +67,7 @@ TEST(ParameterGroupsfTest, Test_pgFind)
|
|||
|
||||
motorConfig_t motorConfig2;
|
||||
memset(&motorConfig2, 0, sizeof(motorConfig_t));
|
||||
motorConfig()->motorPwmRate = 500;
|
||||
motorConfigMutable()->motorPwmRate = 500;
|
||||
pgStore(pgRegistry, &motorConfig2, sizeof(motorConfig_t), 0);
|
||||
EXPECT_EQ(1150, motorConfig2.minthrottle);
|
||||
EXPECT_EQ(1850, motorConfig2.maxthrottle);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue