Размещение переменной по абсолютному адресу в памяти микроконтроллера
При помощи указателей
int *var = (int*)0x20000500;
int main ()
{
*var = 0x44556677;
//...
}
Объявление / определение: переменная var содержит адрес, по которому расположено значение типа int (в текущей реализации 4 байта). Может располагаться в любом месте до первого использования.
Инициализация присваиванием / присваивание: разыменовыванием указателя получаем доступ к значению переменной типа int, расположенной по адресу var. В Си нельзя исполнять код вне функций, поэтому присваивание должно располагаться в теле функции. Например: в main().
Ячейка памяти должна быть доступна для записи, а значит располагаться или в доступной области ОЗУ (статус rw - read-write), или быть доступным регистром периферийного устройста. Инициализировать подобным образом ячейку ПЗУ (статус ro - read-only) не получится, программа "свалится" в исключение.
При помощи атрибута at: __attribute__((at(address)))
uint32_t var __attribute__((at(0x08004000))) = 100500u;
Работает только с компилятором ARMCC от Keil. Не поддерживается GNU GCC и другими компиляторами. Компилятор ARM 6-й версии основан на компиляторе clang (llvm) и так же не поддерживает данный атрибут. Не рекомендуется использовать в настоящее время.
Данный метод размещает переменную var значением 100500 (беззнаковое целое) по адресу 0x0800'4000, и не требует изменения scatter-файла.
При помощи атрибута section: __attribute__((section("inf_section")))
uint32_t var __attribute__((section("inf_section"))) = 0x33445566u;
Работает с компилятором ARMCC от Keil, Clang (llvm), GNU GCC.
Данный метод размещает переменную var значением 0x33445566 (беззнаковое целое) в секции inf_section, адрес которой должен быть указан в scatter-файле.
Исходный scatter-файл, формируемый Keil по умолчанию:
; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************
LR_IROM1 0x08000000 0x00010000 { ; load region size_region
ER_IROM1 0x08000000 0x00010000 { ; load address = execution address
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
.ANY (+XO)
}
RW_IRAM1 0x20000000 0x00005000 { ; RW data
.ANY (+RW +ZI)
}
}
Микроконтроллер STM32F103C8T6 имеет 64 кБ flash-памяти: всего 64 страницы (нумерация страниц памяти: 0 ... 63) по 1 кБ (0x400 байт).
Страница памяти 63 (Page 63) имеет адреса начало/конец: 0x0800'FC00 ... 0x0800'FFFF. Если разместить секцию inf_section на последней странице, то scatter-файл будет таким:
; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************
LR_IROM1 0x08000000 0x00010000 { ; load region size_region
ER_IROM1 0x08000000 0x0000FC00 { ; load address = execution address
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
.ANY (+XO)
}
ER_IROM2 0x0800FC00 FIXED { ;
*.o (inf_section)
}
RW_IRAM1 0x20000000 0x00005000 { ; RW data
.ANY (+RW +ZI)
}
}
Уменьшили размер основного региона flash-памяти на размер страницы. Создали второй регион ER_IROM2 во flash-памяти вплотную за концом основного (root) региона. Использован атрибут FIXED для размещения региона ER_IROM2 по фиксированному адресу. Подключили к региону секцию inf_section из любого объектного файла.
Чтобы компоновщик не убрал при оптимизации во время сборки проекта созданную секцию, надо указать в командной строке --keep=''main.o(inf_section)''. Указывается в опциях проекта -> вкладка Linker -> поле Misc Controls.