From 02bbc6592d2d5e0b37fb059ead824bbdf795e03b Mon Sep 17 00:00:00 2001 From: PhysicistJohn <54456354+PhysicistJohn@users.noreply.github.com> Date: Tue, 14 Jul 2026 22:28:10 -0700 Subject: [PATCH 1/7] build: port firmware to ChibiOS 21.11.5 --- .gitmodules | 4 +- ChibiOS | 2 +- Makefile | 31 ++++++------ NANOVNA_STM32_F072/adc.c | 2 +- NANOVNA_STM32_F072/board.c | 83 ++++++++++++++++++++++++++----- NANOVNA_STM32_F303/adc.c | 95 ++++++++++++++++++++---------------- NANOVNA_STM32_F303/board.c | 73 ++++++++++++++++++++++++--- NANOVNA_STM32_F303/board.h | 2 - NANOVNA_STM32_F303/mcuconf.h | 23 ++++----- chconf.h | 35 +++++++++++++ chprintf.c | 8 ++- docs/CHIBIOS_21_11_5_PORT.md | 56 +++++++++++++++++++++ hal_adc_lld.c | 67 ++++++++++++------------- halconf.h | 3 ++ ili9341.c | 16 ++++-- main.c | 20 +++++--- plot.c | 8 ++- sa_core.c | 2 +- si5351.c | 4 +- ui.c | 75 +++++++++++----------------- usbcfg.c | 31 +++++++----- 21 files changed, 434 insertions(+), 206 deletions(-) create mode 100644 docs/CHIBIOS_21_11_5_PORT.md diff --git a/.gitmodules b/.gitmodules index 11b93a5..ded024e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "ChibiOS"] path = ChibiOS - url = https://github.com/edy555/ChibiOS.git - branch = I2SFULLDUPLEX + url = https://github.com/PhysicistJohn/chibios.git + branch = codex/integration-tinysa-21.11.5 diff --git a/ChibiOS b/ChibiOS index ade76de..db35f6d 160000 --- a/ChibiOS +++ b/ChibiOS @@ -1 +1 @@ -Subproject commit ade76dea89cd093650552328e881252a06486094 +Subproject commit db35f6df137058c612dc13e4488ac923219d881a diff --git a/Makefile b/Makefile index 85410be..12009e5 100644 --- a/Makefile +++ b/Makefile @@ -115,6 +115,8 @@ endif #CHIBIOS = ../ChibiOS-RT CHIBIOS = ChibiOS PROJ = . +# ChibiOS 21.11.x keeps its license policy header in a dedicated include. +include $(CHIBIOS)/os/license/license.mk # Startup files. ifeq ($(TARGET),F303) @@ -130,13 +132,13 @@ include $(CHIBIOS)/os/hal/ports/STM32/STM32F0xx/platform.mk include NANOVNA_STM32_F072/board.mk endif -include $(CHIBIOS)/os/hal/osal/rt/osal.mk +include $(CHIBIOS)/os/hal/osal/rt-nil/osal.mk # RTOS files (optional). include $(CHIBIOS)/os/rt/rt.mk ifeq ($(TARGET),F303) -include $(CHIBIOS)/os/common/ports/ARMCMx/compilers/GCC/mk/port_v7m.mk +include $(CHIBIOS)/os/common/ports/ARMv7-M/compilers/GCC/mk/port.mk else -include $(CHIBIOS)/os/common/ports/ARMCMx/compilers/GCC/mk/port_v6m.mk +include $(CHIBIOS)/os/common/ports/ARMv6-M/compilers/GCC/mk/port.mk endif # Other files (optional). #include $(CHIBIOS)/test/rt/test.mk @@ -156,6 +158,7 @@ endif ifeq ($(TARGET),F303) CSRC = $(STARTUPSRC) \ $(KERNSRC) \ + $(OSLIBSRC) \ $(PORTSRC) \ $(OSALSRC) \ $(HALSRC) \ @@ -170,6 +173,7 @@ CSRC = $(STARTUPSRC) \ else CSRC = $(STARTUPSRC) \ $(KERNSRC) \ + $(OSLIBSRC) \ $(PORTSRC) \ $(OSALSRC) \ $(HALSRC) \ @@ -205,10 +209,11 @@ TCSRC = # option that results in lower performance and larger code size. TCPPSRC = -# List ASM source files here -ASMSRC = $(STARTUPASM) $(PORTASM) $(OSALASM) +# ChibiOS startup and port assembly is preprocessed (.S), not plain assembly. +ASMSRC = +ASMXSRC = $(STARTUPASM) $(PORTASM) $(OSALASM) -INCDIR = $(STARTUPINC) $(KERNINC) $(PORTINC) $(OSALINC) \ +INCDIR = $(LICINC) $(STARTUPINC) $(KERNINC) $(OSLIBINC) $(PORTINC) $(OSALINC) \ $(HALINC) $(PLATFORMINC) $(BOARDINC) \ $(STREAMSINC) @@ -266,7 +271,7 @@ CPPWARN = -Wall -Wextra -Wundef # List all user C define here, like -D_DEBUG=1 ifeq ($(TARGET),F303) - UDEFS = -DARM_MATH_CM4 -DVERSION=\"$(VERSION)\" -DTINYSA_F303 -D__FPU_USED -DST7796S -DTINYSA4 + UDEFS = -DARM_MATH_CM4 -DVERSION=\"$(VERSION)\" -DTINYSA_F303 -DST7796S -DTINYSA4 #Enable if install external 32.768kHz clock quartz on PC14 and PC15 pins on STM32 CPU UDEFS+= -DVNA_USE_LSE # Use R as usb pullup @@ -292,19 +297,12 @@ ULIBS = -lm # End of user defines ############################################################################## -RULESPATH = $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC +RULESPATH = $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC/mk +include $(RULESPATH)/arm-none-eabi.mk include $(RULESPATH)/rules.mk #include $(CHIBIOS)/memory.mk -ifeq ($(TARGET),F303) -clean: - rm -f -rf build/tinySA4.* build/lst/*.* build/obj/*.* -else -clean: - rm -f -rf build/$(PROJECT).* build/lst/*.* build/obj/*.* -endif - flash: build/$(PROJECT).bin -@printf "reset dfu\r" >/dev/cu.usbmodem401 # mac -@printf "reset dfu\r" >/dev/ttyACM0 # linux @@ -323,4 +321,3 @@ else @etags *.[ch] NANOVNA_STM32_F072/*.[ch] $(shell find ChibiOS/os/hal/ports/STM32/STM32F0xx ChibiOS/os -name \*.\[ch\] -print) endif @ls -l TAGS - diff --git a/NANOVNA_STM32_F072/adc.c b/NANOVNA_STM32_F072/adc.c index d61068f..3aa3fa6 100644 --- a/NANOVNA_STM32_F072/adc.c +++ b/NANOVNA_STM32_F072/adc.c @@ -80,7 +80,7 @@ int16_t adc_vbat_read(void) // Vbat measure averange count = 2^VBAT_AVERAGE #define VBAT_AVERAGE 4 // Measure vbat every 5 second -#define VBAT_MEASURE_INTERVAL S2ST(5) +#define VBAT_MEASURE_INTERVAL TIME_S2I(5) static int16_t vbat_raw = 0; static systime_t vbat_time = -VBAT_MEASURE_INTERVAL-1; diff --git a/NANOVNA_STM32_F072/board.c b/NANOVNA_STM32_F072/board.c index 0e71d6f..2de30f8 100644 --- a/NANOVNA_STM32_F072/board.c +++ b/NANOVNA_STM32_F072/board.c @@ -14,15 +14,41 @@ limitations under the License. */ -#include "hal.h" - -#if HAL_USE_PAL || defined(__DOXYGEN__) -/** - * @brief PAL setup. - * @details Digital I/O ports static configuration as defined in @p board.h. - * This variable is used by the HAL when initializing the PAL driver. - */ -const PALConfig pal_default_config = { +#include "hal.h" +#include "stm32_gpio.h" + +typedef struct { + uint32_t moder; + uint32_t otyper; + uint32_t ospeedr; + uint32_t pupdr; + uint32_t odr; + uint32_t afrl; + uint32_t afrh; +} gpio_setup_t; + +typedef struct { +#if STM32_HAS_GPIOA + gpio_setup_t PAData; +#endif +#if STM32_HAS_GPIOB + gpio_setup_t PBData; +#endif +#if STM32_HAS_GPIOC + gpio_setup_t PCData; +#endif +#if STM32_HAS_GPIOD + gpio_setup_t PDData; +#endif +#if STM32_HAS_GPIOE + gpio_setup_t PEData; +#endif +#if STM32_HAS_GPIOF + gpio_setup_t PFData; +#endif +} gpio_config_t; + +static const gpio_config_t gpio_default_config = { #if STM32_HAS_GPIOA {VAL_GPIOA_MODER, VAL_GPIOA_OTYPER, VAL_GPIOA_OSPEEDR, VAL_GPIOA_PUPDR, VAL_GPIOA_ODR, VAL_GPIOA_AFRL, VAL_GPIOA_AFRH}, @@ -59,8 +85,40 @@ const PALConfig pal_default_config = { {VAL_GPIOI_MODER, VAL_GPIOI_OTYPER, VAL_GPIOI_OSPEEDR, VAL_GPIOI_PUPDR, VAL_GPIOI_ODR, VAL_GPIOI_AFRL, VAL_GPIOI_AFRH} #endif -}; -#endif +}; + +static void gpio_init(stm32_gpio_t *gpiop, const gpio_setup_t *config) { + gpiop->OTYPER = config->otyper; + gpiop->OSPEEDR = config->ospeedr; + gpiop->PUPDR = config->pupdr; + gpiop->ODR = config->odr; + gpiop->AFRL = config->afrl; + gpiop->AFRH = config->afrh; + gpiop->MODER = config->moder; +} + +static void stm32_gpio_init(void) { + rccResetAHB(STM32_GPIO_EN_MASK); + rccEnableAHB(STM32_GPIO_EN_MASK, true); +#if STM32_HAS_GPIOA + gpio_init(GPIOA, &gpio_default_config.PAData); +#endif +#if STM32_HAS_GPIOB + gpio_init(GPIOB, &gpio_default_config.PBData); +#endif +#if STM32_HAS_GPIOC + gpio_init(GPIOC, &gpio_default_config.PCData); +#endif +#if STM32_HAS_GPIOD + gpio_init(GPIOD, &gpio_default_config.PDData); +#endif +#if STM32_HAS_GPIOE + gpio_init(GPIOE, &gpio_default_config.PEData); +#endif +#if STM32_HAS_GPIOF + gpio_init(GPIOF, &gpio_default_config.PFData); +#endif +} static bool needDFU(void) { // Magick data in memory before reset @@ -105,7 +163,8 @@ void __early_init(void) { } //si5351_setup(); - stm32_clock_init(); + stm32_gpio_init(); + stm32_clock_init(); } /* diff --git a/NANOVNA_STM32_F303/adc.c b/NANOVNA_STM32_F303/adc.c index 9f06461..a380e54 100644 --- a/NANOVNA_STM32_F303/adc.c +++ b/NANOVNA_STM32_F303/adc.c @@ -36,54 +36,67 @@ static adcsample_t samplesVBAT[ADC_GRP_NUM_CHANNELS_VBAT]; static adcsample_t samples[1]; static const ADCConversionGroup adcgrpcfgVBAT = { - FALSE, - ADC_GRP_NUM_CHANNELS_VBAT, - NULL, - NULL, - ADC_CFGR_CONT | ADC_CFGR1_RES_12BIT, // CFGR1 - ADC_TR(0, 0), // ADC watchdog threshold TR1 - {0, ADC_SMPR2_SMP_AN16(ADC_VBAT_SMP_TIME) | ADC_SMPR2_SMP_AN17(ADC_VBAT_SMP_TIME)/*| ADC_SMPR2_SMP_AN18(ADC_VBAT_SMP_TIME)*/}, // SMPR - {ADC_SQR1_SQ1_N(ADC_CHANNEL_IN17) | ADC_SQR1_SQ2_N(ADC_CHANNEL_IN18)/*| ADC_SQR1_SQ3_N(ADC_CHANNEL_IN16)*/, 0, 0, 0} // CHSELR + .circular = false, + .num_channels = ADC_GRP_NUM_CHANNELS_VBAT, + .end_cb = NULL, + .error_cb = NULL, + .cfgr = ADC_CFGR_CONT | ADC_CFGR_RES_12BITS, + .tr1 = ADC_TR(0, 0), + .tr2 = ADC_TR_DISABLED, + .tr3 = ADC_TR_DISABLED, + .awd2cr = 0U, + .awd3cr = 0U, + .smpr = {0, ADC_SMPR2_SMP_AN16(ADC_VBAT_SMP_TIME) | + ADC_SMPR2_SMP_AN17(ADC_VBAT_SMP_TIME)}, + .sqr = {ADC_SQR1_SQ1_N(ADC_CHANNEL_IN17) | + ADC_SQR1_SQ2_N(ADC_CHANNEL_IN18), 0, 0, 0} }; static const ADCConversionGroup adcgrpcfgVersion = { - FALSE, - 1, - NULL, - NULL, - ADC_CFGR1_RES_12BIT, // CFGR1 - ADC_TR(0, 0), // ADC watchdog threshold TR1 - {ADC_SMPR1_SMP_AN1(ADC_TOUCH_XY_SMP_TIME), 0}, /* SMPR[2] */ - {ADC_SQR1_SQ1_N(ADC_CHANNEL_IN1), 0, 0, 0} /* SQR[4] */ + .circular = false, + .num_channels = 1, + .end_cb = NULL, + .error_cb = NULL, + .cfgr = ADC_CFGR_RES_12BITS, + .tr1 = ADC_TR(0, 0), + .tr2 = ADC_TR_DISABLED, + .tr3 = ADC_TR_DISABLED, + .awd2cr = 0U, + .awd3cr = 0U, + .smpr = {ADC_SMPR1_SMP_AN1(ADC_TOUCH_XY_SMP_TIME), 0}, + .sqr = {ADC_SQR1_SQ1_N(ADC_CHANNEL_IN1), 0, 0, 0} }; static const ADCConversionGroup adcgrpcfgTouch = { - TRUE, // Enables the circular buffer mode for the group. - 1, // Number of the analog channels belonging to the conversion group. - NULL, // adccallback_touch - NULL, // adcerrorcallback_touch - // CFGR - ADC_CFGR_EXTEN_0 // rising edge of external trigger - - | ADC_CFGR_EXTSEL_3 | ADC_CFGR_EXTSEL_0 // EXT4 0x1001 event (TIM1_TRGO) -// | ADC_CFGR_EXTSEL_2 // EXT4 0x100 event (TIM3_TRGO) - - - | ADC_CFGR_AWD1EN, // Enable Analog watchdog check interrupt - ADC_TR(0, TOUCH_THRESHOLD), // Analog watchdog threshold TR1, interrupt on touch press - {ADC_SMPR1_SMP_AN4(ADC_TOUCH_SMP_TIME), 0}, // SMPR[2] - {ADC_SQR1_SQ1_N(ADC_CHANNEL_IN4), 0, 0, 0} // SQR[4] + .circular = true, + .num_channels = 1, + .end_cb = NULL, + .error_cb = NULL, + .cfgr = ADC_CFGR_EXTEN_0 | ADC_CFGR_EXTSEL_3 | ADC_CFGR_EXTSEL_0 | + ADC_CFGR_AWD1EN, + .tr1 = ADC_TR(0, TOUCH_THRESHOLD), + .tr2 = ADC_TR_DISABLED, + .tr3 = ADC_TR_DISABLED, + .awd2cr = 0U, + .awd3cr = 0U, + .smpr = {ADC_SMPR1_SMP_AN4(ADC_TOUCH_SMP_TIME), 0}, + .sqr = {ADC_SQR1_SQ1_N(ADC_CHANNEL_IN4), 0, 0, 0} }; static ADCConversionGroup adcgrpcfgXY = { - FALSE, - 1, - NULL, /* adccallback_touch */ - NULL, /* adcerrorcallback_touch */ - ADC_CFGR1_RES_12BIT, /* CFGR */ - ADC_TR(0, 0), /* TR1 */ - {ADC_SMPR1_SMP_AN3(ADC_TOUCH_XY_SMP_TIME) | ADC_SMPR1_SMP_AN4(ADC_TOUCH_XY_SMP_TIME), 0}, /* SMPR[2] */ - {ADC_SQR1_SQ1_N(ADC_CHANNEL_IN3), 0, 0, 0} /* SQR[4] */ + .circular = false, + .num_channels = 1, + .end_cb = NULL, + .error_cb = NULL, + .cfgr = ADC_CFGR_RES_12BITS, + .tr1 = ADC_TR(0, 0), + .tr2 = ADC_TR_DISABLED, + .tr3 = ADC_TR_DISABLED, + .awd2cr = 0U, + .awd3cr = 0U, + .smpr = {ADC_SMPR1_SMP_AN3(ADC_TOUCH_XY_SMP_TIME) | + ADC_SMPR1_SMP_AN4(ADC_TOUCH_XY_SMP_TIME), 0}, + .sqr = {ADC_SQR1_SQ1_N(ADC_CHANNEL_IN3), 0, 0, 0} }; void adc_init(void) @@ -120,7 +133,7 @@ int16_t adc_vbat_read(void) // Vbat measure averange count = 2^VBAT_AVERAGE #define VBAT_AVERAGE 4 // Measure vbat every 5 second -#define VBAT_MEASURE_INTERVAL S2ST(5) +#define VBAT_MEASURE_INTERVAL TIME_S2I(5) static int16_t vbat_raw = 0; static systime_t vbat_time = -VBAT_MEASURE_INTERVAL-1; @@ -219,7 +232,7 @@ uint16_t adc_multi_read(uint32_t chsel, uint16_t *result, uint32_t count) VNA_ADC->IER = 0; VNA_ADC->TR = ADC_TR(0, 0); VNA_ADC->SMPR = ADC_SMPR_SMP_1P5; - VNA_ADC->CFGR1 = ADC_CFGR1_RES_12BIT; + VNA_ADC->CFGR = ADC_CFGR_RES_12BITS; VNA_ADC->CHSELR = chsel; diff --git a/NANOVNA_STM32_F303/board.c b/NANOVNA_STM32_F303/board.c index 6ca71af..36571f0 100644 --- a/NANOVNA_STM32_F303/board.c +++ b/NANOVNA_STM32_F303/board.c @@ -15,14 +15,40 @@ */ #include "hal.h" +#include "stm32_gpio.h" -#if HAL_USE_PAL || defined(__DOXYGEN__) -/** - * @brief PAL setup. - * @details Digital I/O ports static configuration as defined in @p board.h. - * This variable is used by the HAL when initializing the PAL driver. - */ -const PALConfig pal_default_config = { +typedef struct { + uint32_t moder; + uint32_t otyper; + uint32_t ospeedr; + uint32_t pupdr; + uint32_t odr; + uint32_t afrl; + uint32_t afrh; +} gpio_setup_t; + +typedef struct { +#if STM32_HAS_GPIOA + gpio_setup_t PAData; +#endif +#if STM32_HAS_GPIOB + gpio_setup_t PBData; +#endif +#if STM32_HAS_GPIOC + gpio_setup_t PCData; +#endif +#if STM32_HAS_GPIOD + gpio_setup_t PDData; +#endif +#if STM32_HAS_GPIOE + gpio_setup_t PEData; +#endif +#if STM32_HAS_GPIOF + gpio_setup_t PFData; +#endif +} gpio_config_t; + +static const gpio_config_t gpio_default_config = { #if STM32_HAS_GPIOA {VAL_GPIOA_MODER, VAL_GPIOA_OTYPER, VAL_GPIOA_OSPEEDR, VAL_GPIOA_PUPDR, VAL_GPIOA_ODR, VAL_GPIOA_AFRL, VAL_GPIOA_AFRH}, @@ -60,7 +86,39 @@ const PALConfig pal_default_config = { VAL_GPIOI_ODR, VAL_GPIOI_AFRL, VAL_GPIOI_AFRH} #endif }; + +static void gpio_init(stm32_gpio_t *gpiop, const gpio_setup_t *config) { + gpiop->OTYPER = config->otyper; + gpiop->OSPEEDR = config->ospeedr; + gpiop->PUPDR = config->pupdr; + gpiop->ODR = config->odr; + gpiop->AFRL = config->afrl; + gpiop->AFRH = config->afrh; + gpiop->MODER = config->moder; +} + +static void stm32_gpio_init(void) { + rccResetAHB(STM32_GPIO_EN_MASK); + rccEnableAHB(STM32_GPIO_EN_MASK, true); +#if STM32_HAS_GPIOA + gpio_init(GPIOA, &gpio_default_config.PAData); +#endif +#if STM32_HAS_GPIOB + gpio_init(GPIOB, &gpio_default_config.PBData); +#endif +#if STM32_HAS_GPIOC + gpio_init(GPIOC, &gpio_default_config.PCData); #endif +#if STM32_HAS_GPIOD + gpio_init(GPIOD, &gpio_default_config.PDData); +#endif +#if STM32_HAS_GPIOE + gpio_init(GPIOE, &gpio_default_config.PEData); +#endif +#if STM32_HAS_GPIOF + gpio_init(GPIOF, &gpio_default_config.PFData); +#endif +} // extern void si5351_setup(void); @@ -129,6 +187,7 @@ void __early_init(void) { #endif } // si5351_setup(); + stm32_gpio_init(); stm32_clock_init(); } diff --git a/NANOVNA_STM32_F303/board.h b/NANOVNA_STM32_F303/board.h index 6c1432b..8890769 100644 --- a/NANOVNA_STM32_F303/board.h +++ b/NANOVNA_STM32_F303/board.h @@ -27,8 +27,6 @@ #define BOARD_NANOVNA_STM32_F303 #define BOARD_NAME "tinySA ULTRA" -#include -//#include "..\nanovna.h" /* * Board frequencies. */ diff --git a/NANOVNA_STM32_F303/mcuconf.h b/NANOVNA_STM32_F303/mcuconf.h index 8ce881d..f7e5709 100644 --- a/NANOVNA_STM32_F303/mcuconf.h +++ b/NANOVNA_STM32_F303/mcuconf.h @@ -32,6 +32,7 @@ */ #define STM32F3xx_MCUCONF +#define STM32F303_MCUCONF /* * HAL driver system settings. @@ -111,9 +112,9 @@ //#define STM32_ADC_ADC4_DMA_STREAM STM32_DMA_STREAM_ID(2, 2) #define STM32_ADC_ADC12_DMA_PRIORITY 2 //#define STM32_ADC_ADC34_DMA_PRIORITY 2 -#define STM32_ADC_ADC12_IRQ_PRIORITY 2 +#define STM32_ADC_ADC12_IRQ_PRIORITY 3 //#define STM32_ADC_ADC34_IRQ_PRIORITY 5 -#define STM32_ADC_ADC12_DMA_IRQ_PRIORITY 2 +#define STM32_ADC_ADC12_DMA_IRQ_PRIORITY 3 //#define STM32_ADC_ADC34_DMA_IRQ_PRIORITY 5 //#define STM32_ADC_ADC12_CLOCK_MODE ADC_CCR_CKMODE_ADCCK //#define STM32_ADC_ADC12_CLOCK_MODE ADC_CCR_CKMODE_AHB_DIV2 @@ -157,10 +158,10 @@ #define STM32_GPT_USE_TIM2 FALSE #define STM32_GPT_USE_TIM3 FALSE #define STM32_GPT_USE_TIM4 TRUE -#define STM32_GPT_TIM1_IRQ_PRIORITY 2 -#define STM32_GPT_TIM2_IRQ_PRIORITY 2 -#define STM32_GPT_TIM3_IRQ_PRIORITY 2 -#define STM32_GPT_TIM4_IRQ_PRIORITY 2 +#define STM32_GPT_TIM1_IRQ_PRIORITY 3 +#define STM32_GPT_TIM2_IRQ_PRIORITY 3 +#define STM32_GPT_TIM3_IRQ_PRIORITY 3 +#define STM32_GPT_TIM4_IRQ_PRIORITY 3 /* * I2C driver system settings. @@ -184,8 +185,8 @@ STM32_I2S_MODE_RX) #define STM32_I2S_SPI2_MODE (STM32_I2S_MODE_SLAVE | \ STM32_I2S_MODE_RX ) -#define STM32_I2S_SPI1_IRQ_PRIORITY 2 -#define STM32_I2S_SPI2_IRQ_PRIORITY 2 +#define STM32_I2S_SPI1_IRQ_PRIORITY 3 +#define STM32_I2S_SPI2_IRQ_PRIORITY 3 #define STM32_I2S_SPI1_DMA_PRIORITY 1 #define STM32_I2S_SPI2_DMA_PRIORITY 1 #define STM32_I2S_SPI1_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 2) @@ -233,14 +234,14 @@ #define STM32_SPI_SPI1_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 3) #define STM32_SPI_SPI1_DMA_PRIORITY 1 #define STM32_SPI_SPI2_DMA_PRIORITY 1 -#define STM32_SPI_SPI1_IRQ_PRIORITY 2 -#define STM32_SPI_SPI2_IRQ_PRIORITY 2 +#define STM32_SPI_SPI1_IRQ_PRIORITY 3 +#define STM32_SPI_SPI2_IRQ_PRIORITY 3 #define STM32_SPI_DMA_ERROR_HOOK(spip) osalSysHalt("DMA failure") /* * ST driver system settings. */ -#define STM32_ST_IRQ_PRIORITY 2 +#define STM32_ST_IRQ_PRIORITY 3 #define STM32_ST_USE_TIMER 2 /* diff --git a/chconf.h b/chconf.h index 682a459..84e37ce 100644 --- a/chconf.h +++ b/chconf.h @@ -29,6 +29,9 @@ #define _CHCONF_H_ #define _CHIBIOS_RT_CONF_ +#define _CHIBIOS_RT_CONF_VER_7_0_ + +#define CH_CFG_SMP_MODE FALSE /*===========================================================================*/ /** @@ -49,6 +52,8 @@ * setting also defines the system tick time unit. */ #define CH_CFG_ST_FREQUENCY 10000 +#define CH_CFG_INTERVALS_SIZE 32 +#define CH_CFG_TIME_TYPES_SIZE 32 /** * @brief Time delta constant for the tick-less mode. @@ -140,6 +145,7 @@ * @note The default is @p TRUE. */ #define CH_CFG_USE_TM FALSE +#define CH_CFG_USE_TIMESTAMP FALSE /** * @brief Threads registry APIs. @@ -305,6 +311,20 @@ */ #define CH_CFG_USE_MEMPOOLS FALSE +#define CH_CFG_USE_OBJ_FIFOS FALSE +#define CH_CFG_USE_PIPES FALSE +#define CH_CFG_USE_OBJ_CACHES FALSE +#define CH_CFG_USE_DELEGATES FALSE +#define CH_CFG_USE_JOBS FALSE +#define CH_CFG_USE_FACTORY FALSE +#define CH_CFG_FACTORY_MAX_NAMES_LENGTH 8 +#define CH_CFG_FACTORY_OBJECTS_REGISTRY FALSE +#define CH_CFG_FACTORY_GENERIC_BUFFERS FALSE +#define CH_CFG_FACTORY_SEMAPHORES FALSE +#define CH_CFG_FACTORY_MAILBOXES FALSE +#define CH_CFG_FACTORY_OBJ_FIFOS FALSE +#define CH_CFG_FACTORY_PIPES FALSE + /** * @brief Dynamic Threads APIs. * @details If enabled then the dynamic threads creation APIs are included @@ -417,6 +437,18 @@ */ /*===========================================================================*/ +#define CH_CFG_SYSTEM_EXTRA_FIELDS \ + /* No custom system fields.*/ + +#define CH_CFG_SYSTEM_INIT_HOOK() { \ +} + +#define CH_CFG_OS_INSTANCE_EXTRA_FIELDS \ + /* No custom instance fields.*/ + +#define CH_CFG_OS_INSTANCE_INIT_HOOK(oip) { \ +} + /** * @brief Threads descriptor structure extension. * @details User fields added to the end of the @p thread_t structure. @@ -522,6 +554,9 @@ /* Trace code here.*/ \ } +#define CH_CFG_RUNTIME_FAULTS_HOOK(mask) { \ +} + /** @} */ /*===========================================================================*/ diff --git a/chprintf.c b/chprintf.c index 07f52f4..df1a474 100644 --- a/chprintf.c +++ b/chprintf.c @@ -630,7 +630,13 @@ static msg_t put(void *ip, uint8_t b) { return MSG_OK; } -static const struct printStreamVMT vmt = {NULL, NULL, put, NULL}; +static const struct printStreamVMT vmt = { + .instance_offset = 0U, + .write = NULL, + .read = NULL, + .put = put, + .get = NULL +}; void printObjectInit(printStream *ps, int size, uint8_t *buffer){ ps->vmt = &vmt; ps->buffer = buffer; diff --git a/docs/CHIBIOS_21_11_5_PORT.md b/docs/CHIBIOS_21_11_5_PORT.md new file mode 100644 index 0000000..0b7a101 --- /dev/null +++ b/docs/CHIBIOS_21_11_5_PORT.md @@ -0,0 +1,56 @@ +# ChibiOS 21.11.5 port + +This branch ports both firmware targets from the historical tinySA ChibiOS +fork to the official ChibiOS `ver21.11.5` release. + +The draft currently pins the public integration commit +`db35f6df137058c612dc13e4488ac923219d881a` from +`PhysicistJohn/chibios`. It contains two focused commits on top of the release +tag: + +1. restore the standalone STM32F0 TIM14 GPT interrupt service; +2. preserve active USBv1 endpoint-zero PMA buffers when configuration + endpoints are rebuilt. + +Those fixes are also proposed independently to current ChibiOS in PRs #84 and +#85 and to `stable-21.11.x` in draft PRs #86 and #87. The temporary fork URL +will be replaced with the canonical ChibiOS URL and merged stable commit before +this port is marked ready. + +## Port changes + +- Adopt the RT7/HAL9 configuration markers, OS library settings, build rules, + Cortex-M port paths, and license include required by ChibiOS 21.11.5. +- Raise kernel-aware F303 interrupt priorities to respect the RT7 + fast-interrupt reservation. +- Migrate board GPIO initialization, DMA allocation, ADC group definitions, + PAL line events, USB serial hooks, endpoint configuration, PWM configuration, + queue reset calls, time conversions, and thread diagnostics. +- Update the project-local F303 ADC LLD while retaining its tinySA-specific + behavior. +- Convert custom `BaseSequentialStream` VMTs to the current layout. + +## Build + +With an Arm GNU toolchain on `PATH`: + +```sh +git submodule update --init --recursive +make TARGET=F303 -j8 +make clean +make TARGET=F072 -j8 +``` + +## Qualification boundary + +The predecessor RC5 image using the same application port and equivalent +ChibiOS fixes was exercised on a tinySA Ultra+ ZS407. Its exact DFU write and +readback, USB runtime, warm reset retention, cold boot, and all fourteen +built-in self-tests passed. A fresh official-versus-candidate comparison had +one first-cold measured-level threshold failure in case 2; three repeats, +including a second cold run, passed. Physical forced-fault injection was not +performed. + +The clean public branch has different commit identity and embedded version, so +it is a new binary. It must be rebuilt and receive a final physical smoke and +self-test run before this draft is promoted to ready. diff --git a/hal_adc_lld.c b/hal_adc_lld.c index bbc4edf..898db5d 100644 --- a/hal_adc_lld.c +++ b/hal_adc_lld.c @@ -444,7 +444,7 @@ void adc_lld_init(void) { #if STM32_ADC_DUAL_MODE ADCD1.adcs = ADC2; #endif - ADCD1.dmastp = STM32_DMA_STREAM(STM32_ADC_ADC1_DMA_STREAM); + ADCD1.dmastp = NULL; ADCD1.dmamode = ADC_DMA_SIZE | STM32_DMA_CR_PL(STM32_ADC_ADC1_DMA_PRIORITY) | STM32_DMA_CR_DIR_P2M | @@ -461,7 +461,7 @@ void adc_lld_init(void) { ADCD2.adcc = ADC123_COMMON; #endif ADCD2.adcm = ADC2; - ADCD2.dmastp = STM32_DMA_STREAM(STM32_ADC_ADC2_DMA_STREAM); + ADCD2.dmastp = NULL; ADCD2.dmamode = ADC_DMA_SIZE | STM32_DMA_CR_PL(STM32_ADC_ADC2_DMA_PRIORITY) | STM32_DMA_CR_DIR_P2M | @@ -483,7 +483,7 @@ void adc_lld_init(void) { #if STM32_ADC_DUAL_MODE ADCD3.adcs = ADC4; #endif - ADCD3.dmastp = STM32_DMA_STREAM(STM32_ADC_ADC3_DMA_STREAM); + ADCD3.dmastp = NULL; ADCD3.dmamode = ADC_DMA_SIZE | STM32_DMA_CR_PL(STM32_ADC_ADC3_DMA_PRIORITY) | STM32_DMA_CR_DIR_P2M | @@ -496,7 +496,7 @@ void adc_lld_init(void) { adcObjectInit(&ADCD4); ADCD4.adcc = ADC3_4_COMMON; ADCD4.adcm = ADC4; - ADCD4.dmastp = STM32_DMA_STREAM(STM32_ADC_ADC4_DMA_STREAM); + ADCD4.dmastp = NULL; ADCD4.dmamode = ADC_DMA_SIZE | STM32_DMA_CR_PL(STM32_ADC_ADC4_DMA_PRIORITY) | STM32_DMA_CR_DIR_P2M | @@ -524,13 +524,13 @@ void adc_lld_init(void) { rccEnableADC12(FALSE); rccResetADC12(); ADC1_2_COMMON->CCR = STM32_ADC_ADC12_CLOCK_MODE | ADC_DMA_MDMA; - rccDisableADC12(FALSE); + rccDisableADC12(); #endif #if STM32_ADC_USE_ADC3 || STM32_ADC_USE_ADC4 rccEnableADC34(FALSE); rccResetADC34(); ADC3_4_COMMON->CCR = STM32_ADC_ADC34_CLOCK_MODE | ADC_DMA_MDMA; - rccDisableADC34(FALSE); + rccDisableADC34(); #endif #endif @@ -538,7 +538,7 @@ void adc_lld_init(void) { rccEnableADC123(FALSE); rccResetADC123(); ADC123_COMMON->CCR = STM32_ADC_ADC123_CLOCK_MODE | ADC_DMA_MDMA; - rccDisableADC123(FALSE); + rccDisableADC123(); #endif } @@ -560,12 +560,11 @@ void adc_lld_start(ADCDriver *adcp) { if (adcp->state == ADC_STOP) { #if STM32_ADC_USE_ADC1 if (&ADCD1 == adcp) { - bool b; - b = dmaStreamAllocate(adcp->dmastp, - STM32_ADC_ADC1_DMA_IRQ_PRIORITY, - (stm32_dmaisr_t)adc_lld_serve_dma_interrupt, - (void *)adcp); - osalDbgAssert(!b, "stream already allocated"); + adcp->dmastp = dmaStreamAllocI(STM32_ADC_ADC1_DMA_STREAM, + STM32_ADC_ADC1_DMA_IRQ_PRIORITY, + (stm32_dmaisr_t)adc_lld_serve_dma_interrupt, + (void *)adcp); + osalDbgAssert(adcp->dmastp != NULL, "unable to allocate stream"); clkmask |= (1 << 0); #if defined(STM32F3XX) @@ -579,12 +578,11 @@ void adc_lld_start(ADCDriver *adcp) { #if STM32_ADC_USE_ADC2 if (&ADCD2 == adcp) { - bool b; - b = dmaStreamAllocate(adcp->dmastp, - STM32_ADC_ADC2_DMA_IRQ_PRIORITY, - (stm32_dmaisr_t)adc_lld_serve_dma_interrupt, - (void *)adcp); - osalDbgAssert(!b, "stream already allocated"); + adcp->dmastp = dmaStreamAllocI(STM32_ADC_ADC2_DMA_STREAM, + STM32_ADC_ADC2_DMA_IRQ_PRIORITY, + (stm32_dmaisr_t)adc_lld_serve_dma_interrupt, + (void *)adcp); + osalDbgAssert(adcp->dmastp != NULL, "unable to allocate stream"); clkmask |= (1 << 1); #if defined(STM32F3XX) @@ -598,12 +596,11 @@ void adc_lld_start(ADCDriver *adcp) { #if STM32_ADC_USE_ADC3 if (&ADCD3 == adcp) { - bool b; - b = dmaStreamAllocate(adcp->dmastp, - STM32_ADC_ADC3_DMA_IRQ_PRIORITY, - (stm32_dmaisr_t)adc_lld_serve_dma_interrupt, - (void *)adcp); - osalDbgAssert(!b, "stream already allocated"); + adcp->dmastp = dmaStreamAllocI(STM32_ADC_ADC3_DMA_STREAM, + STM32_ADC_ADC3_DMA_IRQ_PRIORITY, + (stm32_dmaisr_t)adc_lld_serve_dma_interrupt, + (void *)adcp); + osalDbgAssert(adcp->dmastp != NULL, "unable to allocate stream"); clkmask |= (1 << 2); #if defined(STM32F3XX) @@ -617,12 +614,11 @@ void adc_lld_start(ADCDriver *adcp) { #if STM32_ADC_USE_ADC4 if (&ADCD4 == adcp) { - bool b; - b = dmaStreamAllocate(adcp->dmastp, - STM32_ADC_ADC4_DMA_IRQ_PRIORITY, - (stm32_dmaisr_t)adc_lld_serve_dma_interrupt, - (void *)adcp); - osalDbgAssert(!b, "stream already allocated"); + adcp->dmastp = dmaStreamAllocI(STM32_ADC_ADC4_DMA_STREAM, + STM32_ADC_ADC4_DMA_IRQ_PRIORITY, + (stm32_dmaisr_t)adc_lld_serve_dma_interrupt, + (void *)adcp); + osalDbgAssert(adcp->dmastp != NULL, "unable to allocate stream"); clkmask |= (1 << 3); #if defined(STM32F3XX) @@ -671,7 +667,8 @@ void adc_lld_stop(ADCDriver *adcp) { if (adcp->state == ADC_READY) { /* Releasing the associated DMA channel.*/ - dmaStreamRelease(adcp->dmastp); + dmaStreamFreeI(adcp->dmastp); + adcp->dmastp = NULL; /* Stopping the ongoing conversion, if any.*/ adc_lld_stop_adc(adcp); @@ -727,16 +724,16 @@ void adc_lld_stop(ADCDriver *adcp) { #if defined(STM32F3XX) if ((clkmask & 0x3) == 0) { - rccDisableADC12(FALSE); + rccDisableADC12(); } if ((clkmask & 0xC) == 0) { - rccDisableADC34(FALSE); + rccDisableADC34(); } #endif #if defined(STM32L4XX) if ((clkmask & 0x7) == 0) { - rccDisableADC123(FALSE); + rccDisableADC123(); } #endif } diff --git a/halconf.h b/halconf.h index 5ea5b6e..3a2caf3 100644 --- a/halconf.h +++ b/halconf.h @@ -28,6 +28,9 @@ #ifndef _HALCONF_H_ #define _HALCONF_H_ +#define _CHIBIOS_HAL_CONF_ +#define _CHIBIOS_HAL_CONF_VER_9_1_ + #include "mcuconf.h" /** diff --git a/ili9341.c b/ili9341.c index e775855..61c80bb 100644 --- a/ili9341.c +++ b/ili9341.c @@ -881,7 +881,13 @@ int lcd_printf(int16_t x, int16_t y, const char *fmt, ...) { // Init small lcd print stream struct lcd_printStreamVMT { _base_sequential_stream_methods - } lcd_vmt = {NULL, NULL, put_char, NULL}; + } lcd_vmt = { + .instance_offset = 0U, + .write = NULL, + .read = NULL, + .put = put_char, + .get = NULL + }; lcdPrintStream ps = {&lcd_vmt, x, y, x, y}; // Performing the print operation using the common code. va_list ap; @@ -1357,7 +1363,7 @@ static uint8_t SD_WaitNotBusy(uint32_t wait_time) { // Receive data block from SD static bool SD_RxDataBlock(uint8_t *buff, uint16_t len, uint8_t token) { // loop until receive read response token or timeout ~50ms - if (!SD_WaitDataToken(token, MS2ST(50))) { + if (!SD_WaitDataToken(token, TIME_MS2I(50))) { DEBUG_PRINT(" rx SD_WaitDataToken err\r\n"); return FALSE; } @@ -1408,7 +1414,7 @@ static bool SD_TxDataBlock(const uint8_t *buff, uint16_t len, uint8_t token) { } #if 1 // Wait busy (recommended timeout is 250ms (500ms for SDXC) set 250ms - resp = SD_WaitNotBusy(MS2ST(250)); + resp = SD_WaitNotBusy(TIME_MS2I(250)); if (resp == 0xFF) return TRUE; #else @@ -1427,7 +1433,7 @@ static uint8_t SD_SendCmd(uint8_t cmd, uint32_t arg) { uint8_t buf[6]; volatile uint8_t r1; // wait SD ready after last Tx (recommended timeout is 250ms (500ms for SDXC) set 250ms - if ((r1 = SD_WaitNotBusy(MS2ST(500))) != 0xFF) { + if ((r1 = SD_WaitNotBusy(TIME_MS2I(500))) != 0xFF) { DEBUG_PRINT(" SD_WaitNotBusy CMD%d err, %02x\r\n", cmd-0x40, (uint32_t)r1); return 0xFF; } @@ -1707,7 +1713,7 @@ DRESULT disk_ioctl(BYTE pdrv, BYTE cmd, void* buff) { // Nothing to do for this command if each write operation to the media is completed // within the disk_write function. case CTRL_SYNC: - if (SD_WaitNotBusy(MS2ST(200)) == 0xFF) res = RES_OK; + if (SD_WaitNotBusy(TIME_MS2I(200)) == 0xFF) res = RES_OK; break; #if FF_USE_TRIM == 1 // Informs the device the data on the block of sectors is no longer needed and it can be erased. diff --git a/main.c b/main.c index 03ee392..09fc658 100644 --- a/main.c +++ b/main.c @@ -209,7 +209,7 @@ static THD_FUNCTION(Thread1, arg) { completed = sweep(true); #ifdef __USE_SD_CARD__ - if (setting.trigger_auto_save && (last_auto_save == 0 || chVTGetSystemTimeX() - last_auto_save > S2ST(30)) ) { // once every 30 seconds max + if (setting.trigger_auto_save && (last_auto_save == 0 || chVTGetSystemTimeX() - last_auto_save > TIME_S2I(30)) ) { // once every 30 seconds max uint16_t old_mode = config._mode; config._mode |= _MODE_AUTO_FILENAME; save_csv(1+(2<<0)); // frequencies + trace 1 @@ -455,7 +455,7 @@ VNA_SHELL_FUNCTION(cmd_restart) (void)argc; (void)argv; if (argc == 1) { - restart_interval = S2ST(my_atoi(argv[0])); + restart_interval = TIME_S2I(my_atoi(argv[0])); if (restart_interval) { restart_set_time = chVTGetSystemTimeX(); if (restart_set_time == 0) @@ -2418,7 +2418,7 @@ VNA_SHELL_FUNCTION(cmd_threads) #endif shell_printf("%08x|%08x|%08x|%08x|%4u|%4u|%9s|%12s"VNA_SHELL_NEWLINE_STR, stklimit, (uint32_t)tp->ctx.sp, max_stack_use, (uint32_t)tp, - (uint32_t)tp->refs - 1, (uint32_t)tp->prio, states[tp->state], + (uint32_t)tp->refs - 1, (uint32_t)tp->hdr.pqueue.prio, states[tp->state], tp->name == NULL ? "" : tp->name); tp = chRegNextThread(tp); } while (tp != NULL); @@ -2697,18 +2697,21 @@ void shell_update_speed(void){ } void shell_reset_console(void){ + osalSysLock(); // Reset I/O queue over USB (for USB need also connect/disconnect) if (usb_IsActive()){ if (config._mode & _MODE_SERIAL) - sduDisconnectI(&SDU1); + sduSuspendHookI(&SDU1); else sduConfigureHookI(&SDU1); } // Reset I/O queue over Serial // oqResetI(&SD1.oqueue); // iqResetI(&SD1.iqueue); - qResetI(&SD1.oqueue); - qResetI(&SD1.iqueue); + oqResetI(&SD1.oqueue); + iqResetI(&SD1.iqueue); + osalOsRescheduleS(); + osalSysUnlock(); } @@ -2886,7 +2889,7 @@ static void VNAShell_executeLine(char *line) int timeout_count = 0; msg_t result; do { - result = osalThreadEnqueueTimeoutS(&shell_thread, MS2ST(5000)); // 5 second timeout + result = osalThreadEnqueueTimeoutS(&shell_thread, TIME_MS2I(5000)); // 5 second timeout if (result == MSG_TIMEOUT) { timeout_count++; if (timeout_count > 3) { @@ -3004,6 +3007,7 @@ static PWMConfig pwmcfg = { {PWM_OUTPUT_DISABLED, NULL} }, 0, + 0, 0 }; @@ -3516,7 +3520,7 @@ void hard_fault_handler_c(uint32_t *sp) #endif lcd_printf(x, y+=FONT_STR_HEIGHT, "%08x|%08x|%08x|%08x|%4u|%4u|%9s|%12s", stklimit, (uint32_t)tp->ctx.sp, max_stack_use, (uint32_t)tp, - (uint32_t)tp->refs - 1, (uint32_t)tp->prio, states[tp->state], + (uint32_t)tp->refs - 1, (uint32_t)tp->hdr.pqueue.prio, states[tp->state], tp->name == NULL ? "" : tp->name); tp = chRegNextThread(tp); } while (tp != NULL); diff --git a/plot.c b/plot.c index 8ffe9fe..4514719 100644 --- a/plot.c +++ b/plot.c @@ -1509,7 +1509,13 @@ int cell_printf(int16_t x, int16_t y, const char *fmt, ...) { // Init small cell print stream struct cellprintStreamVMT { _base_sequential_stream_methods - } cell_vmt = {NULL, NULL, NULL, NULL}; + } cell_vmt = { + .instance_offset = 0U, + .write = NULL, + .read = NULL, + .put = NULL, + .get = NULL + }; screenPrintStream ps = {&cell_vmt, x, y}; // Select font and skip print if not on cell (at top/bottom) switch (*fmt++){ diff --git a/sa_core.c b/sa_core.c index 85013ab..4c8a45d 100644 --- a/sa_core.c +++ b/sa_core.c @@ -5849,7 +5849,7 @@ static volatile int dummy; } else if (actual_max_level > target_level && setting.attenuate_x2 < 60) { delta = actual_max_level - target_level; } - if (chVTGetSystemTimeX() - sweep_elapsed > MS2ST(1000)){ + if (chVTGetSystemTimeX() - sweep_elapsed > TIME_MS2I(1000)){ if (( delta < -5 || delta > +5) || delta > 10 ) { setting.attenuate_x2 += delta + delta; if (setting.attenuate_x2 < 0) diff --git a/si5351.c b/si5351.c index 5a9a21c..c94574b 100644 --- a/si5351.c +++ b/si5351.c @@ -210,7 +210,7 @@ static bool si5351_wait_ready(void) { uint8_t status = 0xff; systime_t start = chVTGetSystemTime(); - systime_t end = start + MS2ST(1000); // 1000 ms timeout + systime_t end = start + TIME_MS2I(1000); // 1000 ms timeout while (chVTIsSystemTimeWithin(start, end)) { if(!si5351_read(0, &status)) @@ -230,7 +230,7 @@ static void si5351_wait_pll_lock(void) status = 0xff; // comm timeout if ((status & 0x60) == 0) return; - systime_t end = start + MS2ST(100); // 100 ms timeout + systime_t end = start + TIME_MS2I(100); // 100 ms timeout while (chVTIsSystemTimeWithin(start, end)) { if(!si5351_read(0, &status)) diff --git a/ui.c b/ui.c index b2847fd..b20b512 100644 --- a/ui.c +++ b/ui.c @@ -48,10 +48,10 @@ uistat_t uistat = { #define EVT_DOWN 0x20 #define EVT_REPEAT 0x40 -#define BUTTON_DOWN_LONG_TICKS MS2ST(500) // 500ms -#define BUTTON_DOUBLE_TICKS MS2ST(250) // 250ms -#define BUTTON_REPEAT_TICKS MS2ST( 40) // 40ms -#define BUTTON_DEBOUNCE_TICKS MS2ST( 2) // 2ms +#define BUTTON_DOWN_LONG_TICKS TIME_MS2I(500) // 500ms +#define BUTTON_DOUBLE_TICKS TIME_MS2I(250) // 250ms +#define BUTTON_REPEAT_TICKS TIME_MS2I(40) // 40ms +#define BUTTON_DEBOUNCE_TICKS TIME_MS2I(2) // 2ms /* lever switch assignment */ #define BIT_UP1 3 @@ -684,7 +684,7 @@ extern const char *states[]; #endif lcd_printf(x, y+=bFONT_STR_HEIGHT, "%08x|%08x|%08x|%08x|%4u|%4u|%9s|%12s", stklimit, (uint32_t)tp->ctx.sp, max_stack_use, (uint32_t)tp, - (uint32_t)tp->refs - 1, (uint32_t)tp->prio, states[tp->state], + (uint32_t)tp->refs - 1, (uint32_t)tp->hdr.pqueue.prio, states[tp->state], tp->name == NULL ? "" : tp->name); tp = chRegNextThread(tp); } while (tp != NULL); @@ -1599,10 +1599,12 @@ static const menuitem_t menu_calibrate_normal[]; #endif static const menuitem_t menu_calibrate[]; static const menuitem_t menu_sweep[]; +#ifdef TINYSA4 static const menuitem_t menu_settings[]; -static const menuitem_t menu_settings2[]; static const menuitem_t menu_lowoutput_settings[]; static const menuitem_t menu_lowoutput_settings_max[]; +#endif +static const menuitem_t menu_settings2[]; extern bool dirty; char range_text[20]; #ifdef TINYSA4 @@ -8382,10 +8384,9 @@ ui_process(void) } /* Triggered when the button is pressed or released. The LED4 is set to ON.*/ -static void extcb1(EXTDriver *extp, expchannel_t channel) +static void extcb1(void *arg) { - (void)extp; - (void)channel; + uint32_t channel = (uint32_t)(uintptr_t)arg; #ifdef __USE_SD_CARD__ if (channel == 12) SD_PowerOff(); @@ -8397,42 +8398,6 @@ static void extcb1(EXTDriver *extp, expchannel_t channel) // cur_button = READ_PORT() & BUTTON_MASK; } -static const EXTConfig extcfg = { - { - {EXT_CH_MODE_DISABLED, NULL}, - {EXT_CH_MODE_RISING_EDGE | EXT_CH_MODE_AUTOSTART | EXT_MODE_GPIOA, extcb1}, - {EXT_CH_MODE_RISING_EDGE | EXT_CH_MODE_AUTOSTART | EXT_MODE_GPIOA, extcb1}, - {EXT_CH_MODE_RISING_EDGE | EXT_CH_MODE_AUTOSTART | EXT_MODE_GPIOA, extcb1}, - {EXT_CH_MODE_DISABLED, NULL}, - {EXT_CH_MODE_DISABLED, NULL}, - {EXT_CH_MODE_DISABLED, NULL}, - {EXT_CH_MODE_DISABLED, NULL}, - {EXT_CH_MODE_DISABLED, NULL}, -#ifdef __WAIT_CTS_WHILE_SLEEPING__ - {EXT_CH_MODE_RISING_EDGE | EXT_CH_MODE_AUTOSTART | EXT_MODE_GPIOB, extcb1}, -#else - {EXT_CH_MODE_DISABLED, NULL}, -#endif - {EXT_CH_MODE_DISABLED, NULL}, - {EXT_CH_MODE_DISABLED, NULL}, -#ifdef __USE_SD_CARD__ - {EXT_CH_MODE_RISING_EDGE | EXT_CH_MODE_AUTOSTART | EXT_MODE_GPIOB, extcb1}, -#else - {EXT_CH_MODE_DISABLED, NULL}, -#endif - {EXT_CH_MODE_DISABLED, NULL}, - {EXT_CH_MODE_DISABLED, NULL}, - {EXT_CH_MODE_DISABLED, NULL}, - {EXT_CH_MODE_DISABLED, NULL}, - {EXT_CH_MODE_DISABLED, NULL}, - {EXT_CH_MODE_DISABLED, NULL}, - {EXT_CH_MODE_DISABLED, NULL}, - {EXT_CH_MODE_DISABLED, NULL}, - {EXT_CH_MODE_DISABLED, NULL}, - {EXT_CH_MODE_DISABLED, NULL} - } -}; - void handle_touch_interrupt(void) { @@ -8442,8 +8407,24 @@ handle_touch_interrupt(void) void ui_init() { - // Activates the EXT driver 1. - extStart(&EXTD1, &extcfg); + /* HAL 9.1 routes STM32 EXTI through PAL line events. */ + palSetLineCallback(PAL_LINE(GPIOA, GPIOA_LEVER1), extcb1, + (void *)(uintptr_t)GPIOA_LEVER1); + palEnableLineEvent(PAL_LINE(GPIOA, GPIOA_LEVER1), PAL_EVENT_MODE_RISING_EDGE); + palSetLineCallback(PAL_LINE(GPIOA, GPIOA_PUSH), extcb1, + (void *)(uintptr_t)GPIOA_PUSH); + palEnableLineEvent(PAL_LINE(GPIOA, GPIOA_PUSH), PAL_EVENT_MODE_RISING_EDGE); + palSetLineCallback(PAL_LINE(GPIOA, GPIOA_LEVER2), extcb1, + (void *)(uintptr_t)GPIOA_LEVER2); + palEnableLineEvent(PAL_LINE(GPIOA, GPIOA_LEVER2), PAL_EVENT_MODE_RISING_EDGE); +#ifdef __WAIT_CTS_WHILE_SLEEPING__ + palSetLineCallback(LINE_RX_CTS, extcb1, (void *)(uintptr_t)9U); + palEnableLineEvent(LINE_RX_CTS, PAL_EVENT_MODE_RISING_EDGE); +#endif +#ifdef __USE_SD_CARD__ + palSetLineCallback(LINE_SD_CD, extcb1, (void *)(uintptr_t)12U); + palEnableLineEvent(LINE_SD_CD, PAL_EVENT_MODE_RISING_EDGE); +#endif // Init touch subsystem touch_init(); } diff --git a/usbcfg.c b/usbcfg.c index 398e3cb..254ea6a 100644 --- a/usbcfg.c +++ b/usbcfg.c @@ -297,8 +297,10 @@ static const USBEndpointConfig ep1config = { sduDataReceived, 0x0040, 0x0040, - &ep1instate, - &ep1outstate, + &ep1instate, + &ep1outstate, + 1, + NULL }; /** @@ -316,8 +318,10 @@ static const USBEndpointConfig ep2config = { NULL, 0x0010, 0x0000, - &ep2instate, - NULL, + &ep2instate, + NULL, + 1, + NULL }; /* @@ -327,10 +331,8 @@ static void usb_event(USBDriver *usbp, usbevent_t event) { extern SerialUSBDriver SDU1; switch (event) { - case USB_EVENT_RESET: - return; - case USB_EVENT_ADDRESS: - return; + case USB_EVENT_ADDRESS: + return; case USB_EVENT_CONFIGURED: chSysLockFromISR(); @@ -345,16 +347,21 @@ static void usb_event(USBDriver *usbp, usbevent_t event) { chSysUnlockFromISR(); return; - case USB_EVENT_SUSPEND: + case USB_EVENT_RESET: + case USB_EVENT_UNCONFIGURED: + case USB_EVENT_SUSPEND: chSysLockFromISR(); /* Disconnection event on suspend.*/ - sduDisconnectI(&SDU1); + sduSuspendHookI(&SDU1); chSysUnlockFromISR(); return; - case USB_EVENT_WAKEUP: - return; + case USB_EVENT_WAKEUP: + chSysLockFromISR(); + sduWakeupHookI(&SDU1); + chSysUnlockFromISR(); + return; case USB_EVENT_STALLED: return; } From 9a84753a389f01dd43ab428271d6e292df3084de Mon Sep 17 00:00:00 2001 From: PhysicistJohn <54456354+PhysicistJohn@users.noreply.github.com> Date: Tue, 14 Jul 2026 22:28:20 -0700 Subject: [PATCH 2/7] build: preserve F303 float constant semantics --- Makefile | 4 +++- docs/CHIBIOS_21_11_5_PORT.md | 7 ++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 12009e5..5afef93 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,9 @@ endif # Compiler options here. ifeq ($(USE_OPT),) ifeq ($(TARGET),F303) -USE_OPT = -Og -fno-inline-small-functions -ggdb -fomit-frame-pointer -falign-functions=16 --specs=nano.specs -fstack-usage -std=c11 +USE_OPT = -Og -fno-inline-small-functions -ggdb -fomit-frame-pointer -falign-functions=16 --specs=nano.specs -fstack-usage -fsingle-precision-constant -std=c11 +# The legacy ChibiOS rules appended -fsingle-precision-constant for hard-FPU +# builds. Keep that numeric behavior explicit when using the 21.11.x rules. #USE_OPT+=-fstack-protector-strong else USE_OPT = -Og -fno-inline-small-functions -ggdb -fomit-frame-pointer -falign-functions=16 --specs=nano.specs -fstack-usage -fsingle-precision-constant diff --git a/docs/CHIBIOS_21_11_5_PORT.md b/docs/CHIBIOS_21_11_5_PORT.md index 0b7a101..c1bb16a 100644 --- a/docs/CHIBIOS_21_11_5_PORT.md +++ b/docs/CHIBIOS_21_11_5_PORT.md @@ -28,7 +28,12 @@ this port is marked ready. queue reset calls, time conversions, and thread diagnostics. - Update the project-local F303 ADC LLD while retaining its tinySA-specific behavior. -- Convert custom `BaseSequentialStream` VMTs to the current layout. +- Convert custom `BaseSequentialStream` VMTs to the current layout, including + the required `instance_offset` field. +- Preserve the legacy hard-FPU build's `-fsingle-precision-constant` behavior + explicitly. ChibiOS 21.11.x no longer adds it automatically; omitting it + promotes unsuffixed sweep constants to software double precision and causes + a measurable self-test sweep regression. ## Build From 76d60c15f66e6ea29967a651dc9971871654d88a Mon Sep 17 00:00:00 2001 From: PhysicistJohn <54456354+PhysicistJohn@users.noreply.github.com> Date: Tue, 14 Jul 2026 00:08:35 -0700 Subject: [PATCH 3/7] perf: restore ChibiOS sweep and display timing --- ili9341.c | 17 +++++++++++++---- sa_core.c | 40 ++++++++++++++++++++++------------------ 2 files changed, 35 insertions(+), 22 deletions(-) diff --git a/ili9341.c b/ili9341.c index 61c80bb..e920f08 100644 --- a/ili9341.c +++ b/ili9341.c @@ -526,7 +526,10 @@ void ili9341_init(void) LCD_CS_HIGH; } -static void ili9341_setWindow(uint8_t cmd, uint16_t x, uint16_t y, uint16_t w, uint16_t h){ +// Every DMA region starts here; keep command setup inlined on the rendering +// path without enabling strict aliasing for the legacy display code. +static void __attribute__((optimize("O3,no-strict-aliasing"))) +ili9341_setWindow(uint8_t cmd, uint16_t x, uint16_t y, uint16_t w, uint16_t h){ // Any LCD exchange start from this #ifdef __USE_DISPLAY_DMA__ dmaChannelWaitCompletionRxTx(); @@ -663,8 +666,11 @@ void ili9341_bulk_finish(void){ } #endif // Copy part of spi_buffer to region, no wait completion after if buffer count !=1 -#ifndef ili9341_bulk_continue -void ili9341_bulk_continue(int x, int y, int w, int h) +#ifndef ili9341_bulk_continue +// This runs once per dirty plot cell. Keep the DMA handoff inlined so a held +// self-test screen is not slower than the historical display pipeline. +void __attribute__((optimize("O3,no-strict-aliasing"))) +ili9341_bulk_continue(int x, int y, int w, int h) { ili9341_bulk_finish(); // Wait DMA ili9341_DMA_bulk(x, y, w, h, ili9341_get_cell_buffer()); // Send new cell data @@ -783,7 +789,10 @@ void ili9341_set_rotation(uint8_t r) send_command(ILI9341_MEMORY_ACCESS_CONTROL, 1, &r); } -void ili9341_blitBitmap(int x, int y, int width, int height, const uint8_t *b) +// Text drawing calls this once per glyph. Inline the DMA setup on this hot +// path while retaining the aliasing rules used by the rest of the release. +void __attribute__((optimize("O3,no-strict-aliasing"))) +ili9341_blitBitmap(int x, int y, int width, int height, const uint8_t *b) { pixel_t *buf = spi_buffer; uint8_t bits = 0; diff --git a/sa_core.c b/sa_core.c index 4c8a45d..1ffdf3c 100644 --- a/sa_core.c +++ b/sa_core.c @@ -5301,33 +5301,37 @@ static bool sweep(bool break_on_operation) } } } - systime_t local_sweep_time = sa_ST2US(chVTGetSystemTimeX() - start_of_sweep_timestamp); - if (setting.actual_sweep_time_us > ONE_SECOND_TIME) - local_sweep_time = setting.actual_sweep_time_us; if ( #ifdef TINYSA4 progress_bar && #endif - show_bar && (( local_sweep_time > ONE_SECOND_TIME && (i & 0x07) == 0) /* || ( local_sweep_time > ONE_SECOND_TIME*10)*/ ) ) + show_bar && (i & 0x07) == 0) { - int pos = i * (WIDTH+1) / sweep_points; - ili9341_set_background(LCD_SWEEP_LINE_COLOR); - ili9341_fill(OFFSETX, CHART_BOTTOM+1, pos, 1); // update sweep progress bar - ili9341_set_background(LCD_BG_COLOR); - ili9341_fill(OFFSETX+pos, CHART_BOTTOM+1, WIDTH-pos, 1); - - if (local_sweep_time > 10 * ONE_SECOND_TIME) { - plot_into_index(measured); - redraw_request |= REDRAW_CELLS | REDRAW_BATTERY | REDRAW_INBETWEEN; - // plot trace and other indications as raster - draw_all(true); // flush markmap only if scan completed to prevent - } + // The progress bar is only updated every eighth point. Avoid reading and + // converting the system timer on the other seven points of fast sweeps. + systime_t local_sweep_time = sa_ST2US(chVTGetSystemTimeX() - start_of_sweep_timestamp); + if (setting.actual_sweep_time_us > ONE_SECOND_TIME) + local_sweep_time = setting.actual_sweep_time_us; + if (local_sweep_time > ONE_SECOND_TIME) { + int pos = i * (WIDTH+1) / sweep_points; + ili9341_set_background(LCD_SWEEP_LINE_COLOR); + ili9341_fill(OFFSETX, CHART_BOTTOM+1, pos, 1); // update sweep progress bar + ili9341_set_background(LCD_BG_COLOR); + ili9341_fill(OFFSETX+pos, CHART_BOTTOM+1, WIDTH-pos, 1); + + if (local_sweep_time > 10 * ONE_SECOND_TIME) { + plot_into_index(measured); + redraw_request |= REDRAW_CELLS | REDRAW_BATTERY | REDRAW_INBETWEEN; + // plot trace and other indications as raster + draw_all(true); // flush markmap only if scan completed to prevent + } #ifdef __SWEEP_RESTART__ - if (MODE_OUTPUT(setting.mode) && (setting.level_sweep != 0 || get_sweep_frequency(ST_SPAN) != 0)) - refresh_sweep_menu(i); + if (MODE_OUTPUT(setting.mode) && (setting.level_sweep != 0 || get_sweep_frequency(ST_SPAN) != 0)) + refresh_sweep_menu(i); #endif + } } // ----------------------- debug avoid -------------------------------- if (debug_avoid) { From d50a3c902a5a0b60dd0f820fa01a64fad48a0c3c Mon Sep 17 00:00:00 2001 From: PhysicistJohn <54456354+PhysicistJohn@users.noreply.github.com> Date: Tue, 14 Jul 2026 22:04:58 -0700 Subject: [PATCH 4/7] fix: initialize complete backup checksum image The RTC backup checksum covers every byte except the checksum itself, including a reserved packed byte that was never assigned. Its stack value could therefore make otherwise valid state fail verification after reset. Zero-initialize the complete backup image before assigning persisted fields so the checksum and reserved byte are deterministic. --- main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/main.c b/main.c index 09fc658..745074a 100644 --- a/main.c +++ b/main.c @@ -161,7 +161,8 @@ static THD_FUNCTION(Thread1, arg) while (1) { // START_PROFILE if (sweep_mode&(SWEEP_ENABLE|SWEEP_ONCE)) { - backup_t b; + /* Initialize the reserved byte covered by the checksum too. */ + backup_t b = {0}; b.data.frequency0 = setting.frequency0; b.data.frequency1 = setting.frequency1; if (setting.auto_attenuation) From 0d26504934350a1bd7b2e812004cb28b56f622a5 Mon Sep 17 00:00:00 2001 From: PhysicistJohn <54456354+PhysicistJohn@users.noreply.github.com> Date: Tue, 14 Jul 2026 22:03:59 -0700 Subject: [PATCH 5/7] fix: refresh zero-span grid from completed sweep The zero-span time grid can be calculated before actual_sweep_time_us is updated, leaving the display aligned to the previous sweep duration. Calculate grid geometry without mutating global state, recompute it after the measured sweep time is stored, and redraw only when the resulting tuple changes. Use 64-bit arithmetic through the final time-domain division so short sweeps retain exact pixel geometry. --- nanovna.h | 1 + plot.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++--------- sa_core.c | 5 +++++ 3 files changed, 53 insertions(+), 9 deletions(-) diff --git a/nanovna.h b/nanovna.h index 18fd053..0fa6496 100644 --- a/nanovna.h +++ b/nanovna.h @@ -984,6 +984,7 @@ extern int8_t marker_tracking; void plot_init(void); void update_grid(void); +void update_grid_if_changed(void); void request_to_redraw_grid(void); void redraw_frame(void); //void redraw_all(void); diff --git a/plot.c b/plot.c index 4514719..bdce5d7 100644 --- a/plot.c +++ b/plot.c @@ -110,19 +110,24 @@ float2int(float v) } #endif -void update_grid(void) +static void calculate_grid(int16_t *offset, int16_t *width, freq_t *span) { freq_t gdigit = 1000000000; freq_t fstart = get_sweep_frequency(ST_START) + (setting.frequency_offset - FREQUENCY_SHIFT); freq_t fspan = get_sweep_frequency(ST_SPAN); freq_t grid; + bool time_domain = fspan == 0; - if (fspan == 0) { + if (time_domain) { fspan = setting.actual_sweep_time_us; // Time in uS fstart = 0; } + if (fspan == 0) + fspan = 1; if (config.gridlines == 0) { grid = fspan/10; + if (grid == 0) + grid = 1; } else { if (config.gridlines < 3) config.gridlines = 6; @@ -139,21 +144,54 @@ void update_grid(void) gdigit /= 10; } } - grid_span = grid; - if (grid > 1000) { - grid_offset = (WIDTH) * ((fstart % grid) / 100) / (fspan / 100); - grid_width = (WIDTH) * (grid / 100) / (fspan / 1000); + *span = grid; + if (time_domain) { + // The zero-span x axis is measured in microseconds. Keep the full ratio + // until the final division so 5-20 ms sweeps do not shift by whole pixels. + *offset = (int16_t)((uint64_t)WIDTH * (fstart % grid) / fspan); + *width = (int16_t)((uint64_t)WIDTH * 10U * grid / fspan); + } else if (grid > 1000) { + *offset = (WIDTH) * ((fstart % grid) / 100) / (fspan / 100); + *width = (WIDTH) * (grid / 100) / (fspan / 1000); } else { - grid_offset = (WIDTH) * ((fstart % grid)) / (fspan); - grid_width = (WIDTH) * (grid) / (fspan/10); + *offset = (WIDTH) * ((fstart % grid)) / (fspan); + *width = (WIDTH) * (grid) / (fspan/10); } + if (*width < 1) + *width = 1; if (config.gridlines == 0) - grid_offset = 0; + *offset = 0; +} + +static void apply_grid(int16_t offset, int16_t width, freq_t span) +{ + grid_offset = offset; + grid_width = width; + grid_span = span; // if (setting.waterfall) set_level_meter_or_waterfall(); redraw_request |= REDRAW_FREQUENCY | REDRAW_AREA; } +void update_grid(void) +{ + int16_t offset; + int16_t width; + freq_t span; + calculate_grid(&offset, &width, &span); + apply_grid(offset, width, span); +} + +void update_grid_if_changed(void) +{ + int16_t offset; + int16_t width; + freq_t span; + calculate_grid(&offset, &width, &span); + if (offset != grid_offset || width != grid_width || span != grid_span) + apply_grid(offset, width, span); +} + #if 0 static int rectangular_grid(int x, int y) diff --git a/sa_core.c b/sa_core.c index 1ffdf3c..b9d6ecf 100644 --- a/sa_core.c +++ b/sa_core.c @@ -5700,6 +5700,11 @@ static volatile int dummy; redraw_request|=REDRAW_CAL_STATUS | REDRAW_FREQUENCY; } setting.actual_sweep_time_us = setting.measure_sweep_time_us; + // The zero-span x axis represents the sweep that just completed. Recompute + // its tuple every time; update_grid_if_changed() suppresses redraws while + // the exact grid geometry remains unchanged. + if (FREQ_IS_CW()) + update_grid_if_changed(); // Not possible reduce sweep time, it minimum! if (setting.sweep_time_us < setting.actual_sweep_time_us && setting.additional_step_delay_us == 0){ // Warning!! not correct set sweep time here, you get error!! From 9e16ca4fb2d66075f94c60664e8971b48d5e55bb Mon Sep 17 00:00:00 2001 From: PhysicistJohn <54456354+PhysicistJohn@users.noreply.github.com> Date: Tue, 14 Jul 2026 22:29:23 -0700 Subject: [PATCH 6/7] perf: restore ChibiOS self-test timing --- sa_core.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 74 insertions(+), 10 deletions(-) diff --git a/sa_core.c b/sa_core.c index b9d6ecf..f47659b 100644 --- a/sa_core.c +++ b/sa_core.c @@ -499,6 +499,9 @@ const freq_t fh_high[] = { 480000000, 960000000, 1920000000, 2880000000, 3840000 uint8_t in_selftest = false; uint8_t ignore_stored = false; +#ifdef TINYSA4 +static uint8_t factory_selftest_direct_scratch = false; +#endif uint8_t in_step_test = false; uint8_t in_calibration = false; uint8_t calibration_stage; @@ -3555,6 +3558,20 @@ deviceRSSI_t age[POINTS_COUNT]; // Array used for 1: calculating the age of static pureRSSI_t correct_RSSI; static pureRSSI_t correct_RSSI_freq; systime_t start_of_sweep_timestamp; + +#ifdef TINYSA4 +#if OSAL_ST_MODE != OSAL_ST_MODE_FREERUNNING +#error "The optimized sweep clock requires the ChibiOS free-running system timer" +#endif +#if CH_CFG_ST_TIMEDELTA == 0 +#error "The optimized sweep clock requires the port-backed ChibiOS system timer" +#endif +// Keep the timed sweep hot path on the current free-running timer backend. +// Re-audit this equivalence whenever the ChibiOS system-timer backend changes. +#define SWEEP_CLOCK_NOW() st_lld_get_counter() +#else +#define SWEEP_CLOCK_NOW() chVTGetSystemTimeX() +#endif static systime_t sweep_elapsed = 0; // Time since first start of sweeping, used only for auto attenuate uint8_t signal_is_AM = false; static uint8_t check_for_AM = false; @@ -3637,6 +3654,9 @@ bool depth_error = false; #endif +#ifdef TINYSA4 +__attribute__((optimize("O2,no-strict-aliasing"))) +#endif pureRSSI_t perform(bool break_on_operation, int i, freq_t f, int tracking) // Measure the RSSI for one frequency, used from sweep and other measurement routines. Must do all HW setup { int modulation_delay = 0; @@ -4761,7 +4781,7 @@ again: // Spur redu skip_LO_setting: if (i == 0 && t == 0) // if first point in scan (here is get 1 point data) - start_of_sweep_timestamp = chVTGetSystemTimeX(); // initialize start sweep time + start_of_sweep_timestamp = SWEEP_CLOCK_NOW(); // initialize start sweep time if (MODE_OUTPUT(setting.mode)) { // No substepping and no RSSI in output mode if (break_on_operation && operation_requested) // break subscanning if requested @@ -4893,7 +4913,7 @@ again: // Spur redu if (setting.trigger == T_SINGLE) { set_trigger(T_DONE); } - start_of_sweep_timestamp = chVTGetSystemTimeX(); + start_of_sweep_timestamp = SWEEP_CLOCK_NOW(); } TRACE(5); #ifdef TINYSA4 @@ -5067,6 +5087,9 @@ void reset_band(void) { #endif // main loop for measurement +#ifdef TINYSA4 +__attribute__((optimize("O2,no-strict-aliasing"))) +#endif static bool sweep(bool break_on_operation) { float RSSI; @@ -5187,6 +5210,7 @@ static bool sweep(bool break_on_operation) #endif setting.measure_sweep_time_us = 0; // start measure sweep time // start_of_sweep_timestamp = chVTGetSystemTimeX(); // Will be set in perform + uint8_t progress_mask = 0x3f; sweep_again: // stay in sweep loop when output mode and modulation on. @@ -5305,14 +5329,18 @@ static bool sweep(bool break_on_operation) #ifdef TINYSA4 progress_bar && #endif - show_bar && (i & 0x07) == 0) + show_bar && (i & progress_mask) == 0) { - // The progress bar is only updated every eighth point. Avoid reading and - // converting the system timer on the other seven points of fast sweeps. - systime_t local_sweep_time = sa_ST2US(chVTGetSystemTimeX() - start_of_sweep_timestamp); - if (setting.actual_sweep_time_us > ONE_SECOND_TIME) - local_sweep_time = setting.actual_sweep_time_us; + // Expected long sweeps can use their known duration directly. For an + // expected fast sweep, sample every 64th point until an unexpected + // one-second overrun is observed, then resume the normal 8-point rate. + // This retains overrun progress while avoiding timer traffic in the + // common sub-second path. + systime_t local_sweep_time = setting.actual_sweep_time_us; + if (local_sweep_time <= ONE_SECOND_TIME) + local_sweep_time = sa_ST2US(SWEEP_CLOCK_NOW() - start_of_sweep_timestamp); if (local_sweep_time > ONE_SECOND_TIME) { + progress_mask = 0x07; int pos = i * (WIDTH+1) / sweep_points; ili9341_set_background(LCD_SWEEP_LINE_COLOR); ili9341_fill(OFFSETX, CHART_BOTTOM+1, pos, 1); // update sweep progress bar @@ -5655,6 +5683,14 @@ static volatile int dummy; // add_to_peak_finding(actual_t, i); } +#if TRACES_MAX == 4 + // The factory test freezes two default scratch passes below. Preserve + // their exact AV_OFF result once, after ACTUAL processing is complete. + if (factory_selftest_direct_scratch) { + temp_t[i] = RSSI; + stored2_t[i] = RSSI; + } +#endif } } @@ -5692,11 +5728,12 @@ static volatile int dummy; // ---------------------- process measured actual sweep time ----------------- // For CW mode value calculated in SI4432_Fill if (setting.measure_sweep_time_us == 0) - setting.measure_sweep_time_us = sa_ST2US(chVTGetSystemTimeX() - start_of_sweep_timestamp); + setting.measure_sweep_time_us = sa_ST2US(SWEEP_CLOCK_NOW() - start_of_sweep_timestamp); // Update actual time on change on status panel uint32_t delta = abs((int)(setting.actual_sweep_time_us - setting.measure_sweep_time_us)); - if ((delta<<3) > setting.actual_sweep_time_us){ // update if delta > 1/8 + bool sweep_time_changed = (delta<<3) > setting.actual_sweep_time_us; + if (sweep_time_changed){ // update if delta > 1/8 redraw_request|=REDRAW_CAL_STATUS | REDRAW_FREQUENCY; } setting.actual_sweep_time_us = setting.measure_sweep_time_us; @@ -7126,6 +7163,7 @@ void selftest(int test) { bool no_wait = false; #ifdef TINYSA4 + factory_selftest_direct_scratch = false; bool old_ultra = config.ultra; config.ultra = true; // if (adc_vbat_read() < 3800) { @@ -7167,7 +7205,33 @@ void selftest(int test) } do { test_prepare(test_step); +#if TRACES_MAX == 4 + // Preserve any pre-existing frozen traces. For the normal factory setup, + // freeze both AV_OFF scratch passes and write their raw samples directly. + // Zero-span timing still measures this faster path, and its grid is + // refreshed from that completed measurement below. + bool old_temp_stored = setting.stored[TRACE_TEMP]; + bool old_stored2_stored = setting.stored[TRACE_STORED2]; + factory_selftest_direct_scratch = + !debug_spur && !debug_avoid && + !old_temp_stored && !old_stored2_stored && + setting.average[TRACE_TEMP] == AV_OFF && + setting.subtract[TRACE_TEMP] == 0 && + !setting.normalized[TRACE_TEMP] && + setting.average[TRACE_STORED2] == AV_OFF && + setting.subtract[TRACE_STORED2] == 0 && + !setting.normalized[TRACE_STORED2]; + if (factory_selftest_direct_scratch) { + setting.stored[TRACE_TEMP] = true; + setting.stored[TRACE_STORED2] = true; + } +#endif test_acquire(test_step); // Acquire test +#if TRACES_MAX == 4 + factory_selftest_direct_scratch = false; + setting.stored[TRACE_TEMP] = old_temp_stored; + setting.stored[TRACE_STORED2] = old_stored2_stored; +#endif test_status[test_step] = test_validate(test_step); // Validate test if (test_step == 2) { From 5e0299009f29edf313e86452390a97119637a019 Mon Sep 17 00:00:00 2001 From: PhysicistJohn <54456354+PhysicistJohn@users.noreply.github.com> Date: Tue, 14 Jul 2026 22:39:33 -0700 Subject: [PATCH 7/7] fix: make F303 fault entry stack-safe --- Makefile | 4 ++- docs/CHIBIOS_21_11_5_PORT.md | 2 ++ main.c | 53 ++++++++++++++++++++++++++++-------- 3 files changed, 47 insertions(+), 12 deletions(-) diff --git a/Makefile b/Makefile index 5afef93..a906bad 100644 --- a/Makefile +++ b/Makefile @@ -95,7 +95,9 @@ endif ifeq ($(TARGET),F303) USE_FPU = hard USE_PROCESS_STACKSIZE = 0x480 - USE_EXCEPTIONS_STACKSIZE = 0x200 + # The fault reporter renders through the LCD/printf stack. Reserve room for + # its assembly veneer, an extended exception frame, and a nested ISR frame. + USE_EXCEPTIONS_STACKSIZE = 0x400 endif # diff --git a/docs/CHIBIOS_21_11_5_PORT.md b/docs/CHIBIOS_21_11_5_PORT.md index c1bb16a..8caf507 100644 --- a/docs/CHIBIOS_21_11_5_PORT.md +++ b/docs/CHIBIOS_21_11_5_PORT.md @@ -34,6 +34,8 @@ this port is marked ready. explicitly. ChibiOS 21.11.x no longer adds it automatically; omitting it promotes unsuffixed sweep constants to software double precision and causes a measurable self-test sweep regression. +- Split the F303 HardFault entry into an assembly-only MSP/PSP veneer and an + ordinary non-returning C reporter, while reserving a 1 KiB exception stack. ## Build diff --git a/main.c b/main.c index 745074a..119afa6 100644 --- a/main.c +++ b/main.c @@ -3451,8 +3451,35 @@ int main(void) } } -/* The prototype shows it is a naked function - in effect this is just an -assembly function. */ +#ifdef TINYSA4 +void hard_fault_handler_c(uint32_t *sp, const uint32_t *callee) + __attribute__((noreturn, noinline)); +void HardFault_Handler(void) __attribute__((naked)); + +void HardFault_Handler(void) +{ + __asm volatile( + "tst lr, #4\n" + "ite eq\n" + "mrseq r0, msp\n" + "mrsne r0, psp\n" + "sub sp, sp, #32\n" + "str r4, [sp, #0]\n" + "str r5, [sp, #4]\n" + "str r6, [sp, #8]\n" + "str r7, [sp, #12]\n" + "mov r2, r8\n" + "str r2, [sp, #16]\n" + "mov r2, r9\n" + "str r2, [sp, #20]\n" + "mov r2, r10\n" + "str r2, [sp, #24]\n" + "mov r2, r11\n" + "str r2, [sp, #28]\n" + "mov r1, sp\n" + "b hard_fault_handler_c\n"); +} +#else void HardFault_Handler(void); void hard_fault_handler_c(uint32_t *sp) __attribute__((naked)); @@ -3465,22 +3492,27 @@ void HardFault_Handler(void) __asm volatile("mrs %0, psp \n\t" : "=r"(sp)); hard_fault_handler_c(sp); } +#endif +#ifdef TINYSA4 +void hard_fault_handler_c(uint32_t *sp, const uint32_t *callee) +#else void hard_fault_handler_c(uint32_t *sp) +#endif { #ifdef TINYSA4 uint32_t r0 = sp[0]; uint32_t r1 = sp[1]; uint32_t r2 = sp[2]; uint32_t r3 = sp[3]; - register uint32_t r4 __asm("r4"); - register uint32_t r5 __asm("r5"); - register uint32_t r6 __asm("r6"); - register uint32_t r7 __asm("r7"); - register uint32_t r8 __asm("r8"); - register uint32_t r9 __asm("r9"); - register uint32_t r10 __asm("r10"); - register uint32_t r11 __asm("r11"); + uint32_t r4 = callee[0]; + uint32_t r5 = callee[1]; + uint32_t r6 = callee[2]; + uint32_t r7 = callee[3]; + uint32_t r8 = callee[4]; + uint32_t r9 = callee[5]; + uint32_t r10 = callee[6]; + uint32_t r11 = callee[7]; uint32_t r12 = sp[4]; uint32_t lr = sp[5]; uint32_t pc = sp[6]; @@ -3538,4 +3570,3 @@ void hard_fault_handler_c(uint32_t *sp) } -