Commit 4f05a016 authored by Pavel Semerad's avatar Pavel Semerad
Browse files

add using FLASH for model memories

parent b42815e0
*0.6.0 ()
increased model memories by using FLASH also
*0.5.1 (26 Apr 2012)
added possibility to connect potentiometer instead of CH3 button
......
......@@ -17,9 +17,24 @@ You can backup your original firmware and config, use tabs "PROGRAM MEMORY" and
Flashing firmware
=================
- uset STVP (ST Visual Programmer)
- select "PROGRAM MEMORY" tab
- load firmware "gt3b-VERSION.s19"
- program it with "Program -> Address Range..."
- set "Start @" to "8000"
- set "End @" to "E4FF" (it is possible to find place where zeroes
will start and use last non-zero address)
- press "OK"
- if "Program -> Current tab" will be used, all models stored at FLASH
will be erased
Global characteristics:
=======================
- 17/14/12 model memories for 3/6/8 channel firmware
- 72 model memories, first 8 in EEPROM, rest in FLASH
> numbers 10-19 will show with Right-Arrow
> numbers 20-29 will show with Left-Arrow
> numbers 30-39 will show with Left+Right-Arrows
......@@ -312,6 +327,7 @@ Global setup menu:
L backlight time 5s,10s...10m...MAX
I inactivity alarm OFF,1m...10m
LOW POWER! battery low voltage 2.0...10.5V
C default number of channels 2..8
E maximum allowed endpoint value 100...150%
DANGER - values greater than 120% can damage
your servo or will not be possible
......
? use also flash memory to store model configs
- some models in EEPROM
- other models in FLASH, one empty place
- table in EEPROM mapping model numbers to EEPROM/FLASH pos
- order table of used EEPROM models
- when FLASH model requested, move least used EEPROM model to empty
FLASH pos and requested FLASH model to this EEPROM pos
- use Word FLASH/EEPROM programming
- do one 8-channel fw with global default channels setting
- use program range in STVP to not overwrite memory models
? more multi-positions - 2 for 6ch, 4 for 8ch, 4-pos is enought
? PPM input for head tracking gyro
......
......@@ -26,7 +26,7 @@
// actual configuration
config_global_s config_global;
config_model_s config_model;
@near config_model_s config_model;
......@@ -44,6 +44,7 @@ u8 config_global_set_default(void) {
cg.magic_global = CONFIG_GLOBAL_MAGIC;
cg.magic_model = CONFIG_MODEL_MAGIC;
cg.model = 0;
cg.channels_default = 2; // 3 channels as model channels default
cg.steering_dead_zone = 2;
cg.throttle_dead_zone = 2;
......@@ -70,10 +71,6 @@ u8 config_global_set_default(void) {
cg.rotate_reverse = 0; // not-reversed
cg.ch3_pot = 0; // CH3 is button
cg.unused1 = 0;
cg.unused2 = 0;
cg.unused3 = 0;
// set calibrate values only when they are out of limits
cc |= check_val(&cg.calib_steering_left, 0, CALIB_ST_LOW_MID, 0);
cc |= check_val(&cg.calib_steering_mid, CALIB_ST_LOW_MID, CALIB_ST_MID_HIGH, 512);
......@@ -83,6 +80,10 @@ u8 config_global_set_default(void) {
cc |= check_val(&cg.calib_throttle_bck, CALIB_TH_MID_HIGH, 1023, 1023);
check_val(&cg.calib_ch3_left, 0, 512, 0);
check_val(&cg.calib_ch3_right, 512, 1023, 1023);
cg.unused = 0;
memset(cg.reserve, 0, sizeof(cg.reserve));
return cc;
}
......@@ -117,52 +118,99 @@ static void default_model_name(u8 model, u8 *name) {
// set model configuration to default
void config_model_set_default(void) {
default_model_name(cg.model, cm.name);
cm.channels = cg.channels_default;
cm.reverse = 0;
memset(cm.subtrim, 0, MAX_CHANNELS);
memset(cm.endpoint, 100, MAX_CHANNELS * 2);
memset(cm.subtrim, 0, sizeof(cm.subtrim));
memset(cm.endpoint, 100, sizeof(cm.endpoint));
cm.trim_steering = 0;
cm.trim_throttle = 0;
cm.dr_steering = 100;
cm.dr_forward = 100;
cm.dr_back = 100;
cm.expo_steering = 0;
cm.expo_forward = 0;
cm.expo_back = 0;
memset(cm.speed, 100, sizeof(cm.speed));
cm.stspd_return = 100;
cm.abs_type = 0;
cm.brake_off = 0;
cm.thspd_onlyfwd = 0;
cm.channel_4WS = 0;
cm.channel_DIG = 0;
cm.channel_MP = 0;
memset(cm.multi_position, (u8)MULTI_POSITION_END, sizeof(cm.multi_position));
cm.multi_position[0]= -100;
memset(&cm.multi_position[1], (u8)MULTI_POSITION_END,
NUM_MULTI_POSITION - 1);
memset(&cm.speed[0], 100, MAX_CHANNELS);
cm.stspd_return = 100;
cm.channels = MAX_CHANNELS - 1; // it is one lower to fit also 8
cm.unused = 0;
memcpy(&cm.key_mapping, &default_key_mapping, sizeof(config_key_mapping_s));
if (cg.ch3_pot) {
*ck_ch3_pot_func = 0;
*ck_ch3_pot_rev = 0;
}
cm.unused = 0;
cm.unused2 = 0;
cm.unused3 = 0;
memset(cm.reserve, 0, sizeof(cm.reserve));
}
// read model config from eeprom, if empty, set to defaults
// read model config from eeprom/flash, if empty, set to defaults
// first read after poweron should read FLASH model from
// eeprom CONFIG_MODEL_TMP, because FLASH memory is updated
// only after model change (so when powered off, current memory
// is only at tmp eeprom and not in FLASH)
void config_model_read(void) {
eeprom_read_model(config_global.model);
if (config_model.name[0] != CONFIG_MODEL_EMPTY) return;
config_model_set_default();
static _Bool not_first;
u8 model = cg.model;
if (!not_first && cg.model > CONFIG_MODEL_TMP)
model = CONFIG_MODEL_TMP;
if (model < CONFIG_MODEL_TMP || !not_first) {
// it is in eeprom
not_first = 1;
eeprom_read_model(model);
// if it was empty place, set it to defaults
if (config_model.name[0] != CONFIG_MODEL_EMPTY) return;
config_model_set_default();
return;
}
// it is in flash, read it and save to tmp eeprom
flash_read_model((u8)(cg.model - CONFIG_MODEL_TMP));
// if not configured, set to defaults before saving
// to tmp eeprom
if (config_model.name[0] == CONFIG_MODEL_EMPTY)
config_model_set_default();
// save it to tmp eeprom
eeprom_write_model(CONFIG_MODEL_TMP);
}
// return model name for given model number
u8 *config_model_name(u8 model) {
@near static u8 fake_name[3];
u8 *addr = EEPROM_CONFIG_MODEL + sizeof(config_model_s) * model;
u8 *addr;
if (model == cg.model) return cm.name; // actual model
if (model < CONFIG_MODEL_TMP)
// eeprom
addr = EEPROM_CONFIG_MODEL + sizeof(config_model_s) * model;
else
// flash
addr = (u8 *)(0 - sizeof(config_model_s) * (model - CONFIG_MODEL_TMP + 1));
// if not configured, return default name
if (*addr == CONFIG_MODEL_EMPTY) {
default_model_name(model, fake_name);
addr = fake_name;
......@@ -171,39 +219,54 @@ u8 *config_model_name(u8 model) {
}
// empty all model memories
void config_empty_models(void) {
eeprom_empty_models();
// save model config to eeprom (to temporary location if model is in FLASH)
void config_model_save(void) {
u8 model = cg.model;
if (model > CONFIG_MODEL_TMP) model = CONFIG_MODEL_TMP; // temporary place
eeprom_write_model(model);
}
// set new global model, if previous model was from flash, save it
// there firstly
void config_set_model(u8 model) {
if (cg.model >= CONFIG_MODEL_TMP)
flash_write_model((u8)(cg.model - CONFIG_MODEL_TMP));
cg.model = model;
config_global_save();
}
// read global config from eeprom, if MAGICs changed, set to defaults
// returns 1 if calibration values was set to defaults
u8 config_global_read(void) {
u8 calib_changed = 0;
eeprom_read_global();
if (config_global.magic_global != CONFIG_GLOBAL_MAGIC) {
if (cg.magic_global != CONFIG_GLOBAL_MAGIC) {
// global config changed, initialize whole eeprom
eeprom_empty_models();
calib_changed = config_global_set_default();
// do not write magic_global yet to eliminate interrupted initialization
// (for example flash-verify after flash-write in STVP)
config_global.magic_global = 0;
cg.magic_global = 0;
config_global_save();
// and now as last set magic_global
config_global.magic_global = CONFIG_GLOBAL_MAGIC;
cg.magic_global = CONFIG_GLOBAL_MAGIC;
config_global_save();
}
else if (config_global.magic_model != CONFIG_MODEL_MAGIC) {
else if (cg.magic_model != CONFIG_MODEL_MAGIC) {
// model config changed, empty all models
eeprom_empty_models();
// set model number to 0
config_global.model = 0;
cg.model = 0;
config_global_save();
// set new model magic
config_global.magic_model = CONFIG_MODEL_MAGIC;
cg.magic_model = CONFIG_MODEL_MAGIC;
config_global_save();
}
......
......@@ -29,9 +29,10 @@
// global config
// change MAGIC number when changing global config
// also add code to setting default values
// 38 bytes
#define CONFIG_GLOBAL_MAGIC 0xf906
// also add code to setting default values at config.c
// length must by multiple of 4 because of EEPROM Word programming
// 52 bytes (16 reserved)
#define CONFIG_GLOBAL_MAGIC 0xf807
typedef struct {
u8 steering_dead_zone;
u8 throttle_dead_zone;
......@@ -44,11 +45,16 @@ typedef struct {
u16 backlight_time;
u16 magic_global; // it is here because of problems when flashing
u16 magic_model; // original fw back (1.byte was not overwrited)
u8 model; // selected model
u16 calib_ch3_left;
u16 calib_ch3_right;
u16 battery_calib; // raw ADC value for 10 Volts
u8 model; // selected model
u8 battery_low; // low battery threshold in .1 Volts
u8 endpoint_max; // max allowed endpoint value (def 120%)
u8 long_press_delay; // long press delay in 5ms steps
u8 timer1_alarm; // alarm of timer
u8 timer2_alarm;
u8 inactivity_alarm:4; // time (min) of inactivity warning
u8 key_beep:1; // beep on key press
u8 reset_beep:1; // beep on center/reset value
......@@ -58,17 +64,13 @@ typedef struct {
u8 timer2_type:3;
u8 poweron_warn:1; // beep 3 times when not-centered poweron
u8 rotate_reverse:1; // reverse rotate encoder sense
u8 timer1_alarm; // alarm of timer
u8 timer2_alarm;
u8 ppm_length:4; // length of PPM sync signal (3..) or frame length (9..)
u8 ppm_sync_frame:1; // 0 = constant SYNC length, 1 = constant frame length
u8 channels_default:3; // default number of channels for model
u8 ch3_pot:1; // potentiometer connected instead of CH3 button
u8 unused1:2; // reserve
u8 unused2;
u16 calib_ch3_left;
u16 calib_ch3_right;
u16 unused3;
u8 unused:7; // reserve
u8 reserve[16];
} config_global_s;
extern config_global_s config_global;
......@@ -119,6 +121,7 @@ typedef struct {
#define NUM_TRIMS 4
#define NUM_KEYS 3
// 22 bytes
typedef struct {
config_key_map_s key_map[NUM_KEYS]; // will expand to following et_map
config_et_map_s et_map[NUM_TRIMS];
......@@ -128,43 +131,54 @@ typedef struct {
#define MULTI_POSITION_END -128
#define MP_DIG 0x0f
// change MAGIC number when changing model config
// also add code to setting default values
// 24 + 22(keys) + channels * 4 bytes
#define CONFIG_MODEL_MAGIC (0xf820 | (MAX_CHANNELS - 1))
// length must by multiple of 4 because of EEPROM/FLASH Word programming
// 54(30 reserved) + 22(keys) + channels * 4 bytes = 108 for 8-channel fw
#define CONFIG_MODEL_MAGIC (0xe018 | (MAX_CHANNELS - 1))
typedef struct {
u8 name[3];
u8 reverse; // bit for each channel
s8 subtrim[MAX_CHANNELS];
u8 endpoint[MAX_CHANNELS][2];
s8 trim[2]; // for steering and throttle
u8 name[3];
u8 reverse; // bit for each channel
s8 subtrim[MAX_CHANNELS];
u8 endpoint[MAX_CHANNELS][2];
u8 speed[MAX_CHANNELS]; // reduce speed of servo
#define stspd_turn speed[0]
#define thspd speed[1]
u8 stspd_return; // steering speed return
s8 trim[2]; // for steering and throttle
#define trim_steering trim[0]
#define trim_throttle trim[1]
u8 dualrate[3]; // for steering and throttle
u8 dualrate[3]; // for steering and throttle
#define dr_steering dualrate[0]
#define dr_forward dualrate[1]
#define dr_back dualrate[2]
s8 expo[3]; // steering/forward/back
s8 expo[3]; // steering/forward/back
#define expo_steering expo[0]
#define expo_forward expo[1]
#define expo_back expo[2]
u8 abs_type:2;
u8 brake_off:1; // don't use brake side of throttle
u8 thspd_onlyfwd:1; // throttle speed only at forward side
u8 channel_4WS:4; // channel for 4WS mix or 0 when off
u8 channel_DIG:4; // channel for DIG mix or 0 when off
u8 channel_MP:4; // channel for MultiPosition or 0 when off
s8 multi_position[NUM_MULTI_POSITION]; // values for MultiPosition
u8 speed[MAX_CHANNELS]; // reduce speed of servo
#define stspd_turn speed[0]
#define thspd speed[1]
u8 stspd_return; // steering speed return
u8 channels:3; // number of channels for this model - 1
u8 unused:5;
s8 multi_position[NUM_MULTI_POSITION]; // values for MultiPosition
u8 channels:3; // number of channels for this model - 1
u8 brake_off:1; // don't use brake side of throttle
u8 channel_4WS:4; // channel for 4WS mix or 0 when off
u8 channel_DIG:4; // channel for DIG mix or 0 when off
u8 channel_MP:4; // channel for MultiPosition or 0 when off
u8 thspd_onlyfwd:1; // throttle speed only at forward side
u8 abs_type:2;
u8 unused:5;
u16 unused2;
u16 unused3;
config_key_mapping_s key_mapping;
u8 reserve[26];
} config_model_s;
extern config_model_s config_model;
extern @near config_model_s config_model;
#define cm config_model
#define ck cm.key_mapping
#define ck_ch3_pot_func ((u8 *)&ck.key_map[0])
......@@ -172,14 +186,34 @@ extern config_model_s config_model;
#include "eeprom.h"
#define CONFIG_MODEL_MAX_EEPROM ((EEPROM_SIZE - sizeof(config_global_s)) / \
sizeof(config_model_s))
#define CONFIG_MODEL_MAX CONFIG_MODEL_MAX_EEPROM
// when name[0] is 0xff, that model memory was empty
#define CONFIG_MODEL_EMPTY 0xff
// number of model memories (eeprom + flash)
#define CONFIG_MODEL_MAX_EEPROM ((EEPROM_SIZE - sizeof(config_global_s)) / \
sizeof(config_model_s))
// last eeprom model config will be used to temporarily store
// flash model config
#define CONFIG_MODEL_TMP (CONFIG_MODEL_MAX_EEPROM - 1)
// DefaultInterrupt is last used place in FLASH, skip 1 byte ret instruction
// and compute how many models will fit to flash
extern @interrupt void DefaultInterrupt (void);
#define CONFIG_MODEL_MAX_FLASH ((u8)(((u16)0xffff - (u16)DefaultInterrupt) / \
sizeof(config_model_s)))
#define CONFIG_MODEL_MAX (CONFIG_MODEL_MAX_EEPROM - 1 + CONFIG_MODEL_MAX_FLASH)
// when name[0] is 0x00, that model memory was empty
#define CONFIG_MODEL_EMPTY 0x00
......@@ -192,11 +226,12 @@ extern void config_model_set_default(void);
extern void config_model_read(void);
extern u8 config_global_read(void);
extern u8 *config_model_name(u8 model);
extern void config_empty_models(void);
// write values to eeprom
extern void config_set_model(u8 model);
extern void config_model_save(void);
#define config_global_save() eeprom_write_global()
#define config_model_save() eeprom_write_model(config_global.model)
#define config_empty_models() eeprom_empty_models()
#endif
......
......@@ -18,6 +18,18 @@
/*
first N models are stored at EEPROM after global config
last eeprom model config is used to temporarily store of FLASH model
memory, because EEPROM has 1M write cycles (flash 10K) and
writing to FLASH stops microcontroller (EEPROM has ReadWhileWrite)
models at FLASH are stored from end of FLASH down to end of program
when FLASH model is selected, it is copied to temporary EEPROM place
and is stored back only after another model is selected
*/
#include <string.h>
#include "eeprom.h"
......@@ -42,36 +54,69 @@ void eeprom_read_model(u8 model) {
eeprom_read(EEPROM_CONFIG_MODEL + model * size, &config_model, size);
}
void flash_read_model(u8 model) {
u8 size = sizeof(config_model_s);
eeprom_read((u8 *)(0 - (model + 1) * size), &config_model, size);
}
// write to eeprom
static void eeprom_write(u8 *ee_addr, u8 *ram_addr, u16 length) {
_Bool writed = 0;
static void eeprom_make_writable(void *addr) {
u8 i = 10;
// enable write to eeprom
FLASH_DUKR = 0xAE;
FLASH_DUKR = 0x56;
// wait for write enabled
do {
if (BCHK(FLASH_IAPSR, 3)) break;
} while (--i);
// write only values, which are different
// enable write to eeprom/flash
if ((u16)addr < 0x8000) {
// eeprom
FLASH_DUKR = 0xAE;
FLASH_DUKR = 0x56;
// wait for Data EEPROM area unlocked flag
do {
if (BCHK(FLASH_IAPSR, 3)) break;
} while (--i);
}
else {
// flash
FLASH_PUKR = 0x56;
FLASH_PUKR = 0xAE;
// wait for Flash Program memory unlocked flag
do {
if (BCHK(FLASH_IAPSR, 1)) break;
} while (--i);
}
}
static void eeprom_make_readonly(void *addr) {
if ((u16)addr < 0x8000) BRES(FLASH_IAPSR, 3);
else BRES(FLASH_IAPSR, 1);
}
static void eeprom_write(u8 *ee_addr, u8 *ram_addr, u16 length) {
eeprom_make_writable(ee_addr);
// write only values, which are different, check and write at
// Word mode (4 bytes)
length /= 4;
do {
if (*ee_addr != *ram_addr) {
*ee_addr = *ram_addr;
writed = 1;
if (*(u16 *)ee_addr != *(u16 *)ram_addr ||
*(u16 *)(ee_addr + 2) != *(u16 *)(ram_addr + 2)) {
// enable Word programming
BSET(FLASH_CR2, 6);
BRES(FLASH_NCR2, 6);
// write 4-byte value
ee_addr[0] = ram_addr[0];
ee_addr[1] = ram_addr[1];
ee_addr[2] = ram_addr[2];
ee_addr[3] = ram_addr[3];
// wait for EndOfProgramming flag
while (!BCHK(FLASH_IAPSR, 2)) pause();
}
ee_addr++;
ram_addr++;
ee_addr += 4;
ram_addr += 4;
} while (--length);
// wait to end of write and disable write to eeprom, but only when
// some byte was writed
if (writed)
while (!BCHK(FLASH_IAPSR, 2)) pause();
BRES(FLASH_IAPSR, 3);
eeprom_make_readonly(ee_addr);
}
......@@ -85,34 +130,45 @@ void eeprom_write_model(u8 model) {
eeprom_write(EEPROM_CONFIG_MODEL + model * size, (u8 *)&config_model, size);
}
void flash_write_model(u8 model) {
u8 size = sizeof(config_model_s);
eeprom_write((u8 *)(0 - (model + 1) * size), (u8 *)&config_model, size);
}
// initialize model memories to empty one
void eeprom_empty_models(void) {
_Bool writed = 0;
u8 i = 0;
u8 cnt = CONFIG_MODEL_MAX_EEPROM;
config_model_s *cm = (config_model_s *)EEPROM_CONFIG_MODEL;
// enable write to eeprom
FLASH_DUKR = 0xAE;
FLASH_DUKR = 0x56;
// wait for write enabled
do {
if (BCHK(FLASH_IAPSR, 3)) break;
} while (--i);
// write 0xFF to first letter of each model config
// EEPROM first
eeprom_make_writable(cm);
// write 0x00 to first letter of each model config
do {
if (cm->name[0] != CONFIG_MODEL_EMPTY) {
cm->name[0] = CONFIG_MODEL_EMPTY;
writed = 1;
// wait for EOP
while (!BCHK(FLASH_IAPSR, 2)) pause();
}
cm++;
} while (--cnt);
// wait to end of write and disable write to eeprom, but only when
// some byte was writed
if (writed)
while (!BCHK(FLASH_IAPSR, 2)) pause();
BRES(FLASH_IAPSR, 3);
eeprom_make_readonly(cm);
// FLASH second
cnt = CONFIG_MODEL_MAX_FLASH;
cm = (config_model_s *)(-sizeof(config_model_s));
eeprom_make_writable(cm);
// write 0x00 to first letter of each model config
do {
if (cm->name[0] != CONFIG_MODEL_EMPTY) {
cm->name[0] = CONFIG_MODEL_EMPTY;
// wait for EOP
while (!BCHK(FLASH_IAPSR, 2)) pause();
}
cm--;
} while (--cnt);
eeprom_make_readonly(cm);
}
......@@ -39,8 +39,10 @@
extern void eeprom_read_global(void);
extern void eeprom_read_model(u8 model);
extern void flash_read_model(u8 model);
extern void eeprom_write_global(void);
extern void eeprom_write_model(u8 model);