Merge pull request #3553 from cesanta/stm32-examples

Add STM32 examples
This commit is contained in:
Sergey Lyubka authored and GitHub committed 2026-05-21 11:42:12 +01:00
commit 948b56a3ec
33 files changed
+3428 -1

No files matched your search

+1 -1
View File
@@ -44,7 +44,7 @@ pico2_w_dashboard_full pico2_w_dashboard_minimal: pico2_w_dashboard_%:
define ST_DASHBOARD_RULE
$(1)_dashboard_$(2): $(TOP)/tutorials/stm32/$(1)/cubemx/$(1).ioc
mkdir -p $$@
cp $$^ $$@
cp $(TOP)/tutorials/stm32/$(1)/cubemx/* $$@ 2>/dev/null || true
echo config load `realpath $$@/$(1).ioc` > gen.mx
echo project generate >> gen.mx
echo exit >> gen.mx
+44
View File
@@ -2,6 +2,7 @@
# IOC="${1:-}"
DIR="${1:-}"
ROOT_DIR=$DIR
DASH=${2:-}
CURL="curl -fsSL"
RAW="https://raw.githubusercontent.com/cesanta/mongoose/HEAD/"
@@ -12,6 +13,7 @@ fail() { echo $@ >&2 ; exit 1 ; }
test -d "$DIR" || fail "Usage: $0 DIRECTORY"
test -f $DIR/FSBL/Core/Src/main.c && DIR=$DIR/FSBL
test -f $DIR/CM7/Core/Src/main.c && DIR=$DIR/CM7
# Add mongoose core files
test -d $DIR/Mongoose || mkdir -p $DIR/Mongoose
@@ -29,12 +31,54 @@ grep -q 'mg_mgr_init' $MAIN_C || perl -i -ne 'print; print " struct mg_mgr mgr;
grep -q 'mg_mgr_poll' $MAIN_C || perl -i -ne 'print " mg_mgr_poll(&mgr, 0);\n" if /USER CODE END WHILE/; print;' $MAIN_C
test -f $DIR/STM32H723XG_FLASH.ld && (grep -q eth_ram $DIR/STM32H723XG_FLASH.ld || perl -i -ne 'print ; print "\n /* Mongoose Ethernet driver */\n .eth_ram : { *(.eth_ram .eth_ram*) } > RAM_D2 AT > FLASH\n" if /_sidata =/' $DIR/STM32H723XG_FLASH.ld)
test -f $DIR/LICENSE || $CURL $RAW/LICENSE -o $DIR/LICENSE
case $DIR in portenta*) (
grep -q 'void hwspecific_sdio_init(void);' "$MAIN_C" || \
perl -i -ne 'print; print "void hwspecific_sdio_init(void);\n" if /USER CODE BEGIN 0/' "$MAIN_C"
grep -q 'hwspecific_sdio_init();' "$MAIN_C" || \
perl -i -ne 'print " hwspecific_sdio_init();\n" if /mg_mgr_init\(&mgr\);/; print;' "$MAIN_C"
) ;; esac
case $DIR in *u5a5*) (
grep -q 'void hwspecific_spi_init(void);' "$MAIN_C" || \
perl -i -ne 'print; print "void hwspecific_spi_init(void);\n" if /USER CODE BEGIN 0/' "$MAIN_C"
grep -q 'hwspecific_spi_init();' "$MAIN_C" || \
perl -i -ne 'print " hwspecific_spi_init();\n" if /mg_mgr_init\(&mgr\);/; print;' "$MAIN_C"
) ;; esac
# Add Mongoose dir to the build
test -f $DIR/CMakeLists.txt && (grep -q 'Mongoose/$' $DIR/CMakeLists.txt || perl -i -ne 'print; print " \${CMAKE_SOURCE_DIR}/Mongoose/\n" if /Add user defined include paths/' $DIR/CMakeLists.txt)
test -f $DIR/CMakeLists.txt && (grep -q 'mongoose.c$' $DIR/CMakeLists.txt || perl -i -ne 'print; print " \${CMAKE_SOURCE_DIR}/Mongoose/mongoose.c\n" if /Add user sources here/' $DIR/CMakeLists.txt)
case $DIR in portenta*) (
test -f $ROOT_DIR/mongoose_config.h && mv $ROOT_DIR/mongoose_config.h $DIR/Mongoose
test -f $ROOT_DIR/wifi.c && mv $ROOT_DIR/wifi.c $DIR/Mongoose
test -f $ROOT_DIR/getfw.cmake && mv $ROOT_DIR/getfw.cmake $DIR/
test -f $DIR/CMakeLists.txt && (grep -q 'Mongoose/wifi.c$' "$DIR/CMakeLists.txt" || \
perl -i -ne 'print; print " \${CMAKE_SOURCE_DIR}/Mongoose/wifi.c\n" if /Add user sources here/' "$DIR/CMakeLists.txt")
test -f $DIR/CMakeLists.txt && (grep -q '^[[:space:]]*\${CMAKE_SOURCE_DIR}[[:space:]]*$' "$DIR/CMakeLists.txt" || \
perl -i -ne 'print; print " \${CMAKE_SOURCE_DIR}\n" if /Add user defined include paths/' "$DIR/CMakeLists.txt")
test -f $DIR/CMakeLists.txt && (grep -q 'COMPONENT_WHD$' "$DIR/CMakeLists.txt" || \
perl -i -ne 'print; print " \${CMAKE_SOURCE_DIR}/mbed/targets/TARGET_STM/TARGET_STM32H7/TARGET_STM32H747xI/TARGET_PORTENTA_H7/COMPONENT_WHD\n" if /Add user defined include paths/' "$DIR/CMakeLists.txt")
test -f $DIR/CMakeLists.txt && (grep -q '^add_custom_target(getfw' "$DIR/CMakeLists.txt" || \
cat >> "$DIR/CMakeLists.txt" <<'EOF'
add_custom_target(getfw
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_SOURCE_DIR}/getfw.cmake
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
)
add_dependencies(${CMAKE_PROJECT_NAME} getfw)
EOF
)
) ;; esac
case $DIR in *u5a5*) (
test -f $ROOT_DIR/mongoose_config.h && mv $ROOT_DIR/mongoose_config.h $DIR/Mongoose
test -f $ROOT_DIR/wifi.c && mv $ROOT_DIR/wifi.c $DIR/Mongoose
test -f $DIR/CMakeLists.txt && (grep -q 'Mongoose/wifi.c$' "$DIR/CMakeLists.txt" || \
perl -i -ne 'print; print " \${CMAKE_SOURCE_DIR}/Mongoose/wifi.c\n" if /Add user sources here/' "$DIR/CMakeLists.txt")
) ;; esac
# If dashboard is specified, copy it to the project
#if test "$DASH" = "full" -o "$DASH" = "minimal" ; then
if test "$DASH" == "full" -o "$DASH" == "minimal" ; then
test -f $DIR/Mongoose/dashboard.c || $CURL $RAW/tutorials/device-dashboard/$DASH/dashboard.c -o $DIR/Mongoose/dashboard.c
test -f $DIR/Mongoose/dashboard.html || $CURL $RAW/tutorials/device-dashboard/$DASH/dashboard.html -o $DIR/Mongoose/dashboard.html
@@ -0,0 +1,75 @@
# Nucleo-U5A5ZJ-Q Mongoose Integration
This guide explains how to bring up a production-grade Web UI dashboard on the
Nucleo-U5A5ZJ-Q board with the X-NUCLEO-67W61M1 Wi-Fi
expansion board for STM32 Nucleo boards, based on the ST67W611M1 Wi-Fi module.
A pre-configured, ready-to-run project zip is available for download at
https://mongoose.ws.
To integrate the Web UI dashboard into an existing CubeMX project, or to create a
new project from scratch:
1. Configure the peripherals (described below).
2. Regenerate your project code and integrate Mongoose core by following
https://mongoose.ws/docs/guides/stm32-tcpip-stack/.
3. Add the Web UI dashboard by following
https://mongoose.ws/docs/guides/web-dashboard-guide/.
Note: to setup your WiFi login credentials, open the `wifi.c` file located in the
`Mongoose` directory and edit the `WIFI_NAME` and `WIFI_PASS` macros.
## Configure peripherals
An `.ioc` file is included in this directory for your reference. It was created
using the following steps:
- Open CubeMX. If you are starting a new project, select the STM32U5A5ZJT6Q
microcontroller and create a new project.
- In Clock Configuration, set the maximum clock frequency to 160 MHz.
- In Pinout & Configuration / Connectivity, enable USART1 in asynchronous mode.
Configure pins PA9 and PA10 for USART.
## Wi-Fi module firmware setup
Before running this project, the correct firmware must be flashed onto the Wi-Fi board.
Steps:
1. Install the X-CUBE-ST67W61 package.
Open `STM32CubeMX`, then navigate to:
`Help -> Manage embedded software packages`.
In the `STMicroelectronics` tab, locate `X-CUBE-ST67W61`.
Select the latest version and click `Install`.
2. Connect the X-NUCLEO-67W61M1 board to the Nucleo-U5A5ZJ-Q board using the
Arduino connectors CN7, CN8, CN9 and CN10.
See Figure 5 in the Wi-Fi board
[user manual](https://www.st.com/resource/en/user_manual/um3475-getting-started-with-xcubest67w61-stmicroelectronics.pdf)
for the connector layout.
3. Program the firmware to the ST67W61 module.
Connect the Nucleo board to your PC using a USB cable.
Navigate to the STM32Cube repository directory. The installed package is located at:
`Packs/STMicroelectronics/X-CUBE-ST67W61/<version>/`
Then navigate to:
`<version>/Projects/ST67W6X_Utilities/Binaries`
Run the appropriate script depending on your operating system:
- `NCP_update_mission_profile_t02.sh` (Linux)
- `NCP_update_mission_profile_t02.bat` (Windows)
Ensure that `STM32CubeProgrammer` is installed and available in your system `PATH`.
For more details on the programming process, see Section 4.3 and Table 2 of the
Wi-Fi board
[user manual](https://www.st.com/resource/en/user_manual/um3475-getting-started-with-xcubest67w61-stmicroelectronics.pdf).
@@ -0,0 +1,5 @@
#pragma once
#define MG_ARCH MG_ARCH_CUBE
#define MG_ENABLE_DRIVER_ST67W6 1
extern void wifi_setconfig(void *data);
#define MG_SET_WIFI_CONFIG(data) wifi_setconfig(data)
@@ -0,0 +1,252 @@
#MicroXplorer Configuration settings - do not modify
CAD.formats=
CAD.pinconfig=
CAD.provider=
CORTEX_M33_NS.userName=CORTEX_M33
File.Version=6
GPIO.groupedBy=Group By Peripherals
KeepUserPlacement=false
MMTAppReg1.MEMORYMAP.AP=RW_priv_only
MMTAppReg1.MEMORYMAP.AppRegionName=RAM
MMTAppReg1.MEMORYMAP.ContextName=CortexM33
MMTAppReg1.MEMORYMAP.CoreName=ARM Cortex-M33
MMTAppReg1.MEMORYMAP.DefaultDataRegion=true
MMTAppReg1.MEMORYMAP.IPParameters=StartAddress,Size,CoreName,DefaultDataRegion,ContextName,Name,AP
MMTAppReg1.MEMORYMAP.Name=RAM
MMTAppReg1.MEMORYMAP.Size=2555904
MMTAppReg1.MEMORYMAP.StartAddress=0x20000000
MMTAppReg2.MEMORYMAP.AppRegionName=RAM Reserved Alias Region
MMTAppReg2.MEMORYMAP.CoreName=ARM Cortex-M33
MMTAppReg2.MEMORYMAP.DefaultDataRegion=false
MMTAppReg2.MEMORYMAP.IPParameters=StartAddress,Size,CoreName,DefaultDataRegion,ReservedRegion,Name
MMTAppReg2.MEMORYMAP.Name=RAM Reserved Alias Region
MMTAppReg2.MEMORYMAP.ReservedRegion=true
MMTAppReg2.MEMORYMAP.Size=2555904
MMTAppReg2.MEMORYMAP.StartAddress=0x0A000000
MMTAppReg3.MEMORYMAP.AP=RO_priv_only
MMTAppReg3.MEMORYMAP.AppRegionName=FLASH
MMTAppReg3.MEMORYMAP.Cacheability=WTRA
MMTAppReg3.MEMORYMAP.ContextName=CortexM33
MMTAppReg3.MEMORYMAP.CoreName=ARM Cortex-M33
MMTAppReg3.MEMORYMAP.DefaultCodeRegion=true
MMTAppReg3.MEMORYMAP.DefaultDataRegion=false
MMTAppReg3.MEMORYMAP.IPParameters=StartAddress,Size,CoreName,DefaultDataRegion,MemType,ContextName,Name,AP,Cacheability,DefaultCodeRegion
MMTAppReg3.MEMORYMAP.MemType=ROM
MMTAppReg3.MEMORYMAP.Name=FLASH
MMTAppReg3.MEMORYMAP.Size=4194304
MMTAppReg3.MEMORYMAP.StartAddress=0x08000000
MMTAppRegionsCount=3
MMTConfigApplied=false
MMTSectionSuffix=
Mcu.CPN=STM32U5A5ZJT6Q
Mcu.ContextProject=TrustZoneDisabled
Mcu.Family=STM32U5
Mcu.IP0=CORTEX_M33_NS
Mcu.IP1=LPBAM
Mcu.IP2=LPBAMQUEUE
Mcu.IP3=MEMORYMAP
Mcu.IP4=NVIC
Mcu.IP5=PWR
Mcu.IP6=RCC
Mcu.IP7=RNG
Mcu.IP8=SYS
Mcu.IP9=USART1
Mcu.IPNb=10
Mcu.Name=STM32U5A5ZJTxQ
Mcu.Package=LQFP144
Mcu.Pin0=PA5
Mcu.Pin1=PA6
Mcu.Pin10=VP_RNG_VS_RNG
Mcu.Pin11=VP_SYS_VS_Systick
Mcu.Pin12=VP_LPBAM_VS_SIG1
Mcu.Pin13=VP_LPBAM_VS_SIG4
Mcu.Pin14=VP_MEMORYMAP_VS_MEMORYMAP
Mcu.Pin2=PA7
Mcu.Pin3=PE11
Mcu.Pin4=PE13
Mcu.Pin5=PD14
Mcu.Pin6=PA9
Mcu.Pin7=PA10
Mcu.Pin8=VP_LPBAMQUEUE_VS_QUEUE
Mcu.Pin9=VP_PWR_VS_SECSignals
Mcu.PinsNb=15
Mcu.ThirdPartyNb=0
Mcu.UserConstants=
Mcu.UserName=STM32U5A5ZJTxQ
MxCube.Version=6.17.0
MxDb.Version=DB.6.0.170
NVIC.BusFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
NVIC.DebugMonitor_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
NVIC.ForceEnableDMAVector=true
NVIC.HardFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
NVIC.MemoryManagement_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
NVIC.NonMaskableInt_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
NVIC.PendSV_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
NVIC.PriorityGroup=NVIC_PRIORITYGROUP_4
NVIC.SVCall_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
NVIC.SysTick_IRQn=true\:15\:0\:false\:false\:true\:false\:true\:false
NVIC.UsageFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
PA10.Mode=Asynchronous
PA10.Signal=USART1_RX
PA5.GPIOParameters=GPIO_Speed,GPIO_PuPd,GPIO_Label
PA5.GPIO_Label=SPI_SCK
PA5.GPIO_PuPd=GPIO_PULLDOWN
PA5.GPIO_Speed=GPIO_SPEED_FREQ_VERY_HIGH
PA5.Locked=true
PA5.Signal=SPI1_SCK
PA6.GPIOParameters=GPIO_Speed,GPIO_Label
PA6.GPIO_Label=SPI_MISO
PA6.GPIO_Speed=GPIO_SPEED_FREQ_VERY_HIGH
PA6.Locked=true
PA6.Signal=SPI1_MISO
PA7.GPIOParameters=GPIO_Speed,GPIO_Label
PA7.GPIO_Label=SPI_MOSI
PA7.GPIO_Speed=GPIO_SPEED_FREQ_VERY_HIGH
PA7.Locked=true
PA7.Signal=SPI1_MOSI
PA9.Mode=Asynchronous
PA9.Signal=USART1_TX
PD14.GPIOParameters=GPIO_Speed,GPIO_Label
PD14.GPIO_Label=SPI_CS
PD14.GPIO_Speed=GPIO_SPEED_FREQ_HIGH
PD14.Locked=true
PD14.Signal=GPIO_Output
PE11.GPIOParameters=GPIO_Speed,GPIO_Label
PE11.GPIO_Label=SPI_CHIP_EN
PE11.GPIO_Speed=GPIO_SPEED_FREQ_HIGH
PE11.Locked=true
PE11.Signal=GPIO_Output
PE13.GPIOParameters=GPIO_Label
PE13.GPIO_Label=SPI_RDY
PE13.Locked=true
PE13.Signal=GPIO_Input
PinOutPanel.RotationAngle=0
ProjectManager.AskForMigrate=true
ProjectManager.BackupPrevious=false
ProjectManager.CompilerLinker=GCC
ProjectManager.CompilerOptimize=6
ProjectManager.ComputerToolchain=false
ProjectManager.CoupleFile=false
ProjectManager.CustomerFirmwarePackage=
ProjectManager.DefaultFWLocation=true
ProjectManager.DeletePrevious=true
ProjectManager.DeviceId=STM32U5A5ZJTxQ
ProjectManager.FirmwarePackage=STM32Cube FW_U5 V1.8.0
ProjectManager.FreePins=false
ProjectManager.FreePinsContext=
ProjectManager.HalAssertFull=false
ProjectManager.HeapSize=0x10000
ProjectManager.KeepUserCode=true
ProjectManager.LPBAM.generateCode=
ProjectManager.LastFirmware=true
ProjectManager.LibraryCopy=1
ProjectManager.MainLocation=Core/Src
ProjectManager.NoMain=false
ProjectManager.PreviousToolchain=STM32CubeIDE
ProjectManager.ProjectBuild=false
ProjectManager.ProjectFileName=nucleo_u5a5zj_q.ioc
ProjectManager.ProjectName=nucleo_u5a5zj_q
ProjectManager.ProjectStructure=
ProjectManager.RegisterCallBack=
ProjectManager.StackSize=0x2000
ProjectManager.TargetToolchain=CMake
ProjectManager.ToolChainLocation=
ProjectManager.UAScriptAfterPath=
ProjectManager.UAScriptBeforePath=
ProjectManager.UnderRoot=false
ProjectManager.functionlistsort=1-SystemClock_Config-RCC-false-HAL-false,2-MX_GPIO_Init-GPIO-false-HAL-true,3-MX_RNG_Init-RNG-false-HAL-true,4-MX_USART1_UART_Init-USART1-false-HAL-true,0-MX_CORTEX_M33_NS_Init-CORTEX_M33_NS-false-HAL-true,0-MX_PWR_Init-PWR-false-HAL-true
RCC.ADCFreq_Value=16000000
RCC.ADF1Freq_Value=160000000
RCC.AHBFreq_Value=160000000
RCC.APB1Freq_Value=160000000
RCC.APB1TimFreq_Value=160000000
RCC.APB2Freq_Value=160000000
RCC.APB2TimFreq_Value=160000000
RCC.APB3Freq_Value=160000000
RCC.CK48Freq_Value=48000000
RCC.CRSFreq_Value=48000000
RCC.CortexFreq_Value=160000000
RCC.DACFreq_Value=32000
RCC.EPOD_VALUE=48000000
RCC.FCLKCortexFreq_Value=160000000
RCC.FDCANFreq_Value=80000000
RCC.FamilyName=M
RCC.HCLKFreq_Value=160000000
RCC.HSE_VALUE=16000000
RCC.HSI48_VALUE=48000000
RCC.HSI_VALUE=16000000
RCC.I2C1Freq_Value=160000000
RCC.I2C2Freq_Value=160000000
RCC.I2C3Freq_Value=160000000
RCC.I2C4Freq_Value=160000000
RCC.I2C5Freq_Value=160000000
RCC.I2C6Freq_Value=160000000
RCC.IPParameters=ADCFreq_Value,ADF1Freq_Value,AHBFreq_Value,APB1Freq_Value,APB1TimFreq_Value,APB2Freq_Value,APB2TimFreq_Value,APB3Freq_Value,CK48Freq_Value,CRSFreq_Value,CortexFreq_Value,DACFreq_Value,EPOD_VALUE,FCLKCortexFreq_Value,FDCANFreq_Value,FamilyName,HCLKFreq_Value,HSE_VALUE,HSI48_VALUE,HSI_VALUE,I2C1Freq_Value,I2C2Freq_Value,I2C3Freq_Value,I2C4Freq_Value,I2C5Freq_Value,I2C6Freq_Value,LPTIM2Freq_Value,LPUART1Freq_Value,LSCOPinFreq_Value,LSE_VALUE,LSIDIV_VALUE,LSI_VALUE,MCO1PinFreq_Value,MDF1Freq_Value,MSIClockRange,MSI_VALUE,OCTOSPIMFreq_Value,PLL1R,PLL2FRACN,PLL2PoutputFreq_Value,PLL2QoutputFreq_Value,PLL2RoutputFreq_Value,PLL3FRACN,PLL3PoutputFreq_Value,PLL3QoutputFreq_Value,PLL3RoutputFreq_Value,PLLFRACN,PLLM,PLLN,PLLPoutputFreq_Value,PLLQoutputFreq_Value,PLLRCLKFreq_Value,RNGFreq_Value,SAESFreq_Value,SAI1Freq_Value,SAI2Freq_Value,SDMMCFreq_Value,SPI1Freq_Value,SPI2Freq_Value,SPI3Freq_Value,SYSCLKFreq_VALUE,SYSCLKSource,UART4Freq_Value,UART5Freq_Value,USART1Freq_Value,USART2Freq_Value,USART3Freq_Value,USART6Freq_Value,USBPHYFreq_Value,VCOInput2Freq_Value,VCOInput3Freq_Value,VCOInputFreq_Value,VCOOutputFreq_Value,VCOPLL2OutputFreq_Value,VCOPLL3OutputFreq_Value
RCC.LPTIM2Freq_Value=160000000
RCC.LPUART1Freq_Value=160000000
RCC.LSCOPinFreq_Value=32000
RCC.LSE_VALUE=32768
RCC.LSIDIV_VALUE=32000
RCC.LSI_VALUE=32000
RCC.MCO1PinFreq_Value=160000000
RCC.MDF1Freq_Value=160000000
RCC.MSIClockRange=RCC_MSIRANGE_0
RCC.MSI_VALUE=48000000
RCC.OCTOSPIMFreq_Value=160000000
RCC.PLL1R=1
RCC.PLL2FRACN=0
RCC.PLL2PoutputFreq_Value=3096000000
RCC.PLL2QoutputFreq_Value=3096000000
RCC.PLL2RoutputFreq_Value=3096000000
RCC.PLL3FRACN=0
RCC.PLL3PoutputFreq_Value=3096000000
RCC.PLL3QoutputFreq_Value=3096000000
RCC.PLL3RoutputFreq_Value=3096000000
RCC.PLLFRACN=0
RCC.PLLM=3
RCC.PLLN=10
RCC.PLLPoutputFreq_Value=80000000
RCC.PLLQoutputFreq_Value=80000000
RCC.PLLRCLKFreq_Value=160000000
RCC.RNGFreq_Value=48000000
RCC.SAESFreq_Value=48000000
RCC.SAI1Freq_Value=3096000000
RCC.SAI2Freq_Value=3096000000
RCC.SDMMCFreq_Value=80000000
RCC.SPI1Freq_Value=160000000
RCC.SPI2Freq_Value=160000000
RCC.SPI3Freq_Value=160000000
RCC.SYSCLKFreq_VALUE=160000000
RCC.SYSCLKSource=RCC_SYSCLKSOURCE_PLLCLK
RCC.UART4Freq_Value=160000000
RCC.UART5Freq_Value=160000000
RCC.USART1Freq_Value=160000000
RCC.USART2Freq_Value=160000000
RCC.USART3Freq_Value=160000000
RCC.USART6Freq_Value=160000000
RCC.USBPHYFreq_Value=80000000
RCC.VCOInput2Freq_Value=48000000
RCC.VCOInput3Freq_Value=48000000
RCC.VCOInputFreq_Value=16000000
RCC.VCOOutputFreq_Value=160000000
RCC.VCOPLL2OutputFreq_Value=6192000000
RCC.VCOPLL3OutputFreq_Value=6192000000
RNG.ClockErrorDetection=RNG_CED_DISABLE
RNG.IPParameters=ClockErrorDetection
USART1.IPParameters=VirtualMode-Asynchronous
USART1.VirtualMode-Asynchronous=VM_ASYNC
VP_LPBAMQUEUE_VS_QUEUE.Mode=QUEUEMODE
VP_LPBAMQUEUE_VS_QUEUE.Signal=LPBAMQUEUE_VS_QUEUE
VP_LPBAM_VS_SIG1.Mode=SmartRun
VP_LPBAM_VS_SIG1.Signal=LPBAM_VS_SIG1
VP_LPBAM_VS_SIG4.Mode=LPDMA1
VP_LPBAM_VS_SIG4.Signal=LPBAM_VS_SIG4
VP_MEMORYMAP_VS_MEMORYMAP.Mode=CurAppReg
VP_MEMORYMAP_VS_MEMORYMAP.Signal=MEMORYMAP_VS_MEMORYMAP
VP_PWR_VS_SECSignals.Mode=Security/Privilege
VP_PWR_VS_SECSignals.Signal=PWR_VS_SECSignals
VP_RNG_VS_RNG.Mode=RNG_Activate
VP_RNG_VS_RNG.Signal=RNG_VS_RNG
VP_SYS_VS_Systick.Mode=SysTick
VP_SYS_VS_Systick.Signal=SYS_VS_Systick
board=custom
@@ -0,0 +1,133 @@
#include "mongoose.h"
#include "main.h"
#define WIFISPI SPI1
#ifndef WIFI_NAME
#define WIFI_NAME ""
#endif
#ifndef WIFI_PASS
#define WIFI_PASS ""
#endif
#ifndef WIFI_AP_NAME
#define WIFI_AP_NAME "Mongoose"
#endif
#ifndef WIFI_AP_PASS
#define WIFI_AP_PASS "mongoose"
#endif
#ifndef ENABLE_WIFI_AP
#define ENABLE_WIFI_AP 0
#endif
#define BIT(x) (1UL << (x))
#define CLRSET(reg, clear, set) ((reg) = ((reg) & ~(clear)) | (set))
static inline void spin(volatile uint32_t n) {
while (n--)
(void)0;
}
static inline void hal_spi_init(SPI_TypeDef *spi, uint16_t div) {
(void)spi;
uint32_t br = 0; // Baud rate prescaler: div = 2,4,8...256 -> MBR = 0..7
while (div > 2)
div >>= 1, br++;
if (br > 7)
br = 7;
CLRSET(RCC->CCIPR1, 3 << 20, 1); // SYSCLK source
RCC->APB2ENR |= BIT(12); // SPI1EN
spin(100);
spi->CR1 = 0; // disable SPI
spi->CR1 = SPI_CR1_SSI_Msk; // SSI
spi->CFG1 = (7 << SPI_CFG1_DSIZE_Pos) | (br << SPI_CFG1_MBR_Pos); // DSIZE = 8-bit, MBR
spi->CFG2 = SPI_CFG2_SSM_Msk | SPI_CFG2_MASTER_Msk;
spi->CR1 |= SPI_CR1_SPE_Msk; // Enable SPI
spi->CR1 |= SPI_CR1_CSTART_Msk; // Start transfer
}
static inline uint8_t hal_spi_xfer_byte(SPI_TypeDef *spi, uint8_t byte) {
(void)spi;
while ((spi->SR & SPI_SR_TXP_Msk) == 0)
spin(1); // TXP
*(volatile uint8_t *) &spi->TXDR = byte;
while (((spi->SR & SPI_SR_RXP_Msk) == 0) && ((spi->SR & SPI_SR_RXWNE_Msk) == 0) && ((spi->SR & SPI_SR_RXPLVL_Msk) == 0))
spin(1); // RXP
uint8_t rx = *(volatile uint8_t *) &spi->RXDR;
return rx;
}
static inline void hal_spi_tx_rx(SPI_TypeDef *spi, uint8_t *tx, uint8_t *rx, size_t len) {
for (size_t i = 0; i < len; i++) {
uint8_t rx_ = hal_spi_xfer_byte(spi, tx ? tx[i] : 0);
if (rx) rx[i] = rx_;
}
}
static inline bool hal_spi_ready(SPI_TypeDef *spi) {
(void) spi;
return HAL_GPIO_ReadPin(SPI_RDY_GPIO_Port, SPI_RDY_Pin);
}
static inline void hal_spi_select(SPI_TypeDef *spi, bool s) {
(void)spi;
HAL_GPIO_WritePin(SPI_CS_GPIO_Port, SPI_CS_Pin, s);
}
static inline void hal_spi_done(SPI_TypeDef *spi) {
while((spi->SR & SPI_SR_TXC_Msk) == 0) spin(1);
spi->IFCR = 0xffff; // clear all flags
}
static bool hwspecific_is_ready(void) {
return hal_spi_ready(WIFISPI);
}
static void hwspecific_spi_begin(void *arg) {
hal_spi_select(WIFISPI, true);
(void) arg;
}
static void hwspecific_spi_txn(void *arg, uint8_t *txdata, uint8_t *rxdata, size_t len) {
while (len--) {
uint8_t rx = hal_spi_xfer_byte(WIFISPI, txdata != NULL ? *txdata++ : 0);
if (rxdata != NULL) *rxdata++ = rx;
}
hal_spi_done(WIFISPI);
(void) arg;
}
static void hwspecific_spi_end(void *arg) {
hal_spi_select(WIFISPI, false);
(void) arg;
}
void hwspecific_spi_init(void) {
HAL_GPIO_WritePin(SPI_CHIP_EN_GPIO_Port, SPI_CHIP_EN_Pin, 0);
hal_spi_init(WIFISPI, 128); // runs at 1.25MHz (SYS_CLK/128)
mg_delayms(10);
HAL_GPIO_WritePin(SPI_CHIP_EN_GPIO_Port, SPI_CHIP_EN_Pin, 1);
}
static const struct mg_tcpip_spi spi = {NULL, hwspecific_spi_begin, hwspecific_spi_end, hwspecific_spi_txn};
void wifi_setconfig(void *data) {
struct mg_tcpip_driver_st67w6_data *d = (struct mg_tcpip_driver_st67w6_data *) data;
d->wifi.ssid = WIFI_NAME;
d->wifi.pass = WIFI_PASS;
d->wifi.apssid = WIFI_AP_NAME;
d->wifi.appass = WIFI_AP_PASS;
d->wifi.apip = MG_IPV4(192, 168, 169, 1);
d->wifi.apmask = MG_IPV4(255, 255, 255, 0);
d->wifi.security = 0;
d->wifi.apsecurity = 0;
d->wifi.apchannel = 10;
d->wifi.apmode = ENABLE_WIFI_AP;
d->spi = (struct mg_tcpip_spi *) &spi;
d->is_ready = hwspecific_is_ready;
}
@@ -0,0 +1,37 @@
# Environment setup: https://mongoose.ws/documentation/tutorials/tools/
CFLAGS = -W -Wall -Wextra -Werror -Wundef -Wshadow -Wdouble-promotion
CFLAGS += -Wformat-truncation -fno-common -Wconversion -Wno-sign-conversion
CFLAGS += -g3 -Os -ffunction-sections -fdata-sections
CFLAGS += -I. -Icmsis_core/CMSIS/Core/Include -Icmsis_u5/Include
CFLAGS += -mcpu=cortex-m33 -mthumb -mfpu=fpv5-sp-d16 -mfloat-abi=hard
LDFLAGS ?= -Tlink.ld -nostdlib -nostartfiles --specs nano.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map
SOURCES = main.c hal.c wifi.c $(wildcard mongoose/*.c)
SOURCES += cmsis_u5/Source/Templates/gcc/startup_stm32u5a5xx.s
TOP = ../../../..
CFLAGS += -I$(TOP)
SOURCES += $(TOP)/mongoose.c
all build: firmware.bin
firmware.bin: firmware.elf
arm-none-eabi-objcopy -O binary $< $@
@echo
@echo "To flash, run 'make flash', or use STM32CubeProgrammer"
firmware.elf: cmsis_core cmsis_u5 $(SOURCES) $(wildcard *.h) link.ld Makefile
arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(LDFLAGS) -o $@
flash: firmware.bin
STM32_Programmer_CLI -c port=swd -e all -w $< 0x8000000 -hardRst
cmsis_core:
git clone -q -c advice.detachedHead=false --depth 1 -b 5.9.0 https://github.com/ARM-software/CMSIS_5 $@
cmsis_u5:
git clone -q -c advice.detachedHead=false --depth 1 -b v1.4.2 https://github.com/STMicroelectronics/cmsis_device_u5 $@
clean:
rm -rf firmware.*
@@ -0,0 +1,125 @@
// Copyright (c) 2026 Cesanta Software Limited
// All rights reserved
#include "hal.h"
bool hal_timer_expired(volatile uint64_t *t, uint64_t period, uint64_t now) {
uint64_t diff = now - *t; // Wrap-safe elapsed time since last expiry
if (period == 0) return false; // Avoid division by zero
if (diff < period) return false; // Period has not elapsed yet
*t += (diff / period) * period; // Preserve cadence, skip missed periods
return true;
}
static volatile uint64_t s_ticks; // Milliseconds since boot
void SysTick_Handler(void) { // SyStick IRQ handler, triggered every 1ms
s_ticks++;
}
uint64_t hal_get_tick(void) {
return s_ticks;
};
uint32_t SystemCoreClock = SYS_FREQUENCY;
void SystemInit(void) { // Called automatically by startup code
hal_system_init(); // Enable FPU
hal_clock_init();
SysTick_Config(SystemCoreClock / 1000); // Sys tick every 1ms
}
void ExitRun0Mode(void) {
}
struct stat;
__attribute__((weak)) int _fstat(int fd, struct stat *st) {
(void) fd, (void) st;
return -1;
}
extern unsigned char _end[]; // End of data section, start of heap. See link.ld
static unsigned char *s_current_heap_end = _end;
size_t hal_ram_used(void) {
return (size_t) (s_current_heap_end - _end);
}
size_t hal_ram_free(void) {
unsigned char endofstack;
return (size_t) (&endofstack - s_current_heap_end);
}
void *_sbrk(int incr) {
unsigned char *prev_heap;
unsigned char *heap_end = (unsigned char *) ((size_t) &heap_end - 256);
prev_heap = s_current_heap_end;
// Check how much space we got from the heap end to the stack end
if (s_current_heap_end + incr > heap_end) return (void *) -1;
s_current_heap_end += incr;
return prev_heap;
}
__attribute__((weak)) int _open(const char *path) {
(void) path;
return -1;
}
__attribute__((weak)) int _close(int fd) {
(void) fd;
return -1;
}
__attribute__((weak)) int _isatty(int fd) {
(void) fd;
return 1;
}
__attribute__((weak)) int _lseek(int fd, int ptr, int dir) {
(void) fd, (void) ptr, (void) dir;
return 0;
}
__attribute__((weak)) void _exit(int status) {
(void) status;
for (;;) asm volatile("BKPT #0");
}
__attribute__((weak)) void _kill(int pid, int sig) {
(void) pid, (void) sig;
}
__attribute__((weak)) int _getpid(void) {
return -1;
}
__attribute__((weak)) int _write(int fd, char *ptr, int len) {
(void) fd, (void) ptr, (void) len;
return -1;
}
__attribute__((weak)) int _read(int fd, char *ptr, int len) {
(void) fd, (void) ptr, (void) len;
return -1;
}
__attribute__((weak)) int _link(const char *a, const char *b) {
(void) a, (void) b;
return -1;
}
__attribute__((weak)) int _unlink(const char *a) {
(void) a;
return -1;
}
__attribute__((weak)) int _stat(const char *path, struct stat *st) {
(void) path, (void) st;
return -1;
}
__attribute__((weak)) int mkdir(const char *path, int mode) {
(void) path, (void) mode;
return -1;
}
__attribute__((weak)) void _init(void) {
}
@@ -0,0 +1,322 @@
// Copyright (c) 2026 Cesanta Software Limited
// All rights reserved
//
// RM0456
// https://www.st.com/resource/en/reference_manual/rm0456-stm32u5-series-armbased-32bit-mcus-stmicroelectronics.pdf
// https://www.st.com/resource/en/user_manual/um2861-stm32u5-nucleo144-board-mb1549-stmicroelectronics.pdf
#pragma once
#define LED_1 PIN('C', 7) // On-board LED pin (green)
#define LED_2 PIN('B', 7) // On-board LED pin (blue)
#define LED_3 PIN('G', 2) // On-board LED pin (red)
#define LED LED_1 // Use green LED for blinking
#define SPI_RDY PIN('E', 13)
#define SPI_CS PIN('D', 14)
#define SPI_CHIP_EN PIN('E', 11)
#ifndef HAL_DEBUG_UART
#define HAL_DEBUG_UART USART1
#endif
#ifndef UART_DEBUG
#define UART_DEBUG HAL_DEBUG_UART
#endif
#ifndef WIFISPI
#define WIFISPI SPI1
#endif
#include <stm32u5a5xx.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
extern bool hal_timer_expired(volatile uint64_t *t, uint64_t prd, uint64_t now);
extern uint64_t hal_get_tick(void);
extern size_t hal_ram_free(void);
extern size_t hal_ram_used(void);
extern uint32_t SystemCoreClock;
#define BIT(x) (1UL << (x))
#define CLRSET(reg, clear, set) ((reg) = ((reg) & ~(clear)) | (set))
#define PIN(bank, num) ((((bank) - 'A') << 8) | (num))
#define PINNO(pin) (pin & 255)
#define PINBANK(pin) (pin >> 8)
void hal_init(void);
size_t hal_ram_free(void);
size_t hal_ram_used(void);
enum {
HAL_D1PRE = 1, // actual divisor value
HAL_HPRE = 1, // actual divisor value
HAL_D2PRE = 1,
HAL_D3PRE = 1,
};
enum { PLL1_M = 1, PLL1_N = 80, PLL1_P = 2, PLL1_Q = 2, PLL1_R = 2 };
#define MSIS_FREQUENCY (4 * 1024 * 1024) // 4 Mhz
#define SYS_FREQUENCY (MSIS_FREQUENCY * PLL1_N / PLL1_M / PLL1_P / HAL_D1PRE) // 160MHz
#define AHB_FREQUENCY (SYS_FREQUENCY / HAL_HPRE)
#define APB2_FREQUENCY (AHB_FREQUENCY / HAL_D2PRE)
#define APB1_FREQUENCY (AHB_FREQUENCY / HAL_D1PRE)
static inline void spin(volatile uint32_t n) {
while (n--)
(void)0;
}
enum {
HAL_GPIO_MODE_INPUT,
HAL_GPIO_MODE_OUTPUT,
HAL_GPIO_MODE_AF,
HAL_GPIO_MODE_ANALOG
};
enum { HAL_GPIO_OTYPE_PUSH_PULL, HAL_GPIO_OTYPE_OPEN_DRAIN };
enum {
HAL_GPIO_SPEED_LOW,
HAL_GPIO_SPEED_MEDIUM,
HAL_GPIO_SPEED_HIGH,
HAL_GPIO_SPEED_INSANE
};
enum { HAL_GPIO_PULL_NONE, HAL_GPIO_PULL_UP, HAL_GPIO_PULL_DOWN };
#define HAL_GPIO(N) ((GPIO_TypeDef *)(GPIOA_BASE_NS + 0x400 * (N)))
static GPIO_TypeDef *hal_gpio_bank(uint16_t pin) {
return HAL_GPIO(PINBANK(pin));
}
static inline void hal_gpio_toggle(uint16_t pin) {
GPIO_TypeDef *gpio = hal_gpio_bank(pin);
uint32_t mask = BIT(PINNO(pin));
gpio->BSRR = mask << (gpio->ODR & mask ? 16 : 0);
}
static inline int hal_gpio_read(uint16_t pin) {
return hal_gpio_bank(pin)->IDR & BIT(PINNO(pin)) ? 1 : 0;
}
static inline void hal_gpio_write(uint16_t pin, bool val) {
GPIO_TypeDef *gpio = hal_gpio_bank(pin);
gpio->BSRR = BIT(PINNO(pin)) << (val ? 0 : 16);
}
static inline void hal_gpio_init(uint16_t pin, uint8_t mode, uint8_t type,
uint8_t speed, uint8_t pull, uint8_t af) {
GPIO_TypeDef *gpio = hal_gpio_bank(pin);
uint8_t n = (uint8_t)(PINNO(pin));
RCC->AHB2ENR1 |= BIT(PINBANK(pin)); // Enable GPIO clock
CLRSET(gpio->OTYPER, 1UL << n, ((uint32_t)type) << n);
CLRSET(gpio->OSPEEDR, 3UL << (n * 2), ((uint32_t)speed) << (n * 2));
CLRSET(gpio->PUPDR, 3UL << (n * 2), ((uint32_t)pull) << (n * 2));
CLRSET(gpio->AFR[n >> 3], 15UL << ((n & 7) * 4),
((uint32_t)af) << ((n & 7) * 4));
CLRSET(gpio->MODER, 3UL << (n * 2), ((uint32_t)mode) << (n * 2));
}
static inline void hal_gpio_input(uint16_t pin) {
hal_gpio_init(pin, HAL_GPIO_MODE_INPUT, HAL_GPIO_OTYPE_PUSH_PULL,
HAL_GPIO_SPEED_HIGH, HAL_GPIO_PULL_NONE, 0);
}
static inline void hal_gpio_output(uint16_t pin) {
hal_gpio_init(pin, HAL_GPIO_MODE_OUTPUT, HAL_GPIO_OTYPE_PUSH_PULL,
HAL_GPIO_SPEED_HIGH, HAL_GPIO_PULL_NONE, 0);
}
// D2 Kernel clock (8.7.21) USART1 defaults to pclk2 (APB2), while USART2,3
// default to pclk1 (APB1). Even if using other kernel clocks, the APBx clocks
// must be enabled for CPU access, as the kernel clock drives the BRR, not the
// APB bus interface
static inline void hal_uart_init(USART_TypeDef *uart, unsigned long baud) {
uint8_t af = 7; // Alternate function
uint16_t rx = 0, tx = 0; // pins
uint32_t freq =
SYS_FREQUENCY; // Bus frequency. UART1 is on APB2, rest on APB1
if (uart == USART1)
freq = APB2_FREQUENCY, RCC->APB2ENR |= BIT(14);
if (uart == USART2)
freq = APB1_FREQUENCY, RCC->APB1ENR1 |= BIT(17);
if (uart == USART3)
freq = APB1_FREQUENCY, RCC->APB1ENR1 |= BIT(18);
if (uart == USART1)
tx = PIN('A', 9), rx = PIN('A', 10);
if (uart == USART2)
tx = PIN('A', 2), rx = PIN('A', 3);
if (uart == USART3)
tx = PIN('D', 8), rx = PIN('D', 9);
#if 0 // CONSTANT BAUD RATE FOR DEBUGGING
CLRSET(RCC->CCIPR2, 7 << 3, 3 << 3); // use HSI for UART1
freq = 64000000;
#endif
hal_gpio_init(tx, HAL_GPIO_MODE_AF, HAL_GPIO_OTYPE_PUSH_PULL,
HAL_GPIO_SPEED_HIGH, 0, af);
hal_gpio_init(rx, HAL_GPIO_MODE_AF, HAL_GPIO_OTYPE_PUSH_PULL,
HAL_GPIO_SPEED_HIGH, 0, af);
uart->CR1 = 0; // Disable this UART
uart->BRR = freq / baud; // Set baud rate
uart->CR1 = BIT(0) | BIT(2) | BIT(3); // Set UE, RE, TE
}
static inline void hal_uart_write_byte(USART_TypeDef *uart, uint8_t byte) {
uart->TDR = byte;
while ((uart->ISR & BIT(7)) == 0)
spin(1);
}
static inline void hal_uart_write_buf(USART_TypeDef *uart, char *buf,
size_t len) {
while (len-- > 0)
hal_uart_write_byte(uart, *(uint8_t *)buf++);
}
static inline int hal_uart_read_ready(USART_TypeDef *uart) {
return uart->ISR & BIT(5); // If RXNE bit is set, data is ready
}
static inline uint8_t hal_uart_read_byte(USART_TypeDef *uart) {
return (uint8_t)(uart->RDR & 255);
}
static inline void hal_rng_init(void) {
RCC->CCIPR2 |= RCC_CCIPR2_RNGSEL_0; // RNG clock source pll1_q_ck
RCC->AHB2ENR1 |= RCC_AHB2ENR1_RNGEN; // Enable RNG clock
RNG->CR = RNG_CR_RNGEN; // Enable RNG
}
static inline uint32_t hal_rng_read(void) {
while ((RNG->SR & RNG_SR_DRDY) == 0)
(void)0;
return RNG->DR;
}
#define MG_HAL_DISABLE_ETHERNET 1
static inline unsigned int hal_ahb_div2prescval(unsigned int div) {
if (div == 1)
return 0;
unsigned int val = 7;
while (div >>= 1)
++val;
return val;
}
static inline unsigned int hal_apb_div2prescval(unsigned int div) {
if (div <= 1)
return 0;
unsigned int val = 0;
while (div >>= 1)
++val;
return val + 2;
}
static inline void hal_clock_init(void) {
FLASH->ACR |= BIT(8); // prefetch
RCC->AHB3ENR |= RCC_AHB3ENR_PWREN;
PWR->SVMCR |= PWR_SVMCR_IO2SV; // Enable VDDIO2 supply
PWR->UCPDR |= PWR_UCPDR_UCPD_DBDIS; // Disable the internal Pull-Up in Dead
// Battery pins of UCPD peripheral
PWR->CR3 |= PWR_CR3_REGSEL; // enable SMPS
while ((PWR->SVMSR & BIT(1)) == 0)
spin(1);
if ((PWR->SVMSR & PWR_SVMSR_ACTVOS) != (PWR_VOSR_VOS_0 | PWR_VOSR_VOS_1)) {
// control voltage scaling
PWR->VOSR |= PWR_VOSR_VOS_0 | PWR_VOSR_VOS_1 | PWR_VOSR_BOOSTEN;
while ((PWR->VOSR & PWR_VOSR_VOSRDY) == 0)
spin(1);
while ((PWR->SVMSR & PWR_SVMSR_ACTVOSRDY) == 0)
spin(1);
}
PWR->DBPR |= PWR_DBPR_DBP; // enable backup access
CLRSET(RCC->BDCR, 3 << 3, 0); // RCC_LSEDRIVE_LOW
RCC->BDCR |= RCC_BDCR_LSEON; // LSE enable
while ((RCC->BDCR & RCC_BDCR_LSERDY) == 0)
spin(1);
RCC->BDCR |= RCC_BDCR_LSESYSEN; // LSESYS enable
while ((RCC->BDCR & RCC_BDCR_LSESYSRDY) == 0)
spin(1);
PWR->VOSR &= ~PWR_VOSR_BOOSTEN;
RCC->CR &= ~BIT(24); // configure PLL1
while ((RCC->CR & RCC_CR_PLL1RDY) != 0)
spin(1);
RCC->PLL1CFGR = BIT(0) | ((PLL1_M - 1) << 8) | BIT(16) | BIT(17) |
BIT(18); // source, PLL1_M, enable PLL1_P, PLL1_Q
RCC->PLL1DIVR = (PLL1_N - 1) | ((PLL1_P - 1) << 9) | ((PLL1_Q - 1) << 16) |
((PLL1_R - 1) << 24);
PWR->VOSR |= PWR_VOSR_BOOSTEN;
RCC->CR |= BIT(24); // PLL1ON
while ((RCC->CR & RCC_CR_PLL1RDY) == 0)
spin(1);
CLRSET(FLASH->ACR, 0xf, 4); // flash latency 4
RCC->CFGR1 |= 3; // set PLL as sysclk source
CLRSET(RCC->CFGR2, 0xf, hal_ahb_div2prescval(HAL_HPRE)); // HPRE
CLRSET(RCC->CFGR2, 0x70, hal_apb_div2prescval(HAL_D1PRE) << 4); // D1PRE
CLRSET(RCC->CFGR2, 0x700, hal_apb_div2prescval(HAL_D2PRE) << 8); // D2PRE
CLRSET(RCC->CFGR3, 0x70, hal_apb_div2prescval(HAL_D3PRE) << 4); // D3PRE
}
static inline void hal_system_init(void) {
SCB->CPACR |= ((3UL << 10 * 2) | (3UL << 11 * 2)); // Enable FPU
__DSB();
__ISB();
SCB->VTOR = FLASH_BASE; // Vector Table Relocation in Internal FLASH
}
static inline void hal_spi_init(SPI_TypeDef *spi, uint16_t div) {
(void)spi;
uint32_t br = 0; // Baud rate prescaler: div = 2,4,8...256 -> MBR = 0..7
while (div > 2)
div >>= 1, br++;
if (br > 7)
br = 7;
CLRSET(RCC->CCIPR1, 3 << 20, 1); // SYSCLK source
RCC->APB2ENR |= BIT(12); // SPI1EN
spin(100);
hal_gpio_init(PIN('A', 5), HAL_GPIO_MODE_AF, HAL_GPIO_OTYPE_PUSH_PULL,
HAL_GPIO_SPEED_INSANE, HAL_GPIO_PULL_DOWN, 5); // SCK
hal_gpio_init(PIN('A', 6), HAL_GPIO_MODE_AF, HAL_GPIO_OTYPE_PUSH_PULL,
HAL_GPIO_SPEED_INSANE, HAL_GPIO_PULL_NONE, 5); // MISO
hal_gpio_init(PIN('A', 7), HAL_GPIO_MODE_AF, HAL_GPIO_OTYPE_PUSH_PULL,
HAL_GPIO_SPEED_INSANE, HAL_GPIO_PULL_NONE, 5); // MOSI
hal_gpio_output(SPI_CS); // CS
hal_gpio_output(SPI_CHIP_EN); // CHIP_EN
hal_gpio_input(SPI_RDY); // RDY
spi->CR1 = 0; // disable SPI
spi->CR1 = SPI_CR1_SSI_Msk; // SSI
spi->CFG1 = (7 << SPI_CFG1_DSIZE_Pos) | (br << SPI_CFG1_MBR_Pos); // DSIZE = 8-bit, MBR
spi->CFG2 = SPI_CFG2_SSM_Msk | SPI_CFG2_MASTER_Msk;
spi->CR1 |= SPI_CR1_SPE_Msk; // Enable SPI
spi->CR1 |= SPI_CR1_CSTART_Msk; // Start transfer
}
static inline uint8_t hal_spi_xfer_byte(SPI_TypeDef *spi, uint8_t byte) {
(void)spi;
while ((spi->SR & SPI_SR_TXP_Msk) == 0)
spin(1); // TXP
*(volatile uint8_t *) &spi->TXDR = byte;
while (((spi->SR & SPI_SR_RXP_Msk) == 0) && ((spi->SR & SPI_SR_RXWNE_Msk) == 0) && ((spi->SR & SPI_SR_RXPLVL_Msk) == 0))
spin(1); // RXP
uint8_t rx = *(volatile uint8_t *) &spi->RXDR;
return rx;
}
static inline void hal_spi_tx_rx(SPI_TypeDef *spi, uint8_t *tx, uint8_t *rx, size_t len) {
for (size_t i = 0; i < len; i++) {
uint8_t rx_ = hal_spi_xfer_byte(spi, tx ? tx[i] : 0);
if (rx) rx[i] = rx_;
}
}
static inline bool hal_spi_ready(SPI_TypeDef *spi) {
(void) spi;
return hal_gpio_read(SPI_RDY);
}
static inline void hal_spi_select(SPI_TypeDef *spi, bool s) {
(void)spi;
hal_gpio_write(SPI_CS, s);
}
static inline void hal_spi_done(SPI_TypeDef *spi) {
while((spi->SR & SPI_SR_TXC_Msk) == 0) spin(1);
spi->IFCR = 0xffff; // clear all flags
}
@@ -0,0 +1,31 @@
ENTRY(Reset_Handler);
MEMORY {
flash(rx) : ORIGIN = 0x08000000, LENGTH = 4096k
sram(rwx) : ORIGIN = 0x20000000, LENGTH = 256k
}
_estack = ORIGIN(sram) + LENGTH(sram); /* stack points to end of SRAM */
SECTIONS {
.vectors : { KEEP(*(.isr_vector)) } > flash
.text : { *(.text* .text.*) } > flash
.rodata : { *(.rodata*) } > flash
.data : {
_sdata = .;
*(.first_data)
*(.data SORT(.data.*))
*(.iram .iram* .iram.*)
_edata = .;
} > sram AT > flash
_sidata = LOADADDR(.data);
.bss : {
_sbss = .;
*(.bss SORT(.bss.*) COMMON)
_ebss = .;
} > sram
. = ALIGN(8);
_end = .;
}
@@ -0,0 +1,71 @@
// Copyright (c) 2026 Cesanta Software Limited
// All rights reserved
#include "mongoose.h"
#include "hal.h"
#define DEBUG_UART USART1
#define LED1 PIN('C', 7) // On-board LED pin (green)
#define LED2 PIN('B', 7) // On-board LED pin (blue)
#define LED3 PIN('G', 2) // On-board LED pin (red)
// Redirect stdout debug output to UART
int _write(int fd, char *ptr, int len) {
if (fd == 1 || fd == 2) hal_uart_write_buf(DEBUG_UART, ptr, (size_t) len);
return len;
}
static void blink_task(void) {
static uint64_t blink_timer = 0;
if (hal_timer_expired(&blink_timer, 500, hal_get_tick())) {
hal_gpio_toggle(LED1);
}
}
uint64_t mg_millis(void) {
return hal_get_tick();
}
bool mg_random(void *buf, size_t len) {
for (size_t n = 0; n < len; n += sizeof(uint32_t)) {
uint32_t r = hal_rng_read();
memcpy((char *) buf + n, &r, n + sizeof(r) > len ? len - n : sizeof(r));
}
return true;
}
static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), hal_get_tick());
} else {
mg_http_reply(c, 200, "", "Hi from Mongoose, tick %llu\n",
hal_get_tick());
}
}
}
int main(void) {
hal_clock_init();
hal_rng_init();
hal_gpio_output(LED1);
hal_gpio_output(LED2);
hal_gpio_output(LED3);
hal_uart_init(DEBUG_UART, 115200);
extern void hwspecific_spi_init();
hwspecific_spi_init();
// Start a minimal web server
struct mg_mgr mgr;
mg_mgr_init(&mgr);
mg_http_listen(&mgr, "http://0.0.0.0", http_ev_handler, NULL);
for (;;) {
mg_mgr_poll(&mgr, 0);
blink_task();
}
return 0;
}
@@ -0,0 +1,14 @@
#pragma once
// See https://mongoose.ws/documentation/#build-options
#define MG_ARCH MG_ARCH_ARMGCC
#define MG_ENABLE_TCPIP 1
#define MG_TLS MG_TLS_BUILTIN
#define MG_ENABLE_CUSTOM_MILLIS 1
#define MG_ENABLE_CUSTOM_RANDOM 1
#define MG_ENABLE_DRIVER_ST67W6 1
// Set your Wi-Fi configuration using a custom function:
extern void wifi_setconfig(void *data);
#define MG_SET_WIFI_CONFIG(data) wifi_setconfig(data)
@@ -0,0 +1,76 @@
// SPDX-FileCopyrightText: 2026 Cesanta Software Limited
// SPDX-License-Identifier: GPL-2.0-only or commercial
// Generated by Mongoose Wizard, https://mongoose.ws/wizard/
#include "hal.h"
#include "mongoose.h"
#ifndef WIFI_NAME
#define WIFI_NAME ""
#endif
#ifndef WIFI_PASS
#define WIFI_PASS ""
#endif
#ifndef WIFI_AP_NAME
#define WIFI_AP_NAME "Mongoose"
#endif
#ifndef WIFI_AP_PASS
#define WIFI_AP_PASS "mongoose"
#endif
#ifndef ENABLE_WIFI_AP
#define ENABLE_WIFI_AP 1
#endif
static bool hwspecific_is_ready(void) {
return hal_gpio_read(SPI_RDY);
}
static void hwspecific_spi_begin(void *arg) {
hal_spi_select(WIFISPI, true);
(void) arg;
}
static void hwspecific_spi_txn(void *arg, uint8_t *txdata, uint8_t *rxdata, size_t len) {
while (len--) {
uint8_t rx = hal_spi_xfer_byte(WIFISPI, txdata != NULL ? *txdata++ : 0);
if (rxdata != NULL) *rxdata++ = rx;
}
hal_spi_done(WIFISPI);
(void) arg;
}
static void hwspecific_spi_end(void *arg) {
hal_spi_select(WIFISPI, false);
(void) arg;
}
void hwspecific_spi_init(void) {
hal_gpio_output(SPI_CHIP_EN);
hal_gpio_write(SPI_CHIP_EN, 0);
hal_gpio_input(SPI_RDY);
hal_spi_init(WIFISPI, 128); // runs at 1.25MHz (SYS_CLK/128)
mg_delayms(10);
hal_gpio_write(SPI_CHIP_EN, 1);
}
static const struct mg_tcpip_spi spi = {NULL, hwspecific_spi_begin, hwspecific_spi_end, hwspecific_spi_txn};
void wifi_setconfig(void *data) {
struct mg_tcpip_driver_st67w6_data *d = (struct mg_tcpip_driver_st67w6_data *) data;
d->wifi.ssid = WIFI_NAME;
d->wifi.pass = WIFI_PASS;
d->wifi.apssid = WIFI_AP_NAME;
d->wifi.appass = WIFI_AP_PASS;
d->wifi.apip = MG_IPV4(192, 168, 169, 1);
d->wifi.apmask = MG_IPV4(255, 255, 255, 0);
d->wifi.security = 0;
d->wifi.apsecurity = 0;
d->wifi.apchannel = 10;
d->wifi.apmode = ENABLE_WIFI_AP;
d->spi = (struct mg_tcpip_spi *) &spi;
d->is_ready = hwspecific_is_ready;
}
@@ -0,0 +1,30 @@
# Portenta H7 Mongoose Integration
This guide explains how to bring up a production-grade Web UI dashboard on the
Portenta H7 board.
A pre-configured, ready-to-run project zip is available for download at
https://mongoose.ws.
To integrate the Web UI dashboard into an existing CubeMX project, or to create a
new project from scratch:
1. Configure the peripherals (described below).
2. Regenerate your project code and integrate Mongoose core by following
https://mongoose.ws/docs/guides/stm32-tcpip-stack/.
3. Add the Web UI dashboard by following
https://mongoose.ws/docs/guides/web-dashboard-guide/.
Note: to setup your WiFi login credentials, open the `wifi.c` file located in the
`Mongoose` directory and edit the `WIFI_NAME` and `WIFI_PASS` macros.
## Configure peripherals
An `.ioc` file is included in this directory for your reference. It was created
using the following steps:
- Open CubeMX. If you are starting a new project, select the STM32747XI
microcontroller and create a new project.
- In Clock Configuration, set the maximum clock frequency to 200 MHz.
- In Pinout & Configuration / Connectivity, enable USART1 in asynchronous mode.
Configure pins PA9 and PA10 for USART.
@@ -0,0 +1,7 @@
if(NOT EXISTS mbed/.git)
execute_process(COMMAND git clone --depth 1 --no-checkout -b extrapatches-6.17.0 https://github.com/arduino/mbed-os mbed)
endif()
execute_process(COMMAND git -C mbed sparse-checkout set targets/TARGET_STM/TARGET_STM32H7/TARGET_STM32H747xI/TARGET_PORTENTA_H7/COMPONENT_WHD/resources)
execute_process(COMMAND git -C mbed checkout)
file(TOUCH wiced_resource.h)
@@ -0,0 +1,5 @@
#pragma once
#define MG_ARCH MG_ARCH_CUBE
#define MG_ENABLE_DRIVER_CYW_SDIO 1
extern void wifi_setconfig(void *data);
#define MG_SET_WIFI_CONFIG(data) wifi_setconfig(data)
@@ -0,0 +1,406 @@
#MicroXplorer Configuration settings - do not modify
CAD.formats=
CAD.pinconfig=
CAD.provider=
CortexM4.IPs=BDMA,DMA,FATFS_M4\:I,FREERTOS_M4\:I,IWDG2\:I,MDMA,NVIC2\:I,RCC,USB_DEVICE_M4\:I,USB_HOST_M4\:I,WWDG2\:I,ADC1\:I,DCMI\:I,TIM3\:I,DEBUG,PDM2PCM_M4\:I,PWR,RESMGR_UTILITY,SYS_M4\:I,CORTEX_M4\:I,OPENAMP_M4\:I,VREFBUF,GPIO
CortexM7.IPs=BDMA\:I,DMA\:I,FATFS_M7\:I,FREERTOS_M7\:I,IWDG1\:I,MDMA\:I,NVIC1\:I,RCC\:I,USB_DEVICE_M7\:I,USB_HOST_M7\:I,WWDG1\:I,CORTEX_M7\:I,DEBUG\:I,PDM2PCM_M7\:I,PWR\:I,RESMGR_UTILITY\:I,SYS\:I,OPENAMP_M7\:I,VREFBUF\:I,GPIO\:I,RNG\:I,USART1\:I,SDMMC1\:I,MEMORYMAP\:I
CortexM7.Pins=PK5,PK6,PK7,PJ1,PJ5
File.Version=6
GPIO.groupedBy=Group By Peripherals
KeepUserPlacement=false
MMTAppReg1.MEMORYMAP.AppRegionName=RAM
MMTAppReg1.MEMORYMAP.ContextName=CortexM7
MMTAppReg1.MEMORYMAP.CoreName=ARM Cortex-M7
MMTAppReg1.MEMORYMAP.DefaultDataRegion=true
MMTAppReg1.MEMORYMAP.IPParameters=StartAddress,Size,CoreName,DefaultDataRegion,ContextName,Name
MMTAppReg1.MEMORYMAP.Name=RAM
MMTAppReg1.MEMORYMAP.Size=131072
MMTAppReg1.MEMORYMAP.StartAddress=0x20000000
MMTAppReg10.MEMORYMAP.AppRegionName=M4 non Shared Region
MMTAppReg10.MEMORYMAP.ContextLink=reserved
MMTAppReg10.MEMORYMAP.ContextName=CortexM7
MMTAppReg10.MEMORYMAP.CoreName=ARM Cortex-M7
MMTAppReg10.MEMORYMAP.IPParameters=StartAddress,Size,CoreName,MemType,ContextName,Name,ReservedRegion,ContextLink
MMTAppReg10.MEMORYMAP.MemType=ROM
MMTAppReg10.MEMORYMAP.Name=M4 non Shared Region
MMTAppReg10.MEMORYMAP.ReservedRegion=true
MMTAppReg10.MEMORYMAP.Size=1048576
MMTAppReg10.MEMORYMAP.StartAddress=0x08100000
MMTAppReg2.MEMORYMAP.AppRegionName=ITCMRAM
MMTAppReg2.MEMORYMAP.Cacheability=WTRA
MMTAppReg2.MEMORYMAP.ContextName=CortexM7
MMTAppReg2.MEMORYMAP.CoreName=ARM Cortex-M7
MMTAppReg2.MEMORYMAP.IPParameters=StartAddress,Size,CoreName,ContextName,Name,Cacheability
MMTAppReg2.MEMORYMAP.Name=ITCMRAM
MMTAppReg2.MEMORYMAP.Size=65536
MMTAppReg2.MEMORYMAP.StartAddress=0x00000000
MMTAppReg3.MEMORYMAP.AppRegionName=RAM_D1
MMTAppReg3.MEMORYMAP.ContextLink=shared
MMTAppReg3.MEMORYMAP.ContextName=CortexM7
MMTAppReg3.MEMORYMAP.CoreName=ARM Cortex-M7
MMTAppReg3.MEMORYMAP.IPParameters=StartAddress,Size,CoreName,ContextName,Name,ContextLink
MMTAppReg3.MEMORYMAP.Name=RAM_D1
MMTAppReg3.MEMORYMAP.Size=524288
MMTAppReg3.MEMORYMAP.StartAddress=0x24000000
MMTAppReg4.MEMORYMAP.AppRegionName=M7 non Shared Region
MMTAppReg4.MEMORYMAP.ContextLink=reserved
MMTAppReg4.MEMORYMAP.ContextName=CortexM4
MMTAppReg4.MEMORYMAP.CoreName=ARM Cortex-M4
MMTAppReg4.MEMORYMAP.IPParameters=StartAddress,Size,CoreName,ContextName,Name,ReservedRegion,ContextLink
MMTAppReg4.MEMORYMAP.Name=M7 non Shared Region
MMTAppReg4.MEMORYMAP.ReservedRegion=true
MMTAppReg4.MEMORYMAP.Size=524288
MMTAppReg4.MEMORYMAP.StartAddress=0x24000000
MMTAppReg5.MEMORYMAP.AP=RO_priv_only
MMTAppReg5.MEMORYMAP.AppRegionName=FLASH
MMTAppReg5.MEMORYMAP.Cacheability=WTRA
MMTAppReg5.MEMORYMAP.ContextLink=shared
MMTAppReg5.MEMORYMAP.ContextName=CortexM7
MMTAppReg5.MEMORYMAP.CoreName=ARM Cortex-M7
MMTAppReg5.MEMORYMAP.DefaultCodeRegion=true
MMTAppReg5.MEMORYMAP.IPParameters=StartAddress,Size,CoreName,MemType,ContextName,Name,AP,Cacheability,DefaultCodeRegion,ISRRegion,RootBootRegion,ContextLink
MMTAppReg5.MEMORYMAP.ISRRegion=true
MMTAppReg5.MEMORYMAP.MemType=ROM
MMTAppReg5.MEMORYMAP.Name=FLASH
MMTAppReg5.MEMORYMAP.RootBootRegion=true
MMTAppReg5.MEMORYMAP.Size=786432
MMTAppReg5.MEMORYMAP.StartAddress=0x08040000
MMTAppReg6.MEMORYMAP.AppRegionName=M7 non Shared Region
MMTAppReg6.MEMORYMAP.ContextLink=reserved
MMTAppReg6.MEMORYMAP.ContextName=CortexM4
MMTAppReg6.MEMORYMAP.CoreName=ARM Cortex-M4
MMTAppReg6.MEMORYMAP.IPParameters=StartAddress,Size,CoreName,MemType,ContextName,Name,ReservedRegion,ContextLink
MMTAppReg6.MEMORYMAP.MemType=ROM
MMTAppReg6.MEMORYMAP.Name=M7 non Shared Region
MMTAppReg6.MEMORYMAP.ReservedRegion=true
MMTAppReg6.MEMORYMAP.Size=786432
MMTAppReg6.MEMORYMAP.StartAddress=0x8040000
MMTAppReg7.MEMORYMAP.AppRegionName=RAM
MMTAppReg7.MEMORYMAP.Cacheability=NonCacheable
MMTAppReg7.MEMORYMAP.ContextLink=shared
MMTAppReg7.MEMORYMAP.ContextName=CortexM4
MMTAppReg7.MEMORYMAP.CoreName=ARM Cortex-M4
MMTAppReg7.MEMORYMAP.DefaultDataRegion=true
MMTAppReg7.MEMORYMAP.IPParameters=StartAddress,Size,CoreName,ContextName,Name,Cacheability,DefaultDataRegion,ContextLink
MMTAppReg7.MEMORYMAP.Name=RAM
MMTAppReg7.MEMORYMAP.Size=294912
MMTAppReg7.MEMORYMAP.StartAddress=0x10000000
MMTAppReg8.MEMORYMAP.AppRegionName=M4 non Shared Region
MMTAppReg8.MEMORYMAP.ContextLink=reserved
MMTAppReg8.MEMORYMAP.ContextName=CortexM7
MMTAppReg8.MEMORYMAP.CoreName=ARM Cortex-M7
MMTAppReg8.MEMORYMAP.IPParameters=StartAddress,Size,CoreName,ContextName,Name,ReservedRegion,ContextLink
MMTAppReg8.MEMORYMAP.Name=M4 non Shared Region
MMTAppReg8.MEMORYMAP.ReservedRegion=true
MMTAppReg8.MEMORYMAP.Size=294912
MMTAppReg8.MEMORYMAP.StartAddress=0x10000000
MMTAppReg9.MEMORYMAP.AP=RO_priv_only
MMTAppReg9.MEMORYMAP.AppRegionName=FLASH
MMTAppReg9.MEMORYMAP.Cacheability=NonCacheable
MMTAppReg9.MEMORYMAP.ContextLink=shared
MMTAppReg9.MEMORYMAP.ContextName=CortexM4
MMTAppReg9.MEMORYMAP.CoreName=ARM Cortex-M4
MMTAppReg9.MEMORYMAP.DefaultCodeRegion=true
MMTAppReg9.MEMORYMAP.IPParameters=StartAddress,Size,CoreName,MemType,ContextName,Name,AP,Cacheability,DefaultCodeRegion,ISRRegion,RootBootRegion,ContextLink
MMTAppReg9.MEMORYMAP.ISRRegion=true
MMTAppReg9.MEMORYMAP.MemType=ROM
MMTAppReg9.MEMORYMAP.Name=FLASH
MMTAppReg9.MEMORYMAP.RootBootRegion=true
MMTAppReg9.MEMORYMAP.Size=1048576
MMTAppReg9.MEMORYMAP.StartAddress=0x08100000
MMTAppRegionsCount=10
MMTConfigApplied=false
MMTProjectManagerApplied=true
MMTSectionSuffix=
Mcu.CPN=STM32H747XIH6
Mcu.Context0=CortexM7
Mcu.Context1=CortexM4
Mcu.ContextNb=2
Mcu.Family=STM32H7
Mcu.IP0=CORTEX_M4
Mcu.IP1=CORTEX_M7
Mcu.IP2=MEMORYMAP
Mcu.IP3=NVIC1
Mcu.IP4=NVIC2
Mcu.IP5=RCC
Mcu.IP6=RNG
Mcu.IP7=SYS
Mcu.IP8=SYS_M4
Mcu.IP9=USART1
Mcu.IPNb=10
Mcu.Name=STM32H747XIHx
Mcu.Package=TFBGA240
Mcu.Pin0=PK5
Mcu.Pin1=PC10
Mcu.Pin10=PC8
Mcu.Pin11=PC9
Mcu.Pin12=PH0-OSC_IN (PH0)
Mcu.Pin13=PJ1
Mcu.Pin14=PJ5
Mcu.Pin15=VP_RNG_VS_RNG
Mcu.Pin16=VP_SYS_VS_Systick
Mcu.Pin17=VP_SYS_M4_VS_Systick
Mcu.Pin18=VP_MEMORYMAP_VS_MEMORYMAP
Mcu.Pin2=PC11
Mcu.Pin3=PC14-OSC32_IN (OSC32_IN)
Mcu.Pin4=PK6
Mcu.Pin5=PC12
Mcu.Pin6=PK7
Mcu.Pin7=PD2
Mcu.Pin8=PA10
Mcu.Pin9=PA9
Mcu.PinsNb=19
Mcu.ThirdPartyNb=0
Mcu.UserConstants=
Mcu.UserName=STM32H747XIHx
MxCube.Version=6.17.0
MxDb.Version=DB.6.0.170
NVIC1.BusFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
NVIC1.DebugMonitor_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
NVIC1.ForceEnableDMAVector=true
NVIC1.HardFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
NVIC1.MemoryManagement_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
NVIC1.NonMaskableInt_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
NVIC1.PendSV_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
NVIC1.PriorityGroup=NVIC_PRIORITYGROUP_4
NVIC1.SVCall_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
NVIC1.SysTick_IRQn=true\:0\:0\:false\:false\:true\:false\:true\:false
NVIC1.UsageFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
NVIC2.BusFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
NVIC2.DebugMonitor_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
NVIC2.ForceEnableDMAVector=true
NVIC2.HardFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
NVIC2.MemoryManagement_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
NVIC2.NonMaskableInt_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
NVIC2.PendSV_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
NVIC2.PriorityGroup=NVIC_PRIORITYGROUP_4
NVIC2.SVCall_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
NVIC2.SysTick_IRQn=true\:0\:0\:false\:false\:true\:false\:true\:false
NVIC2.UsageFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
PA10.GPIOParameters=GPIO_Label,PinAttribute
PA10.GPIO_Label=USART1_RX
PA10.Locked=true
PA10.Mode=Asynchronous
PA10.PinAttribute=CortexM7
PA10.Signal=USART1_RX
PA9.GPIOParameters=GPIO_Label,PinAttribute
PA9.GPIO_Label=USART1_TX
PA9.Locked=true
PA9.Mode=Asynchronous
PA9.PinAttribute=CortexM7
PA9.Signal=USART1_TX
PC10.GPIOParameters=GPIO_PuPd,GPIO_Label,PinAttribute
PC10.GPIO_Label=SDIO_D2
PC10.GPIO_PuPd=GPIO_PULLUP
PC10.Locked=true
PC10.PinAttribute=CortexM7
PC10.Signal=SDMMC1_D2
PC11.GPIOParameters=GPIO_PuPd,GPIO_Label,PinAttribute
PC11.GPIO_Label=SDIO_D3
PC11.GPIO_PuPd=GPIO_PULLUP
PC11.Locked=true
PC11.PinAttribute=CortexM7
PC11.Signal=SDMMC1_D3
PC12.GPIOParameters=GPIO_PuPd,GPIO_Label,PinAttribute
PC12.GPIO_Label=SDIO_CLK
PC12.GPIO_PuPd=GPIO_PULLUP
PC12.Locked=true
PC12.PinAttribute=CortexM7
PC12.Signal=SDMMC1_CK
PC14-OSC32_IN\ (OSC32_IN).GPIOParameters=GPIO_Label,PinAttribute
PC14-OSC32_IN\ (OSC32_IN).GPIO_Label=OSC32_IN
PC14-OSC32_IN\ (OSC32_IN).Locked=true
PC14-OSC32_IN\ (OSC32_IN).Mode=LSE-External-Clock-Source
PC14-OSC32_IN\ (OSC32_IN).PinAttribute=CortexM7
PC14-OSC32_IN\ (OSC32_IN).Signal=RCC_OSC32_IN
PC8.GPIOParameters=GPIO_PuPd,GPIO_Label,PinAttribute
PC8.GPIO_Label=SDIO_D0
PC8.GPIO_PuPd=GPIO_PULLUP
PC8.Locked=true
PC8.PinAttribute=CortexM7
PC8.Signal=SDMMC1_D0
PC9.GPIOParameters=GPIO_PuPd,GPIO_Label,PinAttribute
PC9.GPIO_Label=SDIO_D1
PC9.GPIO_PuPd=GPIO_PULLUP
PC9.Locked=true
PC9.PinAttribute=CortexM7
PC9.Signal=SDMMC1_D1
PD2.GPIOParameters=GPIO_PuPd,GPIO_Label,PinAttribute
PD2.GPIO_Label=SDIO_MMC
PD2.GPIO_PuPd=GPIO_PULLUP
PD2.Locked=true
PD2.PinAttribute=CortexM7
PD2.Signal=SDMMC1_CMD
PH0-OSC_IN\ (PH0).GPIOParameters=GPIO_Label,PinAttribute
PH0-OSC_IN\ (PH0).GPIO_Label=OSC25M_IN
PH0-OSC_IN\ (PH0).Locked=true
PH0-OSC_IN\ (PH0).Mode=HSE-External-Clock-Source
PH0-OSC_IN\ (PH0).PinAttribute=Free
PH0-OSC_IN\ (PH0).Signal=RCC_OSC_IN
PJ1.ContextOwner=CortexM7
PJ1.GPIOParameters=GPIO_Label,PinAttribute
PJ1.GPIO_Label=PWR
PJ1.Locked=true
PJ1.PinAttribute=CortexM7
PJ1.Signal=GPIO_Output
PJ5.ContextOwner=CortexM7
PJ5.GPIOParameters=GPIO_Label,PinAttribute
PJ5.GPIO_Label=WAKE
PJ5.Locked=true
PJ5.PinAttribute=CortexM7
PJ5.Signal=GPIO_Input
PK5.ContextOwner=CortexM7
PK5.GPIOParameters=PinState,GPIO_Label,PinAttribute
PK5.GPIO_Label=LED R
PK5.Locked=true
PK5.PinAttribute=CortexM7
PK5.PinState=GPIO_PIN_SET
PK5.Signal=GPIO_Output
PK6.ContextOwner=CortexM7
PK6.GPIOParameters=PinState,GPIO_Label,PinAttribute
PK6.GPIO_Label=LED G
PK6.Locked=true
PK6.PinAttribute=CortexM7
PK6.PinState=GPIO_PIN_SET
PK6.Signal=GPIO_Output
PK7.ContextOwner=CortexM7
PK7.GPIOParameters=PinState,GPIO_Label,PinAttribute
PK7.GPIO_Label=LED B
PK7.Locked=true
PK7.PinAttribute=CortexM7
PK7.PinState=GPIO_PIN_SET
PK7.Signal=GPIO_Output
PinOutPanel.CurrentBGAView=Top
PinOutPanel.RotationAngle=0
ProjectManager.AskForMigrate=true
ProjectManager.BackupPrevious=false
ProjectManager.BootMode=boot0
ProjectManager.CompilerLinker=GCC
ProjectManager.CompilerOptimize=6
ProjectManager.ComputerToolchain=false
ProjectManager.CoupleFile=false
ProjectManager.CustomerFirmwarePackage=
ProjectManager.DefaultFWLocation=true
ProjectManager.DeletePrevious=true
ProjectManager.DeviceId=STM32H747XIHx
ProjectManager.FirmwarePackage=STM32Cube FW_H7 V1.13.0
ProjectManager.FreePins=false
ProjectManager.FreePinsContext=CortexM7
ProjectManager.HalAssertFull=false
ProjectManager.HeapSize=M4-0x200,M7-0x10000
ProjectManager.KeepUserCode=true
ProjectManager.LastFirmware=true
ProjectManager.LibraryCopy=1
ProjectManager.MainLocation=Core/Src
ProjectManager.NoMain=false
ProjectManager.PreviousToolchain=STM32CubeIDE
ProjectManager.ProjectBuild=false
ProjectManager.ProjectFileName=portenta_h7.ioc
ProjectManager.ProjectName=portenta_h7
ProjectManager.ProjectStructure=M7\:CortexM7 Project\:true;M4\:CortexM4 Project\:true;
ProjectManager.RegisterCallBack=
ProjectManager.StackSize=M4-0x400,M7-0x2000
ProjectManager.TargetToolchain=CMake
ProjectManager.ToolChainLocation=
ProjectManager.UAScriptAfterPath=
ProjectManager.UAScriptBeforePath=
ProjectManager.UnderRoot=false
ProjectManager.functionlistsort=1-SystemClock_Config-RCC-false-HAL-false-CortexM7,2-MX_GPIO_Init-GPIO-false-HAL-true-CortexM7,3-MX_USART1_UART_Init-USART1-false-HAL-true-CortexM7,4-MX_RNG_Init-RNG-false-HAL-true-CortexM7,0-MX_CORTEX_M7_Init-CORTEX_M7-false-HAL-true-CortexM7,0-MX_CORTEX_M4_Init-CORTEX_M4-false-HAL-true-CortexM4
RCC.ADCFreq_Value=80000000
RCC.AHB12Freq_Value=200000000
RCC.AHB4Freq_Value=200000000
RCC.APB1Freq_Value=100000000
RCC.APB2Freq_Value=100000000
RCC.APB3Freq_Value=100000000
RCC.APB4Freq_Value=100000000
RCC.AXIClockFreq_Value=200000000
RCC.CECFreq_Value=32000
RCC.CKPERFreq_Value=64000000
RCC.CPU2Freq_Value=200000000
RCC.CPU2SystikFreq_Value=200000000
RCC.CortexFreq_Value=400000000
RCC.CpuClockFreq_Value=400000000
RCC.D1CPREFreq_Value=400000000
RCC.D1PPRE=RCC_APB3_DIV2
RCC.D2PPRE1=RCC_APB1_DIV2
RCC.D2PPRE2=RCC_APB2_DIV2
RCC.D3PPRE=RCC_APB4_DIV2
RCC.DFSDMACLkFreq_Value=200000000
RCC.DFSDMFreq_Value=100000000
RCC.DIVM1=4
RCC.DIVM2=4
RCC.DIVN1=50
RCC.DIVN2=10
RCC.DIVP1Freq_Value=400000000
RCC.DIVP2Freq_Value=80000000
RCC.DIVP3Freq_Value=129000000
RCC.DIVQ1=4
RCC.DIVQ1Freq_Value=200000000
RCC.DIVQ2Freq_Value=80000000
RCC.DIVQ3Freq_Value=129000000
RCC.DIVR1Freq_Value=400000000
RCC.DIVR2Freq_Value=80000000
RCC.DIVR3Freq_Value=129000000
RCC.DSIFreq_Value=62500000
RCC.DSITXEscFreq_Value=15625000
RCC.FDCANFreq_Value=200000000
RCC.FMCFreq_Value=200000000
RCC.FamilyName=M
RCC.HCLK3ClockFreq_Value=200000000
RCC.HCLKFreq_Value=200000000
RCC.HPRE=RCC_HCLK_DIV2
RCC.HRTIMFreq_Value=200000000
RCC.I2C123Freq_Value=100000000
RCC.I2C4Freq_Value=100000000
RCC.IPParameters=ADCFreq_Value,AHB12Freq_Value,AHB4Freq_Value,APB1Freq_Value,APB2Freq_Value,APB3Freq_Value,APB4Freq_Value,AXIClockFreq_Value,CECFreq_Value,CKPERFreq_Value,CPU2Freq_Value,CPU2SystikFreq_Value,CortexFreq_Value,CpuClockFreq_Value,D1CPREFreq_Value,D1PPRE,D2PPRE1,D2PPRE2,D3PPRE,DFSDMACLkFreq_Value,DFSDMFreq_Value,DIVM1,DIVM2,DIVN1,DIVN2,DIVP1Freq_Value,DIVP2Freq_Value,DIVP3Freq_Value,DIVQ1,DIVQ1Freq_Value,DIVQ2Freq_Value,DIVQ3Freq_Value,DIVR1Freq_Value,DIVR2Freq_Value,DIVR3Freq_Value,DSIFreq_Value,DSITXEscFreq_Value,FDCANFreq_Value,FMCFreq_Value,FamilyName,HCLK3ClockFreq_Value,HCLKFreq_Value,HPRE,HRTIMFreq_Value,I2C123Freq_Value,I2C4Freq_Value,LPTIM1Freq_Value,LPTIM2Freq_Value,LPTIM345Freq_Value,LPUART1Freq_Value,LTDCFreq_Value,MCO1PinFreq_Value,MCO2PinFreq_Value,PLL2FRACN,PLLDSIVCOFreq_Value,PLLFRACN,PWR_Regulator_Voltage_Scale,QSPIFreq_Value,RNGFreq_Value,RTCFreq_Value,SAI1Freq_Value,SAI23Freq_Value,SAI4AFreq_Value,SAI4BFreq_Value,SDMMCFreq_Value,SPDIFRXFreq_Value,SPI123Freq_Value,SPI45Freq_Value,SPI6Freq_Value,SWPMI1Freq_Value,SYSCLKFreq_VALUE,SYSCLKSource,SupplySource,Tim1OutputFreq_Value,Tim2OutputFreq_Value,TraceFreq_Value,USART16Freq_Value,USART234578Freq_Value,USBFreq_Value,VCO1OutputFreq_Value,VCO2OutputFreq_Value,VCO3OutputFreq_Value,VCOInput1Freq_Value,VCOInput2Freq_Value,VCOInput3Freq_Value
RCC.LPTIM1Freq_Value=100000000
RCC.LPTIM2Freq_Value=100000000
RCC.LPTIM345Freq_Value=100000000
RCC.LPUART1Freq_Value=100000000
RCC.LTDCFreq_Value=129000000
RCC.MCO1PinFreq_Value=64000000
RCC.MCO2PinFreq_Value=400000000
RCC.PLL2FRACN=0
RCC.PLLDSIVCOFreq_Value=1000000000
RCC.PLLFRACN=0
RCC.PWR_Regulator_Voltage_Scale=PWR_REGULATOR_VOLTAGE_SCALE1
RCC.QSPIFreq_Value=200000000
RCC.RNGFreq_Value=48000000
RCC.RTCFreq_Value=32000
RCC.SAI1Freq_Value=200000000
RCC.SAI23Freq_Value=200000000
RCC.SAI4AFreq_Value=200000000
RCC.SAI4BFreq_Value=200000000
RCC.SDMMCFreq_Value=200000000
RCC.SPDIFRXFreq_Value=200000000
RCC.SPI123Freq_Value=200000000
RCC.SPI45Freq_Value=100000000
RCC.SPI6Freq_Value=100000000
RCC.SWPMI1Freq_Value=100000000
RCC.SYSCLKFreq_VALUE=400000000
RCC.SYSCLKSource=RCC_SYSCLKSOURCE_PLLCLK
RCC.SupplySource=PWR_DIRECT_SMPS_SUPPLY
RCC.Tim1OutputFreq_Value=200000000
RCC.Tim2OutputFreq_Value=200000000
RCC.TraceFreq_Value=64000000
RCC.USART16Freq_Value=100000000
RCC.USART234578Freq_Value=100000000
RCC.USBFreq_Value=200000000
RCC.VCO1OutputFreq_Value=800000000
RCC.VCO2OutputFreq_Value=160000000
RCC.VCO3OutputFreq_Value=258000000
RCC.VCOInput1Freq_Value=16000000
RCC.VCOInput2Freq_Value=16000000
RCC.VCOInput3Freq_Value=2000000
USART1.IPParameters=VirtualMode-Asynchronous
USART1.VirtualMode-Asynchronous=VM_ASYNC
VP_MEMORYMAP_VS_MEMORYMAP.Mode=CurAppReg
VP_MEMORYMAP_VS_MEMORYMAP.Signal=MEMORYMAP_VS_MEMORYMAP
VP_RNG_VS_RNG.Mode=RNG_Activate
VP_RNG_VS_RNG.Signal=RNG_VS_RNG
VP_SYS_M4_VS_Systick.Mode=SysTick
VP_SYS_M4_VS_Systick.Signal=SYS_M4_VS_Systick
VP_SYS_VS_Systick.Mode=SysTick
VP_SYS_VS_Systick.Signal=SYS_VS_Systick
board=STM32H747I-DISCO
boardIOC=true
+153
View File
@@ -0,0 +1,153 @@
#include "mongoose.h"
#include "main.h"
typedef enum {RESOURCE_IN_MEMORY,RESOURCE_IN_EXTERNAL_STORAGE} resource_location_t;
typedef struct {resource_location_t l;unsigned long sz;union{struct {unsigned long o;const char *f;}fs;struct {const char *m;}mem;}val;} resource_hnd_t;
#include "resources/firmware/COMPONENT_4343W_FS/4343WA1_clm_blob.c"
#include "resources/firmware/COMPONENT_4343W_FS/4343WA1_bin.c"
#include "resources/nvram/wifi_nvram_image.h"
#ifndef WIFI_NAME
#define WIFI_NAME ""
#endif
#ifndef WIFI_PASS
#define WIFI_PASS ""
#endif
#ifndef WIFI_AP_NAME
#define WIFI_AP_NAME "Mongoose"
#endif
#ifndef WIFI_AP_PASS
#define WIFI_AP_PASS "mongoose"
#endif
#ifndef ENABLE_WIFI_AP
#define ENABLE_WIFI_AP 0
#endif
// - 58.4 Table 473
// - 58.5.8 AHB bw >= 3x SDMMC bus bw; Table 492; SD HS => AHB >= ~20MHz
// - SDIO 2.0 section 3.1
static inline void sdmmc_set_ds(void) { // NO FLOW CONTROL ??? REALLY ??? *******************************
MG_SET_BITS(SDMMC1->CLKCR, (1 << 17) | 0x3ff, (1 << 14) | ((200000000 / (2 * 25000000)) & 0x3ff)); // 4-bit bus, 25MHz
}
static inline void sdmmc_set_hs(void) { // mbed enables flow control... AIROC does not
MG_SET_BITS(SDMMC1->CLKCR, 0x3ff, (1 << 17) | (1 << 14) | ((200000000 / (2 * 50000000)) & 0x3ff)); // hw flow control, 4-bit bus, 50MHz
}
static inline void sdmmc_init(void) {
RCC->AHB3RSTR |= MG_BIT(16); // Reset SDIO block
RCC->AHB3RSTR &= ~MG_BIT(16);
RCC->D1CCIPR &= ~MG_BIT(16); // 9.7.18, use PLL1Q
RCC->AHB3ENR |= MG_BIT(16); // Enable SDIO clock
// SDIO pins initialized by Cube; Portenta H7 doesn't pull-up the SDIO bus
// 1-bit bus, rising edge, clock always on, no flow control, 400KHz
SDMMC1->CLKCR = (200000000 / (2 * 400000)) & 0x3ff;
SDMMC1->POWER |= 3 << 0; // SDMMC_POWER_PWRCTRL
SDMMC1->DCTRL = MG_BIT(10); // Read Wait control by stopping SDMMCCLK
// wait 74 clocks (~200us) before accessing the "card"
}
static inline bool sdmmc_transact(uint8_t cmd, uint32_t arg, uint32_t *resp) {
unsigned int wresp = (resp != NULL) ? 1 : 0; // 58.10.4: 2-bit; cmds used
uint32_t cmdr = MG_BIT(12) | (wresp << 8) | ((cmd & 0x3f) << 0); // CPSMEN
SDMMC1->ARG = arg;
MG_SET_BITS(SDMMC1->CMD, 0x00011f3f /* CMDSUSPEND CPSMEN WAITPEND WAITINT WAITRESP CMD */, cmdr);
if (wresp == 0) {
while ((SDMMC1->STA & SDMMC_STA_CMDSENT) == 0) (void)0;
SDMMC1->ICR = 0x002000C5; // BUSYD0END CMDSENT CMDREND CTIMEOUT CCRCFAIL
return true;
}
//while ((SDMMC1->STA & 0x00200045 /*BUSYD0END CMDREND CTIMEOUT CCRCFAIL*/) == 0 || SDMMC1->STA & SDMMC_STA_CPSMACT) spin(1);
while (SDMMC1->STA & SDMMC_STA_CPSMACT) (void)0;
bool res = (SDMMC1->STA & 0x00200005 /*BUSYD0END CTIMEOUT CCRCFAIL*/) == 0;
SDMMC1->ICR = 0x002000C5; // BUSYD0END CMDSENT CMDREND CTIMEOUT CCRCFAIL
*resp = SDMMC1->RESP1;
if (!res || SDMMC1->RESPCMD != cmd) return false;
return true;
}
static inline unsigned int _log2(uint16_t x) {
unsigned int i = 0;
while (x != 0) x = ((uint16_t)x) >> 1, ++i;
return i - 1;
}
static inline bool sdmmc_transfer(bool write, uint32_t arg, uint16_t blksz, uint32_t *data, uint32_t length, uint32_t *resp) {
uint32_t cmdr = MG_BIT(12) | (1 << 8) | (53 << 0); // CPSMEN WRESP CMD=53
bool bmode = (blksz != 0);
uint8_t dblksz = bmode ? (uint8_t)_log2(blksz) : 0;
uint32_t dctrlr = (dblksz << 4) | (bmode ? (0 << 2) : (1 << 2)) | (write ? 0 : MG_BIT(1)); // DBLOCKSIZE=f(blksz) DTMODE=f(bmode) DTDIR=!write !DTEN
SDMMC1->DCTRL = SDMMC_DCTRL_RWMOD;
SDMMC1->IDMACTRL = SDMMC_IDMA_IDMAEN; // single buffer
SDMMC1->IDMABASE0 = (uint32_t)data;
SDMMC1->DTIMER = (uint32_t) 50000000; // 58.10.7, set for 1 sec @50MHz (2 secs @25MHz)
SDMMC1->DLEN = length; // 58.10.8 call with a multiple of blksz (we do)
MG_SET_BITS(SDMMC1->DCTRL, 0xff /* DBLOCKSIZE DTMODE DTDIR DTEN */, dctrlr);
SDMMC1->CMD = SDMMC_CMD_CMDTRANS; // CPSM will perform a data transfer
SDMMC1->ARG = arg;
SDMMC1->CMD |= cmdr; // other flags cleared above
while ((SDMMC1->STA & 0x0020003f) == 0 && SDMMC1->STA & SDMMC_STA_DPSMACT)
(void) 0; // incorrect "card" usage will stall here
bool res = (SDMMC1->STA & 0x0020003f /* BUSYD0END RXOVERR TXUNDERR DTIMEOUT CTIMEOUT DCRCFAIL CCRCFAIL */) == 0;
SDMMC1->CMD = 0;
SDMMC1->DLEN = 0;
SDMMC1->IDMACTRL = 0;
SDMMC1->ICR = (uint32_t) ~0; // clear all flags
*resp = SDMMC1->RESP1;
SDMMC1->DCTRL = SDMMC_DCTRL_RWMOD;
if (!res || SDMMC1->RESPCMD != 53) return false;
return true;
}
void hwspecific_sdio_init(void) {
HAL_GPIO_WritePin(PWR_GPIO_Port, PWR_Pin, 0);
mg_delayms(100);
sdmmc_init();
mg_delayms(1);
HAL_GPIO_WritePin(PWR_GPIO_Port, PWR_Pin, 1);
mg_delayms(50);
}
void hwspecific_sdio_config(void *sdio, uint8_t cfg) {
(void) sdio;
if (cfg == 0) return sdmmc_set_ds();
if (cfg == 1) return sdmmc_set_hs();
}
bool hwspecific_sdio_txn(void *sdio, uint8_t cmd, uint32_t arg, uint32_t *resp) {
(void) sdio;
return sdmmc_transact(cmd, arg, resp);
}
bool hwspecific_sdio_xfr(void *sdio, bool write, uint32_t arg, uint16_t blksz, uint32_t *data, uint32_t len, uint32_t *resp){
(void) sdio;
return sdmmc_transfer(write, arg, blksz, data, len, resp);
}
static const struct mg_tcpip_sdio sdio = {NULL, hwspecific_sdio_config, hwspecific_sdio_txn, hwspecific_sdio_xfr};
static const struct mg_tcpip_driver_cyw_firmware fw = {
(const uint8_t *)wifi_firmware_image_data, (size_t)sizeof(wifi_firmware_image_data),
(const uint8_t *)wifi_nvram_image, (size_t)sizeof(wifi_nvram_image),
(const uint8_t *)wifi_firmware_clm_blob_image_data, (size_t)sizeof(wifi_firmware_clm_blob_image_data)};
void wifi_setconfig(void *data) {
struct mg_tcpip_driver_cyw_data *d = (struct mg_tcpip_driver_cyw_data *) data;
d->bus = (struct mg_tcpip_sdio *) &sdio;
d->fw = (struct mg_tcpip_driver_cyw_firmware *)&fw;
d->wifi.ssid = WIFI_NAME;
d->wifi.pass = WIFI_PASS;
d->wifi.apssid = WIFI_AP_NAME;
d->wifi.appass = WIFI_AP_PASS;
d->wifi.apip = MG_IPV4(192, 168, 111, 1);
d->wifi.apmask = MG_IPV4(255, 255, 255, 0);
d->wifi.security = 0;
d->wifi.apsecurity = 0;
d->wifi.apchannel = 10;
d->wifi.apmode = ENABLE_WIFI_AP;
d->hs = false;
}
@@ -0,0 +1,48 @@
# Environment setup: https://mongoose.ws/documentation/tutorials/tools/
CFLAGS = -W -Wall -Wextra -Werror -Wundef -Wshadow -Wdouble-promotion
CFLAGS += -Wformat-truncation -fno-common -Wconversion -Wno-sign-conversion
CFLAGS += -g3 -Os -ffunction-sections -fdata-sections
CFLAGS += -Wno-cast-function-type
CFLAGS += -I. -Icmsis_core/CMSIS/Core/Include -Icmsis_h7/Include
CFLAGS += -mcpu=cortex-m7 -mthumb -mfloat-abi=hard -mfpu=fpv5-d16 -DCORE_CM7
LDFLAGS ?= -Tlink.ld -nostdlib -nostartfiles --specs nosys.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map
SOURCES = main.c wifi.c hal.c
SOURCES += cmsis_h7/Source/Templates/gcc/startup_stm32h747xx.s
TOP = ../../../..
CFLAGS += -I$(TOP)
SOURCES += $(TOP)/mongoose.c
# Wi-Fi chip firmware stuff
CFLAGS += -Imbed/targets/TARGET_STM/TARGET_STM32H7/TARGET_STM32H747xI/TARGET_PORTENTA_H7/COMPONENT_WHD # nvram header includes a text file
all build: firmware.bin
all: firmware.bin
firmware.elf: cmsis_core cmsis_h7 $(SOURCES) mbed $(wildcard *.h) link.ld Makefile
arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(CFLAGS_EXTRA) $(LDFLAGS) -o $@
firmware.bin: firmware.elf
arm-none-eabi-objcopy -O binary $< $@
@echo
@echo "To flash, run 'make flash', or use STM32CubeProgrammer"
# Wi-Fi chip firmware
mbed:
git clone --depth 1 --no-checkout -b extrapatches-6.17.0 https://github.com/arduino/mbed-os $@ && cd $@ && git sparse-checkout set targets/TARGET_STM/TARGET_STM32H7/TARGET_STM32H747xI/TARGET_PORTENTA_H7/COMPONENT_WHD/resources && git checkout
touch wiced_resource.h
flash: firmware.bin
dfu-util -a 0 -s 0x8040000:leave -D $<
cmsis_core:
git clone -q -c advice.detachedHead=false --depth 1 -b 5.9.0 https://github.com/ARM-software/CMSIS_5 $@
cmsis_h7:
git clone -q -c advice.detachedHead=false --depth 1 -b v1.10.4 https://github.com/STMicroelectronics/cmsis_device_h7 $@
clean:
rm -rf firmware.*
+125
View File
@@ -0,0 +1,125 @@
// Copyright (c) 2026 Cesanta Software Limited
// All rights reserved
#include "hal.h"
bool hal_timer_expired(volatile uint64_t *t, uint64_t period, uint64_t now) {
uint64_t diff = now - *t; // Wrap-safe elapsed time since last expiry
if (period == 0) return false; // Avoid division by zero
if (diff < period) return false; // Period has not elapsed yet
*t += (diff / period) * period; // Preserve cadence, skip missed periods
return true;
}
static volatile uint64_t s_ticks; // Milliseconds since boot
void SysTick_Handler(void) { // SyStick IRQ handler, triggered every 1ms
s_ticks++;
}
uint64_t hal_get_tick(void) {
return s_ticks;
};
uint32_t SystemCoreClock = SYS_FREQUENCY;
void SystemInit(void) { // Called automatically by startup code
hal_system_init(); // Enable FPU
hal_clock_init();
SysTick_Config(SystemCoreClock / 1000); // Sys tick every 1ms
}
void ExitRun0Mode(void) {
}
struct stat;
__attribute__((weak)) int _fstat(int fd, struct stat *st) {
(void) fd, (void) st;
return -1;
}
extern unsigned char _end[]; // End of data section, start of heap. See link.ld
static unsigned char *s_current_heap_end = _end;
size_t hal_ram_used(void) {
return (size_t) (s_current_heap_end - _end);
}
size_t hal_ram_free(void) {
unsigned char endofstack;
return (size_t) (&endofstack - s_current_heap_end);
}
void *_sbrk(int incr) {
unsigned char *prev_heap;
unsigned char *heap_end = (unsigned char *) ((size_t) &heap_end - 256);
prev_heap = s_current_heap_end;
// Check how much space we got from the heap end to the stack end
if (s_current_heap_end + incr > heap_end) return (void *) -1;
s_current_heap_end += incr;
return prev_heap;
}
__attribute__((weak)) int _open(const char *path) {
(void) path;
return -1;
}
__attribute__((weak)) int _close(int fd) {
(void) fd;
return -1;
}
__attribute__((weak)) int _isatty(int fd) {
(void) fd;
return 1;
}
__attribute__((weak)) int _lseek(int fd, int ptr, int dir) {
(void) fd, (void) ptr, (void) dir;
return 0;
}
__attribute__((weak)) void _exit(int status) {
(void) status;
for (;;) asm volatile("BKPT #0");
}
__attribute__((weak)) void _kill(int pid, int sig) {
(void) pid, (void) sig;
}
__attribute__((weak)) int _getpid(void) {
return -1;
}
__attribute__((weak)) int _write(int fd, char *ptr, int len) {
(void) fd, (void) ptr, (void) len;
return -1;
}
__attribute__((weak)) int _read(int fd, char *ptr, int len) {
(void) fd, (void) ptr, (void) len;
return -1;
}
__attribute__((weak)) int _link(const char *a, const char *b) {
(void) a, (void) b;
return -1;
}
__attribute__((weak)) int _unlink(const char *a) {
(void) a;
return -1;
}
__attribute__((weak)) int _stat(const char *path, struct stat *st) {
(void) path, (void) st;
return -1;
}
__attribute__((weak)) int mkdir(const char *path, int mode) {
(void) path, (void) mode;
return -1;
}
__attribute__((weak)) void _init(void) {
}
+509
View File
@@ -0,0 +1,509 @@
// Copyright (c) 2022-2023 Cesanta Software Limited
// All rights reserved
//
// RM0399
// https://www.st.com/resource/en/reference_manual/rm0399-stm32h745755-and-stm32h747757-advanced-armbased-32bit-mcus-stmicroelectronics.pdf
// https://www.st.com/resource/en/datasheet/stm32h747xi.pdf
// https://docs.arduino.cc/hardware/portenta-h7/
#pragma once
#define LED_1 PIN('K', 5) // On-board LED pin (red)
#define LED_2 PIN('K', 6) // On-board LED pin (green)
#define LED_3 PIN('K', 7) // On-board LED pin (blue)
#define LED LED_2 // Use green LED for blinking
#ifndef HAL_DEBUG_UART
#define HAL_DEBUG_UART USART1
#endif
#ifndef UART_DEBUG
#define UART_DEBUG HAL_DEBUG_UART
#endif
#include <stm32h747xx.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
extern bool hal_timer_expired(volatile uint64_t *t, uint64_t prd, uint64_t now);
extern uint64_t hal_get_tick(void);
extern size_t hal_ram_free(void);
extern size_t hal_ram_used(void);
extern uint32_t SystemCoreClock;
#define BIT(x) (1UL << (x))
#define CLRSET(reg, clear, set) ((reg) = ((reg) & ~(clear)) | (set))
#define PIN(bank, num) ((((bank) - 'A') << 8) | (num))
#define PINNO(pin) (pin & 255)
#define PINBANK(pin) (pin >> 8)
void hal_init(void);
size_t hal_ram_free(void);
size_t hal_ram_used(void);
// System clock (2.1, Fig 1; 8.5, Fig 45; 8.5.5, Fig 47; 8.5.6, Fig 49)
// - This board defaults to work with an SMPS regulator, so:
// - SYS_FREQUENCY <= 400 MHz; hclk = SYS_FREQUENCY / HPRE ; hclk <= 200 MHz;
// APB clocks <= 100 MHz.
// - D1 domain bus matrix (and so flash) runs at hclk frequency. Configure flash
// latency (WS) in accordance to hclk freq (4.3.8, Table 17)
// - The Ethernet controller is in D2 domain and runs at hclk frequency
enum {
HAL_D1CPRE = 1, // actual divisor value
HAL_HPRE = 2, // actual divisor value
HAL_D1PPRE = 4, // register values, divisor value = BIT(value - 3) = / 2
HAL_D2PPRE1 = 4,
HAL_D2PPRE2 = 4,
HAL_D3PPRE = 4
};
// PLL1_P: odd division factors are not allowed (8.7.13) (according to Cube, '1'
// is also an "odd division factor").
// Make sure your chip is revision 'V', otherwise set PLL1_N = 400
enum { PLL1_HSI = 64, PLL1_M = 32, PLL1_N = 400, PLL1_P = 2, PLL1_Q = 4 };
#define SYS_FREQUENCY \
((PLL1_HSI * PLL1_N / PLL1_M / PLL1_P / HAL_D1CPRE) * 1000000)
// #define SYS_FREQUENCY ((PLL1_HSI / HAL_D1CPRE) * 1000000)
// #define SYS_FREQUENCY 64000000
#define AHB_FREQUENCY (SYS_FREQUENCY / HAL_HPRE)
#define APB2_FREQUENCY (AHB_FREQUENCY / (BIT(HAL_D2PPRE2 - 3)))
#define APB1_FREQUENCY (AHB_FREQUENCY / (BIT(HAL_D2PPRE1 - 3)))
#define PLL1_Q_FREQUENCY ((PLL1_HSI * PLL1_N / PLL1_M / PLL1_Q) * 1000000)
static inline void spin(volatile uint32_t n) {
while (n--)
(void)0;
}
enum {
HAL_GPIO_MODE_INPUT,
HAL_GPIO_MODE_OUTPUT,
HAL_GPIO_MODE_AF,
HAL_GPIO_MODE_ANALOG
};
enum { HAL_GPIO_OTYPE_PUSH_PULL, HAL_GPIO_OTYPE_OPEN_DRAIN };
enum {
HAL_GPIO_SPEED_LOW,
HAL_GPIO_SPEED_MEDIUM,
HAL_GPIO_SPEED_HIGH,
HAL_GPIO_SPEED_INSANE
};
enum { HAL_GPIO_PULL_NONE, HAL_GPIO_PULL_UP, HAL_GPIO_PULL_DOWN };
#define HAL_GPIO(N) ((GPIO_TypeDef *)(0x40000000 + 0x18020000UL + 0x400 * (N)))
static GPIO_TypeDef *hal_gpio_bank(uint16_t pin) {
return HAL_GPIO(PINBANK(pin));
}
static inline void hal_gpio_toggle(uint16_t pin) {
GPIO_TypeDef *gpio = hal_gpio_bank(pin);
uint32_t mask = BIT(PINNO(pin));
gpio->BSRR = mask << (gpio->ODR & mask ? 16 : 0);
}
static inline int hal_gpio_read(uint16_t pin) {
return hal_gpio_bank(pin)->IDR & BIT(PINNO(pin)) ? 1 : 0;
}
static inline void hal_gpio_write(uint16_t pin, bool val) {
GPIO_TypeDef *gpio = hal_gpio_bank(pin);
gpio->BSRR = BIT(PINNO(pin)) << (val ? 0 : 16);
}
static inline void hal_gpio_init(uint16_t pin, uint8_t mode, uint8_t type,
uint8_t speed, uint8_t pull, uint8_t af) {
GPIO_TypeDef *gpio = hal_gpio_bank(pin);
uint8_t n = (uint8_t)(PINNO(pin));
RCC->AHB4ENR |= BIT(PINBANK(pin)); // Enable GPIO clock
CLRSET(gpio->OTYPER, 1UL << n, ((uint32_t)type) << n);
CLRSET(gpio->OSPEEDR, 3UL << (n * 2), ((uint32_t)speed) << (n * 2));
CLRSET(gpio->PUPDR, 3UL << (n * 2), ((uint32_t)pull) << (n * 2));
CLRSET(gpio->AFR[n >> 3], 15UL << ((n & 7) * 4),
((uint32_t)af) << ((n & 7) * 4));
CLRSET(gpio->MODER, 3UL << (n * 2), ((uint32_t)mode) << (n * 2));
}
static inline void hal_gpio_input(uint16_t pin) {
hal_gpio_init(pin, HAL_GPIO_MODE_INPUT, HAL_GPIO_OTYPE_PUSH_PULL,
HAL_GPIO_SPEED_HIGH, HAL_GPIO_PULL_NONE, 0);
}
static inline void hal_gpio_output(uint16_t pin) {
hal_gpio_init(pin, HAL_GPIO_MODE_OUTPUT, HAL_GPIO_OTYPE_PUSH_PULL,
HAL_GPIO_SPEED_HIGH, HAL_GPIO_PULL_NONE, 0);
}
// D2 Kernel clock (8.7.21) USART1 defaults to pclk2 (APB2), while USART2,3
// default to pclk1 (APB1). Even if using other kernel clocks, the APBx clocks
// must be enabled for CPU access, as the kernel clock drives the BRR, not the
// APB bus interface
static inline void hal_uart_init(USART_TypeDef *uart, unsigned long baud) {
uint8_t af = 7; // Alternate function
uint16_t rx = 0, tx = 0; // pins
uint32_t freq = 0; // Bus frequency. UART1 is on APB2, rest on APB1
if (uart == USART1)
freq = APB2_FREQUENCY, RCC->APB2ENR |= BIT(4);
if (uart == USART2)
freq = APB1_FREQUENCY, RCC->APB1LENR |= BIT(17);
if (uart == USART3)
freq = APB1_FREQUENCY, RCC->APB1LENR |= BIT(18);
if (uart == USART1)
tx = PIN('A', 9), rx = PIN('A', 10);
if (uart == USART2)
tx = PIN('A', 2), rx = PIN('A', 3);
if (uart == USART3)
tx = PIN('D', 8), rx = PIN('D', 9);
#if 1 // CONSTANT BAUD RATE FOR DEBUGGING
CLRSET(RCC->D2CCIP2R, 7 << 3, 3 << 3); // use HSI for UART1
freq = 64000000;
#endif
hal_gpio_init(tx, HAL_GPIO_MODE_AF, HAL_GPIO_OTYPE_PUSH_PULL,
HAL_GPIO_SPEED_HIGH, 0, af);
hal_gpio_init(rx, HAL_GPIO_MODE_AF, HAL_GPIO_OTYPE_PUSH_PULL,
HAL_GPIO_SPEED_HIGH, 0, af);
uart->CR1 = 0; // Disable this UART
uart->BRR = freq / baud; // Set baud rate
uart->CR1 = BIT(0) | BIT(2) | BIT(3); // Set UE, RE, TE
}
static inline void hal_uart_write_byte(USART_TypeDef *uart, uint8_t byte) {
uart->TDR = byte;
while ((uart->ISR & BIT(7)) == 0)
spin(1);
}
static inline void hal_uart_write_buf(USART_TypeDef *uart, char *buf,
size_t len) {
while (len-- > 0)
hal_uart_write_byte(uart, *(uint8_t *)buf++);
}
static inline int hal_uart_read_ready(USART_TypeDef *uart) {
return uart->ISR & BIT(5); // If RXNE bit is set, data is ready
}
static inline uint8_t hal_uart_read_byte(USART_TypeDef *uart) {
return (uint8_t)(uart->RDR & 255);
}
static inline void hal_rng_init(void) {
RCC->D2CCIP2R |= RCC_D2CCIP2R_RNGSEL_0; // RNG clock source pll1_q_ck
RCC->AHB2ENR |= RCC_AHB2ENR_RNGEN; // Enable RNG clock
RNG->CR = RNG_CR_RNGEN; // Enable RNG
}
static inline uint32_t hal_rng_read(void) {
while ((RNG->SR & RNG_SR_DRDY) == 0)
(void)0;
return RNG->DR;
}
// NO Hw pull-ups on PHY RXD0,1,DV; set them to enable autonegotiation
static inline void ethernet_init(void) {
// Initialise Ethernet. Enable MAC GPIO pins, see schematics
uint16_t pins1[] = {PIN('A', 7), PIN('C', 4), PIN('C', 5)};
for (size_t i = 0; i < sizeof(pins1) / sizeof(pins1[0]); i++) {
hal_gpio_init(pins1[i], HAL_GPIO_MODE_AF, HAL_GPIO_OTYPE_PUSH_PULL,
HAL_GPIO_SPEED_INSANE, HAL_GPIO_PULL_UP,
11); // 11 is the Ethernet function
}
uint16_t pins2[] = {PIN('A', 1), PIN('A', 2), PIN('C', 1),
PIN('G', 11), PIN('G', 12), PIN('G', 13)};
for (size_t i = 0; i < sizeof(pins2) / sizeof(pins2[0]); i++) {
hal_gpio_init(pins2[i], HAL_GPIO_MODE_AF, HAL_GPIO_OTYPE_PUSH_PULL,
HAL_GPIO_SPEED_INSANE, HAL_GPIO_PULL_NONE,
11); // 11 is the Ethernet function
}
NVIC_EnableIRQ(ETH_IRQn); // Setup Ethernet IRQ handler
CLRSET(SYSCFG->PMCR, 7 << 21, 4 << 21); // Use RMII (13.3.1)
RCC->AHB1ENR |= BIT(15) | BIT(16) | BIT(17); // Enable Ethernet clocks
}
static inline char hal_chiprev(void) {
uint16_t rev = (uint16_t)(((uint32_t)DBGMCU->IDCODE) >> 16);
if (rev == 0x1003)
return 'Y';
if (rev == 0x2003)
return 'V';
return '?';
}
static inline unsigned int hal_div2prescval(unsigned int div) {
// 0 --> /1; 8 --> /2 ... 11 --> /16; 12 --> /64 ... 15 --> /512
if (div == 1)
return 0;
if (div > 16)
div /= 2;
unsigned int val = 7;
while (div >>= 1)
++val;
return val;
}
static inline unsigned int hal_pllrge(unsigned int f) {
unsigned int val = 0;
while (f >>= 1)
++val;
return val - 1;
}
// HSE: 25MHz in PH0; LSE: in PC14; both unused
static inline void
hal_clock_init(void) { // Called automatically by startup code
// Set flash latency. RM section 4.9.1, section 4.3.8 table 16
CLRSET(FLASH->ACR, (FLASH_ACR_WRHIGHFREQ_Msk | FLASH_ACR_LATENCY_Msk),
FLASH_ACR_LATENCY_2WS | FLASH_ACR_WRHIGHFREQ_1);
CLRSET(PWR->CR3, BIT(1), BIT(2)); // select SMPS
while ((PWR->CSR1 & BIT(13)) == 0)
spin(1); // ACTVOSRDY
PWR->D3CR |= BIT(15) | BIT(14); // Select VOS1
uint32_t f = PWR->D3CR; // fake read to wait for bus clocking
while ((PWR->CSR1 & BIT(13)) == 0)
spin(1); // ACTVOSRDY
#if 0
SYSCFG->PWRCR |= BIT(0); // ODEN for LDO to go 480MHz
f = SYSCFG->PWRCR;
while ((PWR->CSR1 & BIT(13)) == 0) spin(1); // ACTVOSRDY
#endif
(void)f;
RCC->CFGR &= ~(3 << 0); // Set clock source to HSI (bootloader...)
RCC->CR &= ~BIT(24); // Disable PLL1
CLRSET(RCC->D1CFGR, (0x0F << 8) | (7 << 4) | (0x0F << 0),
(hal_div2prescval(HAL_D1CPRE) << 8) | (HAL_D1PPRE << 4) |
(hal_div2prescval(HAL_HPRE) << 0));
RCC->D2CFGR = (HAL_D2PPRE2 << 8) | (HAL_D2PPRE1 << 4);
RCC->D3CFGR = (HAL_D3PPRE << 4);
// DIVQ1EN DIVP1EN PLL1RGE !PLL1VCOSEL !PLL1FRACEN
CLRSET(RCC->PLLCFGR, (3 << 2) | BIT(1) | BIT(0),
BIT(17) | BIT(16) | hal_pllrge(PLL1_HSI / PLL1_M) << 2);
CLRSET(RCC->PLL1DIVR, (0x7F << 16) | (0x7F << 9) | (0x1FF << 0),
((PLL1_Q - 1) << 16) | ((PLL1_P - 1) << 9) | ((PLL1_N - 1) << 0));
CLRSET(RCC->PLLCKSELR, (0x3F << 4) | (3 << 0), PLL1_M << 4); // PLL1: HSI/M
RCC->CR |= BIT(24); // Enable PLL1
while ((RCC->CR & BIT(25)) == 0)
spin(1); // Wait until done
RCC->CFGR |= (3 << 0); // Set clock source to PLL1
while ((RCC->CFGR & (7 << 3)) != (3 << 3))
spin(1); // Wait until done
RCC->APB4ENR |= RCC_APB4ENR_SYSCFGEN; // Enable SYSCFG
}
static inline void hal_system_init(void) {
SCB->CPACR |= ((3UL << 10 * 2) | (3UL << 11 * 2)); // Enable FPU
SCB_DisableDCache(); // The bootloader enables caches, disable them
SCB_DisableICache();
__DSB();
__ISB();
}
#define PWRPIN PIN('J', 1)
// - 58.4 Table 473
// - 58.5.8 AHB bw >= 3x SDMMC bus bw; Table 492; SD HS => AHB >= ~20MHz
// - SDIO 2.0 section 3.1
static inline void hal_sdmmc_set_ds(void) { // NO FLOW CONTROL
CLRSET(SDMMC1->CLKCR, (1 << 17) | 0x3ff,
(1 << 14) |
((PLL1_Q_FREQUENCY / (2 * 25000000)) & 0x3ff)); // 4-bit bus, 25MHz
}
static inline void
hal_sdmmc_set_hs(void) { // mbed enables flow control... AIROC does not
CLRSET(SDMMC1->CLKCR, 0x3ff,
(1 << 17) | (1 << 14) |
((PLL1_Q_FREQUENCY / (2 * 50000000)) &
0x3ff)); // hw flow control, 4-bit bus, 50MHz
}
static inline void hal_sdmmc_init(void) {
RCC->AHB3RSTR |= BIT(16); // Reset SDIO block
RCC->AHB3RSTR &= ~BIT(16);
RCC->D1CCIPR &= ~BIT(16); // 9.7.18, use PLL1Q
RCC->AHB3ENR |= BIT(16); // Enable SDIO clock
// Initialise SDIO pins, see schematics
uint16_t pins[] = {PIN('C', 8), PIN('C', 9), PIN('C', 10),
PIN('C', 11), PIN('C', 12), PIN('D', 2)};
for (size_t i = 0; i < sizeof(pins) / sizeof(pins[0]); i++) {
// Portenta H7 doesn't pull-up the SDIO bus
hal_gpio_init(pins[i], HAL_GPIO_MODE_AF, HAL_GPIO_OTYPE_PUSH_PULL,
HAL_GPIO_SPEED_INSANE, HAL_GPIO_PULL_UP,
12); // 12 is the SDMMC1 (SDIO) function
}
hal_gpio_input(PIN('J', 5)); // wake, ignored
// 1-bit bus, rising edge, clock always on, no flow control, 400KHz
SDMMC1->CLKCR = (PLL1_Q_FREQUENCY / (2 * 400000)) & 0x3ff;
SDMMC1->POWER |= 3 << 0; // SDMMC_POWER_PWRCTRL
SDMMC1->DCTRL = BIT(10); // Read Wait control by stopping SDMMCCLK
// wait 74 clocks (~200us) before accessing the "card"
}
static inline bool hal_sdmmc_transact(uint8_t cmd, uint32_t arg,
uint32_t *resp) {
unsigned int wresp = (resp != NULL) ? 1 : 0; // 58.10.4: 2-bit; cmds used
uint32_t cmdr = BIT(12) | (wresp << 8) | ((cmd & 0x3f) << 0); // CPSMEN
SDMMC1->ARG = arg;
CLRSET(SDMMC1->CMD,
0x00011f3f /* CMDSUSPEND CPSMEN WAITPEND WAITINT WAITRESP CMD */,
cmdr);
if (wresp == 0) {
while ((SDMMC1->STA & SDMMC_STA_CMDSENT) == 0)
spin(1);
SDMMC1->ICR = 0x002000C5; // BUSYD0END CMDSENT CMDREND CTIMEOUT CCRCFAIL
return true;
}
// while ((SDMMC1->STA & 0x00200045 /*BUSYD0END CMDREND CTIMEOUT CCRCFAIL*/)
// == 0 || SDMMC1->STA & SDMMC_STA_CPSMACT) spin(1);
while (SDMMC1->STA & SDMMC_STA_CPSMACT)
spin(1);
bool res = (SDMMC1->STA & 0x00200005 /*BUSYD0END CTIMEOUT CCRCFAIL*/) == 0;
SDMMC1->ICR = 0x002000C5; // BUSYD0END CMDSENT CMDREND CTIMEOUT CCRCFAIL
*resp = SDMMC1->RESP1;
if (!res || SDMMC1->RESPCMD != cmd)
return false;
return true;
}
static inline unsigned int hal_log2(uint16_t x) {
unsigned int i = 0;
while (x != 0)
x = ((uint16_t)x) >> 1, ++i;
return i - 1;
}
static inline bool hal_sdmmc_transfer(bool write, uint32_t arg, uint16_t blksz,
uint32_t *data, uint32_t length,
uint32_t *resp) {
uint32_t cmdr = BIT(12) | (1 << 8) | (53 << 0); // CPSMEN WRESP CMD=53
bool bmode = (blksz != 0);
uint8_t dblksz = bmode ? (uint8_t)hal_log2(blksz) : 0;
uint32_t dctrlr =
(dblksz << 4) | (bmode ? (0 << 2) : (1 << 2)) |
(write
? 0
: BIT(1)); // DBLOCKSIZE=f(blksz) DTMODE=f(bmode) DTDIR=!write !DTEN
SDMMC1->DCTRL = SDMMC_DCTRL_RWMOD;
SDMMC1->IDMACTRL = SDMMC_IDMA_IDMAEN; // single buffer
SDMMC1->IDMABASE0 = (uint32_t)data;
SDMMC1->DTIMER = (uint32_t)50000000; // 58.10.7, set for 1 sec @50MHz (2 secs @25MHz)
SDMMC1->DLEN = length; // 58.10.8 call with a multiple of blksz (we do)
CLRSET(SDMMC1->DCTRL, 0xff /* DBLOCKSIZE DTMODE DTDIR DTEN */, dctrlr);
SDMMC1->CMD = SDMMC_CMD_CMDTRANS; // CPSM will perform a data transfer
SDMMC1->ARG = arg;
SDMMC1->CMD |= cmdr; // other flags cleared above
while ((SDMMC1->STA & 0x0020003f) == 0 && SDMMC1->STA & SDMMC_STA_DPSMACT)
spin(1); // incorrect "card" usage will stall here
bool res = (SDMMC1->STA & 0x0020003f /* BUSYD0END RXOVERR TXUNDERR DTIMEOUT CTIMEOUT DCRCFAIL CCRCFAIL */) == 0;
SDMMC1->CMD = 0;
SDMMC1->DLEN = 0;
SDMMC1->IDMACTRL = 0;
SDMMC1->ICR = (uint32_t)~0; // clear all flags
*resp = SDMMC1->RESP1;
SDMMC1->DCTRL = SDMMC_DCTRL_RWMOD;
if (!res || SDMMC1->RESPCMD != 53)
return false;
return true;
}
// Bit-banged SPI, pin wiring does not allow using a hw SPI peripheral
#define CYWSPI NULL
#define BBSPI_SPIN 1
static inline void hal_spi_init(void *spi, uint8_t div) {
// Initialise bit-banged SPI, see: schematics; CYW DS 4.1.1 Table 5, 13.7
// Table 22
hal_gpio_input(PIN('C', 8)); // DO -> MISO
hal_gpio_input(PIN('C', 9)); // IRQ -> ignore
hal_gpio_output(PIN('C', 10)); // D2 <-- strap for SPI mode
hal_gpio_output(PIN('C', 11)); // CS
hal_gpio_output(PIN('C', 12)); // SCLK
hal_gpio_output(PIN('D', 2)); // DI -> MOSI
// select SPI mode at power-on/reset time (not WL_REG_ON, actual POR)
// "Sampling occurs a few milliseconds after an internal POR
// or deassertion of the external POR
hal_gpio_write(PIN('C', 10), 0);
hal_gpio_write(PIN('D', 2), 0); // idle values
hal_gpio_write(PIN('C', 12), 0);
hal_gpio_write(PIN('C', 11), 0); // no power at startup
(void)spi;
(void)div;
}
static inline void hal_spi_write_byte(void *spi, uint8_t byte) {
unsigned int n = 8;
while (n--) {
hal_gpio_write(PIN('D', 2), byte & 0x80);
spin(BBSPI_SPIN);
hal_gpio_write(PIN('C', 12), 1);
byte <<= 1;
spin(BBSPI_SPIN);
hal_gpio_write(PIN('C', 12), 0);
}
hal_gpio_write(PIN('D', 2), 0);
(void)spi;
}
static inline uint8_t hal_spi_read_byte(void *spi) {
uint8_t byte = 0;
unsigned int n = 8;
while (n--) {
byte <<= 1;
spin(BBSPI_SPIN);
byte |= hal_gpio_read(PIN('C', 8)) ? 1 : 0;
hal_gpio_write(PIN('C', 12), 1);
spin(BBSPI_SPIN);
hal_gpio_write(PIN('C', 12), 0);
}
(void)spi;
return byte;
}
static inline void hal_spi_select(void *spi, bool s) {
hal_gpio_write(PIN('C', 11), !s);
(void)spi;
}
// Current bootloader sets all outputs in the PMIC, no need to use I2C.
// The bootloader inits I2C1 and chips in there, but we change MCU clock
// DS 4 Table 12, schematic PB6,7
static inline void hal_i2c_init(I2C_TypeDef *i2c) {
CLRSET(RCC->D2CCIP2R, 3 << 12, 2 << 12); // use HSI for I2C1 (64MHz)
RCC->APB1LENR |= BIT(21); // enable I2C clock
hal_gpio_init(PIN('B', 6), HAL_GPIO_MODE_AF, HAL_GPIO_OTYPE_OPEN_DRAIN,
HAL_GPIO_SPEED_INSANE, HAL_GPIO_PULL_NONE, 4);
hal_gpio_init(PIN('B', 7), HAL_GPIO_MODE_AF, HAL_GPIO_OTYPE_OPEN_DRAIN,
HAL_GPIO_SPEED_INSANE, HAL_GPIO_PULL_NONE, 4);
i2c->CR1 = 0; // disable before any config change
i2c->CR1 = BIT(12); // 50.7 disable filters
i2c->TIMINGR = 10U << 28 | 3 << 20 | 2 << 16 | 3 << 8 | 9 << 0; // < 400KHz
i2c->CR1 |= BIT(0); // enable after fully configured
}
static inline void hal_i2c_write_reg(I2C_TypeDef *i2c, uint8_t addr,
uint8_t reg, uint8_t byte) {
i2c->CR2 = BIT(25) | 2 << 16 |
addr << 1; // write byte to reg in chip at addr, AUTOEND
i2c->CR2 |= BIT(13); // START
while ((i2c->ISR & BIT(0)) == 0)
spin(1); // TXE
i2c->TXDR = reg;
while ((i2c->ISR & BIT(0)) == 0)
spin(1); // TXE
i2c->TXDR = byte;
while ((i2c->ISR & BIT(5)) == 0)
spin(1); // STOPF
i2c->ICR |= BIT(5); // STOPCF, clear STOPF
}
static inline uint8_t hal_i2c_read_reg(I2C_TypeDef *i2c, uint8_t addr,
uint8_t reg) {
i2c->CR2 = 1 << 16 | addr << 1; // write reg addr to chip at addr
i2c->CR2 |= BIT(13); // START
while ((i2c->ISR & BIT(0)) == 0)
spin(1); // TXE
i2c->TXDR = reg;
while ((i2c->ISR & BIT(6)) == 0)
spin(1); // TC
i2c->CR2 = BIT(13) | BIT(10) | 1 << 16 | addr << 1; // read 1 byte, AUTOEND
i2c->CR2 |= BIT(25); // START
while ((i2c->ISR & BIT(2)) == 0)
spin(1); // RXNE
uint8_t byte = (uint8_t)i2c->RXDR;
while ((i2c->ISR & BIT(5)) == 0)
spin(1); // STOPF
i2c->ICR |= BIT(5); // STOPCF, clear STOPF
return byte;
}
@@ -0,0 +1,33 @@
/* DTCMRAM (rwx) : ORIGIN = 0x20000400, LENGTH = 128k - 1k */ /* bootloader starts at 0x20000000 */
ENTRY(Reset_Handler);
MEMORY {
flash(rx) : ORIGIN = 0x08040000, LENGTH = 2048k - 0x40000 /* bootloader starts at 0x08000000 */
sram(rwx) : ORIGIN = 0x24000000, LENGTH = 512k
}
_estack = ORIGIN(sram) + LENGTH(sram); /* stack points to end of SRAM */
SECTIONS {
.vectors : { KEEP(*(.isr_vector)) } > flash
.text : { *(.text* .text.*) } > flash
.rodata : { *(.rodata*) } > flash
.data : {
_sdata = .;
*(.first_data)
*(.data SORT(.data.*))
*(.iram .iram* .iram.*)
_edata = .;
} > sram AT > flash
_sidata = LOADADDR(.data);
.bss : {
_sbss = .;
*(.bss SORT(.bss.*) COMMON)
_ebss = .;
} > sram
. = ALIGN(8);
_end = .;
}
@@ -0,0 +1,71 @@
// Copyright (c) 2026 Cesanta Software Limited
// All rights reserved
#include "mongoose.h"
#include "hal.h"
#define DEBUG_UART USART1
#define LED1 PIN('K', 5) // On-board LED pin (red)
#define LED2 PIN('K', 6) // On-board LED pin (green)
#define LED3 PIN('K', 7) // On-board LED pin (blue)
// Redirect stdout debug output to UART
int _write(int fd, char *ptr, int len) {
if (fd == 1 || fd == 2) hal_uart_write_buf(DEBUG_UART, ptr, (size_t) len);
return len;
}
static void blink_task(void) {
static uint64_t blink_timer = 0;
if (hal_timer_expired(&blink_timer, 500, hal_get_tick())) {
hal_gpio_toggle(LED1);
}
}
uint64_t mg_millis(void) {
return hal_get_tick();
}
bool mg_random(void *buf, size_t len) {
for (size_t n = 0; n < len; n += sizeof(uint32_t)) {
uint32_t r = hal_rng_read();
memcpy((char *) buf + n, &r, n + sizeof(r) > len ? len - n : sizeof(r));
}
return true;
}
static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), hal_get_tick());
} else {
mg_http_reply(c, 200, "", "Hi from Mongoose, tick %llu\n",
hal_get_tick());
}
}
}
int main(void) {
hal_clock_init();
hal_rng_init();
hal_gpio_output(LED1);
hal_gpio_output(LED2);
hal_gpio_output(LED3);
hal_uart_init(DEBUG_UART, 115200);
extern void hwspecific_sdio_init(void);
hwspecific_sdio_init();
// Start a minimal web server
struct mg_mgr mgr;
mg_mgr_init(&mgr);
mg_http_listen(&mgr, "http://0.0.0.0", http_ev_handler, NULL);
for (;;) {
mg_mgr_poll(&mgr, 0);
blink_task();
}
return 0;
}
@@ -0,0 +1,16 @@
#pragma once
// See https://mongoose.ws/documentation/#build-options
#define MG_ARCH MG_ARCH_ARMGCC
#define MG_TLS MG_TLS_BUILTIN
#define MG_ENABLE_TCPIP 1
#define MG_ENABLE_CUSTOM_MILLIS 1
#define MG_ENABLE_CUSTOM_RANDOM 1
#define MG_ENABLE_DRIVER_CYW_SDIO 1
// MAC address is read from OTP by the driver
// Set your Wi-Fi configuration using a custom function:
extern void wifi_setconfig(void *data);
#define MG_SET_WIFI_CONFIG(data) wifi_setconfig(data)
@@ -0,0 +1,81 @@
// SPDX-FileCopyrightText: 2025 Cesanta Software Limited
// SPDX-License-Identifier: GPL-2.0-only or commercial
// Generated by Mongoose Wizard, https://mongoose.ws/wizard/
#include "hal.h"
#include "mongoose.h"
#ifndef WIFI_NAME
#define WIFI_NAME ""
#endif
#ifndef WIFI_PASS
#define WIFI_PASS ""
#endif
#ifndef WIFI_AP_NAME
#define WIFI_AP_NAME "Mongoose"
#endif
#ifndef WIFI_AP_PASS
#define WIFI_AP_PASS "mongoose"
#endif
#ifndef ENABLE_WIFI_AP
#define ENABLE_WIFI_AP 1
#endif
void hwspecific_sdio_init(void) {
hal_gpio_output(PWRPIN);
hal_gpio_write(PWRPIN, 0);
mg_delayms(100);
hal_sdmmc_init();
mg_delayms(1);
hal_gpio_write(PWRPIN, 1);
mg_delayms(50);
}
static void hwspecific_sdio_config(void *sdio, uint8_t cfg) {
(void) sdio;
if (cfg == 0) return hal_sdmmc_set_ds();
if (cfg == 1) return hal_sdmmc_set_hs();
}
static bool hwspecific_sdio_txn(void *sdio, uint8_t cmd, uint32_t arg, uint32_t *resp) {
(void) sdio;
return hal_sdmmc_transact(cmd, arg, resp);
}
static bool hwspecific_sdio_xfr(void *sdio, bool write, uint32_t arg, uint16_t blksz, uint32_t *data, uint32_t len, uint32_t *resp){
(void) sdio;
return hal_sdmmc_transfer(write, arg, blksz, data, len, resp);
}
static const struct mg_tcpip_sdio sdio = {NULL, hwspecific_sdio_config, hwspecific_sdio_txn, hwspecific_sdio_xfr};
// clang-format off
typedef enum {RESOURCE_IN_MEMORY,RESOURCE_IN_EXTERNAL_STORAGE} resource_location_t;
typedef struct {resource_location_t l;unsigned long sz;union{struct {unsigned long o;const char *f;}fs;struct {const char *m;}mem;}val;} resource_hnd_t;
#include "mbed/targets/TARGET_STM/TARGET_STM32H7/TARGET_STM32H747xI/TARGET_PORTENTA_H7/COMPONENT_WHD/resources/firmware/COMPONENT_4343W_FS/4343WA1_bin.c"
#include "mbed/targets/TARGET_STM/TARGET_STM32H7/TARGET_STM32H747xI/TARGET_PORTENTA_H7/COMPONENT_WHD/resources/firmware/COMPONENT_4343W_FS/4343WA1_clm_blob.c"
#include "mbed/targets/TARGET_STM/TARGET_STM32H7/TARGET_STM32H747xI/TARGET_PORTENTA_H7/COMPONENT_WHD/resources/nvram/wifi_nvram_image.h"
// clang-format on
static const struct mg_tcpip_driver_cyw_firmware fw = {
(const uint8_t *)wifi_firmware_image_data, (size_t)sizeof(wifi_firmware_image_data),
(const uint8_t *)wifi_nvram_image, (size_t)sizeof(wifi_nvram_image),
(const uint8_t *)wifi_firmware_clm_blob_image_data, (size_t)sizeof(wifi_firmware_clm_blob_image_data)};
void wifi_setconfig(void *data) {
struct mg_tcpip_driver_cyw_data *d = (struct mg_tcpip_driver_cyw_data *) data;
d->bus = (struct mg_tcpip_sdio *) &sdio;
d->fw = (struct mg_tcpip_driver_cyw_firmware *)&fw;
d->wifi.ssid = WIFI_NAME;
d->wifi.pass = WIFI_PASS;
d->wifi.apssid = WIFI_AP_NAME;
d->wifi.appass = WIFI_AP_PASS;
d->wifi.apip = MG_IPV4(192, 168, 111, 1);
d->wifi.apmask = MG_IPV4(255, 255, 255, 0);
d->wifi.security = 0;
d->wifi.apsecurity = 0;
d->wifi.apchannel = 10;
d->wifi.apmode = ENABLE_WIFI_AP;
d->hs = false;
}
@@ -0,0 +1,29 @@
# STM32H573I-DK Mongoose Integration
This guide explains how to bring up a production-grade Web UI dashboard on the
STM32H573I-DK development board.
A pre-configured, ready-to-run project zip is available for download at
https://mongoose.ws.
To integrate the Web UI dashboard into an existing CubeMX project, or to create a
new project from scratch:
1. Configure the peripherals (described below).
2. Regenerate your project code and integrate Mongoose core by following
https://mongoose.ws/docs/guides/stm32-tcpip-stack/.
3. Add the Web UI dashboard by following
https://mongoose.ws/docs/guides/web-dashboard-guide/.
## Configure peripherals
An `.ioc` file is included in this directory for your reference. It was created
using the following steps:
- Open CubeMX. If you are starting a new project, select the STM32H573I
microcontroller and create a new project.
- In Clock Configuration, set the maximum clock frequency to 250 MHz.
- In Pinout & Configuration / Connectivity, enable USART1 in asynchronous mode.
Configure pins PA9 and PA10 for USART.
- In Pinout & Configuration / Connectivity, enable ETH in RMII mode and set pins
PA1, PA2, PA7, PC1, PC4, PC5, PG11, PG12, and PG13 for Ethernet.
@@ -0,0 +1,237 @@
#MicroXplorer Configuration settings - do not modify
CAD.formats=
CAD.pinconfig=
CAD.provider=
CORTEX_M33_NS.userName=CORTEX_M33
ETH.IPParameters=MediaInterface
ETH.MediaInterface=HAL_ETH_RMII_MODE
File.Version=6
GPIO.groupedBy=
KeepUserPlacement=false
MMTAppRegionsCount=0
MMTConfigApplied=false
MMTSectionSuffix=
Mcu.CPN=STM32H573IIK3Q
Mcu.ContextProject=TrustZoneDisabled
Mcu.Family=STM32H5
Mcu.IP0=BOOTPATH
Mcu.IP1=CORTEX_M33_NS
Mcu.IP2=ETH
Mcu.IP3=MEMORYMAP
Mcu.IP4=NVIC
Mcu.IP5=PWR
Mcu.IP6=RCC
Mcu.IP7=RNG
Mcu.IP8=SYS
Mcu.IP9=USART1
Mcu.IPNb=10
Mcu.Name=STM32H573IIKxQ
Mcu.Package=UFBGA176
Mcu.Pin0=PG12
Mcu.Pin1=PG13
Mcu.Pin10=PA7
Mcu.Pin11=VP_CORTEX_M33_NS_VS_Hclk
Mcu.Pin12=VP_PWR_VS_SECSignals
Mcu.Pin13=VP_PWR_VS_LPOM
Mcu.Pin14=VP_RNG_VS_RNG
Mcu.Pin15=VP_SYS_VS_Systick
Mcu.Pin16=VP_BOOTPATH_VS_BOOTPATH
Mcu.Pin17=VP_MEMORYMAP_VS_MEMORYMAP
Mcu.Pin2=PG11
Mcu.Pin3=PA10
Mcu.Pin4=PA9
Mcu.Pin5=PC1
Mcu.Pin6=PA1
Mcu.Pin7=PA2
Mcu.Pin8=PC4
Mcu.Pin9=PC5
Mcu.PinsNb=18
Mcu.ThirdPartyNb=0
Mcu.UserConstants=
Mcu.UserName=STM32H573IIKxQ
MxCube.Version=6.17.0
MxDb.Version=DB.6.0.170
NVIC.BusFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
NVIC.DebugMonitor_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
NVIC.ETH_IRQn=true\:0\:0\:false\:false\:true\:true\:true\:true
NVIC.ForceEnableDMAVector=true
NVIC.HardFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
NVIC.MemoryManagement_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
NVIC.NonMaskableInt_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
NVIC.PendSV_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
NVIC.PriorityGroup=NVIC_PRIORITYGROUP_4
NVIC.SVCall_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
NVIC.SysTick_IRQn=true\:15\:0\:false\:false\:true\:false\:true\:false
NVIC.UsageFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
PA1.Mode=RMII
PA1.Signal=ETH_REF_CLK
PA10.Locked=true
PA10.Mode=Asynchronous
PA10.Signal=USART1_RX
PA2.Mode=RMII
PA2.Signal=ETH_MDIO
PA7.Mode=RMII
PA7.Signal=ETH_CRS_DV
PA9.Locked=true
PA9.Mode=Asynchronous
PA9.Signal=USART1_TX
PC1.Mode=RMII
PC1.Signal=ETH_MDC
PC4.Mode=RMII
PC4.Signal=ETH_RXD0
PC5.Mode=RMII
PC5.Signal=ETH_RXD1
PCC.Checker=false
PCC.Display=Plot\: All Steps
PCC.Line=STM32H563/H573
PCC.MCU=STM32H573IIKxQ
PCC.PartNumber=STM32H573IIKxQ
PCC.Series=STM32H5
PCC.Temperature=25
PCC.Vdd=3.0
PG11.Mode=RMII
PG11.Signal=ETH_TX_EN
PG12.Locked=true
PG12.Mode=RMII
PG12.Signal=ETH_TXD1
PG13.Mode=RMII
PG13.Signal=ETH_TXD0
PinOutPanel.CurrentBGAView=Top
PinOutPanel.RotationAngle=0
ProjectManager.AskForMigrate=true
ProjectManager.BackupPrevious=false
ProjectManager.CompilerLinker=GCC
ProjectManager.CompilerOptimize=6
ProjectManager.ComputerToolchain=false
ProjectManager.CoupleFile=false
ProjectManager.CustomerFirmwarePackage=
ProjectManager.DefaultFWLocation=true
ProjectManager.DeletePrevious=true
ProjectManager.DeviceId=STM32H573IIKxQ
ProjectManager.FirmwarePackage=STM32Cube FW_H5 V1.6.0
ProjectManager.FreePins=false
ProjectManager.FreePinsContext=
ProjectManager.HalAssertFull=false
ProjectManager.HeapSize=0x10000
ProjectManager.KeepUserCode=true
ProjectManager.LastFirmware=true
ProjectManager.LibraryCopy=1
ProjectManager.MainLocation=Core/Src
ProjectManager.NoMain=false
ProjectManager.PreviousToolchain=STM32CubeIDE
ProjectManager.ProjectBuild=false
ProjectManager.ProjectFileName=h573.ioc
ProjectManager.ProjectName=h573
ProjectManager.ProjectStructure=
ProjectManager.RegisterCallBack=
ProjectManager.StackSize=0x2000
ProjectManager.TargetToolchain=CMake
ProjectManager.ToolChainLocation=
ProjectManager.UAScriptAfterPath=
ProjectManager.UAScriptBeforePath=
ProjectManager.UnderRoot=false
ProjectManager.functionlistsort=1-SystemClock_Config-RCC-false-HAL-false,2-MX_GPIO_Init-GPIO-false-HAL-true,3-MX_ETH_Init-ETH-false-HAL-true,4-MX_RNG_Init-RNG-false-HAL-true,5-MX_USART1_UART_Init-USART1-false-HAL-true,0-MX_CORTEX_M33_NS_Init-CORTEX_M33_NS-false-HAL-true,0-MX_PWR_Init-PWR-false-HAL-true
RCC.ADCFreq_Value=250000000
RCC.AHBFreq_Value=250000000
RCC.APB1Freq_Value=250000000
RCC.APB1TimFreq_Value=250000000
RCC.APB2Freq_Value=250000000
RCC.APB2TimFreq_Value=250000000
RCC.APB3Freq_Value=250000000
RCC.CECFreq_Value=32000
RCC.CKPERFreq_Value=32000000
RCC.CRSFreq_Value=48000000
RCC.CSI_VALUE=4000000
RCC.CortexFreq_Value=250000000
RCC.DACFreq_Value=32768
RCC.EPOD_VALUE=4000000
RCC.ETHFreq_Value=250000000
RCC.FCLKCortexFreq_Value=250000000
RCC.FDCANFreq_Value=25000000
RCC.FamilyName=M
RCC.HCLKFreq_Value=250000000
RCC.HSE_VALUE=25000000
RCC.HSI48_VALUE=48000000
RCC.HSI_VALUE=64000000
RCC.I2C1Freq_Value=250000000
RCC.I2C2Freq_Value=250000000
RCC.I2C3Freq_Value=250000000
RCC.I2C4Freq_Value=250000000
RCC.I3C1Freq_Value=250000000
RCC.IPParameters=ADCFreq_Value,AHBFreq_Value,APB1Freq_Value,APB1TimFreq_Value,APB2Freq_Value,APB2TimFreq_Value,APB3Freq_Value,CECFreq_Value,CKPERFreq_Value,CRSFreq_Value,CSI_VALUE,CortexFreq_Value,DACFreq_Value,EPOD_VALUE,ETHFreq_Value,FCLKCortexFreq_Value,FDCANFreq_Value,FamilyName,HCLKFreq_Value,HSE_VALUE,HSI48_VALUE,HSI_VALUE,I2C1Freq_Value,I2C2Freq_Value,I2C3Freq_Value,I2C4Freq_Value,I3C1Freq_Value,LPTIM1Freq_Value,LPTIM2Freq_Value,LPTIM3Freq_Value,LPTIM4Freq_Value,LPTIM5Freq_Value,LPTIM6Freq_Value,LPUART1Freq_Value,LSCOPinFreq_Value,LSE_VALUE,LSIRC_VALUE,MCO1PinFreq_Value,MCO2PinFreq_Value,OCTOSPIMFreq_Value,PLL2FRACN,PLL2PoutputFreq_Value,PLL2QoutputFreq_Value,PLL2RoutputFreq_Value,PLL3FRACN,PLL3PoutputFreq_Value,PLL3QoutputFreq_Value,PLL3RoutputFreq_Value,PLLFRACN,PLLN,PLLPoutputFreq_Value,PLLQoutputFreq_Value,PWRFreq_Value,RNGFreq_Value,SAI1Freq_Value,SAI2Freq_Value,SDMMC1Freq_Value,SDMMC2Freq_Value,SPI1Freq_Value,SPI2Freq_Value,SPI3Freq_Value,SPI4Freq_Value,SPI5Freq_Value,SPI6Freq_Value,SYSCLKFreq_VALUE,SYSCLKSource,UART12Freq_Value,UART4Freq_Value,UART5Freq_Value,UART7Freq_Value,UART8Freq_Value,UART9Freq_Value,UCPD1outputFreq_Value,USART10Freq_Value,USART11Freq_Value,USART1Freq_Value,USART2Freq_Value,USART3Freq_Value,USART6Freq_Value,USBFreq_Value,VCOInput2Freq_Value,VCOInput3Freq_Value,VCOInputFreq_Value,VCOOutputFreq_Value,VCOPLL2OutputFreq_Value,VCOPLL3OutputFreq_Value
RCC.LPTIM1Freq_Value=250000000
RCC.LPTIM2Freq_Value=250000000
RCC.LPTIM3Freq_Value=250000000
RCC.LPTIM4Freq_Value=250000000
RCC.LPTIM5Freq_Value=250000000
RCC.LPTIM6Freq_Value=250000000
RCC.LPUART1Freq_Value=250000000
RCC.LSCOPinFreq_Value=32000
RCC.LSE_VALUE=32768
RCC.LSIRC_VALUE=32000
RCC.MCO1PinFreq_Value=32000000
RCC.MCO2PinFreq_Value=250000000
RCC.OCTOSPIMFreq_Value=250000000
RCC.PLL2FRACN=0
RCC.PLL2PoutputFreq_Value=258000000
RCC.PLL2QoutputFreq_Value=258000000
RCC.PLL2RoutputFreq_Value=258000000
RCC.PLL3FRACN=0
RCC.PLL3PoutputFreq_Value=258000000
RCC.PLL3QoutputFreq_Value=258000000
RCC.PLL3RoutputFreq_Value=258000000
RCC.PLLFRACN=0
RCC.PLLN=125
RCC.PLLPoutputFreq_Value=250000000
RCC.PLLQoutputFreq_Value=250000000
RCC.PWRFreq_Value=250000000
RCC.RNGFreq_Value=48000000
RCC.SAI1Freq_Value=258000000
RCC.SAI2Freq_Value=258000000
RCC.SDMMC1Freq_Value=250000000
RCC.SDMMC2Freq_Value=250000000
RCC.SPI1Freq_Value=250000000
RCC.SPI2Freq_Value=250000000
RCC.SPI3Freq_Value=250000000
RCC.SPI4Freq_Value=250000000
RCC.SPI5Freq_Value=250000000
RCC.SPI6Freq_Value=250000000
RCC.SYSCLKFreq_VALUE=250000000
RCC.SYSCLKSource=RCC_SYSCLKSOURCE_PLLCLK
RCC.UART12Freq_Value=250000000
RCC.UART4Freq_Value=250000000
RCC.UART5Freq_Value=250000000
RCC.UART7Freq_Value=250000000
RCC.UART8Freq_Value=250000000
RCC.UART9Freq_Value=250000000
RCC.UCPD1outputFreq_Value=8000000
RCC.USART10Freq_Value=250000000
RCC.USART11Freq_Value=250000000
RCC.USART1Freq_Value=250000000
RCC.USART2Freq_Value=250000000
RCC.USART3Freq_Value=250000000
RCC.USART6Freq_Value=250000000
RCC.USBFreq_Value=48000000
RCC.VCOInput2Freq_Value=4000000
RCC.VCOInput3Freq_Value=4000000
RCC.VCOInputFreq_Value=4000000
RCC.VCOOutputFreq_Value=500000000
RCC.VCOPLL2OutputFreq_Value=516000000
RCC.VCOPLL3OutputFreq_Value=516000000
USART1.IPParameters=VirtualMode-Asynchronous
USART1.VirtualMode-Asynchronous=VM_ASYNC
VP_BOOTPATH_VS_BOOTPATH.Mode=BP_Activate
VP_BOOTPATH_VS_BOOTPATH.Signal=BOOTPATH_VS_BOOTPATH
VP_CORTEX_M33_NS_VS_Hclk.Mode=Hclk_Mode
VP_CORTEX_M33_NS_VS_Hclk.Signal=CORTEX_M33_NS_VS_Hclk
VP_MEMORYMAP_VS_MEMORYMAP.Mode=CurAppReg
VP_MEMORYMAP_VS_MEMORYMAP.Signal=MEMORYMAP_VS_MEMORYMAP
VP_PWR_VS_LPOM.Mode=PowerOptimisation
VP_PWR_VS_LPOM.Signal=PWR_VS_LPOM
VP_PWR_VS_SECSignals.Mode=Security/Privilege
VP_PWR_VS_SECSignals.Signal=PWR_VS_SECSignals
VP_RNG_VS_RNG.Mode=RNG_Activate
VP_RNG_VS_RNG.Signal=RNG_VS_RNG
VP_SYS_VS_Systick.Mode=SysTick
VP_SYS_VS_Systick.Signal=SYS_VS_Systick
board=custom
@@ -0,0 +1,38 @@
# Copyright (c) 2026 Cesanta Software Limited
# Environment setup: https://mongoose.ws/docs/getting-started/build-environment/
CFLAGS = -W -Wall -Wextra -Wundef -Wshadow -Wdouble-promotion
CFLAGS += -Wformat-truncation -fno-common -Wconversion -Wno-sign-conversion
CFLAGS += -g3 -Os -ffunction-sections -fdata-sections
CFLAGS += -I. -Icmsis_core/CMSIS/Core/Include -Icmsis_h5/Include
CFLAGS += -mcpu=cortex-m33 -mthumb -mfpu=fpv5-sp-d16 -mfloat-abi=hard $(CFLAGS_EXTRA)
LDFLAGS ?= -Tlink.ld -nostdlib -nostartfiles --specs nosys.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map
SOURCES = main.c hal.c
SOURCES += cmsis_h5/Source/Templates/gcc/startup_stm32h573xx.s
TOP = ../../../..
CFLAGS += -I$(TOP)
SOURCES += $(TOP)/mongoose.c
all: firmware.bin
firmware.elf: cmsis_core cmsis_h5 hal.h link.ld Makefile $(SOURCES)
arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(CFLAGS_EXTRA) $(LDFLAGS) -o $@
firmware.bin: firmware.elf
arm-none-eabi-objcopy -O binary $< $@
@echo
@echo "To flash, run 'make flash', or use STM32CubeProgrammer"
flash: firmware.bin
STM32_Programmer_CLI -c port=swd -e all -w firmware.elf -hardRst
cmsis_core:
git clone -q -c advice.detachedHead=false --depth 1 -b 5.9.0 https://github.com/ARM-software/CMSIS_5 $@
cmsis_h5:
git clone -q -c advice.detachedHead=false --depth 1 -b v1.2.0 https://github.com/STMicroelectronics/cmsis_device_h5 $@
clean:
rm -rf firmware.*
+125
View File
@@ -0,0 +1,125 @@
// Copyright (c) 2026 Cesanta Software Limited
// All rights reserved
#include "hal.h"
bool hal_timer_expired(volatile uint64_t *t, uint64_t period, uint64_t now) {
uint64_t diff = now - *t; // Wrap-safe elapsed time since last expiry
if (period == 0) return false; // Avoid division by zero
if (diff < period) return false; // Period has not elapsed yet
*t += (diff / period) * period; // Preserve cadence, skip missed periods
return true;
}
static volatile uint64_t s_ticks; // Milliseconds since boot
void SysTick_Handler(void) { // SyStick IRQ handler, triggered every 1ms
s_ticks++;
}
uint64_t hal_get_tick(void) {
return s_ticks;
};
uint32_t SystemCoreClock = SYS_FREQUENCY;
void SystemInit(void) { // Called automatically by startup code
hal_system_init(); // Enable FPU
hal_clock_init();
SysTick_Config(SystemCoreClock / 1000); // Sys tick every 1ms
}
void ExitRun0Mode(void) {
}
struct stat;
__attribute__((weak)) int _fstat(int fd, struct stat *st) {
(void) fd, (void) st;
return -1;
}
extern unsigned char _end[]; // End of data section, start of heap. See link.ld
static unsigned char *s_current_heap_end = _end;
size_t hal_ram_used(void) {
return (size_t) (s_current_heap_end - _end);
}
size_t hal_ram_free(void) {
unsigned char endofstack;
return (size_t) (&endofstack - s_current_heap_end);
}
void *_sbrk(int incr) {
unsigned char *prev_heap;
unsigned char *heap_end = (unsigned char *) ((size_t) &heap_end - 256);
prev_heap = s_current_heap_end;
// Check how much space we got from the heap end to the stack end
if (s_current_heap_end + incr > heap_end) return (void *) -1;
s_current_heap_end += incr;
return prev_heap;
}
__attribute__((weak)) int _open(const char *path) {
(void) path;
return -1;
}
__attribute__((weak)) int _close(int fd) {
(void) fd;
return -1;
}
__attribute__((weak)) int _isatty(int fd) {
(void) fd;
return 1;
}
__attribute__((weak)) int _lseek(int fd, int ptr, int dir) {
(void) fd, (void) ptr, (void) dir;
return 0;
}
__attribute__((weak)) void _exit(int status) {
(void) status;
for (;;) asm volatile("BKPT #0");
}
__attribute__((weak)) void _kill(int pid, int sig) {
(void) pid, (void) sig;
}
__attribute__((weak)) int _getpid(void) {
return -1;
}
__attribute__((weak)) int _write(int fd, char *ptr, int len) {
(void) fd, (void) ptr, (void) len;
return -1;
}
__attribute__((weak)) int _read(int fd, char *ptr, int len) {
(void) fd, (void) ptr, (void) len;
return -1;
}
__attribute__((weak)) int _link(const char *a, const char *b) {
(void) a, (void) b;
return -1;
}
__attribute__((weak)) int _unlink(const char *a) {
(void) a;
return -1;
}
__attribute__((weak)) int _stat(const char *path, struct stat *st) {
(void) path, (void) st;
return -1;
}
__attribute__((weak)) int mkdir(const char *path, int mode) {
(void) path, (void) mode;
return -1;
}
__attribute__((weak)) void _init(void) {
}
+206
View File
@@ -0,0 +1,206 @@
// Copyright (c) 2026 Cesanta Software Limited
// https://www.st.com/resource/en/reference_manual/dm00031020-stm32f405-415-stm32f407-417-stm32f427-437-and-stm32f429-439-advanced-arm-based-32-bit-mcus-stmicroelectronics.pdf
// https://www.st.com/resource/en/datasheet/stm32f429zi.pdf
#pragma once
#include <stm32h573xx.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
extern bool hal_timer_expired(volatile uint64_t *t, uint64_t prd, uint64_t now);
extern uint64_t hal_get_tick(void);
extern size_t hal_ram_free(void);
extern size_t hal_ram_used(void);
extern uint32_t SystemCoreClock;
#define HAL_ETH_PINS \
PIN('A', 1), /* ETH_REF_CLK */ \
PIN('A', 2), /* ETH_MDIO */ \
PIN('A', 7), /* ETH_CRS_DV */ \
PIN('C', 1), /* ETH_MDC */ \
PIN('C', 4), /* ETH_RXD0 */ \
PIN('C', 5), /* ETH_RXD1 */ \
PIN('G', 11), /* ETH_TX_EN */ \
PIN('G', 12), /* ETH_TXD1 */ \
PIN('G', 13) /* ETH_TXD0 */
#define BIT(x) (1UL << (x))
#define CLRSET(reg, clear, set) ((reg) = ((reg) & ~(clear)) | (set))
#define PIN(bank, num) ((((bank) - 'A') << 8) | (num))
#define PINNO(pin) (pin & 255)
#define PINBANK(pin) (pin >> 8)
void hal_init(void);
size_t hal_ram_free(void);
size_t hal_ram_used(void);
// System clock (11.4, Figure 48; 11.4.5, Figure 51; 11.4.8
// SYS_FREQUENCY <= 250 MHz; (SYS_FREQUENCY / HPRE) ; hclk = SYS_FREQUENCY
// APB clocks <= 250 MHz. Configure flash latency (WS) in accordance to hclk
// freq (7.3.4, Table 37)
enum {
HAL_HPRE = 7, // register value, divisor value = BIT(value - 7) = / 1
HAL_PPRE1 = 4, // register values, divisor value = BIT(value - 3) = / 2
HAL_PPRE2 = 4,
HAL_PPRE3 = 4,
};
// Make sure your chip package uses the internal LDO, otherwise set PLL1_N = 200
enum { PLL1_HSI = 64, PLL1_M = 32, PLL1_N = 250, PLL1_P = 2 };
#define SYS_FREQUENCY \
((PLL1_HSI * PLL1_N / PLL1_M / PLL1_P / (BIT(HAL_HPRE - 7))) * 1000000)
#define AHB_FREQUENCY SYS_FREQUENCY
#define APB2_FREQUENCY (AHB_FREQUENCY / (BIT(HAL_PPRE2 - 3)))
#define APB1_FREQUENCY (AHB_FREQUENCY / (BIT(HAL_PPRE1 - 3)))
static inline void spin(volatile uint32_t n) {
while (n--) (void) 0;
}
enum { HAL_GPIO_MODE_INPUT, HAL_GPIO_MODE_OUTPUT, HAL_GPIO_MODE_AF, HAL_GPIO_MODE_ANALOG };
enum { HAL_GPIO_OTYPE_PUSH_PULL, HAL_GPIO_OTYPE_OPEN_DRAIN };
enum { HAL_GPIO_SPEED_LOW, HAL_GPIO_SPEED_MEDIUM, HAL_GPIO_SPEED_HIGH, HAL_GPIO_SPEED_INSANE };
enum { HAL_GPIO_PULL_NONE, HAL_GPIO_PULL_UP, HAL_GPIO_PULL_DOWN };
#define HAL_GPIO(N) ((GPIO_TypeDef *) ((GPIOA_BASE_NS) + 0x400 * (N)))
static GPIO_TypeDef *hal_gpio_bank(uint16_t pin) {
return HAL_GPIO(PINBANK(pin));
}
static inline void hal_gpio_toggle(uint16_t pin) {
GPIO_TypeDef *gpio = hal_gpio_bank(pin);
uint32_t mask = BIT(PINNO(pin));
gpio->BSRR = mask << (gpio->ODR & mask ? 16 : 0);
}
static inline int hal_gpio_read(uint16_t pin) {
return hal_gpio_bank(pin)->IDR & BIT(PINNO(pin)) ? 1 : 0;
}
static inline void hal_gpio_write(uint16_t pin, bool val) {
GPIO_TypeDef *gpio = hal_gpio_bank(pin);
gpio->BSRR = BIT(PINNO(pin)) << (val ? 0 : 16);
}
static inline void hal_gpio_init(uint16_t pin, uint8_t mode, uint8_t type,
uint8_t speed, uint8_t pull, uint8_t af) {
GPIO_TypeDef *gpio = hal_gpio_bank(pin);
uint8_t n = (uint8_t) (PINNO(pin));
RCC->AHB2ENR |= BIT(PINBANK(pin)); // Enable GPIO clock
CLRSET(gpio->OTYPER, 1UL << n, ((uint32_t) type) << n);
CLRSET(gpio->OSPEEDR, 3UL << (n * 2), ((uint32_t) speed) << (n * 2));
CLRSET(gpio->PUPDR, 3UL << (n * 2), ((uint32_t) pull) << (n * 2));
CLRSET(gpio->AFR[n >> 3], 15UL << ((n & 7) * 4),
((uint32_t) af) << ((n & 7) * 4));
CLRSET(gpio->MODER, 3UL << (n * 2), ((uint32_t) mode) << (n * 2));
}
static inline void hal_gpio_input(uint16_t pin) {
hal_gpio_init(pin, HAL_GPIO_MODE_INPUT, HAL_GPIO_OTYPE_PUSH_PULL, HAL_GPIO_SPEED_HIGH,
HAL_GPIO_PULL_NONE, 0);
}
static inline void hal_gpio_output(uint16_t pin) {
hal_gpio_init(pin, HAL_GPIO_MODE_OUTPUT, HAL_GPIO_OTYPE_PUSH_PULL, HAL_GPIO_SPEED_HIGH,
HAL_GPIO_PULL_NONE, 0);
}
static inline bool hal_uart_init(USART_TypeDef *uart, unsigned long baud) {
uint8_t af = 7; // Alternate function
uint16_t rx = 0, tx = 0; // pins
uint32_t freq = 0; // Bus frequency. UART1 is on APB2, rest on APB1
if (uart == USART1) {
freq = APB2_FREQUENCY, RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
tx = PIN('A', 9), rx = PIN('A', 10);
} else if (uart == USART2) {
freq = APB1_FREQUENCY, RCC->APB1LENR |= RCC_APB1LENR_USART2EN;
tx = PIN('A', 2), rx = PIN('A', 3);
} else if (uart == USART3) {
freq = APB1_FREQUENCY, RCC->APB1LENR |= RCC_APB1LENR_USART3EN;
tx = PIN('D', 8), rx = PIN('D', 9);
} else {
return false;
}
hal_gpio_init(tx, HAL_GPIO_MODE_AF, HAL_GPIO_OTYPE_PUSH_PULL, HAL_GPIO_SPEED_HIGH, 0, af);
hal_gpio_init(rx, HAL_GPIO_MODE_AF, HAL_GPIO_OTYPE_PUSH_PULL, HAL_GPIO_SPEED_HIGH, 0, af);
uart->CR1 = 0; // Disable UART
uart->BRR = freq / baud; // Set baud rate
uart->CR1 = USART_CR1_RE | USART_CR1_TE; // Set mode to TX & RX
uart->CR1 |= USART_CR1_UE; // Enable UART
return true;
}
static inline void hal_uart_write_byte(USART_TypeDef *uart, uint8_t byte) {
uart->TDR = byte;
while ((uart->ISR & BIT(7)) == 0) spin(1);
}
static inline void hal_uart_write_buf(USART_TypeDef *uart, char *buf, size_t len) {
while (len-- > 0) hal_uart_write_byte(uart, *(uint8_t *) buf++);
}
static inline int hal_uart_read_ready(USART_TypeDef *uart) {
return uart->ISR & BIT(5); // If RXNE bit is set, data is ready
}
static inline uint8_t hal_uart_read_byte(USART_TypeDef *uart) {
return (uint8_t) (uart->RDR & 255);
}
static inline void hal_rng_init(void) {
RCC->CCIPR5 |= RCC_CCIPR5_RNGSEL_0; // RNG clock source pll1_q_ck
RCC->AHB2ENR |= RCC_AHB2ENR_RNGEN; // Enable RNG clock
RNG->CR |= RNG_CR_RNGEN; // Enable RNG
}
static inline uint32_t hal_rng_read(void) {
while ((RNG->SR & RNG_SR_DRDY) == 0) spin(1);
return RNG->DR;
}
static inline bool hal_ldo_is_on(void) {
return (PWR->SCCR & PWR_SCCR_LDOEN) == PWR_SCCR_LDOEN;
}
// Hw pull-ups on PHY RXD0,1,DV to enable autonegotiation
static inline void hal_ethernet_init(void) {
// Initialise Ethernet. Enable MAC GPIO pins, see UM3115 section 10.7
uint16_t pins[] = {HAL_ETH_PINS};
for (size_t i = 0; i < sizeof(pins) / sizeof(pins[0]); i++) {
hal_gpio_init(pins[i], HAL_GPIO_MODE_AF, HAL_GPIO_OTYPE_PUSH_PULL, HAL_GPIO_SPEED_INSANE,
HAL_GPIO_PULL_NONE, 11); // 11 is the Ethernet function
}
NVIC_EnableIRQ(ETH_IRQn); // Setup Ethernet IRQ handler
RCC->APB3ENR |= RCC_APB3ENR_SBSEN; // Enable SBS clock
CLRSET(SBS->PMCR, SBS_PMCR_ETH_SEL_PHY, SBS_PMCR_ETH_SEL_PHY_2); // RMII
RCC->AHB1ENR |= RCC_AHB1ENR_ETHEN | RCC_AHB1ENR_ETHRXEN | RCC_AHB1ENR_ETHTXEN;
}
static inline void hal_system_init(void) {
SCB->CPACR |= ((3UL << 20U) | (3UL << 22U)); // Enable FPU
__DSB();
__ISB();
}
static inline void hal_clock_init(void) {
// Set flash latency. RM0481, section 7.11.1, section 7.3.4 table 37
CLRSET(FLASH->ACR, (FLASH_ACR_WRHIGHFREQ_Msk | FLASH_ACR_LATENCY_Msk),
FLASH_ACR_LATENCY_5WS | FLASH_ACR_WRHIGHFREQ_1);
if (hal_ldo_is_on()) {
PWR->VOSCR = PWR_VOSCR_VOS_0 | PWR_VOSCR_VOS_1; // Select VOS0
} else {
PWR->VOSCR = PWR_VOSCR_VOS_1; // Select VOS1
}
uint32_t f = PWR->VOSCR; // fake read to wait for bus clocking
while ((PWR->VOSSR & PWR_VOSSR_ACTVOSRDY) == 0) spin(1);
(void) f;
RCC->CR = RCC_CR_HSION; // Clear HSI clock divisor
while ((RCC->CR & RCC_CR_HSIRDY) == 0) spin(1); // Wait until done
RCC->CFGR2 = (HAL_PPRE3 << 12) | (HAL_PPRE2 << 8) | (HAL_PPRE1 << 4) | (HAL_HPRE << 0);
RCC->PLL1DIVR =
((PLL1_P - 1) << 9) | ((PLL1_N - 1) << 0); // Set PLL1_P PLL1_N
// Enable P and Q divider outputs; set PLL1_M, select HSI as source,
// !PLL1VCOSEL, PLL1RGE=0
RCC->PLL1CFGR =
RCC_PLL1CFGR_PLL1QEN | RCC_PLL1CFGR_PLL1PEN | (PLL1_M << 8) | (1 << 0);
RCC->CR |= RCC_CR_PLL1ON; // Enable PLL1
while ((RCC->CR & RCC_CR_PLL1RDY) == 0) spin(1); // Wait until done
RCC->CFGR1 |= (3 << 0); // Set clock source to PLL1
while ((RCC->CFGR1 & (7 << 3)) != (3 << 3)) spin(1); // Wait until done
}
@@ -0,0 +1,30 @@
ENTRY(Reset_Handler);
MEMORY {
flash(rx) : ORIGIN = 0x08000000, LENGTH = 2048k
sram(rwx) : ORIGIN = 0x20000000, LENGTH = 640k
}
_estack = ORIGIN(sram) + LENGTH(sram); /* End of RAM. stack points here */
SECTIONS {
.vectors : { KEEP(*(.isr_vector)) } > flash
.text : { *(.text* .text.*) } > flash
.rodata : { *(.rodata*) } > flash
.data : {
_sdata = .;
*(.first_data)
*(.ram)
*(.data SORT(.data.*))
_edata = .;
} > sram AT > flash
_sidata = LOADADDR(.data);
.bss : {
_sbss = .;
*(.bss SORT(.bss.*) COMMON)
_ebss = .;
} > sram
. = ALIGN(8);
_end = .;
}
@@ -0,0 +1,70 @@
// Copyright (c) 2026 Cesanta Software Limited
// All rights reserved
#include "mongoose.h"
#include "hal.h"
#define DEBUG_UART USART1
#define LED1 PIN('I', 9) // On-board LED pin (green)
#define LED2 PIN('I', 8) // On-board LED pin (orange)
#define LED3 PIN('F', 1) // On-board LED pin (red)
// Redirect stdout debug output to UART
int _write(int fd, char *ptr, int len) {
if (fd == 1 || fd == 2) hal_uart_write_buf(DEBUG_UART, ptr, (size_t) len);
return len;
}
static void blink_task(void) {
static uint64_t blink_timer = 0;
if (hal_timer_expired(&blink_timer, 500, hal_get_tick())) {
hal_gpio_toggle(LED1);
}
}
uint64_t mg_millis(void) {
return hal_get_tick();
}
bool mg_random(void *buf, size_t len) {
for (size_t n = 0; n < len; n += sizeof(uint32_t)) {
uint32_t r = hal_rng_read();
memcpy((char *) buf + n, &r, n + sizeof(r) > len ? len - n : sizeof(r));
}
return true;
}
static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), hal_get_tick());
} else {
mg_http_reply(c, 200, "", "Hi from Mongoose, tick %llu\n",
hal_get_tick());
}
}
}
int main(void) {
hal_clock_init();
hal_rng_init();
hal_gpio_output(LED1);
hal_gpio_output(LED2);
hal_gpio_output(LED3);
hal_uart_init(DEBUG_UART, 115200);
hal_ethernet_init();
// Start a minimal web server
struct mg_mgr mgr;
mg_mgr_init(&mgr);
mg_http_listen(&mgr, "http://0.0.0.0", http_ev_handler, NULL);
for (;;) {
mg_mgr_poll(&mgr, 0);
blink_task();
}
return 0;
}
@@ -0,0 +1,23 @@
#pragma once
#define MG_ARCH MG_ARCH_ARMGCC
#define MG_TLS MG_TLS_BUILTIN
#define MG_ENABLE_TCPIP 1
#define MG_ENABLE_CUSTOM_MILLIS 1
#define MG_ENABLE_CUSTOM_RANDOM 1
#define MG_ENABLE_DRIVER_STM32H 1
#define MG_OTA MG_OTA_STM32H5
// Construct MAC address from the MCU unique ID. It is defined in the
// ST CMSIS header as UID_BASE
#define MGUID ((uint32_t *) 0x1FFF7A10) // Unique 96-bit chip ID
#define MG_SET_MAC_ADDRESS(mac) \
do { \
mac[0] = 2; \
mac[1] = MGUID[0] & 255; \
mac[2] = (MGUID[0] >> 10) & 255; \
mac[3] = (MGUID[0] >> 19) & 255; \
mac[4] = MGUID[1] & 255; \
mac[5] = MGUID[2] & 255; \
} while (0)