_Desh_ 0 29 августа, 2020 Опубликовано 29 августа, 2020 (изменено) · Жалоба Разрабатываю в STM32CubeIDE. Контроллер STM32F407V. Так я копирую таблицу векторов в ОЗУ: #define VECTOR_EXCEPTION_NUM (16) /* Ссылка на таблицу векторов, размещенную в памяти Flash. Взята из файла * startup.s */ extern uint32_t g_pfnVectors[]; /* Массив для хранения адресов обработчиков прерываний, размещенных в RAM. */ static uint32_t vector_table_ram[94] __attribute__((section(".RAM"))) __attribute__((aligned(512))); void SystemInit(void) { uint32_t i = 0; /* Копируем таблицу векторов. */ for (i = 0; i < 94; i++) { vector_table_ram[i] = g_pfnVectors[i]; } /* Регистрация нового обработчика. */ vector_table_ram[VECTOR_EXCEPTION_NUM + SysTick_IRQn] = (uint32_t)&systick_handler_ram; /* VTOR. */ SCB->VTOR = (uint32_t)vector_table_ram; } Правильность копирования проверял в отладчике. Сама таблица разместилась по адресу 0x20000600, содержимое регистра VTOR соответствует. Обработчик прерывания, который я хочу разместить в ОЗУ: void systick_handler_ram(void) __attribute__((section(".RAM"))); void systick_handler_ram(void) { systick_inc_tick_1ms(); systick_flag = 1; } Обработчик успешно размещается по адресу 0х20000200, в новую таблицу векторов это значение вносится. Программа один раз заходит в обработчик, прошагивает инструкции до конца, а при выходе из него вылетает в HardFault. Если убрать из объявления systick_handler_ram атрибут размещения в секции RAM, обработчик размещается во FLASH, и прерывание работает - по прерыванию я завел счетчик и моргалку. Пробовал добавлять __attribute__((interrupt)) или __attribute__((interrupt("IRQ"))) - программа перестала даже заходить в обработчик - сразу в HardFault. Подскажите, пожалуйста, что я делаю не так? P.S. Пробовал делать так: vector_table_ram[VECTOR_EXCEPTION_NUM + SysTick_IRQn] = ((uint32_t)&systick_handler_ram) | 1UL; Ничего не меняется, компилятор добавляет единицу в младший разряд самостоятельно. Изменено 29 августа, 2020 пользователем _Desh_ Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
VladislavS 39 29 августа, 2020 Опубликовано 29 августа, 2020 · Жалоба А зачем таблицу векторов копировать? Если векторов немного, то проще их устанавливать. #define VTABLE_ADR 0x2000'0000 #define InstallIRQ(name) void name##Handler(); \ *(volatile uint32_t *)(VTABLE_ADR + 0x40 + name##n * 4) = (uint32_t)name##Handler SCB->VTOR = VTABLE_ADR; InstallIRQ(USB_LP_CAN1_RX0_IRQ); Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Forger 26 29 августа, 2020 Опубликовано 29 августа, 2020 · Жалоба Вероятно, что возникает прерывания до того, как все обработчики скопированы в ОЗУ. Делать важно в ТАКОМ ПОРЯДКЕ: 1. запретить все прерывания 2. скопировать обработчики и таблицу (необязательно) 3. перенастроить vtor 4. разрешить прерывания Делать это лучше в main !!! Скорее всего в вашем случае сначала вызывается system_init, а уже потом копируются сами обработчики прерываний из flash в озу (см как работает ram function). Не путать с копированием таблицы! Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
_Desh_ 0 29 августа, 2020 Опубликовано 29 августа, 2020 · Жалоба 14 minutes ago, Forger said: Скорее всего в вашем случае сначала вызывается system_init, а уже потом копируются сами обработчики прерываний из flash в озу Попробовал перенести весь код в main с запретом прерываний на время инициализации - не помогло. Спасибо, но дело похоже не в этом. Первый-то раз в обработчик программа заходит как положено. Сбой при выходе из него. Порядок вызова в startup-файле - System_Init->__libc_init_array->main. Копирование всего, кроме таблицы, происходит до вызова System_Init. Для успокоения совести убирал вызов __libc_init_array - результат прежний. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Forger 26 29 августа, 2020 Опубликовано 29 августа, 2020 · Жалоба Стека прерываний хватает? Если дело не в этом, то проверяйте содержимое регистров непосредственно перед выходом из обработчика (по шагам в disasm). Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
SII 0 29 августа, 2020 Опубликовано 29 августа, 2020 · Жалоба Раз обработчик успешно вызывается -- значит, дело точно не в таблице векторов и не в настройке VTORа. Надо смотреть, что на момент завершения обработчика находится в LR и стеках и куда, собственно, происходит возврат. Кроме того, я проверил бы, а правильно ли присваивается значение переменной systick_flag = 1; Тут дело в том, что процессор не ожидает фактического завершения записи и продолжает выполнять программу; соответственно, если запись обломилась, прерывание произойдёт не на команде записи, а уже позже -- и, если нет правильного обработчика подобных прерываний (а его обычно нет), вывалится HardFault. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Forger 26 29 августа, 2020 Опубликовано 29 августа, 2020 · Жалоба Для начала стоит добиться нормальной работы с пустыми обработчиками (кроме сброса соотв. флага перерывания). Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
_Desh_ 0 29 августа, 2020 Опубликовано 29 августа, 2020 (изменено) · Жалоба Поведение остается прежним и при пустом обработчике. sp = 0x1000ffd0 (я разместил стек в области CCMRAM) lr = 0xfffffff9 Также наблюдал такую картину: содержимое окна дизассемблера кардинально различается в двух вариантах программы - с обработчиком во FLASH и с обработчиком в RAM. Для пустого обработчика во FLASH всего лишь: 080202ec: push {r7} 080202ee: add r7, sp, #0 А вот для обработчика в RAM полная белиберда, ничего похожего, просто непонятный набор инструкций. Как будто никакого копирования кода функции в ОЗУ не произошло. Разве для этого недостаточно просто указать рядом с объявлением/определением функции соответствующий атрибут? Изменено 29 августа, 2020 пользователем _Desh_ Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Forger 26 29 августа, 2020 Опубликовано 29 августа, 2020 · Жалоба 13 minutes ago, _Desh_ said: Как будто никакого копирования кода функции в ОЗУ не произошло. Разве для этого недостаточно просто указать рядом с объявлением/определением функции соответствующий атрибут? Значит входа в обработчик все же нет. А говорили, что входит в обработчик )) Выходит, что валится не туда, куда надо, а вероятнее всего туда, где ничего нужного нет 13 minutes ago, _Desh_ said: Разве для этого недостаточно просто указать рядом с объявлением/определением функции соответствующий атрибут? Конечно нет ))) Читайте в мануале про ram function. Там расписано в картинках что надо делать Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
_Desh_ 0 29 августа, 2020 Опубликовано 29 августа, 2020 · Жалоба 10 minutes ago, Forger said: Читайте в мануале про ram function. Подскажите пожалуйста хотя бы название. Что за мануал - на МК, на среду разработки, на сам GCC? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Forger 26 29 августа, 2020 Опубликовано 29 августа, 2020 · Жалоба 9 minutes ago, _Desh_ said: Подскажите пожалуйста хотя бы название. Что за мануал - на МК, на среду разработки, на сам GCC? Блин, я же не знаю в какой среде разработке работаете и как там это организовано. В двух словах: для файла, где размещены ram функции, нужно выделять соотв. секцию в скрипте линкера. К тому же среда должна поддерживать такие вещщи как ram функции. Ну и как капитан очевидность: гугль в помощь :) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AHTOXA 18 29 августа, 2020 Опубликовано 29 августа, 2020 · Жалоба 4 часа назад, _Desh_ сказал: void systick_handler_ram(void) __attribute__((section(".RAM"))); А покажите как у вас в скрипте линкера объявлена секция .RAM? Или попробуйте поместить функцию в .data. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
_Desh_ 0 29 августа, 2020 Опубликовано 29 августа, 2020 (изменено) · Жалоба Нагуглил и сделал следующее (пусть это наверное всем кроме меня известный баян, напишу хотя бы для упорядочивания в своей голове). Объявил новую секцию RAMFUNC для размещения кода нужных мне функций в скрипте линкера. Пока выбрал 8 Кб, отрезав столько же от остальной RAM. MEMORY { CCMRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K RAMFUNC (rx) : ORIGIN = 0x20000000, LENGTH = 8K RAM (xrw) : ORIGIN = 0x20002000, LENGTH = 120K FLASH (rx) : ORIGIN = 0x08020000, LENGTH = 128K } Описание секции между секциями .data и .bss: _siramfunc = LOADADDR(.ramfunc); .ramfunc : { . = ALIGN(4); _sramfunc = .; /* для копирования кода в startup-файле */ *(.RAMFUNC.ISR) *(.RAMFUNC) *(.RAMFUNC*) . = ALIGN(4); _eramfunc = .; /* для копирования кода в startup-файле */ } >RAMFUNC AT> FLASH В startup-файле вписал объявленные ранее переменные и добавил копирование кода по аналогии с уже имеющимся копированием данных .data и .bss. Как уже указывал уважаемый Forger, копировать код функций надо до вызова основной программы: .word _siramfunc .word _sramfunc .word _eramfunc ... CopyCodeRamFunc: ldr r3, =_siramfunc ldr r3, [r3, r1] str r3, [r0, r1] adds r1, r1, #4 LoopCopyCodeRamFunc: ldr r0, =_sramfunc ldr r3, =_eramfunc adds r2, r0, r1 cmp r2, r3 bcc CopyCodeRamFunc bl SystemInit bl main ... После этого осталось только объявить соответствующий атрибут для нужных мне функций. Для целей тестирования я сделал для одной обычной функции (инициализация Systick) и одного обработчика прерывания (того же Systick): __attribute__((section(".RAMFUNC"))) void systick_config(void) { ... } __attribute__((interrupt)) __attribute__((section(".RAMFUNC.ISR"))) void systick_handler_ram(void) { ... } Теперь обработчик работает корректно. Обычная функция тоже. Извините за простыню. Всем спасибо за подсказки. Изменено 29 августа, 2020 пользователем _Desh_ Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
haker_fox 61 30 августа, 2020 Опубликовано 30 августа, 2020 · Жалоба 10 hours ago, _Desh_ said: Обработчик прерывания, который я хочу разместить в ОЗУ: А для чего вы размещаете сам обработчик в ОЗУ? Пока не могу придумать, зачем это нужно) Мне хватает разместить в ОЗУ только таблицу. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
_Desh_ 0 30 августа, 2020 Опубликовано 30 августа, 2020 (изменено) · Жалоба 2 hours ago, haker_fox said: А для чего вы размещаете сам обработчик в ОЗУ? Пока не могу придумать, зачем это нужно) Мне хватает разместить в ОЗУ только таблицу. Предполагаю стирать/писать флэш-память без запрета прерываний. Раньше я загрузку прошивки на лету ни разу не делал, опыта у меня мало. Сейчас я увидел неудобства, связанные с этим (начинал с Кейла, там гораздо проще делается размещение функций в ОЗУ), возможно в конце концов обойдусь простым запретом прерываний на время стирания/записи. Ну и сами-то функции стирания/записи по-любому придется в ОЗУ размещать. Так что осваивать все равно пришлось бы, пусть и не на примере обработчика. Изменено 30 августа, 2020 пользователем _Desh_ Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться