From 9ef62c0d4d5f0772cb3b0d6ff9da73003fe77a3b Mon Sep 17 00:00:00 2001 From: Edward Emelianov Date: Wed, 8 Apr 2026 23:48:23 +0300 Subject: [PATCH] started writting code --- F1:F103/AS3935-lightning/Makefile | 11 + F1:F103/AS3935-lightning/adc.c | 111 +++++ F1:F103/AS3935-lightning/adc.h | 41 ++ F1:F103/AS3935-lightning/as3935.bin | Bin 0 -> 11864 bytes F1:F103/AS3935-lightning/as3935.c | 221 ++++++++++ F1:F103/AS3935-lightning/as3935.cflags | 1 + F1:F103/AS3935-lightning/as3935.config | 6 + F1:F103/AS3935-lightning/as3935.creator | 1 + F1:F103/AS3935-lightning/as3935.creator.user | 219 ++++++++++ F1:F103/AS3935-lightning/as3935.cxxflags | 1 + F1:F103/AS3935-lightning/as3935.files | 27 ++ F1:F103/AS3935-lightning/as3935.h | 143 ++++++ F1:F103/AS3935-lightning/as3935.includes | 6 + F1:F103/AS3935-lightning/commproto.cpp | 262 +++++++++++ F1:F103/AS3935-lightning/commproto.h | 52 +++ F1:F103/AS3935-lightning/hardware.c | 70 +++ F1:F103/AS3935-lightning/hardware.h | 45 ++ F1:F103/AS3935-lightning/main.c | 84 ++++ F1:F103/AS3935-lightning/openocd.cfg | 4 + F1:F103/AS3935-lightning/ringbuffer.c | 203 +++++++++ F1:F103/AS3935-lightning/ringbuffer.h | 44 ++ F1:F103/AS3935-lightning/spi.c | 58 +++ F1:F103/AS3935-lightning/spi.h | 33 ++ F1:F103/AS3935-lightning/strfunc.c | 266 +++++++++++ F1:F103/AS3935-lightning/strfunc.h | 31 ++ F1:F103/AS3935-lightning/usb_descr.c | 210 +++++++++ F1:F103/AS3935-lightning/usb_descr.h | 68 +++ F1:F103/AS3935-lightning/usb_dev.c | 252 +++++++++++ F1:F103/AS3935-lightning/usb_dev.h | 57 +++ F1:F103/AS3935-lightning/usb_lib.c | 438 +++++++++++++++++++ F1:F103/AS3935-lightning/usb_lib.h | 352 +++++++++++++++ F1:F103/AS3935-lightning/version.inc | 2 + F1:F103/FX3U/adc.c | 2 +- 33 files changed, 3320 insertions(+), 1 deletion(-) create mode 100644 F1:F103/AS3935-lightning/Makefile create mode 100644 F1:F103/AS3935-lightning/adc.c create mode 100644 F1:F103/AS3935-lightning/adc.h create mode 100755 F1:F103/AS3935-lightning/as3935.bin create mode 100644 F1:F103/AS3935-lightning/as3935.c create mode 100644 F1:F103/AS3935-lightning/as3935.cflags create mode 100644 F1:F103/AS3935-lightning/as3935.config create mode 100644 F1:F103/AS3935-lightning/as3935.creator create mode 100644 F1:F103/AS3935-lightning/as3935.creator.user create mode 100644 F1:F103/AS3935-lightning/as3935.cxxflags create mode 100644 F1:F103/AS3935-lightning/as3935.files create mode 100644 F1:F103/AS3935-lightning/as3935.h create mode 100644 F1:F103/AS3935-lightning/as3935.includes create mode 100644 F1:F103/AS3935-lightning/commproto.cpp create mode 100644 F1:F103/AS3935-lightning/commproto.h create mode 100644 F1:F103/AS3935-lightning/hardware.c create mode 100644 F1:F103/AS3935-lightning/hardware.h create mode 100644 F1:F103/AS3935-lightning/main.c create mode 100644 F1:F103/AS3935-lightning/openocd.cfg create mode 100644 F1:F103/AS3935-lightning/ringbuffer.c create mode 100644 F1:F103/AS3935-lightning/ringbuffer.h create mode 100644 F1:F103/AS3935-lightning/spi.c create mode 100644 F1:F103/AS3935-lightning/spi.h create mode 100644 F1:F103/AS3935-lightning/strfunc.c create mode 100644 F1:F103/AS3935-lightning/strfunc.h create mode 100644 F1:F103/AS3935-lightning/usb_descr.c create mode 100644 F1:F103/AS3935-lightning/usb_descr.h create mode 100644 F1:F103/AS3935-lightning/usb_dev.c create mode 100644 F1:F103/AS3935-lightning/usb_dev.h create mode 100644 F1:F103/AS3935-lightning/usb_lib.c create mode 100644 F1:F103/AS3935-lightning/usb_lib.h create mode 100644 F1:F103/AS3935-lightning/version.inc diff --git a/F1:F103/AS3935-lightning/Makefile b/F1:F103/AS3935-lightning/Makefile new file mode 100644 index 0000000..4e506d4 --- /dev/null +++ b/F1:F103/AS3935-lightning/Makefile @@ -0,0 +1,11 @@ +BINARY := as3935 +# MCU code +MCU ?= F103x6 +# change this linking script depending on particular MCU model, +LDSCRIPT ?= stm32f103x6.ld +DEFINES := -DSTM32F10X_MD + +include ../makefile.f1 +include ../../makefile.stm32 + +$(OBJDIR)/commproto.o: commproto.cpp $(VERSION_FILE) diff --git a/F1:F103/AS3935-lightning/adc.c b/F1:F103/AS3935-lightning/adc.c new file mode 100644 index 0000000..b8682ca --- /dev/null +++ b/F1:F103/AS3935-lightning/adc.c @@ -0,0 +1,111 @@ +/* + * This file is part of the as3935 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 "adc.h" + +uint16_t ADC_array[ADC_CHANNELS*9]; + +void adc_setup(){ + uint32_t ctr = 0; + // Enable clocking + RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; + RCC->AHBENR |= RCC_AHBENR_DMA1EN; + __DSB(); + // DMA configuration + DMA1_Channel1->CPAR = (uint32_t) (&(ADC1->DR)); + DMA1_Channel1->CMAR = (uint32_t)(ADC_array); + DMA1_Channel1->CNDTR = ADC_CHANNELS * 9; + DMA1_Channel1->CCR = DMA_CCR_MINC | DMA_CCR_MSIZE_0 | DMA_CCR_PSIZE_0 + | DMA_CCR_CIRC | DMA_CCR_PL | DMA_CCR_EN; + RCC->CFGR = (RCC->CFGR & ~(RCC_CFGR_ADCPRE)) | RCC_CFGR_ADCPRE_DIV8; // ADC clock = RCC / 8 + // sampling time - 239.5 cycles for channels 0, 16 and 17 + ADC1->SMPR2 = ADC_SMPR2_SMP0; + ADC1->SMPR1 = ADC_SMPR1_SMP16 | ADC_SMPR1_SMP17; + // sequence order: 16[tsen] -> 17[vdd] + ADC1->SQR3 = (16 << 0) | (17 << 5); + ADC1->SQR1 = (ADC_CHANNELS - 1) << 20; // amount of conversions + ADC1->CR1 = ADC_CR1_SCAN; // scan mode + // DMA, continuous mode; enable vref & Tsens; enable SWSTART as trigger + ADC1->CR2 = ADC_CR2_DMA | ADC_CR2_TSVREFE | ADC_CR2_CONT | ADC_CR2_EXTSEL | ADC_CR2_EXTTRIG; + // wake up ADC + ADC1->CR2 |= ADC_CR2_ADON; + __DSB(); + // wait for Tstab - at least 1us + IWDG->KR = IWDG_REFRESH; + while(++ctr < 0xff) nop(); + // calibration + ADC1->CR2 |= ADC_CR2_RSTCAL; + ctr = 0; while((ADC1->CR2 & ADC_CR2_RSTCAL) && ++ctr < 0xfffff) IWDG->KR = IWDG_REFRESH; + ADC1->CR2 |= ADC_CR2_CAL; + ctr = 0; while((ADC1->CR2 & ADC_CR2_CAL) && ++ctr < 0xfffff) IWDG->KR = IWDG_REFRESH; + // clear possible errors and start + ADC1->SR = 0; + ADC1->CR2 |= ADC_CR2_SWSTART; +} + + +/** + * @brief getADCval - calculate median value for `nch` channel + * @param nch - number of channel + * @return + */ +uint16_t getADCval(int nch){ + int i, addr = nch; +#define PIX_SORT(a,b) { if ((a)>(b)) PIX_SWAP((a),(b)); } +#define PIX_SWAP(a,b) { register uint16_t temp=(a);(a)=(b);(b)=temp; } + uint16_t p[9]; + for(i = 0; i < 9; ++i, addr += ADC_CHANNELS){ // first we should prepare array for optmed + p[i] = ADC_array[addr]; + } + PIX_SORT(p[1], p[2]) ; PIX_SORT(p[4], p[5]) ; PIX_SORT(p[7], p[8]) ; + PIX_SORT(p[0], p[1]) ; PIX_SORT(p[3], p[4]) ; PIX_SORT(p[6], p[7]) ; + PIX_SORT(p[1], p[2]) ; PIX_SORT(p[4], p[5]) ; PIX_SORT(p[7], p[8]) ; + PIX_SORT(p[0], p[3]) ; PIX_SORT(p[5], p[8]) ; PIX_SORT(p[4], p[7]) ; + PIX_SORT(p[3], p[6]) ; PIX_SORT(p[1], p[4]) ; PIX_SORT(p[2], p[5]) ; + PIX_SORT(p[4], p[7]) ; PIX_SORT(p[4], p[2]) ; PIX_SORT(p[6], p[4]) ; + PIX_SORT(p[4], p[2]) ; + return p[4]; +#undef PIX_SORT +#undef PIX_SWAP +} + +// get voltage @input nch (1/100V) +uint32_t getADCvoltage(int nch){ + uint32_t v = getADCval(nch); + v *= getVdd(); + v /= 0xfff; // 12bit ADC + return v; +} + +// return MCU temperature (degrees of celsius * 10) +int32_t getMCUtemp(){ + // Temp = (V25 - Vsense)/Avg_Slope + 25 + // V_25 = 1.45V, Slope = 4.3e-3 + int32_t Vsense = getVdd() * getADCval(ADC_CH_TSEN); + int32_t temperature = 593920 - Vsense; // 593920 == 145*4096 + temperature /= 172; // == /(4096*10*4.3e-3), 10 - to convert from *100 to *10 + temperature += 250; + return(temperature); +} + +// return Vdd * 100 (V) +uint32_t getVdd(){ + uint32_t vdd = 120 * 4096; // 1.2V + vdd /= getADCval(ADC_CH_VDD); + return vdd; +} diff --git a/F1:F103/AS3935-lightning/adc.h b/F1:F103/AS3935-lightning/adc.h new file mode 100644 index 0000000..1213ffd --- /dev/null +++ b/F1:F103/AS3935-lightning/adc.h @@ -0,0 +1,41 @@ +/* + * This file is part of the as3935 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 + +// ADC channels in array +enum{ + ADC_CH_TSEN, // T sensor + ADC_CH_VDD, // Vdd sensor + ADC_CHANNELS +}; + +/** + * @brief ADC_array - array for ADC channels with median filtering + */ +extern uint16_t ADC_array[]; + +void adc_setup(); +int32_t getMCUtemp(); +uint32_t getVdd(); +uint16_t getADCval(int nch); +uint32_t getADCvoltage(int nch); + diff --git a/F1:F103/AS3935-lightning/as3935.bin b/F1:F103/AS3935-lightning/as3935.bin new file mode 100755 index 0000000000000000000000000000000000000000..d8a2f69c47e2929c4afa01b0c5338cf4c99423eb GIT binary patch literal 11864 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 VWdX$sn^2BYjj{!02g*K_{|z3^9OD20 literal 0 HcmV?d00001 diff --git a/F1:F103/AS3935-lightning/as3935.c b/F1:F103/AS3935-lightning/as3935.c new file mode 100644 index 0000000..dc3dba5 --- /dev/null +++ b/F1:F103/AS3935-lightning/as3935.c @@ -0,0 +1,221 @@ +/* + * This file is part of the as3935 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 "as3935.h" +#include "spi.h" + +#define MODE_READ (1 << 6) +#define MODE_WRITE (0) +#define MODE_MASK (0x3f) + +extern uint32_t Tms; + +// read one register +static int as3935_read(uint8_t reg, uint8_t *data){ + uint8_t word[2]; + word[0] = MODE_READ | (reg & MODE_MASK); + word[1] = 0; + if(0 == SPI_transmit(word, 2)) return FALSE; + if(data) *data = word[1]; + return TRUE; +} + +static int as3935_write(uint8_t reg, uint8_t data){ + uint8_t word[2]; + word[0] = MODE_WRITE | (reg & MODE_MASK); + word[1] = data; + if(0 == SPI_transmit(word, 2)) return FALSE; + return TRUE; +} + +// display on IRQ: nothing (0), TRCO (1), SRCO (2) or LCO (3) +int as3935_displco(uint8_t n){ + if(n > 3) return FALSE; + t_tun_disp t; + if(!as3935_read(TUN_DISP, &t.u8)) return FALSE; // we need to save old `tun_cap` value + t.DISP_LCO = t.DISP_SRCO = t.DISP_TRCO = 0; + switch(n){ + case 1: + t.DISP_TRCO = 1; + break; + case 2: + t.DISP_SRCO = 1; + break; + case 3: + t.DISP_LCO = 1; + break; + default: + break; + } + return as3935_write(TUN_DISP, t.u8); +} + +// tune LCO: change capasitor value +int as3935_tuncap(uint8_t n){ + if(n > 0xf) return FALSE; + t_tun_disp t; + if(!as3935_read(TUN_DISP, &t.u8)) return FALSE; + t.TUN_CAP = n; + return as3935_write(TUN_DISP, t.u8); +} + +// set gain +int as3935_gain(uint8_t n){ + if(n > 0x1f) return FALSE; + t_afe_gain g; + if(!as3935_read(AFE_GAIN, &g.u8)) return FALSE; + g.AFE_GB = n; + return as3935_write(AFE_GAIN, g.u8); +} + +// starting calibration +int as3935_calib_rco(){ + t_tun_disp t; + if(!as3935_read(TUN_DISP, &t.u8)) return FALSE; + if(!as3935_write(CALIB_RCO, DIRECT_COMMAND)) return FALSE; + t.DISP_LCO = t.DISP_TRCO = 0; + t.DISP_SRCO = 1; + if(!as3935_write(TUN_DISP, t.u8)) return FALSE; + uint32_t Tstart = Tms; + while(Tms - Tstart < 5) IWDG->KR = IWDG_REFRESH; // sleep for 5ms + t.DISP_SRCO = 0; + if(!as3935_write(TUN_DISP, t.u8)) return FALSE; + t_calib srco, trco; + if(!as3935_read(CALIB_TRCO, &trco.u8)) return FALSE; + if(!as3935_read(CALIB_SRCO, &srco.u8)) return FALSE; + if(!srco.CALIB_DONE || !trco.CALIB_DONE) return FALSE; + return TRUE; +} + +// wakeup - call this function after first run +int as3935_wakeup(){ + t_afe_gain g; + if(!as3935_read(AFE_GAIN, &g.u8)) return FALSE; + g.PWD = 0; + if(!as3935_write(AFE_GAIN, g.u8)) return FALSE; + return as3935_calib_rco(); +} + +// set amplifier gain +int as3935_set_gain(uint8_t g){ + if(g > 0x1f) return FALSE; + t_afe_gain a = {0}; + a.AFE_GB = g; + return as3935_write(AFE_GAIN, a.u8); +} + +// watchdog threshold +int as3935_wdthres(uint8_t t){ + if(t > 0x0f) return FALSE; + t_threshold thres; + if(!as3935_read(THRESHOLD, &thres.u8)) return FALSE; + thres.WDTH = t; + return as3935_write(THRESHOLD, thres.u8); +} + +// noice floor level +int as3935_nflev(uint8_t l){ + if(l > 7) return FALSE; + t_threshold thres; + if(!as3935_read(THRESHOLD, &thres.u8)) return FALSE; + thres.NF_LEV = l; + return as3935_write(THRESHOLD, thres.u8); +} + +// spike rejection +int as3935_srej(uint8_t s){ + if(s > 0xf) return FALSE; + t_lightning_reg lr; + if(!as3935_read(LIGHTNING_REG, &lr.u8)) return FALSE; + lr.SREJ = s; + return as3935_write(LIGHTNING_REG, lr.u8); +} + +// minimal lighting number +int as3935_minnumlig(uint8_t n){ + if(n > 3) return FALSE; + t_lightning_reg lr; + if(!as3935_read(LIGHTNING_REG, &lr.u8)) return FALSE; + lr.MIN_NUM_LIG = n; + return as3935_write(LIGHTNING_REG, lr.u8); +} + +// clear amount of lightnings for last 15 min +int as3935_clearstat(){ + t_lightning_reg lr; + if(!as3935_read(LIGHTNING_REG, &lr.u8)) return FALSE; + lr.CL_STAT = 1; + return as3935_write(LIGHTNING_REG, lr.u8); +} + +// get interrupt code +int as3935_intcode(uint8_t *code){ + if(!code) return FALSE; + t_int_mask_ant i; + if(!as3935_read(INT_MASK_ANT, &i.u8)) return FALSE; + *code = i.INT; + return TRUE; +} + +// should interrupt react on disturbers? +int as3935_mask_disturber(uint8_t m){ + if(m > 1) return FALSE; + t_int_mask_ant i; + if(!as3935_read(INT_MASK_ANT, &i.u8)) return FALSE; + i.MASK_DIST = m; + return as3935_write(INT_MASK_ANT, i.u8); +} + +// set Fdiv of antenna LCO +int as3935_lco_fdiv(uint8_t d){ + if(d > 3) return FALSE; + t_int_mask_ant i; + if(!as3935_read(INT_MASK_ANT, &i.u8)) return FALSE; + i.LCO_FDIV = d; + return as3935_write(INT_MASK_ANT, i.u8); +} + +// calculate last lightning energy +int as3935_energy(uint32_t *E){ + if(!E) return FALSE; + uint8_t u; uint32_t energy; + t_s_lig_mm mm; + if(!as3935_read(S_LIG_MM, &mm.u8)) return FALSE; + energy = mm.S_LIG_MM << 8; + if(!as3935_read(S_LIG_M, &u)) return FALSE; + energy |= u; + energy <<= 8; + if(!as3935_read(S_LIG_L, &u)) return FALSE; + energy |= u; + *E = energy; + return TRUE; +} + +// get distance +int as3935_distance(uint8_t *d){ + if(!d) return FALSE; + t_distance dist; + if(!as3935_read(DISTANCE, &dist.u8)) return FALSE; + *d = dist.DISTANCE; + return TRUE; +} + +// reset to factory settings +int as3935_resetdef(){ + return as3935_write(PRESET_DEFAULT, DIRECT_COMMAND); +} diff --git a/F1:F103/AS3935-lightning/as3935.cflags b/F1:F103/AS3935-lightning/as3935.cflags new file mode 100644 index 0000000..68d5165 --- /dev/null +++ b/F1:F103/AS3935-lightning/as3935.cflags @@ -0,0 +1 @@ +-std=c17 \ No newline at end of file diff --git a/F1:F103/AS3935-lightning/as3935.config b/F1:F103/AS3935-lightning/as3935.config new file mode 100644 index 0000000..1f04f90 --- /dev/null +++ b/F1:F103/AS3935-lightning/as3935.config @@ -0,0 +1,6 @@ +#define EBUG +#define STM32F1 +#define STM32F103x8 +#define STM32F10X_MD +#define BUILD_NUMBER 1 +#define BUILD_DATE "xx" diff --git a/F1:F103/AS3935-lightning/as3935.creator b/F1:F103/AS3935-lightning/as3935.creator new file mode 100644 index 0000000..e94cbbd --- /dev/null +++ b/F1:F103/AS3935-lightning/as3935.creator @@ -0,0 +1 @@ +[General] diff --git a/F1:F103/AS3935-lightning/as3935.creator.user b/F1:F103/AS3935-lightning/as3935.creator.user new file mode 100644 index 0000000..438cf16 --- /dev/null +++ b/F1:F103/AS3935-lightning/as3935.creator.user @@ -0,0 +1,219 @@ + + + + + + EnvironmentId + {7bd84e39-ca37-46d3-be9d-99ebea85bc0d} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + true + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + KOI8-R + false + 4 + false + 0 + 80 + true + true + 1 + 0 + false + true + false + 0 + true + true + 0 + 8 + true + false + 1 + true + false + true + *.md, *.MD, Makefile + false + true + true + + + + ProjectExplorer.Project.PluginSettings + + + true + false + true + true + true + true + + false + + + 0 + true + + true + true + Builtin.DefaultTidyAndClazy + 8 + true + + + + true + + 0 + + + + ProjectExplorer.Project.Target.0 + + Desktop + true + Desktop + Desktop + {65a14f9e-e008-4c1b-89df-4eaa4774b6e3} + 0 + 0 + 0 + + + + + all + + true + GenericProjectManager.GenericMakeStep + + 1 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + + clean + + true + GenericProjectManager.GenericMakeStep + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Default + GenericProjectManager.GenericBuildConfiguration + 0 + 0 + + + 0 + Deploy + Deploy + ProjectExplorer.BuildSteps.Deploy + + 1 + + false + ProjectExplorer.DefaultDeployConfiguration + + 1 + + true + true + 0 + true + + + + 2 + + false + -e cpu-cycles --call-graph dwarf,4096 -F 250 + + ProjectExplorer.CustomExecutableRunConfiguration + + false + + true + true + + 1 + + 1 + + + 0 + Deploy + Deploy + ProjectExplorer.BuildSteps.Deploy + + 1 + + false + ProjectExplorer.DefaultDeployConfiguration + + 1 + + true + true + 0 + true + + + + 2 + + false + -e cpu-cycles --call-graph dwarf,4096 -F 250 + + ProjectExplorer.CustomExecutableRunConfiguration + + false + + true + true + + 1 + + + + ProjectExplorer.Project.TargetCount + 1 + + + Version + 22 + + diff --git a/F1:F103/AS3935-lightning/as3935.cxxflags b/F1:F103/AS3935-lightning/as3935.cxxflags new file mode 100644 index 0000000..6435dfc --- /dev/null +++ b/F1:F103/AS3935-lightning/as3935.cxxflags @@ -0,0 +1 @@ +-std=c++17 \ No newline at end of file diff --git a/F1:F103/AS3935-lightning/as3935.files b/F1:F103/AS3935-lightning/as3935.files new file mode 100644 index 0000000..7c4798b --- /dev/null +++ b/F1:F103/AS3935-lightning/as3935.files @@ -0,0 +1,27 @@ +adc.c +adc.h +as3935.c +as3935.h +bissC.c +bissC.h +commproto.cpp +commproto.h +flash.c +flash.h +hardware.c +hardware.h +main.c +ringbuffer.c +ringbuffer.h +spi.c +spi.h +strfunc.c +strfunc.h +usart.c +usart.h +usb_descr.c +usb_descr.h +usb_lib.c +usb_lib.h +usb_dev.c +usb_dev.h diff --git a/F1:F103/AS3935-lightning/as3935.h b/F1:F103/AS3935-lightning/as3935.h new file mode 100644 index 0000000..00e5038 --- /dev/null +++ b/F1:F103/AS3935-lightning/as3935.h @@ -0,0 +1,143 @@ +/* + * This file is part of the as3935 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 + +enum AS3935_REGISTERS{ + AFE_GAIN = 0x00, + THRESHOLD, + LIGHTNING_REG, + INT_MASK_ANT, + S_LIG_L, + S_LIG_M, + S_LIG_MM, + DISTANCE, + TUN_DISP, + CALIB_TRCO = 0x3A, + CALIB_SRCO = 0x3B, + PRESET_DEFAULT = 0x3C, + CALIB_RCO = 0x3D +}; + +// REGISTERS + +typedef union{ + struct{ + uint8_t PWD : 1; + uint8_t AFE_GB : 5; + uint8_t RESERVED : 2; + }; uint8_t u8; +} t_afe_gain; + +typedef union{ + struct{ + uint8_t WDTH : 4; + uint8_t NF_LEV : 3; + uint8_t RESERVED : 1; + }; uint8_t u8; +} t_threshold; + +typedef union{ + struct{ + uint8_t SREJ : 4; +// minimal number of lightnings +#define NUM_LIG_1 (0) +#define NUM_LIG_5 (1) +#define NUM_LIG_9 (2) +#define NUM_LIG_16 (3) + uint8_t MIN_NUM_LIG : 2; + uint8_t CL_STAT : 1; + uint8_t RESERVED : 1; + }; uint8_t u8; +} t_lightning_reg; + +typedef union{ + struct{ +// interrupt flags +// noice level too high +#define INT_NH (1) +// disturber detected +#define INT_D (4) +// lightning interrupt +#define INT_L (8) + uint8_t INT : 4; + uint8_t RESERVED : 1; + uint8_t MASK_DIST : 1; + uint8_t LCO_FDIV : 2; + }; uint8_t u8; +} t_int_mask_ant; + +typedef union{ + struct{ + uint8_t S_LIG_MM : 5; + uint8_t RESERVED : 3; + }; uint8_t u8; +} t_s_lig_mm; + +typedef union{ + struct{ + uint8_t DISTANCE : 6; + uint8_t RESERVED : 2; + }; uint8_t u8; +} t_distance; + +typedef union{ + struct{ + uint8_t TUN_CAP : 4; + uint8_t RESERVED : 1; + uint8_t DISP_TRCO : 1; + uint8_t DISP_SRCO : 1; + uint8_t DISP_LCO : 1; + }; uint8_t u8; +} t_tun_disp; + +typedef union{ + struct{ + uint8_t RESERVED : 6; + uint8_t CALIB_NOK : 1; + uint8_t CALIB_DONE : 1; + }; uint8_t u8; +} t_calib; + +// direct command send to PRESET_DEFAULT and CALIB_RCO +#define DIRECT_COMMAND (0x96) +// distance out of range +#define DIST_OUT_OF_RANGE (0x3f) + +int as3935_open(const char *path, uint8_t id); +int as3935_getter(uint8_t reg, uint8_t *data); +int as3935_setter(uint8_t reg, uint8_t data); +int as3935_displco(uint8_t n); +int as3935_tuncap(uint8_t n); +int as3935_gain(uint8_t n); +int as3935_wakeup(); +int as3935_calib_rco(); +int as3935_set_gain(uint8_t g); +int as3935_wdthres(uint8_t t); +int as3935_nflev(uint8_t l); +int as3935_srej(uint8_t s); +int as3935_minnumlig(uint8_t n); +int as3935_clearstat(); +int as3935_intcode(uint8_t *code); +int as3935_mask_disturber(uint8_t m); +int as3935_lco_fdiv(uint8_t d); +int as3935_energy(uint32_t *E); +int as3935_distance(uint8_t *d); +int as3935_resetdef(); diff --git a/F1:F103/AS3935-lightning/as3935.includes b/F1:F103/AS3935-lightning/as3935.includes new file mode 100644 index 0000000..06d1130 --- /dev/null +++ b/F1:F103/AS3935-lightning/as3935.includes @@ -0,0 +1,6 @@ +. +../inc +../inc/Fx +../inc/cm +../inc/ld +../inc/startup diff --git a/F1:F103/AS3935-lightning/commproto.cpp b/F1:F103/AS3935-lightning/commproto.cpp new file mode 100644 index 0000000..4017970 --- /dev/null +++ b/F1:F103/AS3935-lightning/commproto.cpp @@ -0,0 +1,262 @@ +/* + * This file is part of the as3935 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 "adc.h" +#include "commproto.h" +#include "hardware.h" +#include "spi.h" +#include "strfunc.h" +} + +// sending function +static int (*SEND)(const char *str) = nullptr; + +extern volatile uint32_t Tms; + +static uint8_t curbuf[MAXSTRLEN]; + +// COMMAND(dumpconf, "dump global configuration") +// COMMAND(eraseflash, "erase full flash storage") +// COMMAND(readconf, "re-read config from flash") +// COMMAND(saveconf, "save current user configuration into flash") +// COMMAND(USART, "Read USART data or send (USART=hex)") + +// list of all commands and handlers +#define COMMAND_TABLE \ + COMMAND(help, "show this help") \ + COMMAND(mcutemp, "get MCU temperature (degC*10)") \ + COMMAND(mcureset, "reset MCU") \ + COMMAND(SPI, "transfer SPI data: SPI=data (hex)") \ + COMMAND(time, "show current time (ms)") \ + COMMAND(vdd, "get approx Vdd value (V*100)") + +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", +}; + +static 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) + + +/** + * @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 && parval){ + *parval = I32; + return true; + } + return false; +} +#endif + +static errcodes_t cmd_time(const char *cmd, char _U_ *args){ + CMDEQ(); + SEND(u2str(Tms)); SEND("\n"); + return ERR_AMOUNT; +} + +static errcodes_t cmd_mcureset(const char _U_ *cmd, char _U_ *args){ + NVIC_SystemReset(); + return ERR_CANTRUN; // never reached +} +#if 0 +static errcodes_t cmd_readconf(const char _U_ *cmd, char _U_ *args){ + flashstorage_init(); + return ERR_OK; +} + +static errcodes_t cmd_saveconf(const char _U_ *cmd, char _U_ *args){ + if(store_userconf()) return ERR_CANTRUN; + return ERR_OK; +} + +static errcodes_t cmd_eraseflash(const char _U_ *cmd, char _U_ *args){ + if(erase_storage()) return ERR_CANTRUN; + return ERR_OK; +} +#endif + + +static errcodes_t cmd_mcutemp(const char *cmd, char _U_ *args){ + CMDEQ(); + SEND(i2str(getMCUtemp())); SEND("\n"); + return ERR_AMOUNT; +} + +static errcodes_t cmd_vdd(const char *cmd, char _U_ *args){ + CMDEQ(); + SEND(u2str(getVdd())); SEND("\n"); + return ERR_AMOUNT; +} + +static errcodes_t cmd_help(const char _U_ *cmd, char _U_ *args){ + 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; +} + +/** + * @brief parse_hex_data - data parsing in case of `hex + text` input format + * @param input - input string + * @param output - output data + * @param max_len - length of `output` + * @return amount of parsed bytes or -1 in case of overflow or error + */ +static int parse_hex_data(char *input, uint8_t *output, int max_len){ + if(!input || !*input || !output || max_len < 1) return 0; + char *p = input; + int out_idx = 0; + while(*p && out_idx < max_len){ + while(*p == ' ' || *p == ',') ++p; // omit spaces and commas as delimeters + if(*p == '\0') break; // EOL + if(*p == '"'){ // TEXT (start/end) + ++p; + while(*p && *p != '"'){ + if(out_idx >= max_len) return -1; + output[out_idx++] = *p++; + } + if(*p == '"'){ + ++p; // go to next symbol after closing quotation mark + }else return -1; // no closing + }else{ // HEX number + char *start = p; + while(*p && *p != ' ' && *p != ',' && *p != '"') ++p; + char saved = *p; + *p = '\0'; // temporarily for `gethex` + uint32_t val; + const char *end = gethex(start, &val); + if(end != p || val > 0xFF){ // not a hex number or have more than 2 symbols + *p = saved; + return -1; + } + *p = saved; + output[out_idx++] = (uint8_t)val; + } + } + return out_idx; +} + +static errcodes_t cmd_SPI(const char _U_ *cmd, char *args){ + if(!args) return ERR_BADVAL; + if(!(SPI1->CR1 & SPI_CR1_SPE)) return ERR_CANTRUN; + int32_t CHno; + char *setter = splitargs(args, &CHno); + if(!setter) return ERR_BADVAL; + if(CHno < 0 || CHno > 1) return ERR_BADPAR; + int len = parse_hex_data(setter, curbuf, MAXSTRLEN); + if(len <= 0) return ERR_BADVAL; + CS(CHno); + uint8_t ret = SPI_transmit(curbuf, len); + CS_OFF(); + if(!ret) return ERR_BUSY; + CMDEQP(CHno); + if(len > 8) SEND("\n"); + hexdump(SEND, curbuf, len); + 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/F1:F103/AS3935-lightning/commproto.h b/F1:F103/AS3935-lightning/commproto.h new file mode 100644 index 0000000..6e585dd --- /dev/null +++ b/F1:F103/AS3935-lightning/commproto.h @@ -0,0 +1,52 @@ +/* + * This file is part of the as3935 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/F1:F103/AS3935-lightning " 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 + +const char *parse_cmd(int (*sendfun)(const char*), char *buf); diff --git a/F1:F103/AS3935-lightning/hardware.c b/F1:F103/AS3935-lightning/hardware.c new file mode 100644 index 0000000..54c61f3 --- /dev/null +++ b/F1:F103/AS3935-lightning/hardware.c @@ -0,0 +1,70 @@ +/* + * This file is part of the as3935 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 "adc.h" +#include "hardware.h" +#include "spi.h" + +/* + * PA0 - INT0 - PD in + * PA1 - INT1 - PD in + * PA2 - CS0 - PP out + * PA3 - CS1 - PP out + * PA5 - SPI1 SCK + * PA6 - SPI1 MISO + * PA7 - SPI1 MOSI + * PA9 - USART1 Tx + * PA10- USART1 Rx + */ + +static inline void gpio_setup(){ + // Enable clocks to the GPIO subsystems (PB for ADC), turn on AFIO clocking to disable SWD/JTAG + RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_AFIOEN | RCC_APB2ENR_SPI1EN; + // turn off SWJ/JTAG + AFIO->MAPR = AFIO_MAPR_SWJ_CFG_JTAGDISABLE; // for PA15 + // Set led as opendrain output + GPIOC->CRH |= CRH(13, CNF_ODOUTPUT|MODE_SLOW); + // PA pins + GPIOA->ODR = 0; // for pull-down + GPIOA->CRL = CRL(0, CNF_PUDINPUT|MODE_INPUT) | CRL(1, CNF_PUDINPUT|MODE_INPUT) | CRL(2, CNF_PPOUTPUT|MODE_SLOW) | + CRL(3, CNF_PPOUTPUT|MODE_SLOW) | CRL(5, CNF_AFPP|MODE_FAST) | CRL(6, CNF_FLINPUT|MODE_INPUT) | CRL(7, CNF_AFPP|MODE_FAST); + GPIOA->CRH = CRH(9, CNF_AFPP|MODE_FAST) | CRH(10, CNF_FLINPUT|MODE_INPUT) | CRH(15, CNF_PPOUTPUT|MODE_SLOW); + CS_OFF(); +} + +void hw_setup(){ + gpio_setup(); + adc_setup(); + spi_setup(); +} + +#ifndef EBUG +void iwdg_setup(){ + uint32_t tmout = 16000000; + RCC->CSR |= RCC_CSR_LSION; + while((RCC->CSR & RCC_CSR_LSIRDY) != RCC_CSR_LSIRDY){if(--tmout == 0) break;} /* (2) */ + IWDG->KR = IWDG_START; + IWDG->KR = IWDG_WRITE_ACCESS; + IWDG->PR = IWDG_PR_PR_1; + IWDG->RLR = 1250; + tmout = 16000000; + while(IWDG->SR){if(--tmout == 0) break;} + IWDG->KR = IWDG_REFRESH; +} +#endif + diff --git a/F1:F103/AS3935-lightning/hardware.h b/F1:F103/AS3935-lightning/hardware.h new file mode 100644 index 0000000..f89ba38 --- /dev/null +++ b/F1:F103/AS3935-lightning/hardware.h @@ -0,0 +1,45 @@ +/* + * This file is part of the as3935 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 + +// LED0 - PC13 (bluepill), blinking each second +#define LED0_port GPIOC +#define LED0_pin (1<<13) + +// USB pullup (not present in bluepill, should be soldered) - PA15 +#define USBPU_port GPIOA +#define USBPU_pin (1<<15) +#define USBPU_ON() pin_set(USBPU_port, USBPU_pin) +#define USBPU_OFF() pin_clear(USBPU_port, USBPU_pin) + +#define LED_blink(x) pin_toggle(x ## _port, x ## _pin) +#define LED_on(x) pin_clear(x ## _port, x ## _pin) +#define LED_off(x) pin_set(x ## _port, x ## _pin) + +// CS pins: PA2/PA3 +#define CS_OFF() do{GPIOA->BSRR = 0xc;}while(0) +#define CS(x) do{GPIOA->BSRR = (x) ? (1<<19 | 1<<2) : (1<<18 | 1<<3);}while(0) + +// INT pins: PA0/PA1 +#define CHK_INT(x) ((GPIOA->IDR & (1<. + * + * 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 "as3935.h" +#include "hardware.h" +#include "commproto.h" +#include "spi.h" +#include "strfunc.h" +#include "usb_dev.h" + +volatile uint32_t Tms = 0; +static char inbuff[RBINSZ]; + +/* Called when systick fires */ +void sys_tick_handler(void){ + ++Tms; +} + +int main(){ + uint8_t oldint[2] = {0}; // ==1 for interrupted pins + uint32_t lastT = 0; + StartHSE(); + //flashstorage_init(); + hw_setup(); + USBPU_OFF(); + SysTick_Config(72000); + USB_setup(); +#ifndef EBUG + iwdg_setup(); +#endif + //usart_setup(); + USBPU_ON(); + while(1){ + IWDG->KR = IWDG_REFRESH; // refresh watchdog + if(Tms - lastT > 499){ + LED_blink(LED0); + lastT = Tms; + } + int l = USB_receivestr(inbuff, RBINSZ); + if(l < 0) USB_sendstr("ERROR: CMD USB buffer overflow or string was too long"); + else if(l){ + const char *ans = parse_cmd(USB_sendstr, inbuff); + if(ans) USB_sendstr(ans); + } + for(int ch = 0; ch < 2; ++ch){ + if(CHK_INT(ch)){ + if(oldint[ch] == 0){ + oldint[ch] = 1; + USB_sendstr("INTERRUPT @ "); USB_putbyte('0'+ch); newline(); + uint8_t code; + CS(ch); + int result = as3935_intcode(&code); + CS_OFF(); + if(!result) USB_sendstr("Can't get code\n"); + else{ + USB_sendstr("Code: "); + switch(code){ + case INT_NH: USB_sendstr("Noice too high\n"); break; + case INT_D: USB_sendstr("Disturber detected\n"); break; + case INT_L: USB_sendstr("Lightning interrupt\n"); break; + default: USB_sendstr("Unknown\n"); break; + } + } + } + }else oldint[ch] = 0; + } + } + return 0; +} diff --git a/F1:F103/AS3935-lightning/openocd.cfg b/F1:F103/AS3935-lightning/openocd.cfg new file mode 100644 index 0000000..7533dd1 --- /dev/null +++ b/F1:F103/AS3935-lightning/openocd.cfg @@ -0,0 +1,4 @@ +set FLASH_SIZE 0x10000 + +source [find interface/stlink.cfg] +source [find target/stm32f1x.cfg] diff --git a/F1:F103/AS3935-lightning/ringbuffer.c b/F1:F103/AS3935-lightning/ringbuffer.c new file mode 100644 index 0000000..bc2af8d --- /dev/null +++ b/F1:F103/AS3935-lightning/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 "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 = 1; + int l = datalen(b); + b->busy = 0; + 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 = 1; + int ret = hasbyte(b, byte); + b->busy = 0; + 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 = 1; + int r = read(b, s, len); + b->busy = 0; + 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 = 1; + 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 = 0; + 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 = 1; + int n = lento(b, byte); + b->busy = 0; + 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 = 1; + int w = write(b, str, l); + b->busy = 0; + return w; +} + +// just delete all information in buffer `b` +int RB_clearbuf(ringbuffer *b){ + CHK(b); + if(b->busy) return -1; + b->busy = 1; + b->head = 0; + b->tail = 0; + bzero(b->data, b->length); + b->busy = 0; + return 1; +} diff --git a/F1:F103/AS3935-lightning/ringbuffer.h b/F1:F103/AS3935-lightning/ringbuffer.h new file mode 100644 index 0000000..e7312bb --- /dev/null +++ b/F1:F103/AS3935-lightning/ringbuffer.h @@ -0,0 +1,44 @@ +/* + * 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 + +#if defined STM32F0 +#include +#elif defined STM32F1 +#include +#elif defined STM32F3 +#include +#endif + +typedef struct{ + uint8_t *data; // data buffer + const int length; // its length + int head; // head index + int tail; // tail index + volatile int 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/F1:F103/AS3935-lightning/spi.c b/F1:F103/AS3935-lightning/spi.c new file mode 100644 index 0000000..cf1be4b --- /dev/null +++ b/F1:F103/AS3935-lightning/spi.c @@ -0,0 +1,58 @@ +/* + * This file is part of the as3935 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 "spi.h" +#include "hardware.h" +#include "commproto.h" + +// CR1 register default values +// Fpclk/64 +static const uint32_t SPI_CR1 = SPI_CR1_MSTR | SPI_CR1_BR_2 | SPI_CR1_BR_0 | SPI_CR1_SSM | SPI_CR1_SSI | SPI_CR1_CPHA /* | SPI_CR1_CPOL*/; + +spiStatus SPI_status = SPI_NOTREADY; + +void spi_setup(){ + // master, no slave select, BR=F/16, CPOL/CPHA - polarity. + SPI1->CR1 = SPI_CR1; + SPI_status = SPI_READY; + SPI1->CR1 |= SPI_CR1_SPE; // enable SPI +} + +volatile uint32_t wctr; +#define WAITX(x) do{wctr = 0; while((x) && (++wctr < 360000)) IWDG->KR = IWDG_REFRESH; if(wctr==360000) return -1;}while(0) + +/** + * @brief SPI_transmit - transmit data and receive new one + * @param buf - data to transmit/receive + * @param len - its length + * @return amount of transmitted data or -1 if error + */ +uint8_t SPI_transmit(uint8_t *buf, uint8_t len){ + if(!buf || !len) return 0; // bad data format + if(SPI_status != SPI_READY) return 0; // spi not ready to transmit data + for(uint8_t x = 0; x < len; ++x){ + WAITX(!(SPI1->SR & SPI_SR_TXE)); + SPI1->DR = buf[x]; + WAITX(!(SPI1->SR & SPI_SR_BSY)); + WAITX(!(SPI1->SR & SPI_SR_RXNE)); + buf[x] = SPI1->DR; + } + return len; +} + diff --git a/F1:F103/AS3935-lightning/spi.h b/F1:F103/AS3935-lightning/spi.h new file mode 100644 index 0000000..6e4a2f7 --- /dev/null +++ b/F1:F103/AS3935-lightning/spi.h @@ -0,0 +1,33 @@ +/* + * This file is part of the as3935 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 + +typedef enum{ + SPI_NOTREADY, + SPI_READY, + SPI_BUSY +} spiStatus; + +extern spiStatus SPI_status; + +void spi_setup(); +uint8_t SPI_transmit(uint8_t *buf, uint8_t len); + diff --git a/F1:F103/AS3935-lightning/strfunc.c b/F1:F103/AS3935-lightning/strfunc.c new file mode 100644 index 0000000..946aba9 --- /dev/null +++ b/F1:F103/AS3935-lightning/strfunc.c @@ -0,0 +1,266 @@ +/* + * This file is part of the encoders project. + * 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 +#include "usb_dev.h" + +/** + * @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/F1:F103/AS3935-lightning/strfunc.h b/F1:F103/AS3935-lightning/strfunc.h new file mode 100644 index 0000000..f08db63 --- /dev/null +++ b/F1:F103/AS3935-lightning/strfunc.h @@ -0,0 +1,31 @@ +/* + * This file is part of the encoders project. + * 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/F1:F103/AS3935-lightning/usb_descr.c b/F1:F103/AS3935-lightning/usb_descr.c new file mode 100644 index 0000000..f6cc65c --- /dev/null +++ b/F1:F103/AS3935-lightning/usb_descr.c @@ -0,0 +1,210 @@ +/* + * Copyright 2024 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 "usb_descr.h" + +// low/high for uint16_t +#define L16(x) (x & 0xff) +#define H16(x) (x >> 8) + +static const uint8_t USB_DeviceDescriptor[] = { + USB_DT_DEVICE_SIZE, // bLength + USB_DT_DEVICE, // bDescriptorType + L16(bcdUSB), // bcdUSB_L + H16(bcdUSB), // bcdUSB_H + USB_CLASS_MISC, // bDeviceClass + bDeviceSubClass, // bDeviceSubClass + bDeviceProtocol, // bDeviceProtocol + USB_EP0BUFSZ, // bMaxPacketSize + L16(idVendor), // idVendor_L + H16(idVendor), // idVendor_H + L16(idProduct), // idProduct_L + H16(idProduct), // idProduct_H + L16(bcdDevice_Ver), // bcdDevice_Ver_L + H16(bcdDevice_Ver), // bcdDevice_Ver_H + iMANUFACTURER_DESCR, // iManufacturer - indexes of string descriptors in array + iPRODUCT_DESCR, // iProduct + iSERIAL_DESCR, // iSerial + bNumConfigurations // bNumConfigurations +}; + +static const uint8_t USB_DeviceQualifierDescriptor[] = { + USB_DT_QUALIFIER_SIZE, //bLength + USB_DT_QUALIFIER, // bDescriptorType + L16(bcdUSB), // bcdUSB_L + H16(bcdUSB), // bcdUSB_H + USB_CLASS_PER_INTERFACE, // bDeviceClass + bDeviceSubClass, // bDeviceSubClass + bDeviceProtocol, // bDeviceProtocol + USB_EP0BUFSZ, // bMaxPacketSize0 + bNumConfigurations, // bNumConfigurations + 0 // Reserved +}; + +#define wTotalLength (USB_DT_CONFIG_SIZE + (bNumInterfaces * USB_DT_INTERFACE_SIZE) + (bTotNumEndpoints * USB_DT_ENDPOINT_SIZE) + (bNumCsInterfaces * USB_DT_CS_INTERFACE_SIZE) - 1) + +static const uint8_t USB_ConfigDescriptor[] = { + // Configuration Descriptor + USB_DT_CONFIG_SIZE, // bLength: Configuration Descriptor size + USB_DT_CONFIG, // bDescriptorType: Configuration + L16(wTotalLength), // wTotalLength.L :no of returned bytes + H16(wTotalLength), // wTotalLength.H + bNumInterfaces, // bNumInterfaces + 1, // bConfigurationValue: Current configuration value + 0, // iConfiguration: Index of string descriptor describing the configuration or 0 + BusPowered, // bmAttributes - Bus powered + 50, // MaxPower in 2mA units + //--------------------------------------------------------------------------- + // Virtual command Interface Descriptor + USB_DT_INTERFACE_SIZE, // bLength: Interface Descriptor size + USB_DT_INTERFACE, // bDescriptorType: Interface + 0, // bInterfaceNumber: Number of Interface + 0, // bAlternateSetting: Alternate setting + 1, // bNumEndpoints: one for this + USB_CLASS_COMM, // bInterfaceClass + 2, // bInterfaceSubClass: ACM + 1, // bInterfaceProtocol: Common AT commands + iINTERFACE_DESCR1, // iInterface + // ---- CS Interfaces + USB_DT_CS_INTERFACE_SIZE, // bLength + USB_DT_CS_INTERFACE, // bDescriptorType: CS_INTERFACE + 0, // bDescriptorSubtype: Header Func Desc + 0x10, // bcdCDC: spec release number + 1, // bDataInterface + USB_DT_CS_INTERFACE_SIZE, // bLength + USB_DT_CS_INTERFACE, // bDescriptorType: CS_INTERFACE + 1, // bDescriptorSubtype: Call Management Func Desc + 0, // bmCapabilities: D0+D1 + 1, // bDataInterface + USB_DT_CS_INTERFACE_SIZE-1, // bLength + USB_DT_CS_INTERFACE, // bDescriptorType: CS_INTERFACE + 2, // bDescriptorSubtype: Abstract Control Management desc + 2, // bmCapabilities + USB_DT_CS_INTERFACE_SIZE, // bLength + USB_DT_CS_INTERFACE, // bDescriptorType: CS_INTERFACE + 6, // bDescriptorSubtype: Union func desc + 0, // bMasterInterface: Communication class interface + 1, // bSlaveInterface0: Data Class Interface + // Virtual endpoint 1 Descriptor + USB_DT_ENDPOINT_SIZE, // bLength: Endpoint Descriptor size + USB_DT_ENDPOINT, // bDescriptorType: Endpoint + 0x8A, // bEndpointAddress IN10 + USB_BM_ATTR_INTERRUPT, // bmAttributes: Interrupt + L16(USB_EP1BUFSZ), // wMaxPacketSize LO + H16(USB_EP1BUFSZ), // wMaxPacketSize HI + 0x10, // bInterval: 16ms + //--------------------------------------------------------------------------- + // Data interface + USB_DT_INTERFACE_SIZE, // bLength: Interface Descriptor size + USB_DT_INTERFACE, // bDescriptorType: Interface + 1, // bInterfaceNumber: Number of Interface + 0, // bAlternateSetting: Alternate setting + 2, // bNumEndpoints: in and out + USB_CLASS_DATA, // bInterfaceClass + 2, // bInterfaceSubClass: ACM + 0, // bInterfaceProtocol + 0, // iInterface + //Endpoint IN1 Descriptor + USB_DT_ENDPOINT_SIZE, // bLength: Endpoint Descriptor size + USB_DT_ENDPOINT, // bDescriptorType: Endpoint + 0x81, // bEndpointAddress: IN1 + USB_BM_ATTR_BULK, // bmAttributes: Bulk + L16(USB_TXBUFSZ), // wMaxPacketSize LO + H16(USB_TXBUFSZ), // wMaxPacketSize HI + 0, // bInterval: ignore for Bulk transfer + // Endpoint OUT1 Descriptor + USB_DT_ENDPOINT_SIZE, // bLength: Endpoint Descriptor size + USB_DT_ENDPOINT, // bDescriptorType: Endpoint + 0x01, // bEndpointAddress: OUT1 + USB_BM_ATTR_BULK, // bmAttributes: Bulk + L16(USB_RXBUFSZ), // wMaxPacketSize LO + H16(USB_RXBUFSZ), // wMaxPacketSize HI + 0, // bInterval: ignore for Bulk transfer + +}; + +//const uint8_t HID_ReportDescriptor[]; + +_USB_LANG_ID_(LD, LANG_US); +_USB_STRING_(SD, u"0.0.1"); +_USB_STRING_(MD, u"eddy@sao.ru"); +_USB_STRING_(PD, u"AS3935 lightning detector"); +_USB_STRING_(ID, u"lightning_det"); + +static const void* const StringDescriptor[iDESCR_AMOUNT] = { + [iLANGUAGE_DESCR] = &LD, + [iMANUFACTURER_DESCR] = &MD, + [iPRODUCT_DESCR] = &PD, + [iSERIAL_DESCR] = &SD, + [iINTERFACE_DESCR1] = &ID +}; + +static void wr0(const uint8_t *buf, uint16_t size, uint16_t askedsize){ + if(askedsize < size) size = askedsize; // shortened request + if(size < USB_EP0BUFSZ){ + EP_WriteIRQ(0, buf, size); + return; + } + while(size){ + uint16_t l = size; + if(l > USB_EP0BUFSZ) l = USB_EP0BUFSZ; + EP_WriteIRQ(0, buf, l); + buf += l; + size -= l; + uint8_t needzlp = (l == USB_EP0BUFSZ) ? 1 : 0; + if(size || needzlp){ // send last data buffer + uint16_t epstatus = KEEP_DTOG(USB->EPnR[0]); + // keep DTOGs, clear CTR_RX,TX, set TX VALID, leave stat_Rx + USB->EPnR[0] = (epstatus & ~(USB_EPnR_CTR_RX|USB_EPnR_CTR_TX|USB_EPnR_STAT_RX)) + ^ USB_EPnR_STAT_TX; + uint32_t ctr = 1000000; + while(--ctr && (USB->ISTR & USB_ISTR_CTR) == 0){IWDG->KR = IWDG_REFRESH;}; + if((USB->ISTR & USB_ISTR_CTR) == 0){ + return; + } + if(needzlp) EP_WriteIRQ(0, NULL, 0); + } + } +} + +void get_descriptor(config_pack_t *pack){ + uint8_t descrtype = pack->wValue >> 8, + descridx = pack->wValue & 0xff; + switch(descrtype){ + case DEVICE_DESCRIPTOR: + wr0(USB_DeviceDescriptor, sizeof(USB_DeviceDescriptor), pack->wLength); + break; + case CONFIGURATION_DESCRIPTOR: + wr0(USB_ConfigDescriptor, sizeof(USB_ConfigDescriptor), pack->wLength); + break; + case STRING_DESCRIPTOR: + if(descridx < iDESCR_AMOUNT){ + wr0((const uint8_t *)StringDescriptor[descridx], *((uint8_t*)StringDescriptor[descridx]), pack->wLength); + }else{ + EP_WriteIRQ(0, NULL, 0); + } + break; + case DEVICE_QUALIFIER_DESCRIPTOR: + wr0(USB_DeviceQualifierDescriptor, sizeof(USB_DeviceQualifierDescriptor), pack->wLength); + break; + /* case HID_REPORT_DESCRIPTOR: + wr0(HID_ReportDescriptor, sizeof(HID_ReportDescriptor), pack->wLength); + break;*/ + default: + break; + } +} diff --git a/F1:F103/AS3935-lightning/usb_descr.h b/F1:F103/AS3935-lightning/usb_descr.h new file mode 100644 index 0000000..0874763 --- /dev/null +++ b/F1:F103/AS3935-lightning/usb_descr.h @@ -0,0 +1,68 @@ +/* + * Copyright 2024 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 "usb_lib.h" + +// definition of parts common for USB_DeviceDescriptor & USB_DeviceQualifierDescriptor +// bcdUSB: 1.10 +#define bcdUSB 0x0110 +// Class - Misc (EF), subclass - common (2), protocol - interface association descr (1) +#define bDeviceSubClass 0x02 +#define bDeviceProtocol 0x01 +#define idVendor 0x0483 +#define idProduct 0x5740 +#define bcdDevice_Ver 0x0200 +#define bNumConfigurations 1 + +// amount of interfaces and endpoints (except 0) used +#define bNumInterfaces 2 +#define bTotNumEndpoints 3 +#define bNumCsInterfaces 4 + +// powered +#define BusPowered (1<<7) +#define SelfPowered (1<<6) +#define RemoteWakeup (1<<5) + +// buffer sizes +// for USB FS EP0 buffers are from 8 to 64 bytes long +#define USB_EP0BUFSZ 64 +#define USB_EP1BUFSZ 10 +// Rx/Tx EPs +#define USB_RXBUFSZ 64 +#define USB_TXBUFSZ 64 + +// string descriptors +enum{ + iLANGUAGE_DESCR, + iMANUFACTURER_DESCR, + iPRODUCT_DESCR, + iSERIAL_DESCR, + iINTERFACE_DESCR1, + /* iINTERFACE_DESCR2, + iINTERFACE_DESCR3, + iINTERFACE_DESCR4, + iINTERFACE_DESCR5, + iINTERFACE_DESCR6, + iINTERFACE_DESCR7,*/ + iDESCR_AMOUNT +}; + +void get_descriptor(config_pack_t *pack); diff --git a/F1:F103/AS3935-lightning/usb_dev.c b/F1:F103/AS3935-lightning/usb_dev.c new file mode 100644 index 0000000..177c882 --- /dev/null +++ b/F1:F103/AS3935-lightning/usb_dev.c @@ -0,0 +1,252 @@ +/* + * Copyright 2024 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 "ringbuffer.h" +#include "usb_descr.h" +#include "usb_dev.h" + +// Class-Specific Control Requests +#define SEND_ENCAPSULATED_COMMAND 0x00 // unused +#define GET_ENCAPSULATED_RESPONSE 0x01 // unused +#define SET_COMM_FEATURE 0x02 // unused +#define GET_COMM_FEATURE 0x03 // unused +#define CLEAR_COMM_FEATURE 0x04 // unused +#define SET_LINE_CODING 0x20 +#define GET_LINE_CODING 0x21 +#define SET_CONTROL_LINE_STATE 0x22 +#define SEND_BREAK 0x23 + +// control line states +#define CONTROL_DTR 0x01 +#define CONTROL_RTS 0x02 + +// inbuf overflow when receiving +static volatile uint8_t bufovrfl = 0; + +// receive buffer: hold data until chkin() call +static uint8_t volatile rcvbuf[USB_RXBUFSZ] __attribute__((aligned(4))); +static uint8_t volatile rcvbuflen = 0; +// line coding +usb_LineCoding lineCoding = {115200, 0, 0, 8}; +// CDC configured and ready to use +volatile uint8_t CDCready = 0; + +// ring buffers for incoming and outgoing data +static uint8_t obuf[RBOUTSZ], ibuf[RBINSZ]; +static volatile ringbuffer rbout = {.data = obuf, .length = RBOUTSZ, .head = 0, .tail = 0}; +static volatile ringbuffer rbin = {.data = ibuf, .length = RBINSZ, .head = 0, .tail = 0}; +// last send data size +static volatile int lastdsz = 0; + +static void chkin(){ + if(bufovrfl) return; // allow user to know that previous buffer was overflowed and cleared + if(!rcvbuflen) return; + int w = RB_write((ringbuffer*)&rbin, (uint8_t*)rcvbuf, rcvbuflen); + if(w < 0){ + return; + } + if(w != rcvbuflen) bufovrfl = 1; + rcvbuflen = 0; + uint16_t status = KEEP_DTOG(USB->EPnR[1]); // don't change DTOG + USB->EPnR[1] = (status & ~(USB_EPnR_STAT_TX|USB_EPnR_CTR_RX)) ^ USB_EPnR_STAT_RX; // prepare to get next data portion +} + +// called from transmit EP to send next data portion or by user - when new transmission starts +static void send_next(){ + uint8_t usbbuff[USB_TXBUFSZ] __attribute__((aligned(4))); + int buflen = RB_read((ringbuffer*)&rbout, (uint8_t*)usbbuff, USB_TXBUFSZ); + if(buflen == 0){ + if(lastdsz == USB_TXBUFSZ) EP_Write(1, NULL, 0); // send ZLP after USB_TXBUFSZ bits packet when nothing more to send + lastdsz = 0; + return; + }else if(buflen < 0){ + lastdsz = 0; + return; + } + EP_Write(1, (uint8_t*)usbbuff, buflen); + lastdsz = buflen; +} + +// data IN/OUT handler +static void rxtx_handler(){ + uint16_t epstatus = KEEP_DTOG(USB->EPnR[1]); + if(RX_FLAG(epstatus)){ // receive data + if(rcvbuflen){ + bufovrfl = 1; // lost last data + rcvbuflen = 0; + } + rcvbuflen = EP_Read(1, (uint8_t*)rcvbuf); + USB->EPnR[1] = epstatus & ~(USB_EPnR_CTR_RX | USB_EPnR_STAT_RX | USB_EPnR_STAT_TX); // keep RX in STALL state until read data + chkin(); // try to write current data into RXbuf if it's not busy + }else{ // tx successfull + USB->EPnR[1] = (epstatus & ~(USB_EPnR_CTR_TX | USB_EPnR_STAT_TX)) ^ USB_EPnR_STAT_RX; + send_next(); + } +} + +// weak handlers: change them somewhere else if you want to setup USART +// SET_LINE_CODING +void linecoding_handler(usb_LineCoding *lc){ + lineCoding = *lc; +} + +// SET_CONTROL_LINE_STATE +void clstate_handler(uint16_t val){ + CDCready = val; // CONTROL_DTR | CONTROL_RTS -> interface connected; 0 -> disconnected +} + +// SEND_BREAK +void break_handler(){ + CDCready = 0; +} + +// USB is configured: setup endpoints +void set_configuration(){ + EP_Init(1, EP_TYPE_BULK, USB_TXBUFSZ, USB_RXBUFSZ, rxtx_handler); // IN1 and OUT1 +} + +// PL2303 CLASS request +void usb_class_request(config_pack_t *req, uint8_t *data, uint16_t datalen){ + uint8_t recipient = REQUEST_RECIPIENT(req->bmRequestType); + uint8_t dev2host = (req->bmRequestType & 0x80) ? 1 : 0; + switch(recipient){ + case REQ_RECIPIENT_INTERFACE: + switch(req->bRequest){ + case SET_LINE_CODING: + if(!data || !datalen) break; // wait for data + if(datalen == sizeof(usb_LineCoding)) + linecoding_handler((usb_LineCoding*)data); + break; + case GET_LINE_CODING: + EP_WriteIRQ(0, (uint8_t*)&lineCoding, sizeof(lineCoding)); + break; + case SET_CONTROL_LINE_STATE: + clstate_handler(req->wValue); + break; + case SEND_BREAK: + break_handler(); + break; + default: + break; + } + break; + default: + if(dev2host) EP_WriteIRQ(0, NULL, 0); + } + if(!dev2host) EP_WriteIRQ(0, NULL, 0); +} + +// blocking send full content of ring buffer +int USB_sendall(){ + while(lastdsz > 0){ + if(!CDCready) return FALSE; + } + return TRUE; +} + +// put `buf` into queue to send +int USB_send(const uint8_t *buf, int len){ + if(!buf || !CDCready || !len) return FALSE; + while(len){ + IWDG->KR = IWDG_REFRESH; + int l = RB_datalen((ringbuffer*)&rbout); + if(l < 0) continue; + int portion = rbout.length - 1 - l; + if(portion < 1){ + if(lastdsz == 0) send_next(); + continue; + } + if(portion > len) portion = len; + int a = RB_write((ringbuffer*)&rbout, buf, portion); + if(a > 0){ + len -= a; + buf += a; + } else if (a < 0) continue; // do nothing if buffer is in reading state + if(lastdsz == 0) send_next(); // need to run manually - all data sent, so no IRQ on IN + } + return TRUE; +} + +int USB_putbyte(uint8_t byte){ + if(!CDCready) return FALSE; + int l = 0; + while((l = RB_write((ringbuffer*)&rbout, &byte, 1)) != 1){ + if(l < 0) continue; + } + if(lastdsz == 0) send_next(); // need to run manually - all data sent, so no IRQ on IN + return TRUE; +} + +int USB_sendstr(const char *string){ + if(!string || !CDCready) return FALSE; + int len = 0; + const char *b = string; + while(*b++) ++len; + if(!len) return FALSE; + return USB_send((const uint8_t*)string, len); +} + +/** + * @brief USB_receive - get binary data from receiving ring-buffer + * @param buf (i) - buffer for received data + * @param len - length of `buf` + * @return amount of received bytes (negative, if overfull happened) + */ +int USB_receive(uint8_t *buf, int len){ + chkin(); + if(bufovrfl){ + while(1 != RB_clearbuf((ringbuffer*)&rbin)); + bufovrfl = 0; + return -1; + } + int sz = RB_read((ringbuffer*)&rbin, buf, len); + if(sz < 0) return 0; // buffer in writting state + return sz; +} + +/** + * @brief USB_receivestr - get string up to '\n' and replace '\n' with 0 + * @param buf - receiving buffer + * @param len - its length + * @return strlen or negative value indicating overflow + */ +int USB_receivestr(char *buf, int len){ + chkin(); + if(bufovrfl){ + while(1 != RB_clearbuf((ringbuffer*)&rbin)); + bufovrfl = 0; + return -1; + } + int l = RB_datalento((ringbuffer*)&rbin, '\n'); + if(l > len){ // can't read: line too long -> clear it + RB_readto((ringbuffer*)&rbin, '\n', NULL, 0); + return -1; + }else if(l < 1){ // nothing or no '\n' ? + if(rbin.length == RB_datalen((ringbuffer*)&rbin)){ // buffer is full but no '\n' found + while(1 != RB_clearbuf((ringbuffer*)&rbin)); + return -1; + } + return 0; + } + l = RB_readto((ringbuffer*)&rbin, '\n', (uint8_t*)buf, len); + if(l == 0) return 0; + buf[l-1] = 0; // replace '\n' with strend + return l; +} + diff --git a/F1:F103/AS3935-lightning/usb_dev.h b/F1:F103/AS3935-lightning/usb_dev.h new file mode 100644 index 0000000..70ee1bc --- /dev/null +++ b/F1:F103/AS3935-lightning/usb_dev.h @@ -0,0 +1,57 @@ +/* + * Copyright 2024 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 "usb_lib.h" + +typedef struct { + uint32_t dwDTERate; + uint8_t bCharFormat; +#define USB_CDC_1_STOP_BITS 0 +#define USB_CDC_1_5_STOP_BITS 1 +#define USB_CDC_2_STOP_BITS 2 + uint8_t bParityType; +#define USB_CDC_NO_PARITY 0 +#define USB_CDC_ODD_PARITY 1 +#define USB_CDC_EVEN_PARITY 2 +#define USB_CDC_MARK_PARITY 3 +#define USB_CDC_SPACE_PARITY 4 + uint8_t bDataBits; +} __attribute__ ((packed)) usb_LineCoding; + +extern usb_LineCoding lineCoding; +extern volatile uint8_t CDCready; + +void break_handler(); +void clstate_handler(uint16_t val); +void linecoding_handler(usb_LineCoding *lc); + + +// sizes of ringbuffers for outgoing and incoming data +#define RBOUTSZ (1024) +#define RBINSZ (1024) + +#define newline() USB_putbyte('\n') +#define USND(s) do{USB_sendstr(s); USB_putbyte('\n');}while(0) + +int USB_sendall(); +int USB_send(const uint8_t *buf, int len); +int USB_putbyte(uint8_t byte); +int USB_sendstr(const char *string); +int USB_receive(uint8_t *buf, int len); +int USB_receivestr(char *buf, int len); diff --git a/F1:F103/AS3935-lightning/usb_lib.c b/F1:F103/AS3935-lightning/usb_lib.c new file mode 100644 index 0000000..3e7189e --- /dev/null +++ b/F1:F103/AS3935-lightning/usb_lib.c @@ -0,0 +1,438 @@ +/* + * Copyright 2024 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 "usb_lib.h" +#include "usb_descr.h" +#include "usb_dev.h" + +static ep_t endpoints[STM32ENDPOINTS]; + +static uint16_t USB_Addr = 0; +static uint8_t setupdatabuf[EP0DATABUF_SIZE] __attribute__((aligned(4))); +static config_pack_t *setup_packet = (config_pack_t*) setupdatabuf; +volatile uint8_t usbON = 0; // device is configured and active + +static uint16_t configuration = 0; // reply for GET_CONFIGURATION (==1 if configured) +static inline void std_d2h_req(){ + uint16_t st = 0; + switch(setup_packet->bRequest){ + case GET_DESCRIPTOR: + get_descriptor(setup_packet); + break; + case GET_STATUS: + EP_WriteIRQ(0, (uint8_t *)&st, 2); // send status: Bus Powered + break; + case GET_CONFIGURATION: + EP_WriteIRQ(0, (uint8_t*)&configuration, 1); + break; + default: + EP_WriteIRQ(0, NULL, 0); + break; + } +} + +static inline void std_h2d_req(){ + switch(setup_packet->bRequest){ + case SET_ADDRESS: + // new address will be assigned later - after acknowlegement or request to host + USB_Addr = setup_packet->wValue; + break; + case SET_CONFIGURATION: + // Now device configured + configuration = setup_packet->wValue; + set_configuration(); + usbON = 1; + break; + default: + break; + } +} + +void WEAK usb_standard_request(){ + uint8_t recipient = REQUEST_RECIPIENT(setup_packet->bmRequestType); + uint8_t dev2host = (setup_packet->bmRequestType & 0x80) ? 1 : 0; + switch(recipient){ + case REQ_RECIPIENT_DEVICE: + if(dev2host){ + std_d2h_req(); + }else{ + std_h2d_req(); + } + break; + case REQ_RECIPIENT_INTERFACE: + if(dev2host && setup_packet->bRequest == GET_DESCRIPTOR){ + get_descriptor(setup_packet); + } + break; + case REQ_RECIPIENT_ENDPOINT: + if(setup_packet->bRequest == CLEAR_FEATURE){ + }else{ /* wrong */ } + break; + default: + break; + } + if(!dev2host) EP_WriteIRQ(0, NULL, 0); +} + +void WEAK usb_class_request(config_pack_t *req, uint8_t _U_ *data, uint16_t _U_ datalen){ + switch(req->bRequest){ + case GET_INTERFACE: + break; + case SET_CONFIGURATION: // set featuring by req->wValue + break; + default: + break; + } + if(0 == (setup_packet->bmRequestType & 0x80)) // host2dev + EP_WriteIRQ(0, NULL, 0); +} + +void WEAK usb_vendor_request(config_pack_t _U_ *packet, uint8_t _U_ *data, uint16_t _U_ datalen){ + if(0 == (setup_packet->bmRequestType & 0x80)) // host2dev + EP_WriteIRQ(0, NULL, 0); +} + +/* +bmRequestType: 76543210 +7 direction: 0 - host->device, 1 - device->host +65 type: 0 - standard, 1 - class, 2 - vendor +4..0 getter: 0 - device, 1 - interface, 2 - endpoint, 3 - other +*/ +/** + * Endpoint0 (control) handler + */ +static void EP0_Handler(){ + uint8_t ep0dbuflen = 0; + uint8_t ep0databuf[EP0DATABUF_SIZE] __attribute__((aligned(4))); + uint16_t epstatus = KEEP_DTOG(USB->EPnR[0]); // EP0R on input -> return this value after modifications + int rxflag = RX_FLAG(epstatus); + // check direction + if(USB->ISTR & USB_ISTR_DIR){ // OUT interrupt - receive data, CTR_RX==1 (if CTR_TX == 1 - two pending transactions: receive following by transmit) + if(epstatus & USB_EPnR_SETUP){ // setup packet -> copy data to conf_pack + EP_Read(0, setupdatabuf); + // interrupt handler will be called later + }else if(epstatus & USB_EPnR_CTR_RX){ // data packet -> push received data to ep0databuf + //if(endpoints[0].rx_cnt){ } + ep0dbuflen = EP_Read(0, ep0databuf); + } + } + if(rxflag){ + uint8_t reqtype = REQUEST_TYPE(setup_packet->bmRequestType); + switch(reqtype){ + case REQ_TYPE_STANDARD: + if(SETUP_FLAG(epstatus)){ + usb_standard_request(); + }else{ } + break; + case REQ_TYPE_CLASS: + usb_class_request(setup_packet, ep0databuf, ep0dbuflen); + break; + case REQ_TYPE_VENDOR: + usb_vendor_request(setup_packet, ep0databuf, ep0dbuflen); + break; + default: + EP_WriteIRQ(0, NULL, 0); + break; + } + } + if(TX_FLAG(epstatus)){ + // now we can change address after enumeration + if ((USB->DADDR & USB_DADDR_ADD) != USB_Addr){ + USB->DADDR = USB_DADDR_EF | USB_Addr; + usbON = 0; + } + } + //epstatus = KEEP_DTOG(USB->EPnR[0]); + if(rxflag) epstatus ^= USB_EPnR_STAT_TX; // start ZLP or data transmission + else epstatus &= ~USB_EPnR_STAT_TX; // or leave unchanged + // keep DTOGs, clear CTR_RX,TX, set RX VALID + USB->EPnR[0] = (epstatus & ~(USB_EPnR_CTR_RX|USB_EPnR_CTR_TX)) ^ USB_EPnR_STAT_RX; +} + +/** + * Write data to EP buffer (called from IRQ handler) + * @param number - EP number + * @param *buf - array with data + * @param size - its size + */ +void EP_WriteIRQ(uint8_t number, const uint8_t *buf, uint16_t size){ + if(size > endpoints[number].txbufsz) size = endpoints[number].txbufsz; +#ifndef USB32 + uint16_t N2 = (size + 1) >> 1; + // the buffer is 16-bit, so we should copy data as it would be uint16_t + uint16_t *buf16 = (uint16_t *)buf; +#else + int N4 = (size + 3) >> 2; + uint32_t *buf32 = (uint32_t *)buf; +#endif +#if defined USB1_16 + // very bad: what if `size` is odd? + uint32_t *out = (uint32_t *)endpoints[number].tx_buf; + for(int i = 0; i < N2; ++i, ++out){ + *out = buf16[i]; + } +#elif defined USB2_16 + // use memcpy instead? + for(int i = 0; i < N2; ++i){ + endpoints[number].tx_buf[i] = buf16[i]; + } +#elif defined USB32 + for(int i = 0; i < N4; ++i) endpoints[number].tx_buf[i] = buf32[i]; +#else +#error "Define USB1_16 / USB2_16 / USB32" +#endif +#ifndef USB32 + USB_BTABLE->EP[number].USB_COUNT_TX = size; +#else + USB_BTABLE->EP[number].USB_ADDR_COUNT_TX = (USB_BTABLE->EP[number].USB_ADDR_COUNT_TX & 0xffff) | (size << 16); +#endif +} + +/** + * Write data to EP buffer (called outside IRQ handler) + * @param number - EP number + * @param *buf - array with data + * @param size - its size + */ +void EP_Write(uint8_t number, const uint8_t *buf, uint16_t size){ + EP_WriteIRQ(number, buf, size); + uint16_t epstatus = KEEP_DTOG(USB->EPnR[number]); + // keep DTOGs and RX stat, clear CTR_TX & set TX VALID to start transmission + USB->EPnR[number] = (epstatus & ~(USB_EPnR_CTR_TX | USB_EPnR_STAT_RX)) ^ USB_EPnR_STAT_TX; +} + +/* + * Copy data from EP buffer into user buffer area + * @param *buf - user array for data + * @return amount of data read + */ +int EP_Read(uint8_t number, uint8_t *buf){ + int sz = endpoints[number].rx_cnt; + if(!sz) return 0; + endpoints[number].rx_cnt = 0; +#if defined USB1_16 + int n = (sz + 1) >> 1; + uint32_t *in = (uint32_t*)endpoints[number].rx_buf; + uint16_t *out = (uint16_t*)buf; + for(int i = 0; i < n; ++i, ++in) + out[i] = *(uint16_t*)in; +#elif defined USB2_16 + // use memcpy instead? + for(int i = 0; i < sz; ++i) + buf[i] = endpoints[number].rx_buf[i]; +#elif defined USB32 + uint32_t *u32buf = (uint32_t*) buf; + int N4 = (sz + 3) >> 2; + for(int i = 0; i < N4; ++i) u32buf[i] = endpoints[number].rx_buf[i]; +#else +#error "Define USB1_16 / USB2_16 / USB32" +#endif + return sz; +} + + +static uint16_t lastaddr = LASTADDR_DEFAULT; +/** + * Endpoint initialisation + * @param number - EP num (0...7) + * @param type - EP type (EP_TYPE_BULK, EP_TYPE_CONTROL, EP_TYPE_ISO, EP_TYPE_INTERRUPT) + * @param txsz - transmission buffer size @ USB/CAN buffer + * @param rxsz - reception buffer size @ USB/CAN buffer + * @param uint16_t (*func)(ep_t *ep) - EP handler function + * @return 0 if all OK + */ +int EP_Init(uint8_t number, uint8_t type, uint16_t txsz, uint16_t rxsz, void (*func)(ep_t ep)){ +#ifdef STM32G0 + // in STM32G0 all buffers should be aligned by 32 bits + if(txsz & 3) txsz = ((txsz >> 2)+1) << 2; + if(rxsz & 3) rxsz = ((rxsz >> 2)+1) << 2; +#endif + if(number >= STM32ENDPOINTS) return 4; // out of configured amount + if(txsz > USB_BTABLE_SIZE/ACCESSZ || rxsz > USB_BTABLE_SIZE/ACCESSZ) return 1; // buffer too large + if(lastaddr + txsz + rxsz >= USB_BTABLE_SIZE/ACCESSZ) return 2; // out of btable + USB->EPnR[number] = (type << 9) | (number & USB_EPnR_EA); + USB->EPnR[number] ^= USB_EPnR_STAT_RX | USB_EPnR_STAT_TX; + if(rxsz & 1) return 3; // wrong rx buffer size + uint16_t countrx = 0; + if(rxsz < 64) countrx = rxsz / 2; + else{ + if(rxsz & 0x1f) return 3; // should be multiple of 32 + countrx = 31 + rxsz / 32; + } +#ifdef USB32 + endpoints[number].tx_buf = (uint32_t *)(USB_BTABLE_BASE + lastaddr * ACCESSZ); +#else + endpoints[number].tx_buf = (uint16_t *)(USB_BTABLE_BASE + lastaddr * ACCESSZ); +#endif + endpoints[number].txbufsz = txsz; +#ifdef USB32 + USB_BTABLE->EP[number].USB_ADDR_COUNT_TX = (uint32_t) lastaddr; +#else + USB_BTABLE->EP[number].USB_ADDR_TX = lastaddr; + USB_BTABLE->EP[number].USB_COUNT_TX = 0; +#endif + lastaddr += txsz; +#ifdef USB32 + endpoints[number].rx_buf = (uint32_t *)(USB_BTABLE_BASE + lastaddr * ACCESSZ); + USB_BTABLE->EP[number].USB_ADDR_COUNT_RX = (uint32_t) lastaddr | countrx << 26; +#else + endpoints[number].rx_buf = (uint8_t *)(USB_BTABLE_BASE + lastaddr * ACCESSZ); + USB_BTABLE->EP[number].USB_ADDR_RX = lastaddr; + USB_BTABLE->EP[number].USB_COUNT_RX = countrx << 10; +#endif + lastaddr += rxsz; + endpoints[number].func = func; + return 0; +} + +// standard IRQ handler +void USB_IRQ(){ + uint32_t CNTR = USB->CNTR; + USB->CNTR = 0; + uint32_t istr = USB->ISTR; + if(istr & USB_ISTR_RESET){ + usbON = 0; + // Reinit registers + CNTR = USB_CNTR_RESETM | USB_CNTR_CTRM | USB_CNTR_SUSPM; + // Endpoint 0 - CONTROL + // ON USB LS size of EP0 may be 8 bytes, but on FS it should be 64 bytes! + lastaddr = LASTADDR_DEFAULT; + // clear address, leave only enable bit + USB->DADDR = USB_DADDR_EF; + //USB->ISTR = ~(USB_ISTR_RESET); // clear all flags + if(EP_Init(0, EP_TYPE_CONTROL, USB_EP0BUFSZ, USB_EP0BUFSZ, EP0_Handler)){ + return; + }; + } + if(istr & USB_ISTR_CTR){ + // EP number + uint8_t n = istr & USB_ISTR_EPID; + if (istr & USB_ISTR_DIR){ // OUT + }else{ // IN + } + // copy received bytes amount + endpoints[n].rx_cnt = +#ifdef USB32 + (USB_BTABLE->EP[n].USB_ADDR_COUNT_RX >> 16) & 0x3FF; +#else + USB_BTABLE->EP[n].USB_COUNT_RX & 0x3FF; // low 10 bits is counter +#endif + // call EP handler + if(endpoints[n].func) endpoints[n].func(); + } + if(istr & USB_ISTR_WKUP){ // wakeup +#if defined STM32F0 + CNTR &= ~(USB_CNTR_FSUSP | USB_CNTR_LPMODE | USB_CNTR_WKUPM); +#elif defined STM32G0 + CNTR &= ~(USB_CNTR_SUSPEN | USB_CNTR_PDWN | USB_CNTR_WKUPM); +#else + CNTR &= ~(USB_CNTR_FSUSP | USB_CNTR_LP_MODE | USB_CNTR_WKUPM); // clear suspend flags +#endif + //USB->ISTR = ~USB_ISTR_WKUP; + } + if(istr & USB_ISTR_SUSP){ // suspend -> still no connection, may sleep + usbON = 0; +#if defined STM32F0 + CNTR |= USB_CNTR_FSUSP | USB_CNTR_LPMODE | USB_CNTR_WKUPM; +#elif defined STM32G0 + CNTR |= USB_CNTR_SUSPEN | USB_CNTR_WKUPM; +#else + CNTR |= USB_CNTR_FSUSP | USB_CNTR_LP_MODE | USB_CNTR_WKUPM; +#endif + CNTR &= ~(USB_CNTR_SUSPM); + //USB->ISTR = ~USB_ISTR_SUSP; + } + USB->ISTR = 0; // clear all flags + USB->CNTR = CNTR; // rewoke interrupts +} + +// here we suppose that all PIN settings done in hw_setup earlier +void USB_setup(){ + lastaddr = LASTADDR_DEFAULT; // clear last address settings +#if defined STM32F3 + NVIC_DisableIRQ(USB_LP_IRQn); + // remap USB LP & Wakeup interrupts to 75 and 76 - works only on pure F303 + RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; // enable tacting of SYSCFG + SYSCFG->CFGR1 |= SYSCFG_CFGR1_USB_IT_RMP; +#elif defined STM32F1 + NVIC_DisableIRQ(USB_LP_CAN1_RX0_IRQn); + NVIC_DisableIRQ(USB_HP_CAN1_TX_IRQn); +#elif defined STM32F0 + // All is clocking from HSI48 + NVIC_DisableIRQ(USB_IRQn); + RCC->APB1ENR |= RCC_APB1ENR_CRSEN; + RCC->CFGR3 &= ~RCC_CFGR3_USBSW; // reset USB + RCC->CR2 |= RCC_CR2_HSI48ON; // turn ON HSI48 + uint32_t tmout = 16000000; + while(!(RCC->CR2 & RCC_CR2_HSI48RDY)){if(--tmout == 0) break;} + FLASH->ACR = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY; + CRS->CFGR &= ~CRS_CFGR_SYNCSRC; + CRS->CFGR |= CRS_CFGR_SYNCSRC_1; // USB SOF selected as sync source + CRS->CR |= CRS_CR_AUTOTRIMEN; // enable auto trim + CRS->CR |= CRS_CR_CEN; // enable freq counter & block CRS->CFGR as read-only + RCC->CFGR |= RCC_CFGR_SW; +#elif defined STM32G0 + NVIC_DisableIRQ(USB_UCPD1_2_IRQn); + PWR->CR2 |= PWR_CR2_USV; // enable USB powering + //RCC->APBENR2 |= RCC_APBENR2_SYSCFGEN; // enable tacting of SYSCFG + // independent clocking of USB from HSI48 + RCC->CR |= RCC_CR_HSI48ON; + uint32_t tmout = 16000000; + while(!(RCC->CR & RCC_CR_HSI48RDY)) if(--tmout == 0){ break;} + RCC->CCIPR2 &= ~RCC_CCIPR2_USBSEL; // select HSI48 for USB + RCC->APBENR1 |= RCC_APBENR1_CRSEN; // CRS clocking + CRS->CFGR = (31LL << CRS_CFGR_FELIM_Pos) | // tolerance (usually 31) + (48000LL / 1LL - 1LL) << CRS_CFGR_RELOAD_Pos | // 48MHz / 1kHZ (SOF) + CRS_CFGR_SYNCSRC_1; // USB SOF as sync source (0x2) + CRS->CR |= CRS_CR_AUTOTRIMEN | CRS_CR_CEN; // Enable autotrim and turn on Clock Recovery System + RCC->APBENR1 |= RCC_APBENR1_USBEN; +#endif +#ifndef STM32G0 + RCC->APB1ENR |= RCC_APB1ENR_USBEN; + USB->CNTR = USB_CNTR_FRES; // Force USB Reset + USB->BTABLE = 0; +#else + USB->CNTR = USB_CNTR_USBRST; +#endif + for(uint32_t ctr = 0; ctr < 72000; ++ctr) nop(); // wait >1ms + USB->CNTR = USB_CNTR_RESETM; // allow only reset interrupts + USB->DADDR = 0; + USB->ISTR = 0; +#if defined STM32F3 + NVIC_EnableIRQ(USB_LP_IRQn); +#elif defined STM32F1 + NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn); +#elif defined STM32F0 + USB->BCDR |= USB_BCDR_DPPU; + NVIC_EnableIRQ(USB_IRQn); +#elif defined STM32G0 + USB->BCDR |= USB_BCDR_DPPU; // turn ON DP pullup + NVIC_EnableIRQ(USB_UCPD1_2_IRQn); +#endif +} + + +#if defined STM32F3 +void usb_lp_isr() __attribute__ ((alias ("USB_IRQ"))); +#elif defined STM32F1 +void usb_lp_can_rx0_isr() __attribute__ ((alias ("USB_IRQ"))); +#elif defined STM32F0 +void usb_isr() __attribute__ ((alias ("USB_IRQ"))); +#elif defined STM32G0 +void usb_ucpd1_2_isr() __attribute__ ((alias ("USB_IRQ"))); +#endif diff --git a/F1:F103/AS3935-lightning/usb_lib.h b/F1:F103/AS3935-lightning/usb_lib.h new file mode 100644 index 0000000..e7ec4c9 --- /dev/null +++ b/F1:F103/AS3935-lightning/usb_lib.h @@ -0,0 +1,352 @@ +/* + * Copyright 2024 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 + +#ifndef _U_ +#define _U_ __attribute__((unused)) +#endif + +/****************************************************************** + * Hardware registers etc * + *****************************************************************/ +#if defined STM32F0 +#include +#elif defined STM32F1 +#include +// there's no this define in standard header +#define USB_BASE ((uint32_t)0x40005C00) +#elif defined STM32F3 +#include +#elif defined STM32G0 +#include +#endif + +// max endpoints number +#define STM32ENDPOINTS 8 +/** + * Buffers size definition + **/ + +// F0 - USB2_16; F1 - USB1_16; F3 - 1/2 depending on series; G0 - USB32 +#if !defined USB1_16 && !defined USB2_16 && !defined USB32 +#if defined STM32F0 +#define USB2_16 +#elif defined STM32F1 +#define USB1_16 +#elif defined STM32G0 +#define USB32 +#else +#error "Can't determine USB1_16/USB2_16/USB32, define by hands" +#endif +#endif + +// BTABLE_SIZE FOR STM32F3: +// In STM32F303/302xB/C, 512 bytes SRAM is not shared with CAN. +// In STM32F302x6/x8 and STM32F30xxD/E, 726 bytes dedicated SRAM and 256 bytes shared SRAM with CAN i.e. +// 1Kbytes dedicated SRAM in case CAN is disabled. +// remember, that USB_BTABLE_SIZE will be divided by ACCESSZ, so don't divide it twice for 32-bit addressing + +#ifdef NOCAN +#if defined STM32F0 +#define USB_BTABLE_SIZE 1024 +#elif defined STM32F3 +#define USB_BTABLE_SIZE 1024 +//#warning "Please, check real buffer size due to docs" +#else +#error "define STM32F0 or STM32F3" +#endif +#else // !NOCAN: F0/F3 with CAN or F1 (can't simultaneously run CAN and USB) +#if defined STM32F0 +#define USB_BTABLE_SIZE 768 +#elif defined STM32F3 +#define USB_BTABLE_SIZE 768 +#elif defined STM32G0 +#define USB_BTABLE_SIZE 2048 +//#warning "Please, check real buffer size due to docs" +#else // STM32F103: 1024 bytes but with 32-bit addressing +#define USB_BTABLE_SIZE 1024 +#endif +#endif // NOCAN + +// first 64 bytes of USB_BTABLE are registers! +#ifndef STM32G0 +#define USB_BTABLE_BASE 0x40006000 +#else +#define USB_BTABLE_BASE 0x40009800 +#endif +#define USB ((USB_TypeDef *) USB_BASE) + +#ifdef USB_BTABLE +#undef USB_BTABLE +#endif +#define USB_BTABLE ((USB_BtableDef *)(USB_BTABLE_BASE)) +#define USB_ISTR_EPID 0x0000000F +#define USB_FNR_LSOF_0 0x00000800 +#define USB_FNR_lSOF_1 0x00001000 +#define USB_LPMCSR_BESL_0 0x00000010 +#define USB_LPMCSR_BESL_1 0x00000020 +#define USB_LPMCSR_BESL_2 0x00000040 +#define USB_LPMCSR_BESL_3 0x00000080 +#define USB_EPnR_CTR_RX 0x00008000 +#define USB_EPnR_DTOG_RX 0x00004000 +#define USB_EPnR_STAT_RX 0x00003000 +#define USB_EPnR_STAT_RX_0 0x00001000 +#define USB_EPnR_STAT_RX_1 0x00002000 +#define USB_EPnR_SETUP 0x00000800 +#define USB_EPnR_EP_TYPE 0x00000600 +#define USB_EPnR_EP_TYPE_0 0x00000200 +#define USB_EPnR_EP_TYPE_1 0x00000400 +#define USB_EPnR_EP_KIND 0x00000100 +#define USB_EPnR_CTR_TX 0x00000080 +#define USB_EPnR_DTOG_TX 0x00000040 +#define USB_EPnR_STAT_TX 0x00000030 +#define USB_EPnR_STAT_TX_0 0x00000010 +#define USB_EPnR_STAT_TX_1 0x00000020 +#define USB_EPnR_EA 0x0000000F +#define USB_COUNTn_RX_BLSIZE 0x00008000 +#define USB_COUNTn_NUM_BLOCK 0x00007C00 +#define USB_COUNTn_RX 0x0000003F + +#define USB_TypeDef USB_TypeDef_custom + +typedef struct { + __IO uint32_t EPnR[STM32ENDPOINTS]; + __IO uint32_t RESERVED[STM32ENDPOINTS]; + __IO uint32_t CNTR; + __IO uint32_t ISTR; + __IO uint32_t FNR; + __IO uint32_t DADDR; +#ifndef USB32 + __IO uint32_t BTABLE; +#else + __IO uint32_t RESERVED1; // there's no BTABLE register in STM32G0 +#endif +#if defined STM32F0 || defined USB32 + __IO uint32_t LPMCSR; + __IO uint32_t BCDR; +#endif +} USB_TypeDef; + +// F303 D/E have 2x16 access scheme +typedef struct{ +#if defined USB2_16 + __IO uint16_t USB_ADDR_TX; + __IO uint16_t USB_COUNT_TX; + __IO uint16_t USB_ADDR_RX; + __IO uint16_t USB_COUNT_RX; +#define ACCESSZ (1) +#elif defined USB1_16 + __IO uint32_t USB_ADDR_TX; + __IO uint32_t USB_COUNT_TX; + __IO uint32_t USB_ADDR_RX; + __IO uint32_t USB_COUNT_RX; +#define ACCESSZ (2) +#elif defined USB32 + // 32-bit registers: addr & count in one! + __IO uint32_t USB_ADDR_COUNT_TX; + __IO uint32_t USB_ADDR_COUNT_RX; +#define ACCESSZ (1) +#else +#error "Define USB1_16 (16 bits over 32bit register), USB2_16 (16 bits over 16 bit register) or USB32 (32 bist over 32 bit register)" +#endif +} USB_EPDATA_TypeDef; + + +typedef struct{ + __IO USB_EPDATA_TypeDef EP[STM32ENDPOINTS]; +} USB_BtableDef; + +#define EP0DATABUF_SIZE (64) +#define LASTADDR_DEFAULT (STM32ENDPOINTS * 8) + +/****************************************************************** + * Defines from usb.h * + *****************************************************************/ + +/* + * Device and/or Interface Class codes + */ +#define USB_CLASS_PER_INTERFACE 0 +#define USB_CLASS_AUDIO 1 +#define USB_CLASS_COMM 2 +#define USB_CLASS_HID 3 +#define USB_CLASS_PRINTER 7 +#define USB_CLASS_PTP 6 +#define USB_CLASS_MASS_STORAGE 8 +#define USB_CLASS_HUB 9 +#define USB_CLASS_DATA 10 +#define USB_CLASS_MISC 0xef +#define USB_CLASS_VENDOR_SPEC 0xff + +/* + * Descriptor types + */ +#define USB_DT_DEVICE 0x01 +#define USB_DT_CONFIG 0x02 +#define USB_DT_STRING 0x03 +#define USB_DT_INTERFACE 0x04 +#define USB_DT_ENDPOINT 0x05 +#define USB_DT_QUALIFIER 0x06 +#define USB_DT_IAD 0x0B + +#define USB_DT_HID 0x21 +#define USB_DT_REPORT 0x22 +#define USB_DT_PHYSICAL 0x23 +#define USB_DT_CS_INTERFACE 0x24 +#define USB_DT_HUB 0x29 + +/* + * Descriptor sizes per descriptor type + */ +#define USB_DT_DEVICE_SIZE 18 +#define USB_DT_CONFIG_SIZE 9 +#define USB_DT_INTERFACE_SIZE 9 +#define USB_DT_HID_SIZE 9 +#define USB_DT_ENDPOINT_SIZE 7 +#define USB_DT_QUALIFIER_SIZE 10 +#define USB_DT_CS_INTERFACE_SIZE 5 +#define USB_DT_IAD_SIZE 8 + + +// bmRequestType & 0x80 == dev2host (1) or host2dev (0) +// recipient: bmRequestType & 0x1f +#define REQUEST_RECIPIENT(b) (b & 0x1f) +#define REQ_RECIPIENT_DEVICE 0 +#define REQ_RECIPIENT_INTERFACE 1 +#define REQ_RECIPIENT_ENDPOINT 2 +#define REQ_RECIPIENT_OTHER 3 +// type: [bmRequestType & 0x60 >> 5] +#define REQUEST_TYPE(b) ((b&0x60)>>5) +#define REQ_TYPE_STANDARD 0 +#define REQ_TYPE_CLASS 1 +#define REQ_TYPE_VENDOR 2 +#define REQ_TYPE_RESERVED 3 + + +//#define VENDOR_REQUEST 0x01 + +// standard device requests +#define GET_STATUS 0x00 +#define CLEAR_FEATURE 0x01 +#define SET_FEATURE 0x03 +#define SET_ADDRESS 0x05 +#define GET_DESCRIPTOR 0x06 +#define SET_DESCRIPTOR 0x07 +#define GET_CONFIGURATION 0x08 +#define SET_CONFIGURATION 0x09 +// and some standard interface requests +#define GET_INTERFACE 0x0A +#define SET_INTERFACE 0x0B +// and some standard endpoint requests +#define SYNC_FRAME 0x0C + +// Types of descriptors +#define DEVICE_DESCRIPTOR 0x01 +#define CONFIGURATION_DESCRIPTOR 0x02 +#define STRING_DESCRIPTOR 0x03 +#define DEVICE_QUALIFIER_DESCRIPTOR 0x06 +#define DEBUG_DESCRIPTOR 0x0a +#define HID_REPORT_DESCRIPTOR 0x22 + +// EP types for EP_init +#define EP_TYPE_BULK 0x00 +#define EP_TYPE_CONTROL 0x01 +#define EP_TYPE_ISO 0x02 +#define EP_TYPE_INTERRUPT 0x03 + +// EP types for descriptors +#define USB_BM_ATTR_CONTROL 0x00 +#define USB_BM_ATTR_ISO 0x01 +#define USB_BM_ATTR_BULK 0x02 +#define USB_BM_ATTR_INTERRUPT 0x03 + + +/****************************************************************** + * Other stuff * + *****************************************************************/ + +#define RX_FLAG(epstat) (epstat & USB_EPnR_CTR_RX) +#define TX_FLAG(epstat) (epstat & USB_EPnR_CTR_TX) +#define SETUP_FLAG(epstat) (epstat & USB_EPnR_SETUP) + +// EPnR bits manipulation +#define KEEP_DTOG_STAT(EPnR) (EPnR & ~(USB_EPnR_STAT_RX|USB_EPnR_STAT_TX|USB_EPnR_DTOG_RX|USB_EPnR_DTOG_TX)) +#define KEEP_DTOG(EPnR) (EPnR & ~(USB_EPnR_DTOG_RX|USB_EPnR_DTOG_TX)) + +#define LANG_US (uint16_t)0x0409 + +#define _USB_STRING_(name, str) \ +static const struct name \ +{ \ + uint8_t bLength; \ + uint8_t bDescriptorType; \ + uint16_t bString[(sizeof(str) - 2) / 2]; \ + \ +} \ +name = {sizeof(name), 0x03, str} + +#define _USB_LANG_ID_(name, lng_id) \ +static const struct name \ +{ \ + uint8_t bLength; \ + uint8_t bDescriptorType; \ + uint16_t bString; \ + \ +} \ +name = {0x04, 0x03, lng_id} + +// EP0 configuration packet +typedef struct { + uint8_t bmRequestType; + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; +} config_pack_t; + +// endpoints state +typedef struct{ +#ifdef USB32 + uint32_t *tx_buf; // transmission buffer address +#else + uint16_t *tx_buf; // transmission buffer address +#endif + uint16_t txbufsz; // transmission buffer size +#ifdef USB32 + uint32_t *rx_buf; // reception buffer address +#else + uint8_t *rx_buf; // reception buffer address +#endif + void (*func)(); // endpoint action function + unsigned rx_cnt : 10; // received data counter +} ep_t; + +extern volatile uint8_t usbON; + +void USB_setup(); +int EP_Init(uint8_t number, uint8_t type, uint16_t txsz, uint16_t rxsz, void (*func)()); +void EP_WriteIRQ(uint8_t number, const uint8_t *buf, uint16_t size); +void EP_Write(uint8_t number, const uint8_t *buf, uint16_t size); +int EP_Read(uint8_t number, uint8_t *buf); + +// could be [re]defined in usb_dev.c +extern void usb_class_request(config_pack_t *packet, uint8_t *data, uint16_t datalen); +extern void usb_vendor_request(config_pack_t *packet, uint8_t *data, uint16_t datalen); +extern void set_configuration(); diff --git a/F1:F103/AS3935-lightning/version.inc b/F1:F103/AS3935-lightning/version.inc new file mode 100644 index 0000000..6f740fe --- /dev/null +++ b/F1:F103/AS3935-lightning/version.inc @@ -0,0 +1,2 @@ +#define BUILD_NUMBER "14" +#define BUILD_DATE "2026-04-08" diff --git a/F1:F103/FX3U/adc.c b/F1:F103/FX3U/adc.c index cb2952a..ef1b05f 100644 --- a/F1:F103/FX3U/adc.c +++ b/F1:F103/FX3U/adc.c @@ -37,7 +37,7 @@ void adc_setup(){ ADC1->SMPR2 = ADC_SMPR2_SMP0; ADC1->SMPR1 = ADC_SMPR1_SMP16 | ADC_SMPR1_SMP17; // sequence order: 1[0]->3[1]->14[2]->15[3]->10[4]->11[5] -> 16[tsen] -> 17[vdd] - ADC1->SQR3 = (1 << 0) | (3<<5) | (14 << 10) | (15 << 15) | (10 << 20) | (11 < 25); + ADC1->SQR3 = (1 << 0) | (3<<5) | (14 << 10) | (15 << 15) | (10 << 20) | (11 << 25); ADC1->SQR2 = (12 << 0) | (13 << 5) | (16 << 10) | (17 << 15); ADC1->SQR1 = (ADC_CHANNELS - 1) << 20; // amount of conversions ADC1->CR1 = ADC_CR1_SCAN; // scan mode