2024-09-04 11:37 PM
Hello:
I use STM32CubeIDE 1.16.0 to develop an OTA feature in STM32H7A3ZIT6:
The firmware should be programed at flash address 0x08000000,and run in ram, it will receive the whole update image in temporary memory and then program into the flash at the same start address 0x08000000,is it possible in this way? Any detailed examples or documents are appreciated, thanks.
Bill
2024-09-05 01:30 AM
Hello @lbapplem
Yes, it is possible to implement an OTA update feature for the STM32H7A3ZIT6 microcontroller using STM32CubeIDE. The general idea is to have a bootloader that can receive the new firmware image, store it temporarily in RAM or another memory area, and then program it into the flash memory at the start address 0x08000000.
STM32CubeH7 Firmware Package: This package contains examples and libraries for STM32H7 series microcontrollers. You can find it on the STMicroelectronics website.
AN4852 Application Note: This document provides guidelines for developing a bootloader for STM32 microcontrollers. It can be found on the STMicroelectronics website.
2024-09-05 01:44 AM
Hi nouirakh:
Thanks for the quick response, but in my case there may not be a bootloader to do so, only the application will start at the default flash address 0x08000000 where is the start address of the flash, and the application should receive the new firmware image to RAM, then erase the flash from 0x08000000 and program the new image into this address,is it possible? I've used STM32H7A3ZITX_RAM.ld to make the application run in RAM(if it does so), but the application will crash when running to erase the flash from 0x08000000, did I miss something?
Bill
2024-09-05 03:45 AM
hello @lbapplem
OK, so, it is possible to implement an OTA update feature without a separate bootloader, but it requires careful handling since the application will be running from RAM while it erases and reprograms the flash memory.By following these steps, you should be able to implement an OTA update feature:
Ensure that your linker script (STM32H7A3ZITX_RAM.ld) places the code and data in RAM. Here is an example of a linker script for running the application from RAM:
/* Entry Point */
ENTRY(Reset_Handler)
/* Highest address of the user mode stack */
_estack = 0x20040000; /* end of RAM */
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0x200; /* required amount of heap */
_Min_Stack_Size = 0x400; /* required amount of stack */
/* Specify the memory areas */
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
}
/* Define output sections */
SECTIONS
{
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(4);
} >RAM
.text :
{
. = ALIGN(4);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
. = ALIGN(4);
} >RAM
.data : AT(__data_load_addr)
{
. = ALIGN(4);
__data_start__ = .;
*(.data) /* .data sections */
*(.data*) /* .data* sections */
. = ALIGN(4);
__data_end__ = .;
} >RAM
.bss :
{
. = ALIGN(4);
__bss_start__ = .;
*(.bss)
*(.bss*)
. = ALIGN(4);
__bss_end__ = .;
} >RAM
/* User_heap_stack section, used to check that there is enough RAM left */
._user_heap_stack :
{
. = ALIGN(8);
PROVIDE ( end = . );
PROVIDE ( _end = . );
. = . + _Min_Heap_Size;
. = . + _Min_Stack_Size;
. = ALIGN(8);
} >RAM
}
In your startup code, copy the application from flash to RAM and then jump to the RAM execution address. Here is an example of how to do this in your main.c
:
#include "stm32h7xx_hal.h"
#define APPLICATION_ADDRESS 0x08000000
#define RAM_EXECUTION_ADDRESS 0x20000000
void SystemClock_Config(void);
void Error_Handler(void);
void JumpToRAM(void);
int main(void) {
HAL_Init();
SystemClock_Config();
// Copy application from flash to RAM
uint32_t *flash_ptr = (uint32_t *)APPLICATION_ADDRESS;
uint32_t *ram_ptr = (uint32_t *)RAM_EXECUTION_ADDRESS;
for (uint32_t i = 0; i < (128 * 1024) / 4; i++) { // Assuming 128KB application size
ram_ptr[i] = flash_ptr[i];
}
// Jump to RAM execution address
JumpToRAM();
while (1) {
}
}
void JumpToRAM(void) {
typedef void (*pFunction)(void);
uint32_t jumpAddress = *(__IO uint32_t *)(RAM_EXECUTION_ADDRESS + 4);
pFunction jumpToApplication = (pFunction)jumpAddress;
__set_MSP(*(__IO uint32_t *)RAM_EXECUTION_ADDRESS);
jumpToApplication();
}
void SystemClock_Config(void) {
// Configure the system clock
// ...
}
void Error_Handler(void) {
// Implement error handling
while (1) {
}
}
Implement the flash erase and program routines in your application. Ensure that these routines are executed while the application is running from RAM.
void EraseFlash(uint32_t address) {
FLASH_EraseInitTypeDef eraseInitStruct;
uint32_t sectorError;
eraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS;
eraseInitStruct.Sector = FLASH_SECTOR_0; // Adjust as needed
eraseInitStruct.NbSectors = 1; // Adjust as needed
eraseInitStruct.VoltageRange = FLASH_VOLTAGE_RANGE_3;
if (HAL_FLASHEx_Erase(&eraseInitStruct, §orError) != HAL_OK) {
Error_Handler();
}
}
void ProgramFlash(uint32_t address, uint8_t *data, uint32_t length) {
HAL_FLASH_Unlock();
for (uint32_t i = 0; i < length; i += 4) {
if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, address + i, *(uint32_t *)(data + i)) != HAL_OK) {
Error_Handler();
}
}
HAL_FLASH_Lock();
}
2024-09-05 05:58 AM
"Build for 0x20000000 base address, with whatever split for data, rebase the Reset_Handler entry point, copy the entire FLASH image to RAM, set SCB->VTOR to 0x20000000 per SystemInit() and fork."
2024-09-05 09:59 PM
Hi, nouirakh:
Thanks for the detailed steps and I've followed like these:
1.burn the test_ram.hex at 0x08000000 in the evb NUCLEO-H7A3ZI-Q
2.debug in STM32CubeIDE 1.16.0
3.when call JumpToRam(), found it run to main() again
Attached file is my test project, can you help me out here? Thanks.
Bill