1
0
Fork 0
mirror of https://github.com/betaflight/betaflight.git synced 2025-07-23 08:15:30 +03:00

Merge pull request #1860 from martinbudden/bf_pg0

Parameter groups EEPROM migration
This commit is contained in:
Martin Budden 2017-01-25 21:53:07 +00:00 committed by GitHub
commit e72528f020
17 changed files with 932 additions and 378 deletions

View file

@ -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);
}

View file

@ -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);

View file

@ -17,7 +17,6 @@
#pragma once
#include "common/axis.h"
#include "fc/config.h"
#include "fc/rc_controls.h"
#include "flight/pid.h"

View 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__ \
}; \
/**/

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

View 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);

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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'");

View file

@ -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();

View file

@ -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);

View file

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

View file

@ -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
/*

View file

@ -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 = .;

View file

@ -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);