From 6394c3aaff62fdca7502e4c31e056117184350cf Mon Sep 17 00:00:00 2001 From: true Date: Wed, 1 Nov 2023 07:45:44 -0700 Subject: [PATCH] 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. --- include/flash.h | 30 +++++++ include/rgbprog.h | 2 +- platformio.ini | 10 +-- py32f030x6.ld | 2 +- py32f030x6_bl.ld | 2 +- src/flash.c | 216 +++++++++++++++++++++++++++++++++++++++++++++- src/main.c | 3 + src/rgbprog.c | 26 +++--- 8 files changed, 267 insertions(+), 24 deletions(-) diff --git a/include/flash.h b/include/flash.h index 1a89283..2db7bd6 100644 --- a/include/flash.h +++ b/include/flash.h @@ -16,9 +16,39 @@ #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_load_conf(); +void flash_commit_conf(); + #endif /* _INC_FLASH_H */ \ No newline at end of file diff --git a/include/rgbprog.h b/include/rgbprog.h index 60141ee..cb915a2 100644 --- a/include/rgbprog.h +++ b/include/rgbprog.h @@ -14,7 +14,7 @@ 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); diff --git a/platformio.ini b/platformio.ini index 450dfac..e7c6afb 100644 --- a/platformio.ini +++ b/platformio.ini @@ -56,14 +56,14 @@ debug_build_flags = [env:sc7-testobot_REV1-py32f-dbg_pyocd] build_type = debug - +board_upload.maximum_size = 32256 ; 512 bytes for config debug_build_flags = ${common.debug_build_flags} -DTESTO_REV1 [env:sc7-testobot_REV2-py32f-dbg_pyocd] build_type = debug - +board_upload.maximum_size = 32256 ; 512 bytes for config debug_build_flags = ${common.debug_build_flags} -DTESTO_REV2 @@ -71,8 +71,7 @@ debug_build_flags = ${common.debug_build_flags} [env:sc7-testobot_REV1-py32f-rel_bl] build_type = release 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} -DBOOTLOADED -DTESTO_REV1 @@ -81,8 +80,7 @@ build_flags = ${common.build_flags} [env:sc7-testobot_REV2-py32f-rel_bl] build_type = release 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} -DBOOTLOADED -DTESTO_REV2 \ No newline at end of file diff --git a/py32f030x6.ld b/py32f030x6.ld index 84187c7..52acd5a 100644 --- a/py32f030x6.ld +++ b/py32f030x6.ld @@ -30,7 +30,7 @@ _Min_Stack_Size = 0x100; /* required amount of stack: 256 bytes */ MEMORY { RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 4K - FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 32K + FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 32K - 512 } /* Define output sections */ diff --git a/py32f030x6_bl.ld b/py32f030x6_bl.ld index f422511..c2e19d6 100644 --- a/py32f030x6_bl.ld +++ b/py32f030x6_bl.ld @@ -30,7 +30,7 @@ _Min_Stack_Size = 0x100; /* required amount of stack: 256 bytes */ MEMORY { RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 4K - FLASH (rx) : ORIGIN = 0x08000800, LENGTH = 32K - 2K + FLASH (rx) : ORIGIN = 0x08000800, LENGTH = 32K - 2K - 512 } /* Define output sections */ diff --git a/src/flash.c b/src/flash.c index d3846ad..11faf82 100644 --- a/src/flash.c +++ b/src/flash.c @@ -15,6 +15,11 @@ #include "flash.h" + +struct UConf conf = {0}; + + + /* * loads flash timing values. * 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 // 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 + // in testing, either ROM or RM value seems to work. // note: the datasheet doesn't say it anywhere, but if flash is not unlocked, // 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) // 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; + // then trigger the write by writing to a random address shown in the DS *((__IO uint32_t *)(0x40022080)) = 0x55aa55aa; // wait for BSY to go low, then EOP to go high @@ -102,4 +108,212 @@ __attribute__ ((long_call, section(".ramfunc"))) void flash_init() // re-lock flash when done 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); + } + } } \ No newline at end of file diff --git a/src/main.c b/src/main.c index 786d053..d346238 100644 --- a/src/main.c +++ b/src/main.c @@ -104,6 +104,9 @@ int main() flash_init(); // also configures option bytes to enable PF2, if necessary gpio_init(); + // load configuration from flash + flash_load_conf(); + // peripheral initialization led_init(); adc_init(); diff --git a/src/rgbprog.c b/src/rgbprog.c index bd135b9..3b33c18 100644 --- a/src/rgbprog.c +++ b/src/rgbprog.c @@ -117,7 +117,7 @@ void rgbprog_run() // if SET1 is out of range, indicate that instead of // running the normal program if (userio_get_set1_limit()) { - rgbprog_error_flasher(120, 0, 0); + rgbprog_error_flasher(0, 120, 0, 0); return; } @@ -405,22 +405,20 @@ void rgbprog_prog7(uint8_t k0) * * flashes LEDs, indicating a SET1 knob error. */ -static uint8_t err_ctr; +static uint8_t err_ctr = 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) { err_tog ^= 1; } - - rgb[0].r = r; - rgb[0].g = g; - 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]); + + led_setrgb(err_tog, r, g, b); + led_setrgb(err_tog ^ 1, 0, 0, 0); } \ No newline at end of file