From 970d73dc38403bcd40dfe4f3571afa391d38612c Mon Sep 17 00:00:00 2001 From: Edward Emelianov Date: Tue, 4 Aug 2026 18:22:58 +0300 Subject: [PATCH] add CORDIC --- G4:G431/CORDIC/Makefile | 13 ++ G4:G431/CORDIC/commproto.cpp | 224 ++++++++++++++++++++++++++++ G4:G431/CORDIC/commproto.h | 53 +++++++ G4:G431/CORDIC/cordic.bin | Bin 0 -> 12792 bytes G4:G431/CORDIC/cordic.c | 147 ++++++++++++++++++ G4:G431/CORDIC/cordic.cflags | 1 + G4:G431/CORDIC/cordic.config | 3 + G4:G431/CORDIC/cordic.creator | 1 + G4:G431/CORDIC/cordic.cxxflags | 1 + G4:G431/CORDIC/cordic.files | 19 +++ G4:G431/CORDIC/cordic.h | 40 +++++ G4:G431/CORDIC/cordic.includes | 4 + G4:G431/CORDIC/hardware.c | 69 +++++++++ G4:G431/CORDIC/hardware.h | 43 ++++++ G4:G431/CORDIC/main.c | 62 ++++++++ G4:G431/CORDIC/openocd.cfg | 4 + G4:G431/CORDIC/ringbuffer.c | 203 +++++++++++++++++++++++++ G4:G431/CORDIC/ringbuffer.h | 36 +++++ G4:G431/CORDIC/strfunc.c | 264 +++++++++++++++++++++++++++++++++ G4:G431/CORDIC/strfunc.h | 30 ++++ G4:G431/CORDIC/test.c | 109 ++++++++++++++ G4:G431/CORDIC/test.h | 35 +++++ G4:G431/CORDIC/usart.c | 257 ++++++++++++++++++++++++++++++++ G4:G431/CORDIC/usart.h | 49 ++++++ G4:G431/CORDIC/version.inc | 2 + makefile.stm32 | 13 +- 26 files changed, 1676 insertions(+), 6 deletions(-) create mode 100644 G4:G431/CORDIC/Makefile create mode 100644 G4:G431/CORDIC/commproto.cpp create mode 100644 G4:G431/CORDIC/commproto.h create mode 100755 G4:G431/CORDIC/cordic.bin create mode 100644 G4:G431/CORDIC/cordic.c create mode 100644 G4:G431/CORDIC/cordic.cflags create mode 100644 G4:G431/CORDIC/cordic.config create mode 100644 G4:G431/CORDIC/cordic.creator create mode 100644 G4:G431/CORDIC/cordic.cxxflags create mode 100644 G4:G431/CORDIC/cordic.files create mode 100644 G4:G431/CORDIC/cordic.h create mode 100644 G4:G431/CORDIC/cordic.includes create mode 100644 G4:G431/CORDIC/hardware.c create mode 100644 G4:G431/CORDIC/hardware.h create mode 100644 G4:G431/CORDIC/main.c create mode 100644 G4:G431/CORDIC/openocd.cfg create mode 100644 G4:G431/CORDIC/ringbuffer.c create mode 100644 G4:G431/CORDIC/ringbuffer.h create mode 100644 G4:G431/CORDIC/strfunc.c create mode 100644 G4:G431/CORDIC/strfunc.h create mode 100644 G4:G431/CORDIC/test.c create mode 100644 G4:G431/CORDIC/test.h create mode 100644 G4:G431/CORDIC/usart.c create mode 100644 G4:G431/CORDIC/usart.h create mode 100644 G4:G431/CORDIC/version.inc diff --git a/G4:G431/CORDIC/Makefile b/G4:G431/CORDIC/Makefile new file mode 100644 index 0000000..710a93f --- /dev/null +++ b/G4:G431/CORDIC/Makefile @@ -0,0 +1,13 @@ +BINARY := cordic +PREFIX := /usr/bin/arm-none-eabi +CFLAGS = -std=c23 +CPPFLAGS = -std=c++11 +# MCU code +MCU := G431xx +# change this linking script depending on particular MCU model, +LDSCRIPT := stm32g431xb.ld +LDLIBS += -lm + +include ../makefile.g4 +include ../../makefile.stm32 + diff --git a/G4:G431/CORDIC/commproto.cpp b/G4:G431/CORDIC/commproto.cpp new file mode 100644 index 0000000..1337253 --- /dev/null +++ b/G4:G431/CORDIC/commproto.cpp @@ -0,0 +1,224 @@ +/* + * This file is part of the cordic project. + * Copyright 2026 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 + +extern "C"{ +#include + +#include "commproto.h" +#include "hardware.h" +#include "strfunc.h" +#include "test.h" +} + +// sending function +static int (*SEND)(const char *str) = nullptr; + +extern volatile uint32_t Tms; + +//static uint8_t curbuf[MAXSTRLEN]; + +// COMMAND(USART, "Read USART data or send (USART=hex)") + +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) + +// list of all commands and handlers +#define COMMAND_TABLE \ + COMMAND(help, "show this help") \ + COMMAND(testm, "test math function: sin, cos, atan, sqrt, log") \ + COMMAND(testc, "test CORDIC function: sin, cos, atan, sqrt, log") + + +typedef struct { + const char *name; + const char *desc; +} CmdInfo; + +// prototypes +#define COMMAND(name, desc) static errcodes_t cmd_ ## name(const char*, char*); +COMMAND_TABLE +#undef COMMAND + +static const CmdInfo cmdInfo[] = { // command name, description - for `help` +#define COMMAND(name, desc) { #name, desc }, + COMMAND_TABLE +#undef COMMAND +}; + +static const char* errtxt[ERR_AMOUNT] = { + [ERR_OK] = "OK\n", + [ERR_BADCMD] = "BADCMD\n", + [ERR_BADPAR] = "BADPAR\n", + [ERR_BADVAL] = "BADVAL\n", + [ERR_WRONGLEN] = "WRONGLEN\n", + [ERR_CANTRUN] = "CANTRUN\n", + [ERR_BUSY] = "BUSY\n", + [ERR_OVERFLOW] = "OVERFLOW\n", +}; + +const char *EQ = " = "; // equal sign for getters + +// send `command = ` +#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` + * @param args (i) - rest of string after command (like `1 = PU OD OUT`) + * @param parno (o) - parameter number or -1 if none + * @return setter (part after `=` without leading spaces) or NULL if none + */ +static char *splitargs(char *args, int32_t *parno){ + if(!args) return NULL; + uint32_t U32; + char *next = getnum(args, &U32); + int p = -1; + if(next != args && U32 <= MAXPARNO) p = U32; + if(parno) *parno = p; + next = strchr(next, '='); + if(next){ + if(*(++next)) next = omit_spaces(next); + if(*next == 0) next = NULL; + } + return next; +} + +#if 0 +/** + * @brief argsvals - split `args` into `parno` and setter's value + * @param args - rest of string after command + * @param parno (o) - parameter number or -1 if none + * @param parval - integer setter's value + * @return false if no setter or it's not a number, true - got setter's num + */ +static bool argsvals(char *args, int32_t *parno, int32_t *parval){ + char *setter = splitargs(args, parno); + if(!setter) return false; + int32_t I32; + char *next = getint(setter, &I32); + if(next != setter){ + if(parval) *parval = I32; + return true; + } + return false; +} +#endif + +static errcodes_t cmd_help(const char*, char*){ + SEND(REPOURL); + for(size_t i = 0; i < sizeof(cmdInfo)/sizeof(cmdInfo[0]); i++){ + SEND(cmdInfo[i].name); + SEND(" - "); + SEND(cmdInfo[i].desc); SEND("\n"); + } + return ERR_AMOUNT; +} + +static const char* parse_func_name(char *args, int32_t *parno){ + char *setter = splitargs(args, parno); + if(!setter) return nullptr; + // remove trailing spaces + char *p = setter; + while(*p && *p > ' ') ++p; + *p = 0; + return setter; +} + +// test math function +static errcodes_t cmd_testm(const char*, char *args){ + int32_t parno; + const char *fname = parse_func_name(args, &parno); + if(!fname) return ERR_BADPAR; + uint32_t elapsed = 0; + bool ok = true; + if(strcmp(fname, "sin") == 0){ + elapsed = test_math_sin(); + }else if(strcmp(fname, "cos") == 0){ + elapsed = test_math_cos(); + }else if(strcmp(fname, "atan") == 0){ + elapsed = test_math_atan(); + }else if(strcmp(fname, "sqrt") == 0){ + elapsed = test_math_sqrt(); + }else if(strcmp(fname, "log") == 0){ + elapsed = test_math_log(); + }else{ + ok = false; + } + if(!ok) return ERR_BADVAL; + SEND("TIME="); + SEND(u2str(elapsed)); + SEND("\n"); + return ERR_AMOUNT; +} + + +// test CORDIC function +static errcodes_t cmd_testc(const char*, char *args){ + int32_t parno; + const char *fname = parse_func_name(args, &parno); + if(!fname) return ERR_BADPAR; + uint32_t elapsed = 0; + bool ok = true; + if(strcmp(fname, "sin") == 0){ + elapsed = test_cordic_sin(); + }else if(strcmp(fname, "cos") == 0){ + elapsed = test_cordic_cos(); + }else if(strcmp(fname, "atan") == 0){ + elapsed = test_cordic_atan(); + }else if(strcmp(fname, "sqrt") == 0){ + elapsed = test_cordic_sqrt(); + }else if(strcmp(fname, "log") == 0){ + elapsed = test_cordic_log(); + }else{ + ok = false; + } + if(!ok) return ERR_BADVAL; + SEND("TIME="); + SEND(u2str(elapsed)); + SEND("\n"); + return ERR_AMOUNT; +} + +constexpr uint32_t hash(const char* str, uint32_t h = 0){ + return *str ? hash(str + 1, h + ((h << 7) ^ *str)) : h; +} + +const char *parse_cmd(int (*sendfun)(const char *), char *str){ + SEND = sendfun; + char command[CMD_MAXLEN+1]; + int i = 0; + while(*str > '@' && i < CMD_MAXLEN){ command[i++] = *str++; } + command[i] = 0; + while(*str && *str <= ' ') ++str; + char *restof = (char*) str; + uint32_t h = hash(command); + errcodes_t ecode = ERR_AMOUNT; + switch(h){ +#define COMMAND(name, desc) case hash(#name): ecode = cmd_ ## name(command, restof); break; + COMMAND_TABLE +#undef COMMAND + default: SEND("Unknown command, try 'help'\n"); break; + } + if(ecode < ERR_AMOUNT) return errtxt[ecode]; + return NULL; +} diff --git a/G4:G431/CORDIC/commproto.h b/G4:G431/CORDIC/commproto.h new file mode 100644 index 0000000..66b029f --- /dev/null +++ b/G4:G431/CORDIC/commproto.h @@ -0,0 +1,53 @@ +/* + * This file is part of the cordic project. + * Copyright 2026 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 "version.inc" + +#ifdef EBUG +#define RLSDBG "debug" +#else +#define RLSDBG "release" +#endif + +#define REPOURL "https://github.com/eddyem/stm32samples/tree/master/TODO " RLSDBG " build #" BUILD_NUMBER "@" BUILD_DATE "\n" + + +// error codes for answer message +typedef enum{ + ERR_OK, // all OK + ERR_BADCMD, // wrong command + ERR_BADPAR, // wrong parameter + ERR_BADVAL, // wrong value (for setter) + ERR_WRONGLEN, // wrong message length + ERR_CANTRUN, // can't run given command due to bad parameters or other + ERR_BUSY, // target interface busy, try later + ERR_OVERFLOW, // string was too long -> overflow + ERR_AMOUNT // amount of error codes or "send nothing" +} errcodes_t; + +// maximal length of command (without trailing zero) +#define CMD_MAXLEN 15 +// maximal available parameter number +#define MAXPARNO 255 +// maximal string length includint terminating zero +#define MAXSTRLEN 256 + +extern const char *EQ; +const char *parse_cmd(int (*sendfun)(const char*), char *buf); diff --git a/G4:G431/CORDIC/cordic.bin b/G4:G431/CORDIC/cordic.bin new file mode 100755 index 0000000000000000000000000000000000000000..9f5d601d362b988c359275717bb6e259f9c225cb GIT binary patch literal 12792 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 I|4sIP0L#sLD*ylh literal 0 HcmV?d00001 diff --git a/G4:G431/CORDIC/cordic.c b/G4:G431/CORDIC/cordic.c new file mode 100644 index 0000000..1d74445 --- /dev/null +++ b/G4:G431/CORDIC/cordic.c @@ -0,0 +1,147 @@ +/* + * This file is part of the cordic project. + * Copyright 2026 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 "cordic.h" + +// default CSR settings +#define CORDIC_CSR_DEF (5 << CORDIC_CSR_PRECISION_Pos) + +// enable CORDIC +static void cordic_enable(void){ + RCC->AHB1ENR |= RCC_AHB1ENR_CORDICEN; + __DSB(); +} + +// Convert float to Q1.31 in [-1, 1) +static int32_t float_to_q31(float x){ + if(x >= 1.0f) x = 1.0f - 1e-6f; + if(x <= -1.0f) x = -1.0f + 1e-6f; + return (int32_t)(x * 2147483648.0f); +} + +// Convert Q1.31 to float +static float q31_to_float(int32_t q){ + return (float)q / 2147483648.0f; +} + +// Wait CORDIC ready +static void cordic_wait_ready(void){ + while(!(CORDIC->CSR & CORDIC_CSR_RRDY)); +} + +static void cordic_init(void){ + static bool inited = false; + if(!inited){ + cordic_enable(); + inited = true; + } +} + +#if 0 +// Common function for one arg (sin, cos, sqrt, log) +static float cordic_scalar(uint32_t func_mode, float x, int arg_count){ + cordic_init(); + CORDIC->CSR = (func_mode << CORDIC_CSR_FUNC_Pos) | + CORDIC_CSR_INSIZE_1 | // in Q1.31 + CORDIC_CSR_OUTSIZE_0; // out Q1.31 + if(arg_count == 1){ + CORDIC->WDATA = float_to_q31(x); + } else { +... + } + cordic_wait_ready(); + int32_t res = CORDIC->RDATA; + return q31_to_float(res); +} +#endif + +// sin +float cordic_sin(float angle){ + float norm = angle / 3.141592653589793f; + if(norm > 1.0f) norm = 1.0f; + if(norm < -1.0f) norm = -1.0f; + cordic_init(); + CORDIC->CSR = (CORDIC_CSR_FUNC_SIN << CORDIC_CSR_FUNC_Pos) | CORDIC_CSR_DEF; + CORDIC->WDATA = float_to_q31(norm); + cordic_wait_ready(); + int32_t res = CORDIC->RDATA; // первое чтение -> sin + return q31_to_float(res); +} + +// cos +float cordic_cos(float angle){ + float norm = angle / 3.141592653589793f; + if(norm > 1.0f) norm = 1.0f; + if(norm < -1.0f) norm = -1.0f; + cordic_init(); + CORDIC->CSR = (CORDIC_CSR_FUNC_COS << CORDIC_CSR_FUNC_Pos) | CORDIC_CSR_DEF; + CORDIC->WDATA = float_to_q31(norm); + cordic_wait_ready(); + int32_t res = CORDIC->RDATA; // cos + return q31_to_float(res); +} + +// atan2 +float cordic_atan(float val){ + if(val > 1.f || val < -1.f) return NAN; + cordic_init(); + CORDIC->CSR = (CORDIC_CSR_FUNC_ATAN << CORDIC_CSR_FUNC_Pos) | CORDIC_CSR_DEF; + CORDIC->WDATA = float_to_q31(val); + cordic_wait_ready(); + int32_t res = CORDIC->RDATA; + return q31_to_float(res) * 3.141592653589793f; +} + +// sqrt +float cordic_sqrt(float x){ + if(x < 0.0f) x = 0.0f; + float scale = 1.0f; + if(x > 1.0f){ + if(x < 100.f){ scale = 10.f; x /= 100.f;} + else if(x < 1e4f){ scale = 100.f; x /= 1e4f;} + else if(x < 1e6f){ scale = 1e3f; x /= 1e6f;} + else if(x < 1e10f){ scale = 1e5f; x /= 1e10f;} + else return NAN; // number is too big + } + cordic_init(); + CORDIC->CSR = (CORDIC_CSR_FUNC_SQRT << CORDIC_CSR_FUNC_Pos) | CORDIC_CSR_DEF; + CORDIC->WDATA = float_to_q31(x); + cordic_wait_ready(); + int32_t res = CORDIC->RDATA; + return scale * q31_to_float(res); +} + +// log +float cordic_log(float x){ + if(x <= 0.0f) x = 0.00001f; + float add = 0.f; + if(x > 1.0f){ + if(x < 1000.f){ add = 6.907755279f; x /= 1000.f; } + else if(x < 1e6f){ add = 13.815510558f; x /= 1e6f; } + else if(x < 1e12f){ add = 27.63102112f; x /= 1e12f; } + else return NAN; + } + cordic_init(); + CORDIC->CSR = (CORDIC_CSR_FUNC_LOG << CORDIC_CSR_FUNC_Pos) | CORDIC_CSR_DEF; + CORDIC->WDATA = float_to_q31(x); + cordic_wait_ready(); + int32_t res = CORDIC->RDATA; + return q31_to_float(res) * 0.6931471805599453f + add; +} diff --git a/G4:G431/CORDIC/cordic.cflags b/G4:G431/CORDIC/cordic.cflags new file mode 100644 index 0000000..a07354f --- /dev/null +++ b/G4:G431/CORDIC/cordic.cflags @@ -0,0 +1 @@ +-std=c23 \ No newline at end of file diff --git a/G4:G431/CORDIC/cordic.config b/G4:G431/CORDIC/cordic.config new file mode 100644 index 0000000..c4aadd9 --- /dev/null +++ b/G4:G431/CORDIC/cordic.config @@ -0,0 +1,3 @@ +#define EBUG +#define STM32G4 +#define STM32G431xx diff --git a/G4:G431/CORDIC/cordic.creator b/G4:G431/CORDIC/cordic.creator new file mode 100644 index 0000000..e94cbbd --- /dev/null +++ b/G4:G431/CORDIC/cordic.creator @@ -0,0 +1 @@ +[General] diff --git a/G4:G431/CORDIC/cordic.cxxflags b/G4:G431/CORDIC/cordic.cxxflags new file mode 100644 index 0000000..ea5966d --- /dev/null +++ b/G4:G431/CORDIC/cordic.cxxflags @@ -0,0 +1 @@ +-std=c++23 \ No newline at end of file diff --git a/G4:G431/CORDIC/cordic.files b/G4:G431/CORDIC/cordic.files new file mode 100644 index 0000000..a700edc --- /dev/null +++ b/G4:G431/CORDIC/cordic.files @@ -0,0 +1,19 @@ +commproto.cpp +commproto.h +cordic.c +cordic.h +flash.c +flash.h +hardware.c +hardware.h +main.c +ringbuffer.c +ringbuffer.h +servo.c +servo.h +strfunc.c +strfunc.h +test.c +test.h +usart.c +usart.h diff --git a/G4:G431/CORDIC/cordic.h b/G4:G431/CORDIC/cordic.h new file mode 100644 index 0000000..05849bc --- /dev/null +++ b/G4:G431/CORDIC/cordic.h @@ -0,0 +1,40 @@ +/* + * This file is part of the cordic project. + * Copyright 2026 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 + +// functions +enum { + CORDIC_CSR_FUNC_COS = 0, + CORDIC_CSR_FUNC_SIN, + CORDIC_CSR_FUNC_PHASE, + CORDIC_CSR_FUNC_MOD, + CORDIC_CSR_FUNC_ATAN, + CORDIC_CSR_FUNC_COSH, + CORDIC_CSR_FUNC_ATANH, + CORDIC_CSR_FUNC_LOG, + CORDIC_CSR_FUNC_SQRT +}; + +float cordic_sin(float angle); +float cordic_cos(float angle); +float cordic_atan(float val); +float cordic_sqrt(float x); +float cordic_log(float x); diff --git a/G4:G431/CORDIC/cordic.includes b/G4:G431/CORDIC/cordic.includes new file mode 100644 index 0000000..641fd56 --- /dev/null +++ b/G4:G431/CORDIC/cordic.includes @@ -0,0 +1,4 @@ +. +../inc +../inc/Fx +../inc/cm diff --git a/G4:G431/CORDIC/hardware.c b/G4:G431/CORDIC/hardware.c new file mode 100644 index 0000000..50b2a2b --- /dev/null +++ b/G4:G431/CORDIC/hardware.c @@ -0,0 +1,69 @@ +/* + * This file is part of the cordic project. + * Copyright 2026 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 "hardware.h" + +volatile uint32_t Tms = 0; + +/* Called when systick fires */ +void sys_tick_handler(){ + ++Tms; +} + +static void timer_setup(){ + RCC->APB1ENR1 |= RCC_APB1ENR1_TIM2EN; + __DSB(); + TIM2->PSC = 84; // 85 MHz / 85 = 1 MHz + TIM2->ARR = 0xFFFFFFFF; // 32-bit auto-reload (maximum) + TIM2->CR1 = 0; // disable counter +} + +void timer_start(){ + TIM2->CNT = 0; + TIM2->CR1 |= TIM_CR1_CEN; +} + +void timer_stop(){ + TIM2->CR1 = 0; +} + +uint32_t timer_read(){ + return TIM2->CNT; +} + +void gpio_setup(){ + RCC->AHB2ENR = RCC_AHB2ENR_GPIOAEN | RCC_AHB2ENR_GPIOCEN; + __DSB(); + // PC6 as output, PC13 as input with pulldown + GPIOC->MODER = (0xffffffff & ~(GPIO_MODER_MODE6 | GPIO_MODER_MODE13)) | MODER_O(6) | MODER_I(13); + GPIOC->PUPDR = PUPD_PD(13); + GPIOC->OTYPER = OTYPER_PP(6); + // PA9 (Tx), PA10 (Rx) as alternate functions (for USART1) + GPIOA->MODER = (0xabffffff & ~(GPIO_MODER_MODE9 | GPIO_MODER_MODE10)) | + MODER_AF(9) | MODER_AF(10); + GPIOA->AFR[1] = AFRf(7, 9) | AFRf(7, 10); + // count milliseconds + SysTick_Config(SysFreq / 1000); + + // Setup TIM2 for microsecond counting + timer_setup(); +} + + diff --git a/G4:G431/CORDIC/hardware.h b/G4:G431/CORDIC/hardware.h new file mode 100644 index 0000000..ce5c28c --- /dev/null +++ b/G4:G431/CORDIC/hardware.h @@ -0,0 +1,43 @@ +/* + * This file is part of the cordic project. + * Copyright 2026 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 + +// KEY (intpullup->0) - PC13 +// LED - PC6 +#define KEY_PORT GPIOC +#define KEY_PIN (1<<13) +#define LED_PORT GPIOC +#define LED_PIN (1<<6) +#define KEY_PRESSED() (pin_read(KEY_PORT, KEY_PIN) == 1) +#define LED_ON() do{pin_set(LED_PORT, LED_PIN);}while(0) +#define LED_OFF() do{pin_clear(LED_PORT, LED_PIN);}while(0) +#define LED_TOGG() do{pin_toggle(LED_PORT, LED_PIN);}while(0) + +#define SERVO_AMOUNT 4 + +#define MIN_USART_SPEED 1200 +#define MAX_USART_SPEED 3000000 + +extern volatile uint32_t Tms; + +void gpio_setup(); + +void timer_start(); +void timer_stop(); +uint32_t timer_read(); diff --git a/G4:G431/CORDIC/main.c b/G4:G431/CORDIC/main.c new file mode 100644 index 0000000..3fed459 --- /dev/null +++ b/G4:G431/CORDIC/main.c @@ -0,0 +1,62 @@ +/* + * This file is part of the cordic 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 "commproto.h" +#include "hardware.h" +#include "usart.h" + +// 40ms for debouncing +#define TDEBOUNCE (40) + +int main(void){ + StartHSE(); + // init storage first to take base values + gpio_setup(); + usart_setup(115200); + bool pressed = false; + uint32_t Tblink = 0, Tkey = 0; // blink and key pressed time + while(1){ + USART_flags_t f = usart_process(); + if(f.rxovrfl) usart_sendstr("Rx buffer overflow!\n"); + if(f.txerr) usart_sendstr("Tx error!\n"); + char *str = usart_getline(); + if(str){ + const char *ans = parse_cmd(usart_sendstr, str); + if(ans) usart_sendstr(ans); + } + if(Tms - Tblink > 499){ + LED_TOGG(); + Tblink = Tms; + } + if(KEY_PRESSED()){ + if(!pressed){ // new event + pressed = true; + usart_sendstr("Key pressed\n"); + } + Tkey = Tms; + }else{ // key released + if(pressed && (Tms - Tkey) > TDEBOUNCE){ // wait for debounce + pressed = false; + usart_sendstr("Key released\n"); + } + } + } +} diff --git a/G4:G431/CORDIC/openocd.cfg b/G4:G431/CORDIC/openocd.cfg new file mode 100644 index 0000000..22582a6 --- /dev/null +++ b/G4:G431/CORDIC/openocd.cfg @@ -0,0 +1,4 @@ +set FLASH_SIZE 0x20000 + +source [find interface/stlink-v2-1.cfg] +source [find target/stm32g4x.cfg] diff --git a/G4:G431/CORDIC/ringbuffer.c b/G4:G431/CORDIC/ringbuffer.c new file mode 100644 index 0000000..5e28ee0 --- /dev/null +++ b/G4:G431/CORDIC/ringbuffer.c @@ -0,0 +1,203 @@ +/* + * 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 "ringbuffer.h" + +#define CHK(b) do{if(!b) return -1;}while(0) + +static int datalen(ringbuffer *b){ + if(b->tail >= b->head) return (b->tail - b->head); + else return (b->length - b->head + b->tail); +} + +// stored data length +int RB_datalen(ringbuffer *b){ + CHK(b); + if(0 == datalen(b)) return 0; // don't block for empty RO operations + if(b->busy) return -1; + b->busy = true; + int l = datalen(b); + b->busy = false; + return l; +} + +static int hasbyte(ringbuffer *b, uint8_t byte){ + if(b->head == b->tail) return -1; // no data in buffer + int startidx = b->head; + if(b->head > b->tail){ // + for(int found = b->head; found < b->length; ++found) + if(b->data[found] == byte) return found; + startidx = 0; + } + for(int found = startidx; found < b->tail; ++found) + if(b->data[found] == byte) return found; + return -1; +} + +/** + * @brief RB_hasbyte - check if buffer has given byte stored + * @param b - buffer + * @param byte - byte to find + * @return index if found, -1 if none or busy + */ +int RB_hasbyte(ringbuffer *b, uint8_t byte){ + CHK(b); + if(b->busy) return -1; + b->busy = true; + int ret = hasbyte(b, byte); + b->busy = false; + return ret; +} + +// increment head or tail +TRUE_INLINE void incr(ringbuffer *b, volatile int *what, int n){ + *what += n; + if(*what >= b->length) *what -= b->length; +} + +static int read(ringbuffer *b, uint8_t *s, int len){ + int l = datalen(b); + if(!l) return 0; + if(l > len) l = len; + int _1st = b->length - b->head; + if(_1st > l) _1st = l; + if(_1st > len) _1st = len; + memcpy(s, b->data + b->head, _1st); + if(_1st < len && l > _1st){ + memcpy(s+_1st, b->data, l - _1st); + incr(b, &b->head, l); + return l; + } + incr(b, &b->head, _1st); + return _1st; +} + +/** + * @brief RB_read - read data from ringbuffer + * @param b - buffer + * @param s - array to write data + * @param len - max len of `s` + * @return bytes read or -1 if busy + */ +int RB_read(ringbuffer *b, uint8_t *s, int len){ + CHK(b); + if(!s || len < 1) return -1; + if(0 == datalen(b)) return 0; + if(b->busy) return -1; + b->busy = true; + int r = read(b, s, len); + b->busy = false; + return r; +} + +// length of data from current position to `byte` (including byte) +static int lento(ringbuffer *b, uint8_t byte){ + int idx = hasbyte(b, byte); + if(idx < 0) return 0; + int partlen = idx + 1 - b->head; + // now calculate length of new data portion + if(idx < b->head) partlen += b->length; + return partlen; +} + +static int readto(ringbuffer *b, uint8_t byte, uint8_t *s, int len){ + int partlen = lento(b, byte); + if(!partlen) return 0; + if(partlen > len) return -1; + return read(b, s, partlen); +} + +/** + * @brief RB_readto fill array `s` with data until byte `byte` (with it) + * @param b - ringbuffer + * @param byte - check byte + * @param s - buffer to write data or NULL to clear data + * @param len - length of `s` or 0 to clear data + * @return amount of bytes written (negative, if lenbusy) return -1; + b->busy = true; + int n = 0; + if(s && len > 0){ + n = readto(b, byte, s, len); + }else{ + incr(b, &b->head, lento(b, byte)); // just throw data out + } + b->busy = false; + return n; +} + +int RB_datalento(ringbuffer *b, uint8_t byte){ + CHK(b); + if(0 == datalen(b)) return 0; + if(b->busy) return -1; + b->busy = true; + int n = lento(b, byte); + b->busy = false; + return n; +} + +// if l < rest of buffer, truncate and return actually written bytes +static int write(ringbuffer *b, const uint8_t *str, int l){ + int r = b->length - 1 - datalen(b); // rest length + if(r < 1) return 0; + if(l > r) l = r; + int _1st = b->length - b->tail; + if(_1st > l) _1st = l; + memcpy(b->data + b->tail, str, _1st); + if(_1st < l){ // add another piece from start + memcpy(b->data, str+_1st, l-_1st); + } + incr(b, &b->tail, l); + return l; +} + +/** + * @brief RB_write - write some data to ringbuffer + * @param b - buffer + * @param str - data + * @param l - length + * @return amount of bytes written or -1 if busy + */ +int RB_write(ringbuffer *b, const uint8_t *str, int l){ + CHK(b); + if(!str || l < 1) return -1; + if(b->length - datalen(b) < 2) return 0; + if(b->busy) return -1; + b->busy = true; + int w = write(b, str, l); + b->busy = false; + return w; +} + +// just delete all information in buffer `b` +int RB_clearbuf(ringbuffer *b){ + CHK(b); + if(b->busy) return -1; + b->busy = true; + b->head = 0; + b->tail = 0; + memset(b->data, 0, b->length); + b->busy = false; + return 1; +} diff --git a/G4:G431/CORDIC/ringbuffer.h b/G4:G431/CORDIC/ringbuffer.h new file mode 100644 index 0000000..fd859bd --- /dev/null +++ b/G4:G431/CORDIC/ringbuffer.h @@ -0,0 +1,36 @@ +/* + * 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 + +typedef struct{ + uint8_t *data; // data buffer + const int length; // its length + int head; // head index + int tail; // tail index + volatile bool busy; // == TRUE if buffer is busy now +} ringbuffer; + +int RB_read(ringbuffer *b, uint8_t *s, int len); +int RB_readto(ringbuffer *b, uint8_t byte, uint8_t *s, int len); +int RB_hasbyte(ringbuffer *b, uint8_t byte); +int RB_write(ringbuffer *b, const uint8_t *str, int l); +int RB_datalen(ringbuffer *b); +int RB_datalento(ringbuffer *b, uint8_t byte); +int RB_clearbuf(ringbuffer *b); diff --git a/G4:G431/CORDIC/strfunc.c b/G4:G431/CORDIC/strfunc.c new file mode 100644 index 0000000..cc39bb7 --- /dev/null +++ b/G4:G431/CORDIC/strfunc.c @@ -0,0 +1,264 @@ +/* + * Copyright 2025 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 arr - array to dump + * @param len - length of `arr` + */ +void hexdump(int (*sendfun)(const char *), 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; + } + } + 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` > ' ' + */ +char *omit_spaces(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 char *getdec(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!) +char *gethex(char *buf, uint32_t *N){ + 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 char *getoct(char *buf, uint32_t *N){ + 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 char *getbin(char *buf, uint32_t *N){ + 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) + */ +char *getnum(char *txt, uint32_t *N){ + char *nxt = NULL; + 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 +char *getint(char *txt, int32_t *I){ + char *s = omit_spaces(txt); + int32_t sign = 1; + uint32_t U; + if(*s == '-'){ + sign = -1; + ++s; + } + 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/G4:G431/CORDIC/strfunc.h b/G4:G431/CORDIC/strfunc.h new file mode 100644 index 0000000..bd1950c --- /dev/null +++ b/G4:G431/CORDIC/strfunc.h @@ -0,0 +1,30 @@ +/* + * Copyright 2025 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 + +void hexdump(int (*sendfun)(const char*), uint8_t *arr, uint16_t len); +char *u2str(uint32_t val); +char *i2str(int32_t i); +char *uhex2str(uint32_t val); +char *gethex(const char *buf, uint32_t *N); +char *getnum(char *txt, uint32_t *N); +char *omit_spaces(char *buf); +char *getint(char *txt, int32_t *I); + diff --git a/G4:G431/CORDIC/test.c b/G4:G431/CORDIC/test.c new file mode 100644 index 0000000..59b1412 --- /dev/null +++ b/G4:G431/CORDIC/test.c @@ -0,0 +1,109 @@ +/* + * This file is part of the cordic project. + * Copyright 2026 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 + +#include "test.h" +#include "hardware.h" +#include "cordic.h" + +// amount of iterations over test +#define N_TESTS 1000 + +// RNG +static uint32_t rand_state = 123456789; +static uint32_t next_rand(void){ + rand_state = rand_state * 1664525 + 1013904223; + return rand_state; +} + +// fill array with random angles +static void fill_random_sin_cos(float *arr){ + for(int i = 0; i < N_TESTS; ++i){ + // angle from -pi to +pi + arr[i] = (float)(next_rand() % 62831853) / 10000000.0f - 3.14159265f; + } +} + +static void fill_random_atan(float *arr){ + for(int i = 0; i < N_TESTS; ++i){ + arr[i] = (float)(next_rand() % 2000000) / 1e6f - 1e6f; // [-1,1] + } +} + +static void fill_random_sqrt(float *arr){ + for(int i = 0; i < N_TESTS; ++i){ + arr[i] = (float)(next_rand() % 10000) / 100.0f; // [0,100] + } +} + +static void fill_random_log(float *arr){ + for(int i = 0; i < N_TESTS; ++i){ + arr[i] = (float)(next_rand() % 10000 + 1) / 100.0f; // [0.01, 100] + } +} + +// main test template +static uint32_t run_test(void (*gen)(float*), float (*func)(float)){ + float arr[N_TESTS]; + gen(arr); + volatile float result = 0.0f; // don't let gcc to optimize this cycle + timer_start(); + for(int i = 0; i < N_TESTS; ++i){ + result = func(arr[i]); + (void) result; + } + timer_stop(); + return timer_read(); +} + +// ------------- math.h tests ------------- +uint32_t test_math_sin(void){ + return run_test(fill_random_sin_cos, sinf); +} +uint32_t test_math_cos(void){ + return run_test(fill_random_sin_cos, cosf); +} +uint32_t test_math_atan(void){ + return run_test(fill_random_atan, atanf); +} +uint32_t test_math_sqrt(void){ + return run_test(fill_random_sqrt, sqrtf); +} +uint32_t test_math_log(void){ + return run_test(fill_random_log, logf); +} + +// ------------- CORDIC tests ------------- +uint32_t test_cordic_sin(void){ + return run_test(fill_random_sin_cos, cordic_sin); +} +uint32_t test_cordic_cos(void){ + return run_test(fill_random_sin_cos, cordic_cos); +} +uint32_t test_cordic_atan(void){ + return run_test(fill_random_atan, cordic_atan); +} +uint32_t test_cordic_sqrt(void){ + return run_test(fill_random_sqrt, cordic_sqrt); +} +uint32_t test_cordic_log(void){ + return run_test(fill_random_log, cordic_log); +} diff --git a/G4:G431/CORDIC/test.h b/G4:G431/CORDIC/test.h new file mode 100644 index 0000000..fe6d095 --- /dev/null +++ b/G4:G431/CORDIC/test.h @@ -0,0 +1,35 @@ +/* + * This file is part of the cordic project. + * Copyright 2026 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 + +// libmath tests +uint32_t test_math_sin(); +uint32_t test_math_cos(); +uint32_t test_math_atan(); +uint32_t test_math_sqrt(); +uint32_t test_math_log(); + +// CORDIC tests +uint32_t test_cordic_sin(); +uint32_t test_cordic_cos(); +uint32_t test_cordic_atan(); +uint32_t test_cordic_sqrt(); +uint32_t test_cordic_log(); diff --git a/G4:G431/CORDIC/usart.c b/G4:G431/CORDIC/usart.c new file mode 100644 index 0000000..3713473 --- /dev/null +++ b/G4:G431/CORDIC/usart.c @@ -0,0 +1,257 @@ +/* + * This file is part of the cordic project. + * Copyright 2026 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 "hardware.h" // Tms +#include "ringbuffer.h" +#include "usart.h" + +// USART for text-based protocol, each data portion ends with '\n' +// RX works over circular DMA + +// USART-depending part -------> +// USART1 @ PA9 (Tx) - MUX25 - and PA10 (Rx) - MUX24 +// select USART and its DMA channels +#define USARTx USART1 +#define USARTxAPB APB2ENR +#define USARTxEN RCC_APB2ENR_USART1EN +#define USART_APBEN RCC_APB2 +// DMAMUX channels: 24 - USART1Rx, 25 - USART1Tx +#define DMAMUXRXN (24) +#define DMAMUXTXN (25) +// DMA channels: 1 (0 in MUX) - Rx, 2 (1 in MUX) - Tx; TC and error flags +// use DMA ch2/3 because they both have single IRQ +#define DMAx DMA1 +#define DMAxEN (RCC_AHB1ENR_DMA1EN | RCC_AHB1ENR_DMAMUX1EN) +#define DMACHRX DMA1_Channel1 +#define DMARXTCF DMA_ISR_TCIF1 +#define DMARXEF DMA_ISR_TEIF1 +#define DMACHTX DMA1_Channel2 +#define DMATXTCF DMA_ISR_TCIF2 +#define DMATXEF DMA_ISR_TEIF2 +#define DMAMUXRX DMAMUX1_Channel0 +#define DMAMUXTX DMAMUX1_Channel1 +#define USARTIRQn USART1_IRQn +#define DMARXIRQ DMA1_Channel1_IRQn +#define DMATXIRQ DMA1_Channel2_IRQn + +// interrupt aliases +static void usart_isr(); +static void dmatx_isr(); +static void dmarx_isr(); +void usart1_isr() __attribute__ ((alias ("usart_isr"))); +void dma1_channel1_isr() __attribute__ ((alias ("dmarx_isr"))); +void dma1_channel2_isr() __attribute__ ((alias ("dmatx_isr"))); +// <-------- USART-depending part + +// RX/TX DMA->CCR without EN flag +#define DMARXCCR (DMA_CCR_MINC | DMA_CCR_CIRC | DMA_CCR_TEIE) +#define DMATXCCR (DMA_CCR_MINC | DMA_CCR_DIR | DMA_CCR_TCIE | DMA_CCR_TEIE) + +static volatile bool gotstring = true; // got '\n' in input stream -> force data reading to inbuf +static volatile bool txrdy = true; // Tx DMA not busy +static volatile USART_flags_t curflags; // current flags (cleared in `usart_process`) +// rx/tx DMA buffers +static uint8_t dmarxbuf[USARTRXDMABUFSZ]; +static uint8_t dmatxbuf[USARTTXDMABUFSZ]; +// index of last DMA read position +static uint32_t dma_read_idx = 0; +// for ringbuffer +static uint8_t rbrxbuf[USARTRXBUFSZ], rbtxbuf[USARTTXBUFSZ]; +static ringbuffer TxRB = {.data = rbtxbuf, .length = USARTTXBUFSZ}; +static ringbuffer RxRB = {.data = rbrxbuf, .length = USARTRXBUFSZ}; + +#define USART_BRR(speed) ((SysFreq + (speed)/2) / (speed)) + +static void reinit_rx_dma(){ + dma_read_idx = 0; + RB_clearbuf(&RxRB); + DMACHRX->CCR = DMARXCCR; // stop to reload + DMACHRX->CNDTR = USARTRXDMABUFSZ; + DMACHRX->CMAR = (uint32_t) dmarxbuf; + DMACHRX->CCR = DMARXCCR | DMA_CCR_EN; +} + +void usart_setup(uint32_t speed){ + RCC->AHB1ENR |= DMAxEN; // enable DMA + // enable USART clocking + RCC->USARTxAPB |= USARTxEN; + // baudrate + USARTx->BRR = USART_BRR(speed); + // eol character: '/n' + USARTx->CR2 = USART_CR2_ADD_VAL('\n'); + // enable DMA transmission + USARTx->CR3 = USART_CR3_DMAT | USART_CR3_DMAR; + // set up DMA channels + // Tx channel: mem++, mem->periph, 8bit, compl.&err. irq + DMACHTX->CCR = DMATXCCR; + DMACHTX->CPAR = (uint32_t) &USARTx->TDR; // peripherial address + // Rx channel: mem++, periph->mem, 8bit, compl.&err. irq + DMACHRX->CCR = DMARXCCR; + DMACHRX->CPAR = (uint32_t) &USARTx->RDR; // peripherial address + // set up DMAMUX channels + // enumeration of DMAMUX starts from 0 (DMA - from 1)! + DMAMUXRX->CCR = DMAMUXRXN; + DMAMUXTX->CCR = DMAMUXTXN; + // charmatch interrupt, enable transmitter and receiver, enable usart + USARTx->CR1 = USART_CR1_CMIE | USART_CR1_TE | USART_CR1_RE | USART_CR1_UE; + USARTx->ICR = 0xffffffff; // clear all flags + reinit_rx_dma(); + NVIC_EnableIRQ(USARTIRQn); + NVIC_EnableIRQ(DMARXIRQ); + NVIC_EnableIRQ(DMATXIRQ); +} + +/** + * @brief usart_sendbuf - send next data portion + * @return true if sent something + */ +static bool usart_sendbuf(){ + if(!txrdy) return false; + int rd = RB_read(&TxRB, dmatxbuf, USARTTXDMABUFSZ); + if(rd < 1) return false; // nothing to write or busy + // set up DMA + DMACHTX->CCR = DMATXCCR; + DMACHTX->CMAR = (uint32_t) dmatxbuf; + DMACHTX->CNDTR = rd; + USARTx->ICR = USART_ICR_TCCF; // clear TC flag + txrdy = false; + // activate DMA + DMACHTX->CCR = DMATXCCR | DMA_CCR_EN; + return true; +} + +int usart_send(const char *str, int len){ + if(!str || len < 1) return 0; + uint32_t t = Tms; + int sent = 0; + do{ + IWDG->KR = IWDG_REFRESH; + int put = RB_write(&TxRB, (uint8_t*)str, len); + if(put < 0) continue; // busy + else if(put == 0){ + usart_sendbuf(); // no place + t = Tms; + }else{ + len -= put; + sent += put; + str += put; + } + }while(len && (Tms - t) < USARTBLKTMOUT); // not more than `block` ms! + return sent; +} + +int usart_sendstr(const char *str){ + int l = strlen(str); + return usart_send(str, l); +} + +static void addtoreadidx(int adder){ + dma_read_idx += adder; + if(dma_read_idx >= USARTRXDMABUFSZ) dma_read_idx -= USARTRXDMABUFSZ; +} + +// return current flags +USART_flags_t usart_process(){ + static uint32_t Tlast = 0; + USART_flags_t flags = curflags; + curflags.all = 0; + if(RB_datalento(&TxRB, '\n') > 1 || Tms - Tlast >= USARTSENDTMOUT){ // send buffer as we found '\n' or each 10ms + if(usart_sendbuf()) Tlast = Tms; + } + int remained = DMACHRX->CNDTR; + int write_idx = USARTRXDMABUFSZ - remained; // next symbol to be written + int available = (write_idx - dma_read_idx); // length of data available + if(available < 0) available += USARTRXDMABUFSZ; // write to the left of read + if(available == 0) return flags; + // add next data portion to RX ring buffer + if(available >= (USARTRXDMABUFSZ / 2) || gotstring){ + // copy data in one or two chunks (wrap handling) + // check if we can write to RB `available` bytes + int rballow = RxRB.length - 1 - RB_datalen(&RxRB); + if(rballow < available){ + if(available > USARTRXDMABUFSZ - 2){ // near overfull + flags.rxovrfl = 1; + reinit_rx_dma(); + RB_clearbuf(&RxRB); + return flags; + } + if(rballow < 1) return flags; + available = rballow; // read at least as we can + } + if(dma_read_idx + available <= USARTRXDMABUFSZ){ // head before tail + int written = RB_write(&RxRB, &dmarxbuf[dma_read_idx], available); + if(written == available && dmarxbuf[dma_read_idx+available-1] == '\n') gotstring = 0; + if(written > 0) addtoreadidx(written); + }else{ // head after tail - two chunks + int first = USARTRXDMABUFSZ - dma_read_idx; + int written = RB_write(&RxRB, &dmarxbuf[dma_read_idx], first); + if(written != first){ // could write only part - just increase read index + if(written > 0) addtoreadidx(written); + }else{ + dma_read_idx = 0; + int last = available - first; + written = RB_write(&RxRB, dmarxbuf, last); + if(written == last && dmarxbuf[last-1] == '\n') gotstring = 0; + if(written > 0) addtoreadidx(written); + } + } + } + return flags; +} + +char *usart_getline(){ + static char buff[MAX_INPLEN]; + int l = RB_datalento(&RxRB, '\n'); + if(l < 1){ + l = RB_datalen(&RxRB); // Rx ringbuffer could be near overflow but without '\n' + if(l < MAX_INPLEN-1) return NULL; // allow to wait for last symbols + } + if(l > MAX_INPLEN-1){ // overflow -> read at least part of the string + l = MAX_INPLEN-1; + } + if(l != RB_read(&RxRB, (uint8_t*)buff, l)) return NULL; + buff[l] = 0; // return with '\n' at end of line (so user can detect non-finished overflowed lines) + return buff; +} + +// interrupt by '\n' +static void usart_isr(){ + if(USARTx->ISR & USART_ISR_CMF){ // got '\n' @ USARTx + gotstring = true; + } + USARTx->ICR = 0xffffffff; // clear all flags +} + +static void dmarx_isr(){ + volatile uint32_t isr = DMAx->ISR; + if(isr & DMARXEF){ // error + reinit_rx_dma(); + curflags.rxovrfl = 1; + } + DMAx->IFCR = DMARXEF; // clear all flags +} + +static void dmatx_isr(){ + volatile uint32_t isr = DMAx->ISR; + if(isr & DMATXTCF) txrdy = true; + if(isr & DMATXEF) curflags.txerr = 1; + DMAx->IFCR = DMATXTCF | DMATXEF; // clear all flags +} diff --git a/G4:G431/CORDIC/usart.h b/G4:G431/CORDIC/usart.h new file mode 100644 index 0000000..cca1a3b --- /dev/null +++ b/G4:G431/CORDIC/usart.h @@ -0,0 +1,49 @@ +/* + * This file is part of the cordic project. + * Copyright 2026 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 + +// maximal length of input string (including '\n' and terminating zero) +#define MAX_INPLEN (128) +// Rx/Tx ring buffer size +#define USARTTXBUFSZ (512) +#define USARTRXBUFSZ (512) +// Rx/Tx DMA buffer size +#define USARTTXDMABUFSZ (256) +#define USARTRXDMABUFSZ (256) + +// blocking timeout - not more than 5ms +#define USARTBLKTMOUT (5) +// send buffer each 10ms +#define USARTSENDTMOUT (10) + +typedef union{ + struct{ + uint8_t txerr : 1; // transmit error + uint8_t rxovrfl : 1; // receive buffer overflow + }; + uint8_t all; +} USART_flags_t; + +void usart_setup(uint32_t speed); +int usart_send(const char *str, int len); +char *usart_getline(); +int usart_sendstr(const char *str); +USART_flags_t usart_process(); diff --git a/G4:G431/CORDIC/version.inc b/G4:G431/CORDIC/version.inc new file mode 100644 index 0000000..4841fb4 --- /dev/null +++ b/G4:G431/CORDIC/version.inc @@ -0,0 +1,2 @@ +#define BUILD_NUMBER "6" +#define BUILD_DATE "2026-08-04" diff --git a/makefile.stm32 b/makefile.stm32 index 2880721..694d5ce 100644 --- a/makefile.stm32 +++ b/makefile.stm32 @@ -49,11 +49,10 @@ LIB_DIR := $(INC_DIR)/ld ############################################################################### # C flags -CFLAGS += -D__thumb2__=1 -MD -CFLAGS += -Wall -Wextra -Wshadow -Wdouble-promotion -CFLAGS += -fshort-enums -ffunction-sections -fdata-sections -#CFLAGS += -fno-common -fno-stack-protector -CFLAGS += $(ARCH_FLAGS) +FLAGS := -D__thumb2__=1 -MD -Wall -Wextra -Wshadow -Wdouble-promotion -fshort-enums -ffunction-sections -fdata-sections +# -fno-common -fno-stack-protector +CFLAGS += $(FLAGS) $(ARCH_FLAGS) +CPPFLAGS += $(FLAGS) $(ARCH_FLAGS) ############################################################################### # Linker flags @@ -130,6 +129,8 @@ $(VERSION_FILE): *.[ch] @sed -i "s/#define BUILDNO.*/#define BUILDNO $(NEXTVER)/" $(VERSION_FILE) @sed -i "s/#define BUILD_DATE.*/#define BUILD_DATE \"$(BUILDDATE)\"/" $(VERSION_FILE) +$(OBJDIR)/commproto.o: commproto.cpp $(VERSION_FILE) + $(OBJDIR)/proto.o: proto.c $(VERSION_FILE) $(OBJDIR)/usb_dev.o: CFLAGS := $(filter-out -flto,$(CFLAGS)) -fno-lto @@ -140,7 +141,7 @@ $(OBJDIR)/%.o: %.c $(OBJDIR)/%.o: %.cpp @echo " C++ $<" - $(CPP) -c $(CFLAGS) -std=c++11 -fno-exceptions -fno-rtti -Wl,--trace-symbol=_sbrk $(DEFS) $(INCLUDE) -o $@ -c $< + $(CPP) -c $(CPPFLAGS) -fno-exceptions -fno-rtti -Wl,--trace-symbol=_sbrk $(DEFS) $(INCLUDE) -o $@ -c $< $(BIN): $(ELF) @echo " OBJCOPY $(BIN)"