From 940b60df99222f609d171b9b2892d4e959c42b8b Mon Sep 17 00:00:00 2001 From: Edward Emelianov Date: Wed, 29 Jul 2026 21:01:01 +0300 Subject: [PATCH] add flash storage --- F0:F030,F042,F072/3steppersLB/flash.c | 4 +- .../Snippets/Flash_EEPROM/flash.c | 4 +- F0:F030,F042,F072/usbcan_gpio/flash.c | 4 +- G0:G070,G0B1/flash/flash.c | 4 +- G4:G431/Servo/commproto.cpp | 130 +++++++----- G4:G431/Servo/flash.c | 186 ++++++++++++++++++ G4:G431/Servo/flash.h | 52 +++++ G4:G431/Servo/hardware.c | 10 +- G4:G431/Servo/hardware.h | 3 + G4:G431/Servo/main.c | 5 +- G4:G431/Servo/servo.bin | Bin 6108 -> 9092 bytes G4:G431/Servo/servo.c | 5 +- G4:G431/Servo/servo.files | 2 + G4:G431/Servo/servo.h | 9 +- G4:G431/Servo/version.inc | 4 +- 15 files changed, 352 insertions(+), 70 deletions(-) create mode 100644 G4:G431/Servo/flash.c create mode 100644 G4:G431/Servo/flash.h diff --git a/F0:F030,F042,F072/3steppersLB/flash.c b/F0:F030,F042,F072/3steppersLB/flash.c index bef6a36..63ef062 100644 --- a/F0:F030,F042,F072/3steppersLB/flash.c +++ b/F0:F030,F042,F072/3steppersLB/flash.c @@ -94,7 +94,7 @@ static int binarySearch(int r, const uint8_t *start, int stor_size){ */ void flashstorage_init(){ if(FLASH_SIZE > 0 && FLASH_SIZE < 20000){ - uint32_t flsz = FLASH_SIZE * blocksize; // size in bytes + uint32_t flsz = FLASH_SIZE * 1024; // size in bytes flsz -= (uint32_t)(&__varsstart) - FLASH_BASE; maxCnum = flsz / sizeof(user_conf); } @@ -155,7 +155,7 @@ static int erase_flash(const void *start, const void *end){ uint32_t nblocks = 1, flsz = 0; if(!end){ // erase all remaining if(FLASH_SIZE > 0 && FLASH_SIZE < 20000){ - flsz = FLASH_SIZE * blocksize; // size in bytes + flsz = FLASH_SIZE * 1024; // size in bytes flsz -= (uint32_t)start - FLASH_BASE; } }else{ // erase a part diff --git a/F0:F030,F042,F072/Snippets/Flash_EEPROM/flash.c b/F0:F030,F042,F072/Snippets/Flash_EEPROM/flash.c index eea44e7..e48acd3 100644 --- a/F0:F030,F042,F072/Snippets/Flash_EEPROM/flash.c +++ b/F0:F030,F042,F072/Snippets/Flash_EEPROM/flash.c @@ -96,7 +96,7 @@ static int binarySearch(int r, const uint8_t *start, int stor_size){ */ void flashstorage_init(){ if(FLASH_SIZE > 0 && FLASH_SIZE < 20000){ - uint32_t flsz = FLASH_SIZE * blocksize; // size in bytes + uint32_t flsz = FLASH_SIZE * 1024; // size in bytes flsz -= (uint32_t)(&__varsstart) - FLASH_BASE; maxCnum = flsz / sizeof(user_conf); //SEND("flsz="); printu(flsz); @@ -165,7 +165,7 @@ static int erase_flash(const void *start, const void *end){ uint32_t nblocks = 1, flsz = 0; if(!end){ // erase all remaining if(FLASH_SIZE > 0 && FLASH_SIZE < 20000){ - flsz = FLASH_SIZE * blocksize; // size in bytes + flsz = FLASH_SIZE * 1024; // size in bytes flsz -= (uint32_t)start - FLASH_BASE; } }else{ // erase a part diff --git a/F0:F030,F042,F072/usbcan_gpio/flash.c b/F0:F030,F042,F072/usbcan_gpio/flash.c index ee1d11f..d8d64ad 100644 --- a/F0:F030,F042,F072/usbcan_gpio/flash.c +++ b/F0:F030,F042,F072/usbcan_gpio/flash.c @@ -103,7 +103,7 @@ static int binarySearch(int r, const uint8_t *start, int stor_size){ */ void flashstorage_init(){ if(FLASH_SIZE > 0 && FLASH_SIZE < 20000){ - uint32_t flsz = FLASH_SIZE * blocksize; // size in bytes + uint32_t flsz = FLASH_SIZE * 1024; // size in bytes flsz -= (uint32_t)(&__varsstart) - FLASH_BASE; maxCnum = flsz / sizeof(user_conf); } @@ -164,7 +164,7 @@ static int erase_flash(const void *start, const void *end){ uint32_t nblocks = 1, flsz = 0; if(!end){ // erase all remaining if(FLASH_SIZE > 0 && FLASH_SIZE < 20000){ - flsz = FLASH_SIZE * blocksize; // size in bytes + flsz = FLASH_SIZE * 1024; // size in bytes flsz -= (uint32_t)start - FLASH_BASE; } }else{ // erase a part diff --git a/G0:G070,G0B1/flash/flash.c b/G0:G070,G0B1/flash/flash.c index 9a6ab8d..49dea89 100644 --- a/G0:G070,G0B1/flash/flash.c +++ b/G0:G070,G0B1/flash/flash.c @@ -81,7 +81,7 @@ static int binarySearch(int r, const uint8_t *start, int stor_size){ */ void flashstorage_init(){ if(FLASH_SIZE > 0 && FLASH_SIZE < 20000){ - uint32_t flsz = FLASH_SIZE * blocksize; // size in bytes + uint32_t flsz = FLASH_SIZE * 1024; // size in bytes flsz -= (uint32_t)(&__varsstart) - FLASH_BASE; maxCnum = flsz / sizeof(user_conf); } @@ -149,7 +149,7 @@ static int erase_flash(const void *start, const void *end){ if(!start) return 1; uint32_t startb = (((uint32_t)start - FLASH_BASE) + blocksize - 1) / blocksize, endb; if(!end){ // erase all remaining - endb = FLASH_SIZE / blocksize; + endb = FLASH_SIZE * 1024 / blocksize; }else{ // erase a part endb = (((uint32_t)end - FLASH_BASE) + blocksize - 1) / blocksize; } diff --git a/G4:G431/Servo/commproto.cpp b/G4:G431/Servo/commproto.cpp index e08263d..eb7362a 100644 --- a/G4:G431/Servo/commproto.cpp +++ b/G4:G431/Servo/commproto.cpp @@ -22,7 +22,7 @@ extern "C"{ #include #include "commproto.h" -//#include "flash.h" +#include "flash.h" #include "hardware.h" #include "servo.h" #include "strfunc.h" @@ -42,12 +42,22 @@ extern volatile uint32_t Tms; // list of all commands and handlers #define COMMAND_TABLE \ + COMMAND(dumpconf, "dump current config") \ + COMMAND(eraseflash, "erase full flash storage") \ COMMAND(help, "show this help") \ + COMMAND(maxpos, "servoN maximal allowed position (conf)") \ + COMMAND(maxspeed, "servoN maximal allowed speed (from 1 to " STR(SERVO_MAXSPEED) ", conf)") \ COMMAND(mcureset, "reset MCU") \ - COMMAND(servo, "get/set servoN value, mks (from " STR(SG90_MINPULSE) " to " STR(SG90_MAXPULSE) ")") \ - COMMAND(servopos, "get/set servoN target position with given speed") \ - COMMAND(servospeed, "get/set servoN speed, mks per 20ms (1.." STR(SG90_MAXSPEED) ")") \ + COMMAND(minpos, "servoN minimal allowed position (conf)") \ + COMMAND(readconf, "re-read config from flash") \ + COMMAND(saveconf, "save config to flash") \ + COMMAND(servo, "servoN value, mks (in allowed range)") \ + COMMAND(servopos, "servoN target position with given speed") \ + COMMAND(servospeed, "servoN speed, mks per 20ms (1..maxspeed)") \ + COMMAND(startpos, "servoN started position (from " STR(SERVO_MINPULSE) " to " STR(SERVO_MAXPULSE) ", conf)") \ COMMAND(time, "show current time (ms)") \ + COMMAND(usart_speed,"speed of USART (set after reset, conf)") \ + typedef struct { const char *name; @@ -82,7 +92,8 @@ const char *EQ = " = "; // equal sign for getters #define CMDEQ() do{SEND(cmd); SEND(EQ);}while(0) // send `commandXXX = ` #define CMDEQP(x) do{SEND(cmd); SEND(u2str((uint32_t)x)); SEND(EQ);}while(0) - +// the same as last but with command as option and uint value +#define SHOWPARU(cmd, x, val) do{SEND(cmd); SEND(u2str((uint32_t)x)); SEND(EQ); SEND(u2str(val));}while(0) /** * @brief splitargs - get command parameter and setter from `args` @@ -117,8 +128,8 @@ static bool argsvals(char *args, int32_t *parno, int32_t *parval){ if(!setter) return false; int32_t I32; char *next = getint(setter, &I32); - if(next != setter && parval){ - *parval = I32; + if(next != setter){ + if(parval) *parval = I32; return true; } return false; @@ -176,69 +187,84 @@ static errcodes_t cmd_mcureset(const char*, char*){ NVIC_SystemReset(); return ERR_CANTRUN; // never reached } -#if 0 + static errcodes_t cmd_saveconf(const char*, char*){ if(store_userconf()) return ERR_CANTRUN; return ERR_OK; } static errcodes_t cmd_eraseflash(const char*, char*){ - if(erase_storage(-1)) return ERR_CANTRUN; + if(erase_storage()) return ERR_CANTRUN; return ERR_OK; } -static errcodes_t cmd_readconf(const char*, char* args){ - int32_t CHno = -1; - splitargs(args, &CHno); - if(CHno < 0 || CHno >= SENSORS_AMOUNT) return ERR_BADPAR; - as3935_channel = static_cast(CHno); - uint8_t par; - if(!as3935_get_gain(&par)) return ERR_CANTRUN; - the_conf.spars[CHno].AFE_GB = par; - if(!as3935_get_wdthres(&par)) return ERR_CANTRUN; - the_conf.spars[CHno].WDTH = par; - if(!as3935_get_nflev(&par)) return ERR_CANTRUN; - the_conf.spars[CHno].NF_LEV = par; - if(!as3935_get_srej(&par)) return ERR_CANTRUN; - the_conf.spars[CHno].SREJ = par; - if(!as3935_get_minnumlig(&par)) return ERR_CANTRUN; - the_conf.spars[CHno].MIN_NUM_LIG = par; - if(!as3935_get_maskdist(&par)) return ERR_CANTRUN; - the_conf.spars[CHno].MASK_DIST = par; - if(!as3935_get_lco_fdiv(&par)) return ERR_CANTRUN; - the_conf.spars[CHno].LCO_FDIV = par; - if(!as3935_get_tuncap(&par)) return ERR_CANTRUN; - the_conf.spars[CHno].TUN_CAP = par; +static errcodes_t cmd_readconf(const char*, char*){ + flashstorage_init(); return ERR_OK; } -static void showpar(const char *par, uint8_t n, uint8_t v){ - char c[2]; - c[0] = '0' + n; c[1] = 0; - SEND(par); SEND(c); SEND(EQ); - SEND(u2str(v)); -} - static errcodes_t cmd_dumpconf(const char*, char*){ SEND("userconf_sz="); SEND(u2str(the_conf.userconf_sz)); - SEND("\ncurr_idx="); SEND(u2str(currentconfidx)); + SEND("\ncurr_idx="); SEND(i2str(currentconfidx)); SEND("\ncapacity="); SEND(u2str(maxCnum-2)); - cmd_setiface("\nsetiface", NULL); - cmd_restonstart("restonstart", NULL); - for(int i = 0; i < SENSORS_AMOUNT; ++i){ - showpar("gain", i, the_conf.spars[i].AFE_GB); - showpar("\nlco_fdiv", i, the_conf.spars[i].LCO_FDIV); - showpar("\nmaskdist", i, the_conf.spars[i].MASK_DIST); - showpar("\nminnumlig", i, the_conf.spars[i].MIN_NUM_LIG); - showpar("\nnflev", i, the_conf.spars[i].NF_LEV); - showpar("\nsrej", i, the_conf.spars[i].SREJ); - showpar("\ntuncap", i, the_conf.spars[i].TUN_CAP); - showpar("\nwdthres", i, the_conf.spars[i].WDTH); - SEND("\n"); + SEND("\nusart_speed="); SEND(u2str(the_conf.usart_speed)); + for(int i = 0; i < SERVO_AMOUNT ; ++i){ + SHOWPARU("\nstartpos", i, the_conf.startpulse[i]); + SHOWPARU("\nminpos", i, the_conf.minpulse[i]); + SHOWPARU("\nmaxpos", i, the_conf.maxpulse[i]); + SHOWPARU("\nmaxspeed", i, the_conf.maxspeed[i]); } + SEND("\n"); + return ERR_AMOUNT; +} + +static errcodes_t cmd_usart_speed(const char* cmd, char* args){ + int32_t val; + if(argsvals(args, NULL, &val)){ // setter + if(val < MIN_USART_SPEED || val > MAX_USART_SPEED) return ERR_BADVAL; + the_conf.usart_speed = (uint32_t) val; + } + CMDEQ(); + SEND(u2str(the_conf.usart_speed)); + SEND("\n"); + return ERR_AMOUNT; +} + +static errcodes_t servo_valP(const char* cmd, char* args, uint16_t *confval){ + int32_t val, parno; + if(argsvals(args, &parno, &val)){ // setter + if(parno < 0 || parno >= SERVO_AMOUNT) return ERR_BADPAR; + if(val < SERVO_MINPULSE || val > SERVO_MAXPULSE) return ERR_BADVAL; + confval[parno] = (uint16_t) val; + } + SHOWPARU(cmd, parno, confval[parno]); + SEND("\n"); + return ERR_AMOUNT; +} + +static errcodes_t cmd_maxpos(const char* cmd, char* args){ + return servo_valP(cmd, args, the_conf.maxpulse); +} + +static errcodes_t cmd_minpos(const char* cmd, char* args){ + return servo_valP(cmd, args, the_conf.minpulse); +} + +static errcodes_t cmd_startpos(const char* cmd, char* args){ + return servo_valP(cmd, args, the_conf.startpulse); +} + +static errcodes_t cmd_maxspeed(const char* cmd, char* args){ + int32_t val, parno; + if(argsvals(args, &parno, &val)){ // setter + if(parno < 0 || parno >= SERVO_AMOUNT) return ERR_BADPAR; + if(val < 1 || val > SERVO_MAXSPEED) return ERR_BADVAL; + the_conf.maxspeed[parno] = (uint16_t) val; + } + SHOWPARU(cmd, parno, the_conf.maxspeed[parno]); + SEND("\n"); return ERR_AMOUNT; } -#endif static errcodes_t cmd_help(const char*, char*){ SEND(REPOURL); diff --git a/G4:G431/Servo/flash.c b/G4:G431/Servo/flash.c new file mode 100644 index 0000000..7b64cdf --- /dev/null +++ b/G4:G431/Servo/flash.c @@ -0,0 +1,186 @@ +/* + * This file is part of the flash project. + * Copyright 2023 Edward V. Emelianov . + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +#include +#include // memcpy + +#include "flash.h" +#include "servo.h" + +extern const uint32_t __varsstart, _BLOCKSIZE; + +static const uint32_t blocksize = (uint32_t)&_BLOCKSIZE; + +// max amount of Config records stored (will be recalculate in flashstorage_init() +uint32_t maxCnum = 1024 / sizeof(user_conf); // can't use blocksize here + +#define USERCONF_INITIALIZER { \ + .userconf_sz = sizeof(user_conf) \ + ,.usart_speed = 115200 \ + ,.startpulse = {SG90_MIDPULSE, SG90_MIDPULSE, SG90_MIDPULSE, SG90_MIDPULSE} \ + ,.minpulse = {SG90_MINPULSE, SG90_MINPULSE, SG90_MINPULSE, SG90_MINPULSE} \ + ,.maxpulse = {SG90_MAXPULSE, SG90_MAXPULSE, SG90_MAXPULSE, SG90_MAXPULSE} \ + ,.maxspeed = {SG90_MAXSPEED, SG90_MAXSPEED, SG90_MAXSPEED, SG90_MAXSPEED} \ + } + +static int erase_flash(const void*, const void*); +static int write2flash(const void*, const void*, uint32_t); +// don't write `static` here, or get error: +// 'memcpy' forming offset 8 is out of the bounds [0, 4] of object '__varsstart' with type 'uint32_t' +const user_conf *Flash_Data = (const user_conf *)(&__varsstart); + +user_conf the_conf = USERCONF_INITIALIZER; + +int currentconfidx = -1; // index of current configuration + +/** + * @brief binarySearch - binary search in flash for last non-empty cell + * any struct searched should have its sizeof() @ the first field!!! + * @param l - left index + * @param r - right index (should be @1 less than last index!) + * @param start - starting address + * @param stor_size - size of structure to search + * @return index of non-empty cell or -1 + */ +static int binarySearch(int r, const uint8_t *start, int stor_size){ + int l = 0; + while(r >= l){ + int mid = l + (r - l) / 2; + const uint8_t *s = start + mid * stor_size; + if(*((const uint16_t*)s) == stor_size){ + if(*((const uint16_t*)(s + stor_size)) == 0xffff){ // next is free + return mid; + }else{ // element is to the right + l = mid + 1; + } + }else{ // element is to the left + r = mid - 1; + } + } + return -1; // not found +} + +/** + * @brief flashstorage_init - initialization of user conf storage + * run in once @ start + */ +void flashstorage_init(){ + if(FLASH_SIZE > 0 && FLASH_SIZE < 20000){ + uint32_t flsz = FLASH_SIZE * 1024; // size in bytes + flsz -= (uint32_t)(&__varsstart) - FLASH_BASE; + maxCnum = flsz / sizeof(user_conf); + } + // -1 if there's no data at all & flash is clear; maxnum-1 if flash is full + currentconfidx = binarySearch((int)maxCnum-2, (const uint8_t*)Flash_Data, sizeof(user_conf)); + if(currentconfidx > -1){ + memcpy(&the_conf, &Flash_Data[currentconfidx], sizeof(user_conf)); + } +} + +// store new configuration +// @return 0 if all OK +int store_userconf(){ + // maxnum - 3 means that there always should be at least one empty record after last data + // for binarySearch() checking that there's nothing more after it! + if(currentconfidx > (int)maxCnum - 3){ // there's no more place + currentconfidx = 0; + if(erase_flash(Flash_Data, NULL)) return 1; + }else ++currentconfidx; // take next data position (0 - within first run after firmware flashing) + return write2flash((const void*)&Flash_Data[currentconfidx], &the_conf, sizeof(the_conf)); +} + +static int write2flash(const void *start, const void *wrdata, uint32_t stor_size){ + int ret = 0; + if (FLASH->CR & FLASH_CR_LOCK){ // unloch flash + FLASH->KEYR = FLASH_KEY1; + FLASH->KEYR = FLASH_KEY2; + } + // FLASH_SR_BSY2 for some + uint32_t count = (stor_size + 7) / 8; + volatile uint32_t *address = (volatile uint32_t*) start; + const uint32_t *data = (const uint32_t*) wrdata; + for(uint32_t i = 0; i < count; ++i){ + while (FLASH->SR & (FLASH_SR_BSY)); // 1: check BSY + if(FLASH->SR & FLASH_SR_WRPERR){ // 2: check errors + return 1; // write protection + } + FLASH->SR = 0xffff; // clear all flags + FLASH->CR |= FLASH_CR_PG; // 3: set PG bit + IWDG->KR = IWDG_REFRESH; + *address++ = *data++; // 4: write both 32 bit words + *address++ = *data++; + while(FLASH->SR & FLASH_SR_BSY); + if(FLASH->SR & FLASH_SR_PGSERR){ + ret = 1; // program error - meet not 0xffff + break; + }/*else{ + for(int _ = 0; _ < 9999 && (!(FLASH->SR & FLASH_SR_EOP)); ++_); + }*/ + FLASH->SR = 0xffff; + } + FLASH->CR &= ~(FLASH_CR_PG); // 7: clear PG bit at end of process + FLASH->CR |= FLASH_CR_LOCK; // lock it back + return ret; +} + +/** + * @brief erase_flash - erase N pages of flash memory + * @param start - first address + * @param end - last address (or NULL if need to erase all flash remaining) + * @return 0 if succeed + */ +static int erase_flash(const void *start, const void *end){ + int ret = 0; + if(!start) return 1; + uint32_t startb = (((uint32_t)start - FLASH_BASE) + blocksize - 1) / blocksize, endb; + if(!end){ // erase all remaining + endb = FLASH_SIZE * 1024 / blocksize; + }else{ // erase a part + endb = (((uint32_t)end - FLASH_BASE) + blocksize - 1) / blocksize; + } + if ((FLASH->CR & FLASH_CR_LOCK) != 0){ + FLASH->KEYR = FLASH_KEY1; + FLASH->KEYR = FLASH_KEY2; + } + for(uint32_t i = startb; i < endb; ++i){ + IWDG->KR = IWDG_REFRESH; + /* (1) Wait till no operation is on going */ + /* (2) Clear error & EOP bits */ + while ((FLASH->SR & FLASH_SR_BSY) != 0){} /* (1) */ + FLASH->SR = 0xffff; /* (2) */ + /* (1) Set the PER bit in the FLASH_CR register to enable page erasing */ + /* (2) Select the page to erase (PNB) */ + /* (3) Set the STRT bit in the FLASH_CR register to start the erasing */ + /* (4) Wait until BSY1 cleared */ + FLASH->CR |= FLASH_CR_PER | i << FLASH_CR_PNB_Pos; /* (1) (2) */ + FLASH->CR |= FLASH_CR_STRT; /* (3) */ + while ((FLASH->SR & FLASH_SR_BSY) != 0){} /* (4) */ + FLASH->SR = FLASH_SR_EOP; + if(FLASH->SR & FLASH_SR_WRPERR){ /* Check Write protection error */ + ret = 1; + FLASH->SR = FLASH_SR_WRPERR; /* Clear the flag by software by writing it at 1*/ + break; + } + FLASH->CR &= ~FLASH_CR_PER; // clear PER + } + return ret; +} + +int erase_storage(){ + return erase_flash(Flash_Data, NULL); +} diff --git a/G4:G431/Servo/flash.h b/G4:G431/Servo/flash.h new file mode 100644 index 0000000..c7a0d04 --- /dev/null +++ b/G4:G431/Servo/flash.h @@ -0,0 +1,52 @@ +/* + * This file is part of the flash project. + * Copyright 2023 Edward V. Emelianov . + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +#pragma once + +#include +#include "hardware.h" + +// register with flash size (in blocks) +#ifndef FLASH_SIZE_REG +#define FLASH_SIZE_REG ((uint32_t)0x1FFF75E0) +#endif +#ifndef FLASH_SIZE +#define FLASH_SIZE *((uint16_t*)FLASH_SIZE_REG) +#endif + +/* + * struct to save user configurations + */ +typedef struct __attribute__((packed, aligned(8))){ + uint16_t userconf_sz; // "magick number" + uint32_t usart_speed; // speed of interface + uint16_t startpulse[SERVO_AMOUNT]; // starting position + uint16_t minpulse[SERVO_AMOUNT]; // minimal position + uint16_t maxpulse[SERVO_AMOUNT]; // maximal position + uint16_t maxspeed[SERVO_AMOUNT]; // maximal speed +} user_conf; + +extern user_conf the_conf; // global user config (read from FLASH to RAM) +extern int currentconfidx; +extern uint32_t maxCnum; + +void flashstorage_init(); +int store_userconf(); +void dump_userconf(); +int erase_storage(); + diff --git a/G4:G431/Servo/hardware.c b/G4:G431/Servo/hardware.c index 48bece7..b9b9bb1 100644 --- a/G4:G431/Servo/hardware.c +++ b/G4:G431/Servo/hardware.c @@ -18,6 +18,7 @@ #include +#include "flash.h" #include "hardware.h" #include "servo.h" @@ -32,7 +33,7 @@ void sys_tick_handler(){ // try to set servo; if wrong return false bool set_servo(uint8_t N, uint16_t val){ if(N >= SERVO_AMOUNT) return false; - if(val < SG90_MINPULSE || val > SG90_MAXPULSE) return false; + if(val < the_conf.minpulse[N] || val > the_conf.maxpulse[N]) return false; volatile uint32_t *CCR = &(TIM3->CCR1); CCR[N] = val; return true; @@ -56,11 +57,14 @@ static void tim3_setup(){ TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC3PE | TIM_CCMR2_OC4PE; // frequency - TIM3->PSC = 169; // 1MHz -> 1us per tick + TIM3->PSC = 84; // 1MHz -> 1us per tick // ARR for PWM TIM3->ARR = 19999; // 20ms, 50Hz // CCRx - minimal position - TIM3->CCR1 = TIM3->CCR2 = TIM3->CCR3 = TIM3->CCR4 = SG90_MINPULSE; + TIM3->CCR1 = the_conf.startpulse[0]; + TIM3->CCR2 = the_conf.startpulse[1]; + TIM3->CCR3 = the_conf.startpulse[2]; + TIM3->CCR4 = the_conf.startpulse[3]; // enable CCx interrupts TIM3->DIER = TIM_DIER_CC1IE | TIM_DIER_CC2IE | TIM_DIER_CC3IE | TIM_DIER_CC4IE; // enable main output diff --git a/G4:G431/Servo/hardware.h b/G4:G431/Servo/hardware.h index 3eb6e4e..5dee284 100644 --- a/G4:G431/Servo/hardware.h +++ b/G4:G431/Servo/hardware.h @@ -31,6 +31,9 @@ #define SERVO_AMOUNT 4 +#define MIN_USART_SPEED 1200 +#define MAX_USART_SPEED 3000000 + extern volatile uint32_t Tms; extern volatile bool tim_triggered[SERVO_AMOUNT]; diff --git a/G4:G431/Servo/main.c b/G4:G431/Servo/main.c index c5851fa..b22c0ec 100644 --- a/G4:G431/Servo/main.c +++ b/G4:G431/Servo/main.c @@ -20,6 +20,7 @@ #include #include "commproto.h" +#include "flash.h" #include "hardware.h" #include "servo.h" #include "usart.h" @@ -29,8 +30,10 @@ int main(void){ StartHSE(); + // init storage first to take base values + flashstorage_init(); gpio_setup(); - usart_setup(115200); + usart_setup(the_conf.usart_speed); bool pressed = false; uint32_t Tblink = 0, Tkey = 0; // blink and key pressed time while(1){ diff --git a/G4:G431/Servo/servo.bin b/G4:G431/Servo/servo.bin index e7a0cdc04425fe77906fc7d364b17c73e9ed7ff9..0aaa07a1c089731d65222a2b35840b36ceefae78 100755 GIT binary patch delta 5275 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 W_{q@$41nc;Oh5r(Bftiz1pF@}F^CNS delta 2250 zcmZ8h4^R`?8GpN*kPQM75-|jYC5spckPsODsI4KykPrlW+Megxn+=d_P&p#d=vBvr zJF0NK5%Jwz6}-6|dOe(8u|V75>Nufy&h#fpL@a9%az_j2r(Q0AyV(ekEz1&TEQN&hsSM#eDYNu zUlVi?PMj?~DntCQ4KWWH$NRH1DVn7T6j%x20pSh-3QULawD6^@h>{lRw}4_&mDGw2 z^aQbELZJ*$WTHi&lbcZB?GR27B_BY6_E4;~cF{?G!gn&r=Ub@l_ANAbqrj2J(S#6l zdNZQqL~98?LQ|A#ga5rHP3y?gjpi7?2cbDn1;Kp67s;HJh;dfp!!)y1?5|2YoD<_0 zRFSh46v15O-%5&3BOiy~r_=Rec}hi@V#8uB!yqQ~Q)|(e&#uKrVnqIoA%T^e)l+@v zH+TrxBC?HKY$iE0G9exyHx60`t#339R^Koi7CkL*;I6p5ZK7@c*5C1+h^_yuA^cw6 zDz8Pt)drC59*h?%P{wz-ckownY3`$MSM>)ewogp#9GUaD>51CFlrq5VxAjh2skQBCurQWo@x%VqxK5SZc5Z9T@wn^Kiy4pp}apQ72=dVj?NMk1JFW0|N zUp_C9__>t26bHeS2il1g?w}14*stb#jN>gAIlp5#b$=X1uFqYd; z7g~85#P{P3R&D!Re6E%Xgkq3dAINSe*zcJ;fC;T!4kFdqd|JC_&mEug2yk9BM^IO1 zY3h!zPfn=y3rX#v72|5+klJyZPt;kkEunn(q4o&{!LqInUOqN#5dQgTK3dq-j&c+a zlT9(OR#!#gktL6t4EN+b*{(6`Ore$X)w2ew4+YZkUlY=6COr!xG&kY7|Fz&;<(`|q zzd6FYelSamLUN?38)wg~=lgpP!OG37E3~pXlI@r$XA1dHBB_VJM30~^g!^#KM&;Jq zBmF(7l+ddHnE(!^!i^vOmAW=&xX*dq`2#+8RzzIX_YuK5ER7%?PR|(prD3~Ed@tWv zZ?T)t^0F2bND9(Jp_TYm#P*_p$;mm7VFG$v`)c>_ouThTjKHhqrD)VpW|E8_sww8B z+$%$)h7zN3a?~*3D>ZqSG{?J!hoGS^`99{0Vgy8?Ks|}e5_h|AO+&Rgwc`suQQ4;u zt9LKY49-qhK%L3nK7~r{UjD^ksX4SV97HU_Kb!7K?KF(~Xw&FSA+R3=vuAW*uLhMf zS+2Jc`ur8&m>!CZVdFIZNr-e0B7eg!G3R1nV8QhDjgTGO1v!t1Ci}^Tj z_^k_q7bPv=4B!N= z6s!loBe4*Pct(;(-hP6UlQM}1cxO_Dq+xZ`_S?o_W3NFpe#(K=0^FUnpCp6$Lup#r z+?6UxeGmtvTgZYSPLL^LX*iq?gRD0hACc;bzu=!s(}^kksgwoDe@nL#pW%2}>i>o* zaJ4LpI2Z{%6bU^U32prU&?mB7mHr83lAFAh0&A)x#(b%gX62^1l3S(=x7N4jqTAPO z+JcpeeT+~&H5m>{gdk5O=vp$O88}E+!9t!F6-MHD{3k_|dsh`URFB_m+yVBm|7K$u z*evi%z#ag<7_18XBCyrq7lO?JzW}Ted@Wc3_!_Xf@tsqmIhuOhD2K1C4^gIbJgY5! zhRa#wa@RRG1peWhXrOYP1TOPwl#@Thomt}&p5gp!oM1`}#n}kOLB5~DrXsb_AGUE- z&%ubM5L83;-)fABFMaN4&g;MNPigT9;z5e!`bkH;rh7gCL5X(ZxvlReJM71xRS;@* zG@oGF9F46^n87clE2>-VEv{ym`hmSo(`s*HB7P0yvK{-OJ(p=Z+RCV2ZfR~}O0-(0 zt(nmkXtmi9sqi{yb8Gl{eJ$29MLFxvnoz6LZf}SRIqfZsPTK^E1^M}9h1vLzj1p&N z?8EmnDig}|I<2laPg|0wD~&}sTe;nR5IUO+cRAoRpc8NbFb22_SOw6~0!4sb00E>T z15kj +#include "flash.h" #include "hardware.h" #include "servo.h" @@ -45,7 +46,7 @@ void process_servo(){ // set speed in steps per tick bool servo_set_speed(uint8_t N, uint16_t s){ if(N >= SERVO_AMOUNT) return false; - if(s < 1 || s > SG90_MAXSPEED) return false; + if(s < 1 || s > the_conf.maxspeed[N]) return false; servo_speed[N] = s; return true; } @@ -58,7 +59,7 @@ bool servo_get_speed(uint8_t N, uint16_t *s){ bool servo_set_tagpos(uint8_t N, uint16_t pos){ if(N >= SERVO_AMOUNT) return false; - if(pos < SG90_MINPULSE || pos > SG90_MAXPULSE) return false; + if(pos < the_conf.minpulse[N] || pos > the_conf.maxpulse[N]) return false; servo_tagpos[N] = pos; return true; } diff --git a/G4:G431/Servo/servo.files b/G4:G431/Servo/servo.files index 4d8ca89..b91881e 100644 --- a/G4:G431/Servo/servo.files +++ b/G4:G431/Servo/servo.files @@ -1,5 +1,7 @@ commproto.cpp commproto.h +flash.c +flash.h hardware.c hardware.h main.c diff --git a/G4:G431/Servo/servo.h b/G4:G431/Servo/servo.h index 0dbea5e..7f29491 100644 --- a/G4:G431/Servo/servo.h +++ b/G4:G431/Servo/servo.h @@ -18,14 +18,19 @@ #pragma once +// Default starting values // minimal and maximal pulse length for SG90 -#define SG90_MINPULSE 700 -#define SG90_MAXPULSE 2100 +#define SG90_MINPULSE 400 +#define SG90_MAXPULSE 2600 #define SG90_MIDPULSE ((SG90_MINPULSE+SG90_MAXPULSE)/2) #define SG90_AMPL (SG90_MAXPULSE-SG90_MINPULSE) // maximal speed: 0.1s (5 ticks) per 60degr (1/3 of range): (SG90_AMPL/15) #define SG90_MAXSPEED 93 +// Limiting values +#define SERVO_MINPULSE 100 +#define SERVO_MAXPULSE 5000 +#define SERVO_MAXSPEED 300 void process_servo(); bool servo_set_speed(uint8_t N, uint16_t s); diff --git a/G4:G431/Servo/version.inc b/G4:G431/Servo/version.inc index 03a3241..df3da95 100644 --- a/G4:G431/Servo/version.inc +++ b/G4:G431/Servo/version.inc @@ -1,2 +1,2 @@ -#define BUILD_NUMBER "9" -#define BUILD_DATE "2026-07-28" +#define BUILD_NUMBER "16" +#define BUILD_DATE "2026-07-29"