Миландр

Ключевым подразделением нашей компании
является Центр Проектирования интегральных микросхем
Текущее время: 2019-ноя-17 09:48

Часовой пояс: UTC + 3 часа




Начать новую тему Ответить на тему  [ Сообщений: 35 ] 
Автор Сообщение
СообщениеДобавлено: 2019-янв-08 16:07 
Не в сети
Аватара пользователя

Зарегистрирован: 2012-май-05 08:45
Сообщения: 31
Откуда: Москва
Здравствуйте.

Столкнулся со следующей проблемой. В связи с особенностями работы нашего изделия, нам нужно, чтобы код программы выполнялся из внешнего однократно программируемого ПЗУ (используем 4 микросхемы 1645РТ2У в режиме с 32-разрядной шиной данных). Изначально код отлаживался во внутренней flash памяти и проблем с ним не было. При работе использовался DAC0 с внутренним опорным напряжением. После завершения отладки кода и прошивания микросхем ПЗУ было обнаружено, что при выполнении кода из ПЗУ DAC0 непонятно меняет режим работы. При записи в его регистр значений от 0 до 4095 выходное напряжение меняется от 0.3 до 0.9В. Если этот же код (без каких-либо изменений) выполняется из внутренней flash-памяти, то, как и положено, выходное напряжение меняется от практически нуля до практически 3.3 В. Что может происходить при работе кода из внешней ПЗУ (ITCMLAEN=0)?
Программа настраивает тактовую частоту процессора 96 МГц (пробовали 48, разницы нет). Настройка пина, отвечающего за ЦАП, делается так:

PORT_InitPin(PORTE, PORT_Pin_1, PORT_DIR_OUT, PORT_FUNC_MAIN, PORT_MODE_ANALOG, PORT_PULL_UP_OFF, PORT_PULL_DOWN_OFF, PORT_TRIG_SHM_OFF, PORT_PD_DRIVER, PORT_SPEED_MAXFAST, PORT_GFEN_OFF);

Такая работа ЦАП при ITCMLAEN=0 - это особенность микроконтроллера, или мы что-то делаем не так? Есть подозрение, что первичный загрузчик в масочном ПЗУ микроконтроллера при ITCMLAEN=0 меняет значение каких-либо регистров, прямо или косвенно влияющих на работу ЦАП.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 2019-янв-09 09:13 
Не в сети

Зарегистрирован: 2017-апр-26 14:51
Сообщения: 455
Откуда: ПКК "Миландр"
Здравствуйте.

Есть возможность посмотреть, что происходит под отладчиком при записи в ЦАП?
Начальный загрузчик думаю ни при чем, в обоих случаях (ICTMLAEN = 0/1) он отработает одинаково.
Могу предположить, что внешняя шина выбирает данные как-то иначе, чем это делается из flash памяти. Например, если объявлен массив значений, который читается из ПЗУ и выводится в ЦАП. Проверьте отладчиком, какие данные пишутся в ЦАП.

Если отладчик недоступен, выведите пилу в ЦАП, инкрементируя в коде регистр DATA. Это даст понять проблема в настройке ЦАП, или в выборке данных.

_________________
Отдел технической поддержки support@milandr.ru


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 2019-янв-09 10:45 
Не в сети
Аватара пользователя

Зарегистрирован: 2012-май-05 08:45
Сообщения: 31
Откуда: Москва
Есть возможность пользоваться отладчиком. Данные приходят верные. Они не берутся из ПЗУ, они вычисляются программой по формуле, вычисления делаются правильно.
Подпаял вместо микросхем ПЗУ эмулятор ПЗУ, сейчас могу оперативно без перепайки микросхем отлаживать код, выполняемый из внешней шины. Вывожу для отладки в ЦАП просто разные значения (константы). По прежнему, при записи в регистр 0 выставляется напряжение 0.3 В, при записи 4095 выставляется 0.9 В. Пробовал играть с настройками, пока не помогло.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 2019-янв-09 15:49 
Не в сети

Зарегистрирован: 2017-апр-26 14:51
Сообщения: 455
Откуда: ПКК "Миландр"
А читается из регистра DATA то, что в него было записано?

_________________
Отдел технической поддержки support@milandr.ru


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 2019-янв-09 17:17 
Не в сети
Аватара пользователя

Зарегистрирован: 2012-май-05 08:45
Сообщения: 31
Откуда: Москва
Да, читает правильные значения из регистра. Вот ещё что заметил. Если в режиме отладки поставить breakpoint, то при остановке на нем на выходе ЦАП моментально выводятся правильные напряжения. Но стоит только запустить программу выполняться далее, снова выводятся неверные значения напряжения. Аналогичо, если нажать кнопку stop в дебаггере то тоже выводятся правильные напряжения, а при возобновлении работы программы - неправильные. Используется среда keil mdk 5.12.00, отладчик - j-link.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 2019-янв-09 17:28 
Не в сети

Зарегистрирован: 2014-июн-25 09:29
Сообщения: 119
Осциллографом смотрели не выходе ЦАП? Может там не постоянное напряжение, а какие-нибудь импульсы? Например, из-за срабатывания Watchdog и периодической перезагрузки.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 2019-янв-09 17:34 
Не в сети

Зарегистрирован: 2017-апр-26 14:51
Сообщения: 455
Откуда: ПКК "Миландр"
Похоже на то, что запись в ЦАП происходит слишком часто. ЦАП не отработал еще прошлый вывод, а в него уже записывается что-то следующее. Прерывание исполнения точками останова прерывает непрерывный цикл записи, поэтому ЦАП выставляет то, что в него было записано.
Вставьте задержку после каждого вывода в ЦАП, чтобы проверить это предположение.

Если ситуация не прояснится, то вышлите проект минимального размера на support@milandr.ru. Попробуем посмотреть, в чем причина.

_________________
Отдел технической поддержки support@milandr.ru


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 2019-янв-09 18:24 
Не в сети

Зарегистрирован: 2010-сен-21 12:57
Сообщения: 714
Откуда: г. Санкт-Петербург
При работе с внешней шиной PE[2..0] настраивается как шина адреса A14-A16 поэтому ЦАП в этом режиме и не должен работать.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 2019-янв-09 18:50 
Не в сети
Аватара пользователя

Зарегистрирован: 2012-май-05 08:45
Сообщения: 31
Откуда: Москва
Это точно не связано с частотой записи в ЦАП, данные туда пишутся 1 раз в секунду по прерываниям от таймера. Кроме того, этот же код прекрасно работает из внутренней флеш-памяти.
Пины PE(2...0) не используются в проекте, как адресные и переназначаются программно. Т.е. я правильно понял, что даже при принудительном переназначении функции этих портов они все равно не переназначаются при выполнении кода из внешней шины? Но почему тогда работают правильно при остановке в отладчике?


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 2019-янв-09 19:01 
Не в сети
Аватара пользователя

Зарегистрирован: 2012-май-05 08:45
Сообщения: 31
Откуда: Москва
Следуя такой логике, пины PE[3..4] тоже должны работать, как адресные пины A17 и A18. Но у меня в проекте они перенастраиваются на PWM и прекрасно работают, именно как PWM. Если PE3 и PE4 перенастраиваются на PWM, то почему PE1 не перенастраивается на DAC?


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 2019-янв-10 09:21 
Не в сети

Зарегистрирован: 2017-апр-26 14:51
Сообщения: 455
Откуда: ПКК "Миландр"
vasili писал(а):
При работе с внешней шиной PE[2..0] настраивается как шина адреса A14-A16 поэтому ЦАП в этом режиме и не должен работать.


Василий прав. Описание выводов которые используются для внешней шины в режиме ICTMLAEN=0 находится в спецификации на стр. 16 - "Примечание к таблице 2".
Шина адреса А[16:0] - PF[15:3], PD[15], PE[2:0]. Назначение этих выводов производится аппаратно, независимо от того, что выставлено в настройках пинов. Программное подключение пинов к ЦАП конфликтует с аппаратным подключением пинов к контроллеру внешней шины. В точке останова выборка инструкций из внешней памяти прекращается (не проверял, но судя по всему, линии адреса переходят в 3-е состояние) и поэтому ЦАП выводит таки свое напряжение.

РЕ[4:3] не используются для шины адреса, поэтому ШИМ на них работает.

Не понял только, как удалось развести плату чтобы один выход ушел и на нагрузку ЦАП и на линию адреса. Куда схеме подключен вывод PE1?

vasili, спасибо.

_________________
Отдел технической поддержки support@milandr.ru


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 2019-янв-10 10:01 
Не в сети
Аватара пользователя

Зарегистрирован: 2012-май-05 08:45
Сообщения: 31
Откуда: Москва
Понял, спасибо.
PE1. Используется только как DAC, как линия адреса он не используется.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 2019-янв-10 10:05 
Не в сети
Аватара пользователя

Зарегистрирован: 2012-май-05 08:45
Сообщения: 31
Откуда: Москва
Жалко только, что в описании не написано, что эти пины нельзя перенастроить.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 2019-янв-10 10:47 
Не в сети

Зарегистрирован: 2009-июл-21 14:13
Сообщения: 1375
Откуда: Тула
Vasiliy писал(а):
..."Примечание к таблице 2".
Шина адреса А[16:0] - PF[15:3], PD[15], PE[2:0]. Назначение этих выводов производится аппаратно, независимо от того, что выставлено в настройках пинов. Программное подключение пинов к ЦАП конфликтует с аппаратным подключением пинов к контроллеру внешней шины. ...

Если это действительно так, то стоит это и написать в примечании, а то там написано "по умолчанию".

_________________
сочувствующий…


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 2019-янв-10 10:56 
Не в сети

Зарегистрирован: 2017-фев-14 11:21
Сообщения: 132
Здравствуйте!

Добавим обязательно вместо "по умолчанию" - "аппаратно (программно не модифицируется)".

_________________
Отдел технической поддержки support@milandr.ru


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 2019-янв-10 11:09 
Не в сети
Аватара пользователя

Зарегистрирован: 2012-май-05 08:45
Сообщения: 31
Откуда: Москва
А насколько сложно в следующей ревизии чипа пустить сигналы контроллера внешней шины наружу через общий коммутатор? Чтобы была возможность их переназначать. Вроде бы изменения мизерные, а функционал заметно возрастет.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 2019-янв-10 12:50 
Не в сети

Зарегистрирован: 2017-фев-14 11:21
Сообщения: 132
Новых ревизий 1986ВЕ1Т не планируется. Проект закрыт. Пожелание разработчикам передам (для новых разработок).

_________________
Отдел технической поддержки support@milandr.ru


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 2019-янв-10 13:44 
Не в сети

Зарегистрирован: 2017-апр-26 14:51
Сообщения: 455
Откуда: ПКК "Миландр"
При ресете, когда ITСMLAEN = 0, защелкивается внешняя память с 0-го адреса в адр. пространстве, настраивается контроллер шины и выводы. Все это происходит аппаратно.
Далее настройку выводов на контроллер шины удерживает сигнал ITСMLAEN = 0. Если его переключить на ITСMLAEN = 1, то аппаратное подключение выводов к контроллеру внешней шины отключается.

Есть предложение попробовать следующее:
1 - Вывод ITСMLAEN подтянуть к 0, чтобы при ресете всегда происходил старт с внешней памяти.
2 - В ПО продублировать настройку выводов в функции шины.
3 - Переключить программно ITСMLAEN = 1 перетянув подтягивающий резистор. Это можно сделать каким-нибудь свободным выводом GPIO заведенным на вход ITСMLAEN . При этом аппаратное назначение выводов на шину отключится, а текущее назначение подхватит программная настройка. Т.е. выводы продолжат выполнять функцию шины.
4 - Использовать аналоговые выводы по назначению.

_________________
Отдел технической поддержки support@milandr.ru


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 2019-янв-10 16:23 
Не в сети

Зарегистрирован: 2010-сен-21 12:57
Сообщения: 714
Откуда: г. Санкт-Петербург
Vasiliy писал(а):
Есть предложение попробовать следующее:...

Наверно все это лучше делать, выполняя код из внутренней SRAM 0x20100000- 0x20103fff.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 2019-янв-11 11:35 
Не в сети
Аватара пользователя

Зарегистрирован: 2012-май-05 08:45
Сообщения: 31
Откуда: Москва
Ekaterina писал(а):
Новых ревизий 1986ВЕ1Т не планируется. Проект закрыт. Пожелание разработчикам передам (для новых разработок).

Проект закрыт в смысле, что не будет новых ревизий или в смысле, что вообще 1986ВЕ1Т скоро будет недоступен для закупки?


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 2019-янв-11 11:42 
Не в сети
Аватара пользователя

Зарегистрирован: 2012-май-05 08:45
Сообщения: 31
Откуда: Москва
Vasiliy писал(а):
При ресете, когда ITСMLAEN = 0, защелкивается внешняя память с 0-го адреса в адр. пространстве, настраивается контроллер шины и выводы. Все это происходит аппаратно.
Далее настройку выводов на контроллер шины удерживает сигнал ITСMLAEN = 0. Если его переключить на ITСMLAEN = 1, то аппаратное подключение выводов к контроллеру внешней шины отключается.

Есть предложение попробовать следующее:
1 - Вывод ITСMLAEN подтянуть к 0, чтобы при ресете всегда происходил старт с внешней памяти.
2 - В ПО продублировать настройку выводов в функции шины.
3 - Переключить программно ITСMLAEN = 1 перетянув подтягивающий резистор. Это можно сделать каким-нибудь свободным выводом GPIO заведенным на вход ITСMLAEN . При этом аппаратное назначение выводов на шину отключится, а текущее назначение подхватит программная настройка. Т.е. выводы продолжат выполнять функцию шины.
4 - Использовать аналоговые выводы по назначению.

Да, я тоже об этом думал. У меня в схеме даже предусмотрена RC-цепочка на входе itcmlaen, предполагалось задавать пином gpio нужный логический уровень и затем делать аппаратный сброс. А конденсатор для того, чтобы напряжение на входе itcmlaen не менялось за время сброса. Это планировалось для других целей, но сейчас пин, заведенный на itcmpaen через rc-цепочку, позволит сделать то, что Вы выше описали. Попробую так сделать. Если изменится логический уровень на itcmlaen в процессе выполнения кода из внешней памяти, то при правильно программно настроенных пинах, отвечающих за внешнюю шину, зависания произойти ведь не должно?


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 2019-янв-11 12:35 
Не в сети

Зарегистрирован: 2009-июл-21 14:13
Сообщения: 1375
Откуда: Тула
koe писал(а):
Ekaterina писал(а):
Новых ревизий 1986ВЕ1Т не планируется.
Проект закрыт в смысле, что не будет новых ревизий !

_________________
сочувствующий…


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 2019-янв-11 13:04 
Не в сети

Зарегистрирован: 2009-май-22 09:01
Сообщения: 1299
Откуда: АО "ПКК Миландр"
koe писал(а):
зависания произойти ведь не должно?


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

vasili писал(а):
Наверно все это лучше делать, выполняя код из внутренней SRAM 0x20100000- 0x20103fff.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 2019-янв-11 13:52 
Не в сети
Аватара пользователя

Зарегистрирован: 2012-май-05 08:45
Сообщения: 31
Откуда: Москва
Сходу не получилось, очевидно, из-за того, что при передергивании itcmlaen следующий код операции извлекается из диапазона адресов 0x00000000, а шина мэппится в диапазон 0x00100000. Даже если дернуть ногой itcmlaen из ОЗУ, все равно в стеке будет адрес возврата в диапазоне 0x00000000. Попробую в ПЗУ склеить 2 программы. Одна скопирует себя в ОЗУ, настроит шины, дернет ногой itcmlaen, потом сделает безусловный переход назад в ПЗУ уже по правильному адресу.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 2019-янв-11 18:06 
Не в сети

Зарегистрирован: 2017-апр-26 14:51
Сообщения: 455
Откуда: ПКК "Миландр"
Да, действительно, вариант оказался не рабочий.

Проверил на демо плате, если переключаться с основной памяти на внешнюю, то исполнение продолжается из внутренней без проблем - внутренняя защелкнулась и не портится.
А с внешней так не получается, при переключении с ITCMLAEN = 0 на 1, внешняя память с 0-го адреса портится. При этом в диапазоне внешней шины данные остаются правильные - т.е. контроллер внешней шины работает и читает внешнюю программу правильно.

В качестве выхода попытался скомпилировать программу сразу во внешнюю память начиная с адреса 0х0010_0000, чтобы все вектора и код расположились там. Тогда стартуя с 0-го адреса, считав указатель стека и адрес ResetHandle исполнение должно было уйти на адреса внешнего кода и спокойно там исполняться несмотря на состояние ITCMLAEN. Т.е. ну пусть память с 0-х адресов портится, исполнение при ресете уходит по вектору ResetHandle куда-то в адреса 0х0010_0000 и в 0-е адреса не вернется. НО оказалось, что исполнение остается в 0-х адресах.

Ядро не может сделать такой длинный прыжок с 0-го адреса по вектору в 0х0010_0000. Поэтому необходимо оставить в 0-х адресах функцию с длинным переходом на исходный ResetHandle.

Код:
__Vectors       DCD     __initial_sp              ; Top of Stack
                DCD     Reset_Handler             ; Reset Handler
                DCD     NMI_Handler               ; NMI Handler
                DCD     Reset_Handler_Jump  ; ЗАМЕНА вектора НА функцию дальнего прыжка !!!


Код:
; Функция с прыжком
Reset_Handler_Jump   PROC
                LDR     R0,=Reset_Handler
            BX      R0
                ENDP

; Reset Handler - ИСХОДНЫЙ обработчик
Reset_Handler   PROC
                EXPORT  Reset_Handler         [WEAK]
                IMPORT  __main
                LDR     R0,=__main
            BX      R0
                ENDP


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


Вложения:
Комментарий к файлу: пример
ITCMLAEN_Hack.zip [2.28 КБ]
Скачиваний: 105

_________________
Отдел технической поддержки support@milandr.ru
Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 2019-янв-11 18:18 
Не в сети

Зарегистрирован: 2017-апр-26 14:51
Сообщения: 455
Откуда: ПКК "Миландр"
С векторами похоже перемудрил, в адреса 0х0010_0000 все-таки и так допрыгивает. Файл startup_MDR1986VE1T.s править не нужно.
Достаточно собрать программу начиная с адреса IROM1 = 0x0010_0000.

_________________
Отдел технической поддержки support@milandr.ru


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 2019-янв-14 08:54 
Не в сети

Зарегистрирован: 2017-апр-26 14:51
Сообщения: 455
Откуда: ПКК "Миландр"
Забыл, что вектора прерываний остались в 0-й памяти и сломаются вместе с содержимым памяти. А таблицу прерываний в этом МК не перенести.
Вариант с программой в адресах 0x0010_0000 отпадает.

_________________
Отдел технической поддержки support@milandr.ru


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 2019-янв-16 14:10 
Не в сети

Зарегистрирован: 2017-апр-26 14:51
Сообщения: 455
Откуда: ПКК "Миландр"
Посмотрели подробнее логику работы ITCMLAEN. При переключении ITCMLAEN = 1 не будет формироваться сигнал выбора внешней шины для адресного пространства 0x00000000-0x000FFFFF. Все что можно сделать - это запускать программу с адресов внешней шины, но тогда теряются вектора прерываний.

_________________
Отдел технической поддержки support@milandr.ru


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 2019-янв-16 15:15 
Не в сети

Зарегистрирован: 2014-июн-25 09:29
Сообщения: 119
Может быть такая идея поможет решить проблему.
Для МК с ядрами Cortex M0 и M1, не имеющими перемещаемой таблицы векторов прерываний, размещение проекта по произвольному адресу выполняю так (пример заточен под 1986ВЕ4У):
1. Создаю основной проект, в котором для всех используемых прерываний формирую функцию-обработчик и функцию, реализующую обработку. Функция-обработчик вызывает функцию, реализующую обработку, с помощью указателя на функцию, расположенному в ОЗУ по фиксированному адресу:
Код:
// **** Модуль MDR32F9Qx_it.c основного проекта ****

// Таблица указателей на функции, реализующие обработчики прерываний
// В каждом из проектов, имеющихся на камне, эта таблица лежит по одному и тому же адресу
void  (* IT_Table [128])(void) __attribute__((at(0x20003000)));

// Обработчик для прерывания от DMA
void DMA_IRQHandler (void)
{
   // Вызов функции, реализующей обработчик
   (*IT_Table[IT_DMA_IRQ_HANDLER_IDX])();   
}

void DMA_IRQHandler_Implement (void)
{
   // Требуемый код
   // ...
}

2. В функции main основного проекта в самом начале заполняю массив указателей на функции, реализующую обработку:

Код:
// **** Модуль MDR32F9Qx_it.c основного проекта ****
// Индексы функций-обработчиков
enum
{
  IT_UART1_IRQ_HANDLER_IDX = 0,
  IT_DMA_IRQ_HANDLER_IDX,
  IT_TIMER1_IRQ_HANDLER_IDX,

  IT_IRQ_HANDLER_COUNT
};

// Инициализация таблицы векторов-прерываний
void IT_Init (void)
{
   // Сопоставление функций, реализующих обработчики прерываний
   IT_Table[IT_UART1_IRQ_HANDLER_IDX]    = UART1_IRQHandler_Implement;
   IT_Table[IT_DMA_IRQ_HANDLER_IDX]      = DMA_IRQHandler_Implement;
   IT_Table[IT_TIMER1_IRQ_HANDLER_IDX]   = TIMER1_IRQHandler_Implement;
   // ...
}


// **** Модуль main.c основного проекта ****
// Точка входа
int main (void)
{
  // Инициализация таблицы векторов-прерываний
  IT_Init ();

  // Все, что нужно в проекте
  // ...
}


3. Формирую в основном проекте scatter-файл, позволяющий разместить секцию кода по требуемому адресу. В данном случае - по адресу 0x0000c000:
Код:
; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************

LR_IROM1 0x0000C000 0x0000C000  {    ; load region size_region
  ER_IROM1 0x0000C000   {  ; load address = execution address

  *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
  }
  RW_IRAM1 0x20000000 0x00003c00  {  ; RW data
   .ANY (+RW +ZI)
  }
}


4. Заливаю проект. Но работать он пока не будет!
5. Формирую вспомогательный проект. В нем также создаю массив указателей на функции, реализующие обработчики прерываний, и требуемые обработчики прерываний:
Код:
// **** Модуль MDR32F9Qx_it.c вспомогательного проекта ****

// Таблица указателей на функции, реализующие обработчики прерываний
// В каждом из проектов, имеющихся на камне, эта таблица лежит по одному и тому же адресу
void  (* IT_Table [128])(void) __attribute__((at(0x20003000)));

// Индексы функций-обработчиков
enum
{
  IT_UART1_IRQ_HANDLER_IDX = 0,
  IT_DMA_IRQ_HANDLER_IDX,
  IT_TIMER1_IRQ_HANDLER_IDX,

  IT_IRQ_HANDLER_COUNT
};

// Обработчик для прерывания от DMA
void DMA_IRQHandler (void)
{
   // Вызов функции, реализующей обработчик
   (*IT_Table[IT_DMA_IRQ_HANDLER_IDX])();   
}



6. Функция main вспомогательного проекта будет фиктивной:

Код:
// **** Модуль main.c вспомогательного проекта ****
// Этот код никогда не выполнится
int main (void)
{
  while (1)
  {}
}


7. В модуле system_MDR1986ВЕ4.c вспомогательного проекта делаем вызов Reset_Handler основного проекта:

Код:
// **** Модуль system_MDR1986ВЕ4.c вспомогательного проекта ****
void SystemInit (void)
{
   // Указатель на функцию для вызова основного проекта
   void (*Main_Project) (void);
   volatile uint32_t* addr;
   volatile uint32_t value;      

   // Начальный адрес основного проекта
  uint32_t Main_Project_Start_Addr = 0x0000C000;   

   // Это все было изначально
   /* Reset the RST clock configuration to the default reset state */
  /* Reset all clock but RST_CLK & BKP_CLC bits */
  MDR_RST_CLK->PER1_CLOCK   = (uint32_t)0x30;
  MDR_RST_CLK->PER2_CLOCK   = (uint32_t)0x1010;

  /* Reset CPU_CLOCK bits */
  MDR_RST_CLK->CPU_CLOCK   &= (uint32_t)0x00000000;

  /* Reset PLL_CONTROL bits */
  MDR_RST_CLK->PLL_CONTROL &= (uint32_t)0x00000000;

  /* Reset HSEON and HSEBYP bits */
  MDR_RST_CLK->HS_CONTROL  &= (uint32_t)0x00000000;

  /* Reset ADC_MCO_CLOCK bits */
  MDR_RST_CLK->ADC_MCO_CLOCK   &= (uint32_t)0x00000000;

  SystemCoreClockUpdate();
   
   
   // В начале прошивки основного проекта располагается стандартная таблица векторов прерываний __Vectors  (определена в startup_MDR1986ВЕ4.s)
   // В ее 1-м элементе располагается адрес функции-обработчика Reset_Handler, с которой начинается выполнение программы
   addr = (uint32_t*)Main_Project_Start_Addr; addr++;
   Main_Project = (void (*)(void)) (*addr);
   
   value = *addr;         

        // Запустить программу основного проекта, если она есть, вызвав ее Reset_Handler
   if (value != 0xFFFFFFFF &&
         value != 0x00000000 &&
         value != 0xAAAAAAAA
       )
   {
      (*Main_Project)();

      // Сюда уже не попадем
   }

   // Подвиснем тут, если основного проекта нет
  while (1)
   {
   
   }      
}


Отсюда и начнется выполнение основного проекта, а вспомогательный проект так и не запустит свою функцию main.
Когда возникнет прерывание, сработает функция-обработчик из вспомогательного проекта и вызовет функцию, реализующую обработчик прерываний, из основного проекта. Конечно, обработка прерываний несколько замедлится из-за дополнительного вызова функции и возврата из нее. Но это всё равно лучше, чем ничего.
8. Заливаем вспомогательный проект в МК, предварительно выключив полное стирание Flash-памяти МК, и основной проект заработает.
При таком подходе не получится использовать режим отладки основного проекта. Но можно сначала его отладить, разместив по 0-му адресу, а потом уже отлаженный заливать по иному адресу.
Этот же подход можно с успехом применять для размещения нескольких проектов в одном камне, что я обычно и делаю.
PS:
Не забудьте сделать копию файла system_MDR1986ВЕ4.c, поместив ее в каталог с остальными исходниками проекта вспомогательного проекта. Иначе внесенные в этот файл изменения задействуются в других проектах.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 2019-май-14 08:34 
Не в сети
Аватара пользователя

Зарегистрирован: 2019-янв-15 10:14
Сообщения: 33
Откуда: г. Орел
Не проще ли ram функции использовать для этого, если функция находится по фиксируемому адресу в ОЗУ?
Секцию определить в ОЗУ для ram функций и принудительно их там размещать. Компилятор в таблицу векторов нужный адрес встроит. Или же нужно налету менять вектор?...


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 2019-май-14 13:32 
Не в сети

Зарегистрирован: 2014-июн-25 09:29
Сообщения: 119
Shrek
Так-то да, но недостатки будут следующие:
1. Придется отдельную RAM-секцию делать для каждой функции-обработчика прерываний, ибо нельзя, по крайней мере, в Keil расположить Си-функцию по фиксированному адресу.
2. Будут дополнительные затраты RAM, что не очень хорошо для маленьких камней.
3. Код из RAM, как не странно, может выполняться медленнее, чем из FLASH.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 2019-май-14 14:51 
Не в сети
Аватара пользователя

Зарегистрирован: 2019-янв-15 10:14
Сообщения: 33
Откуда: г. Орел
andelie писал(а):
Shrek
Так-то да, но недостатки будут следующие:
1. Придется отдельную RAM-секцию делать для каждой функции-обработчика прерываний, ибо нельзя, по крайней мере, в Keil расположить Си-функцию по фиксированному адресу.
2. Будут дополнительные затраты RAM, что не очень хорошо для маленьких камней.
3. Код из RAM, как не странно, может выполняться медленнее, чем из FLASH.


3. :shock: при длинных переходах нет задержек на чтение как из FLASH
2. Так-то да. Оптимизация должна как то помочь.
1. За keil не скажу для GCC нужна одна секция и линковщик сам функции размещает в секции. Надо всего лишь весь сегмент выгрузить из FLASH в RAM.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 2019-май-14 15:31 
Не в сети

Зарегистрирован: 2014-июн-25 09:29
Сообщения: 119
3. Для 1986ВЕ4 еще не пробовал, но на 1986ВЕ92 код из RAM выполняется реально медленней, чем из Flash. Специально проверял.
1.
Цитата:
За keil не скажу для GCC нужна одна секция и линковщик сам функции размещает в секции. Надо всего лишь весь сегмент выгрузить из FLASH в RAM.

А как указать конкретный абсолютный адрес, по которому будет располагаться функция-обработчик прерывания в каждом из проектов? Если функция в секции всего одна, то все просто, но если функций в секции несколько, то так уже не получается.
2. Возможности оптимизации будем считать исчерпанными :) .


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 2019-май-14 16:40 
Не в сети
Аватара пользователя

Зарегистрирован: 2019-янв-15 10:14
Сообщения: 33
Откуда: г. Орел
andelie писал(а):
3. Для 1986ВЕ4 еще не пробовал, но на 1986ВЕ92 код из RAM выполняется реально медленней, чем из Flash. Специально проверял.
1.
Цитата:
За keil не скажу для GCC нужна одна секция и линковщик сам функции размещает в секции. Надо всего лишь весь сегмент выгрузить из FLASH в RAM.

А как указать конкретный абсолютный адрес, по которому будет располагаться функция-обработчик прерывания в каждом из проектов? Если функция в секции всего одна, то все просто, но если функций в секции несколько, то так уже не получается.
2. Возможности оптимизации будем считать исчерпанными :) .


Линковщик сам переопределяет адреса функций в таблице векторов прерываний. Чтобы таблица векторов была полной ее заполняют функциями заглушками которые при вызове прерывания не вызывают Hard Fault. Эти функции "заглушки" имеют атрибут "weak" то есть могут быть переопределены в другом месте. При переопределении линковщик сам подменяет нужный адрес.

Код:
#ifndef __VECTORS_H__
#define __VECTORS_H__

//------------------------------------------------------------------------------
// Declaration
//------------------------------------------------------------------------------
void LowLevelInit (void);
void Fault_Handler (void);
extern void main (void);
extern unsigned int _etext, _data, _sstack, DATA_SIZE;

//------------------------------------------------------------------------------
// Weaked Functions
//------------------------------------------------------------------------------
__attribute__ ((weak)) extern void NMI_Handler (void) {while(1);}
__attribute__ ((weak)) extern void Default_Handler (void) {while(1);}
void SVC_Handler (void)   __attribute__ ((naked, weak, alias("Default_Handler")));
void PSV_Handler (void)   __attribute__ ((naked, weak, alias("Default_Handler")));
void SYS_Handler (void)   __attribute__ ((naked, weak, alias("Default_Handler")));

void DMA_Handler (void)         __attribute__ ((naked, weak, alias("Default_Handler")));
void UART1_Handler (void)      __attribute__ ((naked, weak, alias("Default_Handler")));
void UART2_Handler (void)      __attribute__ ((naked, weak, alias("Default_Handler")));
void SSP1_Handler (void)      __attribute__ ((naked, weak, alias("Default_Handler")));
void PWR_Handler (void)         __attribute__ ((naked, weak, alias("Default_Handler")));
void WWDG_Handler (void)      __attribute__ ((naked, weak, alias("Default_Handler")));
void Timer0_Handler (void)      __attribute__ ((naked, weak, alias("Default_Handler")));
void Timer1_Handler (void)      __attribute__ ((naked, weak, alias("Default_Handler")));
void ADC_Handler (void)         __attribute__ ((naked, weak, alias("Default_Handler")));
void CMP_Handler (void)         __attribute__ ((naked, weak, alias("Default_Handler")));
void BKP_Handler (void)         __attribute__ ((naked, weak, alias("Default_Handler")));
void EXT_INT1_Handler (void)   __attribute__ ((naked, weak, alias("Default_Handler")));
void EXT_INT2_Handler (void)   __attribute__ ((naked, weak, alias("Default_Handler")));
void EXT_INT3_Handler (void)   __attribute__ ((naked, weak, alias("Default_Handler")));
void ADCIU_CH1_Handler (void)   __attribute__ ((naked, weak, alias("Default_Handler")));
void ADCIU_CH2_Handler (void)   __attribute__ ((naked, weak, alias("Default_Handler")));
void ADCIU_CH3_Handler (void)   __attribute__ ((naked, weak, alias("Default_Handler")));
void ADCIU_CH4_Handler (void)   __attribute__ ((naked, weak, alias("Default_Handler")));
void ADCIU_CH5_Handler (void)   __attribute__ ((naked, weak, alias("Default_Handler")));
void ADCIU_CH6_Handler (void)   __attribute__ ((naked, weak, alias("Default_Handler")));
void ADCIU_CH7_Handler (void)   __attribute__ ((naked, weak, alias("Default_Handler")));
void ADCIU_CH8_Handler (void)   __attribute__ ((naked, weak, alias("Default_Handler")));
void ADCIU_Handler (void)      __attribute__ ((naked, weak, alias("Default_Handler")));

//------------------------------------------------------------------------------
// Global Const
//------------------------------------------------------------------------------

static void (* vTable[]) (void) __attribute__ ((used, section(".vectors"))) = {
   (void (*)(void)) &_sstack,   /* Top of Stack */
   LowLevelInit,         /* LowLevelInit */
   NMI_Handler,         /* NMI Handler */
   Fault_Handler,         /* Hard Fault Handler */
   0, 0, 0, 0, 0, 0, 0,   /* Reserved */
   SVC_Handler,         /* SVCall Handler */
   0, 0,               /* Reserved */
   PSV_Handler,         /* PendSV Handler */
   SYS_Handler,         /* SysTick Handler */

   DMA_Handler,         /* IRQ0 DMA */
   UART1_Handler,         /* IRQ1 UART1 */
   UART2_Handler,         /* IRQ2 UART2 */
   SSP1_Handler,         /* IRQ3 SSP1 */
   PWR_Handler,         /* IRQ4 PWR */
   WWDG_Handler,         /* IRQ5 WWDG */
   Timer0_Handler,         /* IRQ6 Timer0 */
   Timer1_Handler,         /* IRQ7 Timer1 */
   ADC_Handler,         /* IRQ8 ADC */
   CMP_Handler,         /* IRQ9 CMP */
   BKP_Handler,         /* IRQ10 BKP */
   EXT_INT1_Handler,      /* IRQ11 EXT_INT1 */
   EXT_INT2_Handler,      /* IRQ12 EXT_INT2 */
   EXT_INT3_Handler,      /* IRQ13 EXT_INT3 */
   ADCIU_CH1_Handler,      /* IRQ14 ADCIU_CH1 */
   ADCIU_CH2_Handler,      /* IRQ15 ADCIU_CH2 */
   ADCIU_CH3_Handler,      /* IRQ16 ADCIU_CH3 */
   ADCIU_CH4_Handler,      /* IRQ17 ADCIU_CH4 */
   ADCIU_CH5_Handler,      /* IRQ18 ADCIU_CH5 */
   ADCIU_CH6_Handler,      /* IRQ19 ADCIU_CH6 */
   ADCIU_CH7_Handler,      /* IRQ20 ADCIU_CH7 */
   ADCIU_CH8_Handler,      /* IRQ21 ADCIU_CH8 */
   ADCIU_Handler,         /* IRQ22 ADCIU */
   };

#endif


И в каком либо месте кода обработчики прерываний к примеру:
Код:
//------------------------------------------------------------------------------
// IRQ functions
//------------------------------------------------------------------------------
__attribute__ ((section(".ramfunc"), aligned(8))) void Timer0_Handler (void) {
   bitCount = 847; pxFM -> N++; TIMER0_BASE -> STATUS &= ~TIMER_STATUS_ETR_RE_EVENT; printf ("\r\nSerial num %u", pxFM -> N);
}

__attribute__ ((section(".ramfunc"), aligned(8))) void Timer1_Handler (void) {
   if (TIMER1_BASE -> STATUS & TIMER_STATUS_ETR_RE_EVENT) {PORTB_BASE -> RXTX |= PORT_PB12 & bitOut; bitOut = 0; TIMER1_BASE -> STATUS &= ~TIMER_STATUS_ETR_RE_EVENT;}
   else {
      PORTB_BASE -> RXTX &= ~PORT_PB12; TIMER1_BASE -> STATUS &= ~TIMER_STATUS_ETR_FE_EVENT;
      if (bitCount >= 0) {if (bitCount < 832) {if (pxFM -> byte[bitCount >> 3] & (1 << (bitCount & 0x7))) bitOut = PORT_PB12;} bitCount--;}
      }
}

__attribute__ ((section(".ramfunc"), aligned(8))) void DMA_Handler (void) {
   unsigned int temp, max, min;
   printf ("\r\nDMA_ADCIU:", NULL);
   if (DMAPriCtrlBlock[DMA_ID_ADCIU0].cycle_ctrl == DMA_Mode_Stop) {DMAPriCtrlBlock[DMA_ID_ADCIU0].Cfg = ADCUI0_DMA_Cfg; DMA_ChCfg (DMA_ID_ADCIU0, DMA_CHNL_EN); temp = 17; while (temp--) ADCUI0_DATA[temp] ^= 1 << 23; temp = 16; min = max = ADCUI0_DATA[temp]; while (temp--) {if (min > ADCUI0_DATA[temp]) min = ADCUI0_DATA[temp]; if (max < ADCUI0_DATA[temp]) max = ADCUI0_DATA[temp];} printf ("\t%u", max - min);}
   if (DMAPriCtrlBlock[DMA_ID_ADCIU1].cycle_ctrl == DMA_Mode_Stop) {DMAPriCtrlBlock[DMA_ID_ADCIU1].Cfg = ADCUI0_DMA_Cfg; DMA_ChCfg (DMA_ID_ADCIU1, DMA_CHNL_EN); temp = 17; while (temp--) ADCUI1_DATA[temp] ^= 1 << 23; temp = 16; min = max = ADCUI1_DATA[temp]; while (temp--) {if (min > ADCUI1_DATA[temp]) min = ADCUI1_DATA[temp]; if (max < ADCUI1_DATA[temp]) max = ADCUI1_DATA[temp];} printf ("\t%u", max - min);}
   if (DMAPriCtrlBlock[DMA_ID_ADCIU3].cycle_ctrl == DMA_Mode_Stop) {DMAPriCtrlBlock[DMA_ID_ADCIU3].Cfg = ADCUI0_DMA_Cfg; DMA_ChCfg (DMA_ID_ADCIU3, DMA_CHNL_EN); temp = 17; while (temp--) ADCUI3_DATA[temp] ^= 1 << 23; temp = 16; min = max = ADCUI3_DATA[temp]; while (temp--) {if (min > ADCUI3_DATA[temp]) min = ADCUI3_DATA[temp]; if (max < ADCUI3_DATA[temp]) max = ADCUI3_DATA[temp];} printf ("\t%u", max - min);}
   if (DMAPriCtrlBlock[DMA_ID_ADCIU4].cycle_ctrl == DMA_Mode_Stop) {DMAPriCtrlBlock[DMA_ID_ADCIU4].Cfg = ADCUI0_DMA_Cfg; DMA_ChCfg (DMA_ID_ADCIU4, DMA_CHNL_EN); temp = 17; while (temp--) ADCUI4_DATA[temp] ^= 1 << 23; temp = 16; min = max = ADCUI4_DATA[temp]; while (temp--) {if (min > ADCUI4_DATA[temp]) min = ADCUI4_DATA[temp]; if (max < ADCUI4_DATA[temp]) max = ADCUI4_DATA[temp];} printf ("\t%u", max - min);}
   if (DMAPriCtrlBlock[DMA_ID_ADCIU6].cycle_ctrl == DMA_Mode_Stop) {DMAPriCtrlBlock[DMA_ID_ADCIU6].Cfg = ADCUI0_DMA_Cfg; DMA_ChCfg (DMA_ID_ADCIU6, DMA_CHNL_EN); temp = 17; while (temp--) ADCUI6_DATA[temp] ^= 1 << 23; temp = 16; min = max = ADCUI6_DATA[temp]; while (temp--) {if (min > ADCUI6_DATA[temp]) min = ADCUI6_DATA[temp]; if (max < ADCUI6_DATA[temp]) max = ADCUI6_DATA[temp];} printf ("\t%u", max - min);}
   if (DMAPriCtrlBlock[DMA_ID_ADCIU7].cycle_ctrl == DMA_Mode_Stop) {DMAPriCtrlBlock[DMA_ID_ADCIU7].Cfg = ADCUI0_DMA_Cfg; DMA_ChCfg (DMA_ID_ADCIU7, DMA_CHNL_EN); temp = 17; while (temp--) ADCUI7_DATA[temp] ^= 1 << 23; temp = 16; min = max = ADCUI7_DATA[temp]; while (temp--) {if (min > ADCUI7_DATA[temp]) min = ADCUI7_DATA[temp]; if (max < ADCUI7_DATA[temp]) max = ADCUI7_DATA[temp];} printf ("\t%u", max - min);}
   }


ld файл:
Код:
ENTRY(LowLevelInit)

STACK_SIZE = 0x200;

MEMORY
{
   FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x20000
   RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x4000
}

SECTIONS
{
   .vectors ORIGIN(FLASH) : {
      KEEP(*(.vectors));
   } > FLASH
   
   .text : {
      . = ALIGN(4); PROVIDE(_text = .);
      *(.text*);
      *(.rodata*);
   } > FLASH
   
   . = ALIGN(4); PROVIDE(_etext = .);
   
   .dma ORIGIN(RAM) : {
      *(.dma*);
    } > RAM
   
   .data : AT (_etext) {
      . = ALIGN(4); PROVIDE(_data = .);
      *(.ramfunc*);
        *(.data*);
    } > RAM
   
   DATA_SIZE = SIZEOF(.data);
   
   .rtt : {
      . = ALIGN(4); PROVIDE(_rtt = .);
      *(.rtt*);
   } > RAM
   
   .bss (NOLOAD) : {
      . = ALIGN(4); PROVIDE(_bss = .);
      *(.bss);
      *(.bss*);
      *(COMMON);
   } > RAM
   
   .heap (NOLOAD): {
        . = ALIGN(4); PROVIDE(_heap = .);
        . = (ORIGIN(RAM) + LENGTH(RAM) - STACK_SIZE);
    } > RAM
   
    HEAP_SIZE = SIZEOF(.heap);
   
   .stack (NOLOAD) : {
      . = (ORIGIN(RAM) + LENGTH(RAM) - STACK_SIZE); . = ALIGN(4); PROVIDE(_estack = .);
      . = (ORIGIN(RAM) + LENGTH(RAM)); PROVIDE(_sstack = .);
   } > RAM
}


Загрузка всех рамфункций из флэш в рам:
Код:
   unsigned int temp = (unsigned int) &DATA_SIZE;
   unsigned int *dst = &_data; unsigned int *src = &_etext;
   temp >>= 2;   while (temp--) *dst++ = *src++;


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 2019-май-14 18:34 
Не в сети

Зарегистрирован: 2014-июн-25 09:29
Сообщения: 119
Похоже, мы немного о разных вещах говорим. Я имею в виду, что в камень заливается два или более проекта, в каждом из которых нужно работать с одними и теми же прерываниями, но обработчики у них будут в каждом из проектов разными, в том числе, с кодом разного размера. Как же линковщик, которому было указание поместить все функции-обработчики прерываний в один RAM-модуль, сообразит, что каждая из этих функций должна будет размещена по одинаковому адресу в каждом из нескольких проектов?


Вернуться к началу
 Профиль  
 
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 35 ] 

Часовой пояс: UTC + 3 часа


Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 2


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Перейти:  
Powered by phpBB® Forum Software © phpBB Group
Русская поддержка phpBB