Как я могу использовать одну функцию из основного приложения и загрузчика? (Встроенный)
Прежде всего мне нужно сказать, что я разрабатываю приложение для встроенного устройства на базе Cortex M4.
У меня есть функции, которые являются общими для загрузчика и основного приложения. Сейчас я компилирую исходные файлы 2 раза один раз для загрузчика и приложения. Но мне не хватает места для двойного банка dfu, и я хотел бы иметь эти функции только один раз в ПЗУ. Есть идеи, как мне этого добиться?
РЕДАКТИРОВАТЬ:
Использование указателей на функции может быть опасным в некоторых случаях, проверьте мои проблемы - Использование функций указателей - 2 отдельных приложения на 1 устройстве
3 ответа
Это только частичный ответ и предполагает, что вы можете перейти в свой загрузчик из основного кода, используя то же адресное пространство. Затем, обычная техника - предоставить ваш "API загрузчика" в виде таблицы указателей на функции.
Скажем, например, у вас есть следующие функции в вашем загрузчике:
static int do_something(void)
{
return 42;
}
static int do_something_else(int arg)
{
return arg+5;
}
Тогда вы бы объявили свой API в заголовке следующим образом:
struct bootloaderApi
{
int (*do_something)(void);
int (*do_something_else)(int arg);
};
В реализации вашего загрузчика вы определяете эту таблицу в своем собственном разделе:
// this is GCC syntax, use whatever your compiler provides to specify the section
struct bootloaderApi api __attribute__((section("API"))) = {
do_something,
do_something_else
};
Затем при сборке загрузчика убедитесь, что ваш раздел размещен по подходящему фиксированному адресу. Например, когда вы используете компоновщик GNU, в вашем скрипте компоновщика может быть что-то вроде этого:
SECTIONS {
// standard sections, e.g.:
.text : { *(.text) }
.data : { *(.data) }
.bss : { *(.bss) *(COMMON) }
// your API table:
.API 0x10000 : { *(.API) }
}
Теперь предполагается, что ваша таблица API будет размещена на 0x10000
, Затем вы можете сделать следующее, чтобы получить доступ к API из вашего основного кода:
struct bootloaderApi *api = (struct bootloaderApi *)0x10000;
api->do_something();
Все это просто набросок, чтобы дать вам представление, как сделать это разумным способом. Это будет сильно зависеть от вашей целевой платформы и набора инструментов, который вы используете.
Программное прерывание является распространенным средством доступа к функциям в загрузочном коде. Тогда основной программе не нужно знать, где находятся функции. Обычно номер функции нажимается / что угодно фиксированным образом, так что загрузочный код всегда может получить его и переключиться на нужную функцию.
Такой механизм не требует какой-либо специально расположенной области интерфейса, которая является фиксированной и известной как для загрузки, так и для основной, или для какой-либо связи между основным и загрузочным кодом.
Точный механизм (ы) зависит от архитектуры.
Два возможных сценария:
Встроенный загрузчик. Во всех ядрах Cortex я знаю. Вот пример кода для вызова загрузчика из приложения.
#define SYSFLASH 0x1FFFD800 void __attribute__((noreturn)) StartBootLoader(void) { void (*BootLoad)(void) = (void (*)(void))(SYSFLASH + 4); HAL_RCC_DeInit(); // Only the example foe HAL. PLL has to switched off, peripheral clocks as well HAL_DeInit(); // All interrupts have to be disabled. // if HAL not used uC should be restored to the power-on state SysTick -> CTRL = 0; SysTick -> LOAD = 0; SysTick -> VAL = 0; __set_PRIMASK(1); __set_MSP(*(uint32_t *)SYSFLASH); BootLoad(); while(1); }
Кастомный загрузчик - возможен любой сценарий