Implement flash config storage
Calibration needs to be saved for each badge in continuity mode. Added a flash-backed user config storing option with basic wear leveling. Have NOT yet tested the write and erase functions.
This commit is contained in:
parent
ad383a76ac
commit
6394c3aaff
|
@ -16,9 +16,39 @@
|
||||||
#define FLASH_HSI_24MHz (uint32_t *)0x1FFF0F6C
|
#define FLASH_HSI_24MHz (uint32_t *)0x1FFF0F6C
|
||||||
|
|
||||||
|
|
||||||
|
#define UCONF_FLASH_SIZE 512
|
||||||
|
#define UCONF_FLASH_START (FLASH_END + 1) - UCONF_FLASH_SIZE
|
||||||
|
|
||||||
|
#define UCONF_SIZE 64 // size in bytes
|
||||||
|
#define UCONF_COUNT (UCONF_FLASH_SIZE / UCONF_SIZE)
|
||||||
|
|
||||||
|
#define UCONF_KEY 0x3713
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct UConf {
|
||||||
|
uint16_t conf_key; // should read 0x3713
|
||||||
|
uint8_t cont_buzzer; // continuity buzzer on/off
|
||||||
|
uint8_t cont_sensitivity; // continuity sensitivity high/low
|
||||||
|
uint16_t cont_shorted; // ADC counts when probes shorted in continuity mode
|
||||||
|
uint16_t diode_shorted; // unused, unneeded
|
||||||
|
uint32_t rsvd_1[4];
|
||||||
|
uint32_t rsvd_2[8];
|
||||||
|
uint32_t vctr; // increment every update of config
|
||||||
|
uint32_t crc32; // CRC of preceding 60 bytes
|
||||||
|
} UConf; // 64 bytes
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
extern struct UConf conf;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void flash_init();
|
void flash_init();
|
||||||
|
|
||||||
|
void flash_load_conf();
|
||||||
|
void flash_commit_conf();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* _INC_FLASH_H */
|
#endif /* _INC_FLASH_H */
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
void rgbprog_run();
|
void rgbprog_run();
|
||||||
|
|
||||||
void rgbprog_error_flasher(uint8_t r, uint8_t g, uint8_t b);
|
void rgbprog_error_flasher(uint8_t scale, uint8_t r, uint8_t g, uint8_t b);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -56,14 +56,14 @@ debug_build_flags =
|
||||||
|
|
||||||
[env:sc7-testobot_REV1-py32f-dbg_pyocd]
|
[env:sc7-testobot_REV1-py32f-dbg_pyocd]
|
||||||
build_type = debug
|
build_type = debug
|
||||||
|
board_upload.maximum_size = 32256 ; 512 bytes for config
|
||||||
debug_build_flags = ${common.debug_build_flags}
|
debug_build_flags = ${common.debug_build_flags}
|
||||||
-DTESTO_REV1
|
-DTESTO_REV1
|
||||||
|
|
||||||
|
|
||||||
[env:sc7-testobot_REV2-py32f-dbg_pyocd]
|
[env:sc7-testobot_REV2-py32f-dbg_pyocd]
|
||||||
build_type = debug
|
build_type = debug
|
||||||
|
board_upload.maximum_size = 32256 ; 512 bytes for config
|
||||||
debug_build_flags = ${common.debug_build_flags}
|
debug_build_flags = ${common.debug_build_flags}
|
||||||
-DTESTO_REV2
|
-DTESTO_REV2
|
||||||
|
|
||||||
|
@ -71,8 +71,7 @@ debug_build_flags = ${common.debug_build_flags}
|
||||||
[env:sc7-testobot_REV1-py32f-rel_bl]
|
[env:sc7-testobot_REV1-py32f-rel_bl]
|
||||||
build_type = release
|
build_type = release
|
||||||
board_build.ldscript = py32f030x6_bl.ld
|
board_build.ldscript = py32f030x6_bl.ld
|
||||||
board_upload.maximum_size = 30720 ; 2K reserved for bootloader
|
board_upload.maximum_size = 30208 ; 2K reserved for bootloader, 512 bytes for config
|
||||||
|
|
||||||
build_flags = ${common.build_flags}
|
build_flags = ${common.build_flags}
|
||||||
-DBOOTLOADED
|
-DBOOTLOADED
|
||||||
-DTESTO_REV1
|
-DTESTO_REV1
|
||||||
|
@ -81,8 +80,7 @@ build_flags = ${common.build_flags}
|
||||||
[env:sc7-testobot_REV2-py32f-rel_bl]
|
[env:sc7-testobot_REV2-py32f-rel_bl]
|
||||||
build_type = release
|
build_type = release
|
||||||
board_build.ldscript = py32f030x6_bl.ld
|
board_build.ldscript = py32f030x6_bl.ld
|
||||||
board_upload.maximum_size = 30720 ; 2K reserved for bootloader
|
board_upload.maximum_size = 30208 ; 2K reserved for bootloader, 512 bytes for config
|
||||||
|
|
||||||
build_flags = ${common.build_flags}
|
build_flags = ${common.build_flags}
|
||||||
-DBOOTLOADED
|
-DBOOTLOADED
|
||||||
-DTESTO_REV2
|
-DTESTO_REV2
|
|
@ -30,7 +30,7 @@ _Min_Stack_Size = 0x100; /* required amount of stack: 256 bytes */
|
||||||
MEMORY
|
MEMORY
|
||||||
{
|
{
|
||||||
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 4K
|
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 4K
|
||||||
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 32K
|
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 32K - 512
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Define output sections */
|
/* Define output sections */
|
||||||
|
|
|
@ -30,7 +30,7 @@ _Min_Stack_Size = 0x100; /* required amount of stack: 256 bytes */
|
||||||
MEMORY
|
MEMORY
|
||||||
{
|
{
|
||||||
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 4K
|
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 4K
|
||||||
FLASH (rx) : ORIGIN = 0x08000800, LENGTH = 32K - 2K
|
FLASH (rx) : ORIGIN = 0x08000800, LENGTH = 32K - 2K - 512
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Define output sections */
|
/* Define output sections */
|
||||||
|
|
216
src/flash.c
216
src/flash.c
|
@ -15,6 +15,11 @@
|
||||||
#include "flash.h"
|
#include "flash.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct UConf conf = {0};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* loads flash timing values.
|
* loads flash timing values.
|
||||||
* ensure flash is unlocked before calling this function,
|
* ensure flash is unlocked before calling this function,
|
||||||
|
@ -34,6 +39,7 @@ void flash_set_timing(uint32_t *timing_table)
|
||||||
// the values listed in the reference manual. this is true of
|
// the values listed in the reference manual. this is true of
|
||||||
// all versions I have access to - English v1.0 and Chinese v1.2.
|
// all versions I have access to - English v1.0 and Chinese v1.2.
|
||||||
// for example, @8MHz, RM says 0x5dc0 for PERTPE but ROM holds 0x6b60
|
// for example, @8MHz, RM says 0x5dc0 for PERTPE but ROM holds 0x6b60
|
||||||
|
// in testing, either ROM or RM value seems to work.
|
||||||
|
|
||||||
// note: the datasheet doesn't say it anywhere, but if flash is not unlocked,
|
// note: the datasheet doesn't say it anywhere, but if flash is not unlocked,
|
||||||
// then flash timing values aren't writeable
|
// then flash timing values aren't writeable
|
||||||
|
@ -84,8 +90,8 @@ __attribute__ ((long_call, section(".ramfunc"))) void flash_init()
|
||||||
|
|
||||||
// enable EOPIE (per DS, EOP isn't set unless EOPIE is set)
|
// enable EOPIE (per DS, EOP isn't set unless EOPIE is set)
|
||||||
// and signal that we want to write option bits,
|
// and signal that we want to write option bits,
|
||||||
// then trigger the write by writing to a random address shown in the DS
|
|
||||||
FLASH->CR |= FLASH_CR_EOPIE | FLASH_CR_OPTSTRT;
|
FLASH->CR |= FLASH_CR_EOPIE | FLASH_CR_OPTSTRT;
|
||||||
|
// then trigger the write by writing to a random address shown in the DS
|
||||||
*((__IO uint32_t *)(0x40022080)) = 0x55aa55aa;
|
*((__IO uint32_t *)(0x40022080)) = 0x55aa55aa;
|
||||||
|
|
||||||
// wait for BSY to go low, then EOP to go high
|
// wait for BSY to go low, then EOP to go high
|
||||||
|
@ -103,3 +109,211 @@ __attribute__ ((long_call, section(".ramfunc"))) void flash_init()
|
||||||
// re-lock flash when done
|
// re-lock flash when done
|
||||||
FLASH->CR = FLASH_CR_LOCK;
|
FLASH->CR = FLASH_CR_LOCK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* loads latest configuration from flash to conf variable in RAM.
|
||||||
|
*/
|
||||||
|
void flash_load_conf()
|
||||||
|
{
|
||||||
|
int8_t i, j;
|
||||||
|
uint8_t loaded = 0;
|
||||||
|
|
||||||
|
struct UConf *fconf;
|
||||||
|
uint32_t *f32;
|
||||||
|
uint32_t *u32;
|
||||||
|
|
||||||
|
for (i = (UCONF_COUNT - 1); i >= 0; i--) {
|
||||||
|
fconf = (struct UConf *)(UCONF_FLASH_START + (UCONF_SIZE * i));
|
||||||
|
f32 = (uint32_t *)fconf;
|
||||||
|
|
||||||
|
// empty flash = nothing here
|
||||||
|
if (fconf->crc32 == 0xffffffff) continue;
|
||||||
|
|
||||||
|
// invalid header = skip
|
||||||
|
if (fconf->conf_key != UCONF_KEY) continue;
|
||||||
|
|
||||||
|
// calculate crc32
|
||||||
|
CRC->CR = CRC_CR_RESET;
|
||||||
|
while (CRC->CR & CRC_CR_RESET);
|
||||||
|
|
||||||
|
for (j = 0; j < ((UCONF_SIZE / 4) - 1); j++) {
|
||||||
|
CRC->DR = f32[j];
|
||||||
|
}
|
||||||
|
|
||||||
|
// check crc32
|
||||||
|
if (CRC->DR == f32[(UCONF_SIZE / 4) - 1]) {
|
||||||
|
// it passes. copy to RAM and break out
|
||||||
|
u32 = (uint32_t *)&conf;
|
||||||
|
for (j = 0; j < (UCONF_SIZE / 4); j++) {
|
||||||
|
*u32++ = *f32++;
|
||||||
|
}
|
||||||
|
loaded = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!loaded) {
|
||||||
|
// default config of zeroes works
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* erases a flash page at the specified address.
|
||||||
|
*/
|
||||||
|
__attribute__ ((long_call, section(".ramfunc"))) void flash_erase_page(uint32_t addr)
|
||||||
|
{
|
||||||
|
uint32_t primask;
|
||||||
|
|
||||||
|
// ensure flash is not busy
|
||||||
|
while (FLASH->SR & FLASH_SR_BSY);
|
||||||
|
|
||||||
|
// unlock flash
|
||||||
|
FLASH->KEYR = FLASH_KEY1;
|
||||||
|
FLASH->KEYR = FLASH_KEY2;
|
||||||
|
while (FLASH->CR & FLASH_CR_LOCK); // still locked? then stall forever
|
||||||
|
|
||||||
|
// enable EOPIE (per DS, EOP isn't set unless EOPIE is set)
|
||||||
|
// and signal that we want to erase a page
|
||||||
|
FLASH->CR |= FLASH_CR_EOPIE | FLASH_CR_PER;
|
||||||
|
|
||||||
|
// disable interrupts
|
||||||
|
primask = __get_PRIMASK();
|
||||||
|
__disable_irq();
|
||||||
|
|
||||||
|
// trigger the erase by writing to a random address shown in the DS
|
||||||
|
*((__IO uint32_t *)(addr)) = 0x55aa55aa;
|
||||||
|
|
||||||
|
// ensure flash is not busy, then wait for end of programming
|
||||||
|
while (FLASH->SR & FLASH_SR_BSY);
|
||||||
|
while (!(FLASH->SR & FLASH_SR_EOP));
|
||||||
|
|
||||||
|
// re-enable interrupts
|
||||||
|
__set_PRIMASK(primask);
|
||||||
|
|
||||||
|
// clear EOP
|
||||||
|
FLASH->SR |= FLASH_SR_EOP;
|
||||||
|
|
||||||
|
// and re-lock flash
|
||||||
|
FLASH->CR = FLASH_CR_LOCK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* erases a flash page at the specified address.
|
||||||
|
* data must be a pointer to a full page and
|
||||||
|
* must contain 128 bytes (32 words) of data to write.
|
||||||
|
*/
|
||||||
|
__attribute__ ((long_call, section(".ramfunc"))) void flash_write_page(uint32_t addr, uint32_t *data)
|
||||||
|
{
|
||||||
|
uint32_t primask;
|
||||||
|
|
||||||
|
uint8_t i;
|
||||||
|
|
||||||
|
uint32_t *src;
|
||||||
|
volatile uint32_t *dst;
|
||||||
|
|
||||||
|
// ensure flash is not busy
|
||||||
|
while (FLASH->SR & FLASH_SR_BSY);
|
||||||
|
|
||||||
|
// unlock flash
|
||||||
|
FLASH->KEYR = FLASH_KEY1;
|
||||||
|
FLASH->KEYR = FLASH_KEY2;
|
||||||
|
while (FLASH->CR & FLASH_CR_LOCK); // still locked? then stall forever
|
||||||
|
|
||||||
|
// enable EOPIE (per DS, EOP isn't set unless EOPIE is set)
|
||||||
|
// and signal that we want to write a page
|
||||||
|
FLASH->CR |= FLASH_CR_EOPIE | FLASH_CR_PG;
|
||||||
|
|
||||||
|
// disable interrupts
|
||||||
|
primask = __get_PRIMASK();
|
||||||
|
__disable_irq();
|
||||||
|
|
||||||
|
// write data to flash
|
||||||
|
src = data;
|
||||||
|
dst = (volatile uint32_t *)addr;
|
||||||
|
for (i = 0; i < (FLASH_PAGE_SIZE / 4); i++) {
|
||||||
|
// on the last page, enable the page write start bit to commit to flash
|
||||||
|
if (i == (FLASH_PAGE_SIZE / 4) - 1) {
|
||||||
|
FLASH->CR |= FLASH_CR_PGSTRT;
|
||||||
|
}
|
||||||
|
|
||||||
|
*dst++ = *src++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure flash is not busy, then wait for end of programming
|
||||||
|
while (FLASH->SR & FLASH_SR_BSY);
|
||||||
|
while (!(FLASH->SR & FLASH_SR_EOP));
|
||||||
|
|
||||||
|
// re-enable interrupts
|
||||||
|
__set_PRIMASK(primask);
|
||||||
|
|
||||||
|
// clear EOP
|
||||||
|
FLASH->SR |= FLASH_SR_EOP;
|
||||||
|
|
||||||
|
// and re-lock flash
|
||||||
|
FLASH->CR = FLASH_CR_LOCK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* stores configuration from conf variable in RAM to flash.
|
||||||
|
* performs very basic wear leveling.
|
||||||
|
*/
|
||||||
|
void flash_commit_conf()
|
||||||
|
{
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
uint8_t flash_idx = 0;
|
||||||
|
uint8_t flash_full;
|
||||||
|
|
||||||
|
struct UConf *fconf;
|
||||||
|
uint32_t *f32, *u32;
|
||||||
|
uint32_t write[(FLASH_PAGE_SIZE / 4)];
|
||||||
|
|
||||||
|
// find first free page index
|
||||||
|
for (i = 0; i < UCONF_COUNT; i++) {
|
||||||
|
fconf = (struct UConf *)(UCONF_FLASH_START + (UCONF_SIZE * i));
|
||||||
|
if ((fconf->crc32 == 0xffffffff) || (fconf->conf_key != UCONF_KEY)) {
|
||||||
|
flash_idx = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if all pages are occupied, erase the first page
|
||||||
|
if (i == UCONF_COUNT) {
|
||||||
|
flash_full = 1;
|
||||||
|
flash_erase_page(UCONF_FLASH_START);
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy flash page from ROM to the write buffer,
|
||||||
|
// as our data (64 bytes) is smaller than the page write size (128 bytes)
|
||||||
|
f32 = (uint32_t *)fconf;
|
||||||
|
u32 = (uint32_t *)write;
|
||||||
|
for (i = 0; i < (FLASH_PAGE_SIZE / 4); i++) {
|
||||||
|
*u32++ = *f32++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure config header is correct, and calculate CRC
|
||||||
|
conf.conf_key = UCONF_KEY;
|
||||||
|
u32 = (uint32_t *)&conf;
|
||||||
|
|
||||||
|
CRC->CR = CRC_CR_RESET;
|
||||||
|
while (CRC->CR & CRC_CR_RESET);
|
||||||
|
|
||||||
|
for (i = 0; i < (UCONF_SIZE / 4) - 1; i++) {
|
||||||
|
CRC->DR = u32[i];
|
||||||
|
}
|
||||||
|
u32[(UCONF_SIZE / 4) - 1] = CRC->DR;
|
||||||
|
|
||||||
|
// copy user config from RAM to write buffer at the appropriate offset
|
||||||
|
f32 = (uint32_t *)&conf;
|
||||||
|
u32 = (uint32_t *)write;
|
||||||
|
for (i = (flash_idx ? UCONF_SIZE : 0); i < (UCONF_SIZE / 4); i++) {
|
||||||
|
*u32++ = *f32++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if flash was full, erase other pages
|
||||||
|
if (flash_full) {
|
||||||
|
for (i = (UCONF_FLASH_START + FLASH_PAGE_SIZE); i < FLASH_END; i += FLASH_PAGE_SIZE) {
|
||||||
|
flash_erase_page(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -104,6 +104,9 @@ int main()
|
||||||
flash_init(); // also configures option bytes to enable PF2, if necessary
|
flash_init(); // also configures option bytes to enable PF2, if necessary
|
||||||
gpio_init();
|
gpio_init();
|
||||||
|
|
||||||
|
// load configuration from flash
|
||||||
|
flash_load_conf();
|
||||||
|
|
||||||
// peripheral initialization
|
// peripheral initialization
|
||||||
led_init();
|
led_init();
|
||||||
adc_init();
|
adc_init();
|
||||||
|
|
|
@ -117,7 +117,7 @@ void rgbprog_run()
|
||||||
// if SET1 is out of range, indicate that instead of
|
// if SET1 is out of range, indicate that instead of
|
||||||
// running the normal program
|
// running the normal program
|
||||||
if (userio_get_set1_limit()) {
|
if (userio_get_set1_limit()) {
|
||||||
rgbprog_error_flasher(120, 0, 0);
|
rgbprog_error_flasher(0, 120, 0, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -405,22 +405,20 @@ void rgbprog_prog7(uint8_t k0)
|
||||||
*
|
*
|
||||||
* flashes LEDs, indicating a SET1 knob error.
|
* flashes LEDs, indicating a SET1 knob error.
|
||||||
*/
|
*/
|
||||||
static uint8_t err_ctr;
|
static uint8_t err_ctr = 0;
|
||||||
static uint8_t err_tog = 0;
|
static uint8_t err_tog = 0;
|
||||||
void rgbprog_error_flasher(uint8_t r, uint8_t g, uint8_t b)
|
void rgbprog_error_flasher(uint8_t scale, uint8_t r, uint8_t g, uint8_t b)
|
||||||
{
|
{
|
||||||
err_ctr++;
|
uint8_t x;
|
||||||
|
|
||||||
|
x = (1 << scale);
|
||||||
|
err_ctr += x;
|
||||||
|
err_ctr &= ~(x - 1);
|
||||||
|
|
||||||
if (!err_ctr) {
|
if (!err_ctr) {
|
||||||
err_tog ^= 1;
|
err_tog ^= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
rgb[0].r = r;
|
led_setrgb(err_tog, r, g, b);
|
||||||
rgb[0].g = g;
|
led_setrgb(err_tog ^ 1, 0, 0, 0);
|
||||||
rgb[0].b = b;
|
|
||||||
rgb_setled(err_tog, &rgb[0]);
|
|
||||||
|
|
||||||
rgb[0].r = 0;
|
|
||||||
rgb[0].g = 0;
|
|
||||||
rgb[0].b = 0;
|
|
||||||
rgb_setled(err_tog ^ 1, &rgb[0]);
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue