Pull to refresh

Как разместить переменную по определенному адресу в Keil

Reading time4 min
Views8.8K

Изредка возникает задача сохранить во flash памяти контрольную сумму, картинку, строчку текста, настройку. Иногда возникает задача сохранить не просто в ОЗУ, а в определенной области, чтобы для этой области например включить/выключить DCACHE. Или например иметь функцию, исполняемую из ОЗУ чтобы можно было присылать по UART и сразу исполнять новый код функции.

Рассмотрим задачу на примерах. В качестве испытуемого будет народный stm32f401ret6 со следующей адресацией flash памяти (страница 51 даташита):

#define ADDR_FLASH_SECTOR_0((uint32_t) 0x08000000) //Sector 0, 16 Kbytes 
#define ADDR_FLASH_SECTOR_1 ((uint32_t)0x08004000) //Sector 1, 16 Kbytes 
#define ADDR_FLASH_SECTOR_2 ((uint32_t)0x08008000) //Sector 2, 16 Kbytes 
#define ADDR_FLASH_SECTOR_3 ((uint32_t)0x0800C000) //Sector 3, 16 Kbytes 
#define ADDR_FLASH_SECTOR_4 ((uint32_t)0x08010000) //Sector 4, 64 Kbytes 
#define ADDR_FLASH_SECTOR_5 ((uint32_t)0x08020000) //Sector 5, 128 Kbytes

Первый путь - ручной:
uint32_t keyFlash __attribute__((at(0x08004000))) = 0xAABBCCDD;
Здесь мы записали 32-битное число 0xAABBCCDD по адресу 0x08004000. Этот путь имеет следующий недостаток. Пусть мы точно разместили число в flash по адресу 0x08004000, рядом могут располагаться код программы, значения для констант. Если мы захотим перезаписать число по адресу, придётся стирать весь сектор, потому что во flash писать можно только посекторно. Я даже не знаю что будет, если выполняемые в данном секторе flash памяти инструкции попытаться стереть, но это очевидно плохая идея. Так что если предполагается возможность изменения данных во flash в процессе работы, под эти данные следует выделить отдельный сектор (сектора). И это нас приводит к второму пути.

Второй путь заключается в использовании скеттер (scatter) файла.Теорию можно прочесть здесь. Также желательно понимать что такое объектный файл. Совсем в двух словах, после работы препроцессор->компилятор получается множество
файлов *.o
где звездочка значит любое имя. Например, из main.c получается файл main.o

Рассмотрим пример scatter файла.

В дефолтный scatter файла, который генерирует сам Keil.
; *************************************************************
;Scatter-Loading Description File generated by uVision stm32f401ret6
; *************************************************************

LR_IROM1 0x08000000 0x00080000  {    ; load region size_region
  ER_IROM1 0x08000000 0x00080000  {  ; load address = execution address
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
  }
  RW_IRAM1 0x20000000 0x00018000  {  ; RW data
   .ANY (+RW +ZI)
  }
}

Были добавлены две правки. Первое, исходный файл выделял под программу всю флеш:

ER_IROM1 0x08000000 0x00080000 { ; load address = execution address

Но я для примера выделю только 0 сектор (он начинается с 0x08000000) размером 16 кБ (0x00004000 - это 16*1024 байт в шестнадцатеричной системе).

ER_IROM1 0x08000000 0x00004000{ ; Sector 0: 16kB

где 0x08000000 - начальный адрес сектора, 0x00004000 - количество байт в секторе. По аналогии можно выделить два сектора, и не обязательно 0 и 1, а например 4 и 5 или при некоторых ухищрения даже например 0 и 5. (Но в stm32f4 таблица векторов прерываний должна располагаться в 0 секторе, так что в моём случае 0 сектор точно придётся выделить)

Вторая правка, в дефолтный scatter файл был добавлен execution region, который я решил назвать MYREGION. В этом регионе есть секция mysection, которая ищется во всех объектных файлах. Если линкер найдёт синтаксическую единицу (читай переменную) из этой секции, то эта синтаксическая единица попадёт в регион MYREGION.
LR_IROM1 0x08000000 0x08001000 { ; load region size_region
ER_IROM1 0x08000000
0x00004000{ ; Sector 0: 16kB
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
.ANY (+XO)
}

RW_IRAM1 0x20000000 0x00000900 { ; RW data
.ANY (+RW +ZI)
}

MYREGION 0x0800C000 FIXED {
*.o (mysection)
}
}

А теперь собственно в файле main.c заведем нашу синтаксическую единицу для хранения в регионе:
const uint16_t ADC_Buf[7] __attribute__((section("mysection"))) = {0xDDDD, 0xDDDD, 0xDDDD, 0xDDDD, 0xDDDD, 0xDDDD, 0xDDDD};

И тут нас ждёт ловушка. Язык высокого уровня мы любим за оптимизацию. Включаем -O3 и наша неиспользуемая в программе константа исчезает. Тут случай, когда про toolchain можно сказать "слишком умный". Чтобы объяснить, что в секции mysection у нас всё только нужное, зайдём на вкладку Linker в Misc controls и пропишем
--keep=''main.o(mysection)''

Свойства проекта Keil
Свойства проекта Keil

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


MYREGION 0x0800C000 FIXED {
*.o (mysection)
}

пишите
MYREGION 0x0800C000 FIXED {
main.o (mysection)
}

А ещё можно например все переменные из файла пусть main.c заставить жить в вашей секции вот так


MYREGION 0x0800C000 FIXED {
main.o(+RW +ZI)
}

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

Исходный код.

Кому оказалась интересна тема scatter файлов, предлагаю упражнение. Наверняка вы писали программу моргания светодиодом. Я перелагаю вам написать одну программу, которая работает верно: зажигает и тушит светодиод, А вторая программа будет зажигать и зажигать светодиод:

void blink(){
  while(1){
    on();
    delay();
    on();
    delay();
  }
}

После этого я предлагаю вам посмотреть содержимое памяти и глазами найти отличие. Далее blink() с помощью scatter файла поместите в ОЗУ, пусть оттуда выполняется. Затем перед вызовом функции blink() в main() попробуйте написать код, который починит blink(), чтобы она и зажигала и тушила. О своих (не) успехах пишите в лс или комментариях, может быть получится ещё одна заметка.

Также может быть полезно
https://radiohlam.ru/stm32_1/

Дополнительные материалы:
Как не инициализировать переменные в кейл?
Про слово FIXED

Tags:
Hubs:
+13
Comments23

Articles

Change theme settings