Как стать автором
Обновить

Прошиваем программу не только в начало FLASH памяти STM32

Уровень сложностиСредний
Время на прочтение3 мин
Количество просмотров5.7K

Знаете ли вы куда попадает ваша программа после того, как вы нажали кнопку RUN или DEBUG в IDE? Если да, то как изменить этот адрес или даже выйти за пределы постоянной памяти и прожигаться сразу в оперативную память.

Я работаю в CubeIDE и весь мой интерфейс будет именно из этой среды разработки.

Начнем с ответа на первый вопрос, если использовать стандартные настройки IDE и взять стандартный ST-LINK (не важно оригинал или свисток), то при прожигании микроконтроллера будет очищено нужное количество секторов во FLASH памяти и на их место будет записана программа, причем в самое начало FLASH памяти по адресу 0x08000000.

Тут немного остановимся. Для большинства проектов этого достаточно, программа находится во FLASH памяти, при перезагрузке микроконтроллера ни куда не исчезает и исправно запускается. Проблемы начинаются, когда приходит задача, встроить загрузчик, который будет самостоятельно принимать прошивку по одному из интерфейсов, будь то UART/USART, I2C или CAN и сохранять ее в требуемом месте FLASH памяти, а после перезагрузки должен самостоятельно подготовить и передать управление требуемой программе.

Тут уже не обойтись без небольшой настройки линкер-скрипта и кода самой программы.

В CubeIDE в корне вашего проекта вы обязательно найдете файл с расширением .ld, это и есть линкер скрипт в котором можно найти описание начальных адресов и размеров разных секторов памяти (пример STM32F429ZI):

MEMORY
{
  CCRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K
  RAM   (xrw) : ORIGIN = 0x20000000, LENGTH = 192K
  FLASH (rx)  : ORIGIN = 0x08000000, LENGTH = 2048K
}

Тут мы видим, что наш чип имеет две области энергозависимой памяти: CCRAM и RAM размерами 64 и 192 кбайта соответственно и одну область энергонезависимой памяти - FLASH размером аж 2 Мбайта. CCRAM память начинается с адреса 0x10000000 и с учетом размера заканчивается по адресу 0x10010000, а RAM память занимает адресное пространство с 0x20000000 по 0x20030000.

На FLASH памяти остановлюсь чуть подробнее. В отличие от RAM, она не позволяет перезаписывать какие угодно биты тысячи раз в секунду. Во-первых, ресурс перезаписи достаточно быстро закончится, во вторых для того что бы изменить всего один бит с 0 на 1 во FLASH памяти, придется "затереть" целую страницу (page), размер которой (на моем опыте) может достигать 128 кбайт. Поэтому при работе с FLASH памятью нужно всегда держать в уме в каком секторе я сейчас работаю и нет ли в нем чего-то еще важного. Карту вашей FLASH памяти со всеми секторами и их размерами вы можете найти в Reference manual разделе посвященному встроенной памяти (например "Embedded flash memory interface").

Раздел FLASH в линкер скрипте как раз и определяет, куда будет загружена программа, поэтому, если хотите изменить адрес, куда будет загружаться программа и что не мало важно, для какого адреса будет собрана таблица прерываний, то нужно изменить адрес начала FLASH памяти в линкер-скрипте. Например, так:

Теперь программа будет работать из середины FLASH памяти. ВАЖНО: не забывать изменять размер области, что бы не выйти за пределы адресного пространства.

MEMORY
{
  CCRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K
  RAM   (xrw) : ORIGIN = 0x20000000, LENGTH = 192K
  FLASH (rx)  : ORIGIN = 0x08100000, LENGTH = 1024K
}

Так же можно запускать программу из RAM памяти, главное не допускать пересечения областей FLASH и RAM:

MEMORY
{
  CCRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K
  RAM   (xrw) : ORIGIN = 0x20025800, LENGTH = 42K
  FLASH (rx)  : ORIGIN = 0x20000000, LENGTH = 150K
}

Загружаясь в оперативную память, не забывайте, что после любого reset-a ваша программа будет стерта и придется ее прожигать заново.

НО, что бы ваш код работал из нового определенного вами места в памяти, необходимо сделать последний штрих - записать новый адрес начала программы в структуру SCB самым первым шагом в функции main.

#define START_ADDR 0x20000000

int main(void)
{
    /* Пока не будет передан новый адрес таблицы векторов, 
       другие функции не должны вызываться */
    __disable_irq();
    SCB->VTOR = START_ADDR;
    __enable_irq();

    /* Остальная программа начинается тут */
  
    while(1)
    {
    
    }
}

Поздравляю, вы сделали первый шаг к работе из загрузчика и теперь можете контролировать место прожига и хранения вашей программы в памяти микроконтроллера.

Теги:
Хабы:
Всего голосов 19: ↑15 и ↓4+19
Комментарии31

Публикации

Истории

Работа

Программист C++
151 вакансия
QT разработчик
12 вакансий
Программист С
55 вакансий

Ближайшие события

27 августа – 7 октября
Премия digital-кейсов «Проксима»
МоскваОнлайн
28 сентября – 5 октября
О! Хакатон
Онлайн
3 – 18 октября
Kokoc Hackathon 2024
Онлайн
10 – 11 октября
HR IT & Team Lead конференция «Битва за IT-таланты»
МоскваОнлайн
25 октября
Конференция по росту продуктов EGC’24
МоскваОнлайн
7 – 8 ноября
Конференция byteoilgas_conf 2024
МоскваОнлайн
7 – 8 ноября
Конференция «Матемаркетинг»
МоскваОнлайн