Начал знакомство с STM32 с ядра CortexM4F (STM32F407VG).
На сколько я понял из даташита, там вся память (ОЗУ, ПЗУ), регистры и вся периферия идут сплошным файлом.
Приведенный пример ниже загружает массив (скомпилированный бинарник) с флеша в ОЗУ и передает ему управление банальным прыжком в область ОЗУ.
Код: Выделить всё
#include <ST\iostm32f407VG.h>
//////////////////////////////////////////////////////////////////////////////
typedef int (*func)(void);
//////////////////////////////////////////////////////////////////////////////
#pragma section = "FLASH"
const unsigned char rawData[59][4] = {
0x00, 0x84, 0x01, 0x20, 0xE1, 0x01, 0x00, 0x20, 0x9F, 0x01, 0x00, 0x20,
0x9F, 0x01, 0x00, 0x20, 0x9F, 0x01, 0x00, 0x20, 0x9F, 0x01, 0x00, 0x20,
0x9F, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9F, 0x01, 0x00, 0x20,
0x9F, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x9F, 0x01, 0x00, 0x20,
0x9F, 0x01, 0x00, 0x20, 0x4F, 0xF0, 0x00, 0x50, 0x01, 0x68, 0x0A, 0x68,
0x42, 0xF0, 0x08, 0x02, 0x0A, 0x60, 0x41, 0x68, 0x0A, 0x68, 0x42, 0xF0,
0xAA, 0x42, 0x0A, 0x60, 0x81, 0x68, 0x00, 0x22, 0x0A, 0x60, 0xC1, 0x68,
0x0A, 0x60, 0x01, 0x69, 0x0A, 0x60, 0x08, 0xE0, 0x00, 0xBF, 0x49, 0x1C,
0x91, 0x42, 0xFB, 0xDB, 0x41, 0x69, 0x0A, 0x68, 0x82, 0xF4, 0x00, 0x42,
0x0A, 0x60, 0x00, 0x21, 0x00, 0x4A, 0xF5, 0xE7, 0xFF, 0xFF, 0x0F, 0x00,
0x4E, 0xF6, 0x88, 0x51, 0xCE, 0xF2, 0x00, 0x01, 0x08, 0x68, 0x40, 0xF4,
0x70, 0x00, 0x08, 0x60, 0x4F, 0xF0, 0x00, 0x70, 0xE1, 0xEE, 0x10, 0x0A,
0x70, 0x47, 0xFE, 0xE7, 0x00, 0xF0, 0x09, 0xF8, 0x00, 0x28, 0x01, 0xD0,
0xC0, 0x46, 0xC0, 0x46, 0x00, 0x20, 0xFF, 0xF7, 0xC7, 0xFF, 0x00, 0xF0,
0x02, 0xF8, 0x01, 0x20, 0x70, 0x47, 0x00, 0xF0, 0x01, 0xB8, 0x00, 0x00,
0x07, 0x46, 0x38, 0x46, 0x00, 0xF0, 0x02, 0xF8, 0xFB, 0xE7, 0x00, 0x00,
0x80, 0xB5, 0xC0, 0x46, 0xC0, 0x46, 0x02, 0x4A, 0x11, 0x00, 0x18, 0x20,
0xAB, 0xBE, 0xFB, 0xE7, 0x26, 0x00, 0x02, 0x00, 0xC0, 0x46, 0xC0, 0x46,
0xFF, 0xF7, 0xCE, 0xFF, 0xFF, 0xF7, 0xDA, 0xFF
};
////////////////////////////////////////////////////////////////////////////// Таблица адресов периферии
#pragma section = "FLASH"
volatile const uint32_t io_p[6] = {(uint32_t)&RCC->AHB1ENR, (uint32_t)&GPIOD->MODER,
(uint32_t)&GPIOD->OTYPER, (uint32_t)&GPIOD->OSPEEDR,
(uint32_t)&GPIOD->PUPDR, (uint32_t)&GPIOD->ODR};
////////////////////////////////////////////////////////////////////////////// Место, где будет расположена таблица (Начало ОЗУ)
#pragma location = 0x20000000
volatile uint32_t io_ptr[20];
//////////////////////////////////////////////////////////////////////////////
int main()
{
uint32_t size = (sizeof(rawData)/4); //Размер бинарника в словах
uint32_t start_addr = 0x20000100; //Адрес, куда следует поместить бинарник
volatile uint32_t *Raw = (uint32_t*)start_addr; //Передача адреса указателю
for(uint32_t x=0; x<size; x++) //Копирование бинарника в ОЗУ
{
Raw[x] = 0x00000000;
Raw[x] |= (uint32_t)(rawData[x][0]<<0);
Raw[x] |= (uint32_t)(rawData[x][1]<<8);
Raw[x] |= (uint32_t)(rawData[x][2]<<16);
Raw[x] |= (uint32_t)(rawData[x][3]<<24);
}
for(uint32_t y = 0; y<6; y++) //Копирование адресов периферии (для частичного абстрагирования от конкретного камня)
{
io_ptr[y] = io_p[y];
}
func RAM = (func)Raw[1];//копирование адреса начала программы из Reset_Handler бинарника
RAM(); //Собственно, само исполнение программы
while(1); //На случай, если программа завершится
}
При компиляции бинарника, в настройках линкера было задано иное адресное пространство для ОЗУ и ПЗУ.
Данный пример говорит о том, что ядру абсолютно начихать на то, где находятся данные, а где программа, несмотря на его гарвардскую архитектуру.