diff --git a/F1:F103/shutter/flash.c b/F1:F103/shutter/flash.c new file mode 100644 index 0000000..68972cb --- /dev/null +++ b/F1:F103/shutter/flash.c @@ -0,0 +1,184 @@ +/* + * This file is part of the shutter 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 "stm32f1.h" + +#include "flash.h" +#include "proto.h" +#include "usb.h" // printout +#include // memcpy + +extern const uint32_t __varsstart, _BLOCKSIZE; +static const uint32_t FLASH_blocksize = (uint32_t)&_BLOCKSIZE; +static uint32_t maxCnum = 1024 / sizeof(user_conf); // can't use blocksize here + +#define USERCONF_INITIALIZER { \ + .userconf_sz = sizeof(user_conf) \ + ,.hallactive = 0 \ + ,.ccdactive = 1 \ + ,.minvoltage = 500 \ + ,.workvoltage = 1300 \ + } + +static int write2flash(const void*, const void*, uint32_t); +const user_conf *Flash_Data = (const user_conf *)(&__varsstart); +user_conf the_conf = USERCONF_INITIALIZER; + +static 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_storage(-1)) 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; + } + while (FLASH->SR & FLASH_SR_BSY) IWDG->KR = IWDG_REFRESH; + FLASH->SR = FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPRTERR; // clear all flags + FLASH->CR |= FLASH_CR_PG; + const uint16_t *data = (const uint16_t*) wrdata; + volatile uint16_t *address = (volatile uint16_t*) start; + uint32_t i, count = (stor_size + 1) / 2; + for(i = 0; i < count; ++i){ + *(volatile uint16_t*)(address + i) = data[i]; + while(FLASH->SR & FLASH_SR_BSY) IWDG->KR = IWDG_REFRESH; + if(*(volatile uint16_t*)(address + i) != data[i]){ + USB_sendstr("Error: flash is corrupted\n"); + ret = 1; + break; + } + if(FLASH->SR & FLASH_SR_PGERR){ + USB_sendstr("Error: FLASH_SR_PGERR\n"); + ret = 1; // program error - meet not 0xffff + break; + } + FLASH->SR = FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPRTERR; + } + FLASH->CR &= ~(FLASH_CR_PG); + return ret; +} + +// erase Nth page of flash storage (flash should be prepared!) +static int erase_pageN(int N){ + int ret = 0; + FLASH->AR = (uint32_t)Flash_Data + N*FLASH_blocksize; + FLASH->CR |= FLASH_CR_STRT; + while(FLASH->SR & FLASH_SR_BSY) IWDG->KR = IWDG_REFRESH; + FLASH->SR = FLASH_SR_EOP; + if(FLASH->SR & FLASH_SR_WRPRTERR){ /* Check Write protection error */ + ret = 1; + FLASH->SR = FLASH_SR_WRPRTERR; /* Clear the flag by software by writing it at 1*/ + } + return ret; +} + +// erase full storage (npage < 0) or its nth page; @return 0 if all OK +int erase_storage(int npage){ + int ret = 0; + uint32_t end = 1, start = 0, flsz = 0; + if(FLASH_SIZE > 0 && FLASH_SIZE < 20000){ + flsz = FLASH_SIZE * 1024; // size in bytes + flsz -= (uint32_t)Flash_Data - FLASH_BASE; + } + end = flsz / FLASH_blocksize; + if(end == 0 || end >= FLASH_SIZE) return 1; + if(npage > -1){ // erase only one page + if((uint32_t)npage >= end) return 1; + start = npage; + end = start + 1; + } + if((FLASH->CR & FLASH_CR_LOCK) != 0){ + FLASH->KEYR = FLASH_KEY1; + FLASH->KEYR = FLASH_KEY2; + } + while(FLASH->SR & FLASH_SR_BSY) IWDG->KR = IWDG_REFRESH; + FLASH->SR = FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPRTERR; + FLASH->CR |= FLASH_CR_PER; + for(uint32_t i = start; i < end; ++i){ + if(erase_pageN(i)){ + ret = 1; + break; + } + } + FLASH->CR &= ~FLASH_CR_PER; + return ret; +} + +void dump_userconf(){ + USB_sendstr("userconf_sz="); USB_sendstr(u2str(the_conf.userconf_sz)); + USB_sendstr("\nccdactive="); USB_putbyte('0' + the_conf.ccdactive); + USB_sendstr("\nhallactive="); USB_putbyte('0' + the_conf.hallactive); + USB_sendstr("\nminvoltage="); USB_sendstr(u2str(the_conf.minvoltage)); + USB_sendstr("\nworkvoltage="); USB_sendstr(u2str(the_conf.workvoltage)); + newline(); +} diff --git a/F1:F103/shutter/flash.h b/F1:F103/shutter/flash.h new file mode 100644 index 0000000..e24fce7 --- /dev/null +++ b/F1:F103/shutter/flash.h @@ -0,0 +1,42 @@ +/* + * This file is part of the shutter 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 + +#define FLASH_SIZE_REG ((uint32_t)0x1FFFF7E0) +#define FLASH_SIZE *((uint16_t*)FLASH_SIZE_REG) + +/* + * struct to save user configurations + */ +typedef struct __attribute__((packed, aligned(4))){ + uint16_t userconf_sz; // "magick number" + uint8_t ccdactive : 1; // ccd active (open shutter at): 0 - low, 1 - high + uint8_t hallactive : 1; // hall sensor active (shutter is opened when): 0 - low, 1 - high + uint16_t minvoltage; // minimal voltage on C (*100) + uint16_t workvoltage; // working voltage (*100) +} user_conf; + +extern user_conf the_conf; + +void flashstorage_init(); +int store_userconf(); +int erase_storage(int npage); +void dump_userconf(); diff --git a/F1:F103/shutter/hardware.h b/F1:F103/shutter/hardware.h index 79d00f7..bc5fb4e 100644 --- a/F1:F103/shutter/hardware.h +++ b/F1:F103/shutter/hardware.h @@ -20,6 +20,8 @@ #include +#include "flash.h" + // PA7 - SHTR_FB #define FBPORT GPIOA #define FBPIN (1<<7) @@ -52,17 +54,12 @@ typedef enum{ #define BTNPORT GPIOB #define HALLPIN (1<<0) #define CCDPIN (1<<11) -#define CHKHALL() (BTNPORT->IDR & HALLPIN ? 0 : 1) -#define CHKCCD() (BTNPORT->IDR & CCDPIN ? 0 : 1) +#define CHKHALL() ((HALLPIN == (BTNPORT->IDR & HALLPIN)) == the_conf.hallactive) +#define CHKCCD() ((CCDPIN == (BTNPORT->IDR & CCDPIN)) == the_conf.ccdactive) // multiplyer of shutter voltage (due to R divider) #define SHTRVMUL (13) -// minimal voltage on capacitor for shutter can work -#define SHTR_WORK_VOLTAGE (1300) -// minimal voltage on capacitor due to discharging -#define SHTR_MIN_VOLTAGE (100) - extern volatile uint32_t Tms; void hw_setup(); diff --git a/F1:F103/shutter/main.c b/F1:F103/shutter/main.c index 9fb59cb..04d102d 100644 --- a/F1:F103/shutter/main.c +++ b/F1:F103/shutter/main.c @@ -39,6 +39,7 @@ int main(void){ SysTick_Config(72000); USBPU_OFF(); hw_setup(); + flashstorage_init(); USB_setup(); USBPU_ON(); diff --git a/F1:F103/shutter/proto.c b/F1:F103/shutter/proto.c index f441fc9..0dee2de 100644 --- a/F1:F103/shutter/proto.c +++ b/F1:F103/shutter/proto.c @@ -17,6 +17,7 @@ */ #include "adc.h" +#include "flash.h" #include "hardware.h" #include "proto.h" #include "shutter.h" @@ -158,6 +159,13 @@ const char* helpmsg = "'0' - shutter CLO\n" "'1' - shutter OPE\n" "'2' - shutter HIZ\n" + "'< n' - voltage on discharged capacitor (*100)\n" + "'> n' - voltage on fully charged capacitor (*100)\n" + "'c n' - open shutter when CCD ext level is n (0/1)\n" + "'d' - dump current config\n" + "'e' - erase flash storage\n" + "'h n' - shutter is opened when hall level is n (0/1)\n" + "'s' - save configuration into flash\n" "'A' - get raw ADC values\n" "'C' - close shutter / abort exposition\n" "'E n' - expose for n milliseconds\n" @@ -206,6 +214,17 @@ const char *parse_cmd(const char *buf){ SHTRHIZ(); add2buf("regstate=hiz"); break; + case 'd': // dump config + dump_userconf(); return NULL; + break; + case 'e': // erase flash + if(erase_storage(-1)) add2buf(ERR); + else add2buf(OK); + break; + case 's': // save configuration + if(store_userconf()) add2buf(ERR); + else add2buf(OK); + break; case 'A': for(int i = 0; i < NUMBER_OF_ADC_CHANNELS; ++i){ add2buf("\nadc"); @@ -262,15 +281,38 @@ const char *parse_cmd(const char *buf){ } }else{ // long messages uint32_t Num = 0; - char *nxt; - switch(*buf){ + char c = *buf++; + char *nxt = getnum(buf, &Num); + int errnum = 0, overflow = 0; + if(buf == nxt){ + if(Num) overflow = 1; + errnum = 1; + } + switch(c){ + case '<': // min voltage + if(errnum) break; + if(Num < 100 || Num > 1000) return "ERRVAL\n"; + the_conf.minvoltage = Num; + add2buf("minvoltage="); add2buf(u2str(the_conf.minvoltage)); + break; + case '>': // max voltage + if(errnum) break; + if(Num < 500 || Num > 10000) return "ERRVAL\n"; + the_conf.workvoltage = Num; + add2buf("workvoltage="); add2buf(u2str(the_conf.workvoltage)); + break; + case 'c': // CCD active @ + if(errnum) break; + the_conf.ccdactive = Num; + add2buf("ccdactive="); bufputchar('0' + the_conf.ccdactive); + break; + case 'h': // HALL active @ + if(errnum) break; + the_conf.hallactive = Num; + add2buf("hallactive="); bufputchar('0' + the_conf.hallactive); + break; case 'E': - ++buf; - nxt = getnum(buf, &Num); - if(buf == nxt){ - if(Num == 0) return "ERRNUM\n"; - return "I32OVERFLOW\n"; - } + if(errnum) break; if(shutterstate != SHUTTER_RELAX){ add2buf(ERR); break; @@ -279,8 +321,10 @@ const char *parse_cmd(const char *buf){ else add2buf(ERR); break; default: - return buf; + return buf-1; } + if(overflow) return "I32OVERFLOW\n"; + if(errnum) return "ERRNUM\n"; } bufputchar('\n'); return stbuf; diff --git a/F1:F103/shutter/shutter.bin b/F1:F103/shutter/shutter.bin index 2b23016..78419b0 100755 Binary files a/F1:F103/shutter/shutter.bin and b/F1:F103/shutter/shutter.bin differ diff --git a/F1:F103/shutter/shutter.c b/F1:F103/shutter/shutter.c index ee78e6a..1e64615 100644 --- a/F1:F103/shutter/shutter.c +++ b/F1:F103/shutter/shutter.c @@ -44,7 +44,7 @@ static int changestate(int open, shutter_state nxt){ shutterstate = SHUTTER_RELAX; return TRUE; // already opened or closed } - if(getADCvoltage(CHSHTR) < SHTR_WORK_VOLTAGE / SHTRVMUL) return FALSE; + if(getADCvoltage(CHSHTR) < the_conf.workvoltage / SHTRVMUL) return FALSE; if(shutterstate == SHUTTER_ERROR) return FALSE; if(open) SHTROPEN(); else SHTRCLOSE(); @@ -89,7 +89,7 @@ void process_shutter(){ USB_putbyte('\n'); } #endif - if(Tms - Tstart > SHUTTER_TIME || V < SHTR_MIN_VOLTAGE){ + if(Tms - Tstart > SHUTTER_TIME || V < the_conf.minvoltage){ SHTROFF(); shutterstate = SHUTTER_WAIT; Tstart = Tms; @@ -125,7 +125,7 @@ void process_shutter(){ uint8_t s = CHKCCD(); if(oldbtnstate == s) return; // button's state not changed // check button only when can open/close & shutter operations done - if(V >= SHTR_WORK_VOLTAGE && shutterstate == SHUTTER_RELAX){ // shutter state allows to open/close + if(V >= the_conf.workvoltage && shutterstate == SHUTTER_RELAX){ // shutter state allows to open/close if(s){ // pressed if(!CHKHALL()){ if(open_shutter()){oldbtnstate = s; /*USB_sendstr(" open, old->1\n");*/}} else{/*USB_sendstr("pressed when CHKHALL(), old->1\n");*/ oldbtnstate = s;} diff --git a/F1:F103/shutter/shutter.files b/F1:F103/shutter/shutter.files index bff2394..1274a33 100644 --- a/F1:F103/shutter/shutter.files +++ b/F1:F103/shutter/shutter.files @@ -1,5 +1,7 @@ adc.c adc.h +flash.c +flash.h hardware.c hardware.h main.c diff --git a/F1:F103/shutter/version.inc b/F1:F103/shutter/version.inc index 65002d8..4ae7bc7 100644 --- a/F1:F103/shutter/version.inc +++ b/F1:F103/shutter/version.inc @@ -1,2 +1,2 @@ -#define BUILD_NUMBER "80" -#define BUILD_DATE "2023-09-22" +#define BUILD_NUMBER "84" +#define BUILD_DATE "2023-11-29"