diff --git a/G0:G070/flash/Makefile b/G0:G070/flash/Makefile new file mode 100644 index 0000000..5cfdd28 --- /dev/null +++ b/G0:G070/flash/Makefile @@ -0,0 +1,9 @@ +BINARY := flash +# MCU code +MCU := G070xx +# change this linking script depending on particular MCU model, +LDSCRIPT := stm32g070xb.ld + +include ../makefile.g0 +include ../../makefile.stm32 + diff --git a/G0:G070/flash/README b/G0:G070/flash/README new file mode 100644 index 0000000..9b0e6d5 --- /dev/null +++ b/G0:G070/flash/README @@ -0,0 +1,2 @@ +USART3 @ PD8/PD9 (115200, 8N1) +EEPROM in Flash emulation diff --git a/G0:G070/flash/flash.bin b/G0:G070/flash/flash.bin new file mode 100755 index 0000000..ee67622 Binary files /dev/null and b/G0:G070/flash/flash.bin differ diff --git a/G0:G070/flash/flash.c b/G0:G070/flash/flash.c new file mode 100644 index 0000000..824fbf3 --- /dev/null +++ b/G0:G070/flash/flash.c @@ -0,0 +1,198 @@ +/* + * 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 "strfunc.h" +#include "usart.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() +static uint32_t maxCnum = 1024 / sizeof(user_conf); // can't use blocksize here + +#define DEFMF {.haveencoder = 1, .donthold = 1, .eswinv = 1, .keeppos = 1} + +#define USERCONF_INITIALIZER { \ + .userconf_sz = sizeof(user_conf) \ + ,.flagU16 = 0xabcd \ + ,.flagU32 = 0xdeadbeef \ + ,.str = "test string" \ + } + +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; + +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 * blocksize; // 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_BSY1)); // 1: check BSY1 + 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_BSY1); + 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 / 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_BSY1) != 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_BSY1) != 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; +} + +void dump_userconf(){ + SEND("flashsize="); printu(FLASH_SIZE); + SEND("\nuserconf_addr="); printuhex((uint32_t)Flash_Data); + SEND("\nuserconf_idx="); usart3_sendstr(i2str(currentconfidx)); + SEND("\nuserconf_sz="); printu(the_conf.userconf_sz); + SEND("\nflagU16="); printuhex(the_conf.flagU16); + SEND("\nflagU32="); printuhex(the_conf.flagU32); + SEND("\nstr="); SEND(the_conf.str); + newline(); + usart3_sendbuf(); +} + +int erase_storage(){ + return erase_flash(Flash_Data, NULL); +} diff --git a/G0:G070/flash/flash.cflags b/G0:G070/flash/flash.cflags new file mode 100644 index 0000000..68d5165 --- /dev/null +++ b/G0:G070/flash/flash.cflags @@ -0,0 +1 @@ +-std=c17 \ No newline at end of file diff --git a/G0:G070/flash/flash.config b/G0:G070/flash/flash.config new file mode 100644 index 0000000..e94e3ae --- /dev/null +++ b/G0:G070/flash/flash.config @@ -0,0 +1,3 @@ +#define EBUG +#define STM32G0 +#define STM32G070xx diff --git a/G0:G070/flash/flash.creator b/G0:G070/flash/flash.creator new file mode 100644 index 0000000..e94cbbd --- /dev/null +++ b/G0:G070/flash/flash.creator @@ -0,0 +1 @@ +[General] diff --git a/G0:G070/flash/flash.creator.user b/G0:G070/flash/flash.creator.user new file mode 100644 index 0000000..5ceb79b --- /dev/null +++ b/G0:G070/flash/flash.creator.user @@ -0,0 +1,171 @@ + + + + + + EnvironmentId + {7bd84e39-ca37-46d3-be9d-99ebea85bc0d} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + KOI8-R + false + 4 + false + 80 + true + true + 1 + false + true + false + 0 + true + true + 0 + 8 + true + false + 1 + true + false + true + *.md, *.MD, Makefile + false + true + + + + ProjectExplorer.Project.PluginSettings + + + true + false + true + true + true + true + + + 0 + true + + true + true + Builtin.DefaultTidyAndClazy + 2 + + + + true + + + + + ProjectExplorer.Project.Target.0 + + Desktop + Desktop + Desktop + {65a14f9e-e008-4c1b-89df-4eaa4774b6e3} + 0 + 0 + 0 + + /Big/Data/00__Electronics/STM32/G0-nolib/usart + + + + all + + true + GenericProjectManager.GenericMakeStep + + 1 + Сборка + Сборка + ProjectExplorer.BuildSteps.Build + + + + + clean + + true + GenericProjectManager.GenericMakeStep + + 1 + Очистка + Очистка + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + По умолчанию + GenericProjectManager.GenericBuildConfiguration + + 1 + + + 0 + Развёртывание + Развёртывание + ProjectExplorer.BuildSteps.Deploy + + 1 + + false + ProjectExplorer.DefaultDeployConfiguration + + 1 + + + 2 + + ProjectExplorer.CustomExecutableRunConfiguration + + false + true + false + true + + 1 + + + + ProjectExplorer.Project.TargetCount + 1 + + + ProjectExplorer.Project.Updater.FileVersion + 22 + + + Version + 22 + + diff --git a/G0:G070/flash/flash.cxxflags b/G0:G070/flash/flash.cxxflags new file mode 100644 index 0000000..6435dfc --- /dev/null +++ b/G0:G070/flash/flash.cxxflags @@ -0,0 +1 @@ +-std=c++17 \ No newline at end of file diff --git a/G0:G070/flash/flash.files b/G0:G070/flash/flash.files new file mode 100644 index 0000000..a367225 --- /dev/null +++ b/G0:G070/flash/flash.files @@ -0,0 +1,8 @@ +main.c +usart.c + +flash.c +flash.h +strfunc.c +strfunc.h +usart.h diff --git a/G0:G070/flash/flash.h b/G0:G070/flash/flash.h new file mode 100644 index 0000000..eee8455 --- /dev/null +++ b/G0:G070/flash/flash.h @@ -0,0 +1,38 @@ +/* + * 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 + +/* + * struct to save user configurations + */ +typedef struct __attribute__((packed, aligned(8))){ + uint16_t userconf_sz; // "magick number" + uint16_t flagU16; + uint32_t flagU32; + char str[32]; +} user_conf; + +extern user_conf the_conf; // global user config (read from FLASH to RAM) +// data from ld-file: start address of storage + +void flashstorage_init(); +int store_userconf(); +void dump_userconf(); +int erase_storage(); + diff --git a/G0:G070/flash/flash.includes b/G0:G070/flash/flash.includes new file mode 100644 index 0000000..641fd56 --- /dev/null +++ b/G0:G070/flash/flash.includes @@ -0,0 +1,4 @@ +. +../inc +../inc/Fx +../inc/cm diff --git a/G0:G070/flash/main.c b/G0:G070/flash/main.c new file mode 100644 index 0000000..996f415 --- /dev/null +++ b/G0:G070/flash/main.c @@ -0,0 +1,122 @@ +/* + * This file is part of the usart 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 "flash.h" +#include "strfunc.h" +#include "usart.h" + +// KEY (intpullup->0) - PC0 +// LED - PC8 + +volatile uint32_t Tms = 0; + +/* Called when systick fires */ +void sys_tick_handler(void){ + ++Tms; +} + +static void gpio_setup(void){ + RCC->IOPENR |= RCC_IOPENR_GPIOCEN; // enable PC + // set PC8 as opendrain output, PC0 is pullup input, other as default (AIN) + GPIOC->MODER = (0xffffffff & ~(GPIO_MODER_MODE8 | GPIO_MODER_MODE0)) | GPIO_MODER_MODER8_O; + GPIOC->PUPDR = GPIO_PUPDR0_PU; // pullup + GPIOC->OTYPER = GPIO_OTYPER_OT8; // open drain +} + +static const char *helpmsg = + "'1' - change U16 value\n" + "'2' - change U32 value\n" + "'d' - dumpconf\n" + "'e' - erase flash\n" + "'f' - save conf to flash\n" + "'s' - change string (up to 31 symbol)\n" + "'t' - print Tms\n" +; + +static void chstr(const char *s){ + int i = 0; + for(; i < 31 && *s; ++i){ + the_conf.str[i] = *s++; + } + the_conf.str[i] = 0; +} + +int main(void){ + StartHSE(); + SysTick_Config(8000); // 1ms counter + flashstorage_init(); + gpio_setup(); + usart3_setup(); + uint32_t T = 0; + int sent = 0; + /* Do nothing in main loop */ + while (1){ + if(Tms - T > 499){ // blink LED + T = Tms; + pin_toggle(GPIOC, 1<<8); + usart3_sendbuf(); + } + if(pin_read(GPIOC, 1<<0) == 0){ // key pressed - send data over USART + if(!sent){ + usart3_sendstr("Button pressed\n"); + sent = 1; + } + }else sent = 0; + int wasbo = 0; + char *rcv = usart3_getline(&wasbo); + if(wasbo) usart3_sendstr("Buffer overflow occured @ last message\n"); + if(rcv){ + char cmd = *rcv++; + uint32_t U, got = 0; + const char *n = getnum(rcv, &U); + if(n != rcv) got = 1; + switch(cmd){ + case '1': + if(!got || U > 0xffff) SEND("Need an uint16_t number\n"); + else the_conf.flagU16 = (uint16_t)U; + SEND("Changed to "); printuhex(the_conf.flagU16); newline(); + break; + case '2': + if(!got) SEND("Need an uint32_t number\n"); + else the_conf.flagU32 = U; + SEND("Changed to "); printuhex(the_conf.flagU32); newline(); + break; + case 'd': + dump_userconf(); + break; + case 'e': + if(erase_storage()) SEND("Error erasing\n"); + else SEND("Erased OK\n"); + break; + case 'f': + if(store_userconf()) SEND("Error: can't save\n"); + else SEND("OK, stored\n"); + break; + case 's': + chstr(omit_spaces(rcv)); + SEND("Changed to: "); SEND(the_conf.str); newline(); + break; + case 't': + SEND("Tms="); printu(Tms); newline(); + break; + default: SEND(helpmsg); + } + } + } +} diff --git a/G0:G070/flash/openocd.cfg b/G0:G070/flash/openocd.cfg new file mode 100644 index 0000000..fb036da --- /dev/null +++ b/G0:G070/flash/openocd.cfg @@ -0,0 +1,89 @@ +# script for stm32g0x family + +# +# stm32g0 devices support SWD transports only. +# +source [find interface/stlink.cfg] +source [find target/swj-dp.tcl] +source [find mem_helper.tcl] + +if { [info exists CHIPNAME] } { + set _CHIPNAME $CHIPNAME +} else { + set _CHIPNAME stm32g0x +} + +set _ENDIAN little + +# Work-area is a space in RAM used for flash programming +# Smallest proposed target has 8kB ram, use 4kB by default to avoid surprises +if { [info exists WORKAREASIZE] } { + set _WORKAREASIZE $WORKAREASIZE +} else { + set _WORKAREASIZE 0x1000 +} + +#jtag scan chain +if { [info exists CPUTAPID] } { + set _CPUTAPID $CPUTAPID +} else { + # Section 37.5.5 - corresponds to Cortex-M0+ + set _CPUTAPID 0x0bc11477 +} + +swj_newdap $_CHIPNAME cpu -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id $_CPUTAPID +dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu + +set _TARGETNAME $_CHIPNAME.cpu +target create $_TARGETNAME cortex_m -endian $_ENDIAN -dap $_CHIPNAME.dap + +$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0 + +set _FLASHNAME $_CHIPNAME.flash +flash bank $_FLASHNAME stm32l4x 0 0 0 0 $_TARGETNAME + +# reasonable default +adapter speed 2000 + +adapter srst delay 100 +if {[using_jtag]} { + jtag_ntrst_delay 100 +} + +reset_config srst_nogate + +if {![using_hla]} { + # if srst is not fitted use SYSRESETREQ to + # perform a soft reset + cortex_m reset_config sysresetreq +} + +proc stm32g0x_default_reset_start {} { + # Reset clock is HSI16 (16 MHz) + adapter speed 2000 +} + +proc stm32g0x_default_examine_end {} { + # DBGMCU_CR |= DBG_STANDBY | DBG_STOP + mmw 0x40015804 0x00000006 0 + + # Stop watchdog counters during halt + # DBGMCU_APB1_FZ |= DBG_IWDG_STOP | DBG_WWDG_STOP + mmw 0x40015808 0x00001800 0 +} + +proc stm32g0x_default_reset_init {} { + # Increase clock to 64 Mhz + mmw 0x40022000 0x00000002 0x00000005 ;# FLASH_ACR: Latency = 2 + mww 0x4002100C 0x30000802 ;# RCC_PLLCFGR = PLLR=/2, PLLN=8, PLLM=/1, PLLSRC=0x2 + mmw 0x40021000 0x01000000 0x00000000 ;# RCC_CR |= PLLON + mmw 0x40021008 0x00000002 0x00000005 ;# RCC_CFGR: SW=PLLRCLK + + # Boost JTAG frequency + adapter speed 4000 +} + +# Default hooks +$_TARGETNAME configure -event examine-end { stm32g0x_default_examine_end } +$_TARGETNAME configure -event reset-start { stm32g0x_default_reset_start } +$_TARGETNAME configure -event reset-init { stm32g0x_default_reset_init } diff --git a/G0:G070/flash/strfunc.c b/G0:G070/flash/strfunc.c new file mode 100644 index 0000000..232d428 --- /dev/null +++ b/G0:G070/flash/strfunc.c @@ -0,0 +1,269 @@ +/* + * 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 + +/** + * @brief hexdump - dump hex array by 16 bytes in string + * @param sendfun - function to send data + * @param arr - array to dump + * @param len - length of `arr` + */ +void hexdump(int (*sendfun)(const char *s), uint8_t *arr, uint16_t len){ + char buf[52], *bptr = buf; + for(uint16_t l = 0; l < len; ++l, ++arr){ + for(int16_t j = 1; j > -1; --j){ + register uint8_t half = (*arr >> (4*j)) & 0x0f; + if(half < 10) *bptr++ = half + '0'; + else *bptr++ = half - 10 + 'a'; + } + if(l % 16 == 15){ + *bptr++ = '\n'; + *bptr = 0; + sendfun(buf); + bptr = buf; + }else *bptr++ = ' '; + } + if(bptr != buf){ + *bptr++ = '\n'; + *bptr = 0; + sendfun(buf); + } +} + +/** + * @brief _2str - convert value into string buffer + * @param val - |value| + * @param minus - ==0 if value > 0 + * @return buffer with number + */ +static char *_2str(uint32_t val, uint8_t minus){ + static char strbuf[12]; + char *bufptr = &strbuf[11]; + *bufptr = 0; + if(!val){ + *(--bufptr) = '0'; + }else{ + while(val){ + uint32_t x = val / 10; + *(--bufptr) = (val - 10*x) + '0'; + val = x; + //*(--bufptr) = val % 10 + '0'; + //val /= 10; + } + } + if(minus) *(--bufptr) = '-'; + return bufptr; +} + +// return string with number `val` +char *u2str(uint32_t val){ + return _2str(val, 0); +} +char *i2str(int32_t i){ + uint8_t minus = 0; + uint32_t val; + if(i < 0){ + minus = 1; + val = -i; + }else val = i; + return _2str(val, minus); +} + +/** + * @brief uhex2str - print 32bit unsigned int as hex + * @param val - value + * @return string with number + */ +char *uhex2str(uint32_t val){ + static char buf[12] = "0x"; + int npos = 2; + uint8_t *ptr = (uint8_t*)&val + 3; + int8_t i, j, z=1; + for(i = 0; i < 4; ++i, --ptr){ + if(*ptr == 0){ // omit leading zeros + if(i == 3) z = 0; + if(z) continue; + } + else z = 0; + for(j = 1; j > -1; --j){ + uint8_t half = (*ptr >> (4*j)) & 0x0f; + if(half < 10) buf[npos++] = half + '0'; + else buf[npos++] = half - 10 + 'a'; + } + } + buf[npos] = 0; + return buf; +} + +/** + * @brief omit_spaces - eliminate leading spaces and other trash in string + * @param buf - string + * @return - pointer to first character in `buf` > ' ' + */ +const char *omit_spaces(const char *buf){ + while(*buf){ + if(*buf > ' ') break; + ++buf; + } + return buf; +} + +/** + * @brief getdec - read decimal number & return pointer to next non-number symbol + * @param buf - string + * @param N - number read + * @return Next non-number symbol. In case of overflow return `buf` and N==0xffffffff + */ +static const char *getdec(const char *buf, uint32_t *N){ + char *start = (char*)buf; + uint32_t num = 0; + while(*buf){ + char c = *buf; + if(c < '0' || c > '9'){ + break; + } + if(num > 429496729 || (num == 429496729 && c > '5')){ // overflow + *N = 0xffffff; + return start; + } + num *= 10; + num += c - '0'; + ++buf; + } + *N = num; + return buf; +} +// read hexadecimal number (without 0x prefix!) +static const char *gethex(const char *buf, uint32_t *N){ + const char *start = buf; + uint32_t num = 0; + while(*buf){ + char c = *buf; + uint8_t M = 0; + if(c >= '0' && c <= '9'){ + M = '0'; + }else if(c >= 'A' && c <= 'F'){ + M = 'A' - 10; + }else if(c >= 'a' && c <= 'f'){ + M = 'a' - 10; + } + if(M){ + if(num & 0xf0000000){ // overflow + *N = 0xffffff; + return start; + } + num <<= 4; + num += c - M; + }else{ + break; + } + ++buf; + } + *N = num; + return buf; +} +// read octal number (without 0 prefix!) +static const char *getoct(const char *buf, uint32_t *N){ + const char *start = (char*)buf; + uint32_t num = 0; + while(*buf){ + char c = *buf; + if(c < '0' || c > '7'){ + break; + } + if(num & 0xe0000000){ // overflow + *N = 0xffffff; + return start; + } + num <<= 3; + num += c - '0'; + ++buf; + } + *N = num; + return buf; +} +// read binary number (without b prefix!) +static const char *getbin(const char *buf, uint32_t *N){ + const char *start = (char*)buf; + uint32_t num = 0; + while(*buf){ + char c = *buf; + if(c < '0' || c > '1'){ + break; + } + if(num & 0x80000000){ // overflow + *N = 0xffffff; + return start; + } + num <<= 1; + if(c == '1') num |= 1; + ++buf; + } + *N = num; + return buf; +} + +/** + * @brief getnum - read uint32_t from string (dec, hex or bin: 127, 0x7f, 0b1111111) + * @param buf - buffer with number and so on + * @param N - the number read + * @return pointer to first non-number symbol in buf + * (if it is == buf, there's no number or if *N==0xffffffff there was overflow) + */ +const char *getnum(const char *txt, uint32_t *N){ + const char *nxt = NULL; + const char *s = omit_spaces(txt); + if(*s == '0'){ // hex, oct or 0 + if(s[1] == 'x' || s[1] == 'X'){ // hex + nxt = gethex(s+2, N); + if(nxt == s+2) nxt = (char*)txt; + }else if(s[1] > '0'-1 && s[1] < '8'){ // oct + nxt = getoct(s+1, N); + if(nxt == s+1) nxt = (char*)txt; + }else{ // 0 + nxt = s+1; + *N = 0; + } + }else if(*s == 'b' || *s == 'B'){ + nxt = getbin(s+1, N); + if(nxt == s+1) nxt = (char*)txt; + }else{ + nxt = getdec(s, N); + if(nxt == s) nxt = (char*)txt; + } + return nxt; +} + +// get signed integer +const char *getint(const char *txt, int32_t *I){ + const char *s = omit_spaces(txt); + int32_t sign = 1; + uint32_t U; + if(*s == '-'){ + sign = -1; + ++s; + } + const char *nxt = getnum(s, &U); + if(nxt == s) return txt; + if(U & 0x80000000) return txt; // overfull + *I = sign * (int32_t)U; + return nxt; +} + diff --git a/G0:G070/flash/strfunc.h b/G0:G070/flash/strfunc.h new file mode 100644 index 0000000..e244864 --- /dev/null +++ b/G0:G070/flash/strfunc.h @@ -0,0 +1,30 @@ +/* + * 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 + +void hexdump(int (*sendfun)(const char *s), uint8_t *arr, uint16_t len); +char *u2str(uint32_t val); +char *i2str(int32_t i); +char *uhex2str(uint32_t val); +const char *getnum(const char *txt, uint32_t *N); +const char *omit_spaces(const char *buf); +const char *getint(const char *txt, int32_t *I); diff --git a/G0:G070/flash/usart.c b/G0:G070/flash/usart.c new file mode 100644 index 0000000..f10c632 --- /dev/null +++ b/G0:G070/flash/usart.c @@ -0,0 +1,174 @@ +/* + * 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 + +#include "usart.h" + +// RX/TX DMA->CCR without EN flag +#define DMARXCCR (DMA_CCR_MINC | DMA_CCR_TCIE | DMA_CCR_TEIE) +#define DMATXCCR (DMA_CCR_MINC | DMA_CCR_DIR | DMA_CCR_TCIE | DMA_CCR_TEIE) + +static volatile int txrdy = 1, rxrdy = 0; // transmission done, next line received +static volatile int bufovr = 0, wasbufovr = 0; // Rx buffer overflow or error flag -> delete next line +static volatile int rbufno = 0, tbufno = 0; // current buf number +static volatile char rbuf[2][UARTBUFSZ], tbuf[2][UARTBUFSZ]; // receive & transmit buffers +static volatile int rxlen[2] = {0}, txlen[2] = {0}; + +char *usart3_getline(int *wasbo){ + if(wasbo) *wasbo = wasbufovr; + wasbufovr = 0; + if(!rxrdy) return NULL; + rxrdy = 0; // clear ready flag + return (char*)rbuf[!rbufno]; // current buffer is in filling stage, return old - filled - buffer +} + +#define USART_BRR(speed) ((64000000 + speed/2) / speed) + +// USART3 @ PD8 (Tx) and PD9 (Rx) - both AF0 +void usart3_setup(){ + RCC->IOPENR |= RCC_IOPENR_GPIODEN; // enable PD + RCC->AHBENR |= RCC_AHBENR_DMA1EN; // enable DMA1 + // set PD8 and PD9 as AF + GPIOD->MODER = (GPIOD->MODER & ~(GPIO_MODER_MODE8 | GPIO_MODER_MODE9)) + | GPIO_MODER_MODER8_AF | GPIO_MODER_MODER9_AF; + // AF0 for USART3 @ PD8/PD9 + GPIOD->AFR[1] = GPIOD->AFR[1] & ~(GPIO_AFRH_AFSEL8 | GPIO_AFRH_AFSEL9); + // enable USART3 clocking + RCC->APBENR1 |= RCC_APBENR1_USART3EN; + // baudrate 115200 + USART3->BRR = USART_BRR(115200); + // eol character: '/n' + USART3->CR2 = USART_CR2_ADD_VAL('\n'); + // enable DMA transmission + USART3->CR3 = USART_CR3_DMAT | USART_CR3_DMAR; + // set up DMA channels: 2 - Tx, 3 - Rx + // Tx channel: mem++, mem->periph, 8bit, compl.&err. irq + DMA1_Channel2->CCR = DMATXCCR; + DMA1_Channel2->CPAR = (uint32_t) &USART3->TDR; // peripherial address + // Rx channel: mem++, periph->mem, 8bit, compl.&err. irq + DMA1_Channel3->CCR = DMARXCCR; + DMA1_Channel3->CPAR = (uint32_t) &USART3->RDR; // peripherial address + DMA1_Channel3->CNDTR = UARTBUFSZ; + DMA1_Channel3->CMAR = (uint32_t)&rbuf[rbufno]; + // set up DMAMUX channels: 55 - USART3_TX, 54 - USART3_RX + // enumeration of DMAMUX starts from 0 (DMA - from 1)! + DMAMUX1_Channel1->CCR = 55; + DMAMUX1_Channel2->CCR = 54; + // charmatch interrupt, enable transmitter and receiver, enable usart + USART3->CR1 = USART_CR1_CMIE | USART_CR1_TE | USART_CR1_RE | USART_CR1_UE; + USART3->ICR = 0xffffffff; // clear all flags + DMA1_Channel3->CCR = DMARXCCR | DMA_CCR_EN; // start receiving right now + NVIC_EnableIRQ(USART3_4_IRQn); + NVIC_EnableIRQ(DMA1_Channel2_3_IRQn); +} + +static void mymemcpy(char *dest, const char *src, int len){ + while(len--) *dest++ = *src++; +} + +int usart3_send(const char *str, int len){ + int rest = UARTBUFSZ - txlen[tbufno]; + if(rest == 0 && !txrdy) return 0; // buffer is full while transmission in process + if(len < rest) rest = len; + mymemcpy((char*)(tbuf[tbufno] + txlen[tbufno]), str, rest); + txlen[tbufno] += rest; + if(!txrdy) return rest; + if(txlen[tbufno] == UARTBUFSZ) usart3_sendbuf(); + if(rest == len) return len; + len -= rest; + // now fill another - empty - buffer + if(len > UARTBUFSZ) len = UARTBUFSZ; + mymemcpy((char*)tbuf[tbufno], str + rest, len); + txlen[tbufno] = len; + return rest + len; +} + +int usart3_sendstr(const char *str){ + int l = strlen(str); + return usart3_send(str, l); +} + +/** + * @brief usart3_sendbuf - send current buffer + */ +void usart3_sendbuf(){ + if(!txrdy || txlen[tbufno] == 0) return; + // set up DMA + DMA1_Channel2->CCR = DMATXCCR; + DMA1_Channel2->CMAR = (uint32_t)&tbuf[tbufno]; + DMA1_Channel2->CNDTR = txlen[tbufno]; + USART3->ICR = USART_ICR_TCCF; // clear TC flag + txrdy = 0; + // activate DMA + DMA1_Channel2->CCR = DMATXCCR | DMA_CCR_EN; + tbufno = !tbufno; // swap buffers + txlen[tbufno] = 0; +} + +// return amount of bytes sents +int usart3_send_blocking(const char *str, int len){ + if(!txrdy) return 0; + USART3->CR1 |= USART_CR1_TE; + for(int i = 0; i < len; ++i){ + while(!(USART3->ISR & USART_ISR_TXE_TXFNF)); + USART3->TDR = *str++; + } + return len; +} + +// interrupt by '\n' +void usart3_4_isr(){ + if(USART3->ISR & USART_ISR_CMF){ // got '\n' @ USART3 + DMA1_Channel3->CCR = DMARXCCR; + if(!bufovr){ // forget about broken line @ buffer overflow + rxrdy = 1; + int l = UARTBUFSZ - DMA1_Channel3->CNDTR - 1; // strlen + rxlen[rbufno] = l; + rbuf[rbufno][l] = 0; + rbufno = !rbufno; + }else{ + bufovr = 0; + wasbufovr = 1; + } + // reload DMA Rx with next buffer + DMA1_Channel3->CMAR = (uint32_t)&rbuf[rbufno]; + DMA1_Channel3->CNDTR = UARTBUFSZ; + DMA1_Channel3->CCR = DMARXCCR | DMA_CCR_EN; + } + USART3->ICR = 0xffffffff; // clear all flags +} + +// ch2 - Tx, ch3 - Rx +void dma1_channel2_3_isr(){ + uint32_t isr = DMA1->ISR; + if(isr & (DMA_ISR_TCIF2 | DMA_ISR_TEIF2)){ // transfer complete or error + txrdy = 1; + //DMA1_Channel2->CCR = DMATXCCR; + } + if(isr & (DMA_ISR_TCIF3 | DMA_ISR_TEIF3)){ // receive complete or error -> buffer overflow + if(rbuf[rbufno][UARTBUFSZ-1] != '\n'){ // last symbol is not a newline + bufovr = 1; + DMA1_Channel3->CCR = DMARXCCR; + DMA1_Channel3->CNDTR = UARTBUFSZ; + DMA1_Channel3->CCR = DMARXCCR | DMA_CCR_EN; + } + } + DMA1->IFCR = 0xffffffff; // clear all flags +} diff --git a/G0:G070/flash/usart.h b/G0:G070/flash/usart.h new file mode 100644 index 0000000..c14465b --- /dev/null +++ b/G0:G070/flash/usart.h @@ -0,0 +1,35 @@ +/* + * 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 "strfunc.h" + +#define UARTBUFSZ (512) + +#define SEND(x) do{usart3_sendstr(x);}while(0) +#define newline() do{usart3_sendstr("\n");}while(0) +#define printu(x) do{usart3_sendstr(u2str(x));}while(0) +#define printuhex(x) do{usart3_sendstr(uhex2str(x));}while(0) + +void usart3_setup(); +int usart3_send_blocking(const char *str, int len); +int usart3_send(const char *str, int len); +void usart3_sendbuf(); +char *usart3_getline(int *wasbo); +int usart3_sendstr(const char *str);