Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: C++
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > Cредства разработки для МК > IAR
Aprox
Расскажу один назидательный случай, который случился со мной в освоении и отладке программ на С++. После того, как разобрался с помощью гуру, как разместить константы во Flash, а нев SRAM, со мной случилась другая проблема, связанная с загрузкой и JTAJ отладкой. Когда программа на С++ блистала своей красотой и лаконичностью, когда все прекрасно показывал симулятор IAR, настала пора грузить ее в кристалл. И вот тут-то началось! Отладчик JTAG упорно не хотел добегать до метки main, программа ускакивала в ловушку на векторе UndefinedHandler. Опускаю процесс поисков, сразу к сути. Оказывается, нельзя ни в коем случае в глобальных объектах заниматься инициализацией периферии кристалла. Вообще касаться периферии нельзя! Резонов два: -1. порядок вызовов конструкторов не определен, и -2 при JTAG отладке ARM после загрузки FLASH успевает промолотить еще достаточное количество инструкций прежде, чем сработает break по RESET и сдернет программу обратно в address_reset. Последнее меня и погубило. Дело в том, что я записал в конструктор куски инициализации Flash, а отладчик сдергивал кристалл как раз в момент этой инициализации. Возвращался по Reset он уже в испорченное содержимое сегмента-векторов. Победить удалось следующим приемом- из конструкторов глобальных объектов убрал любое обращение к периферии кристалла. Вместо этого, ввел в классы с периферией методы инициализации периферии типа obj.Start() с вызовом этих методов после входа в main(). Мне кажется, пережитые мучения пойдут на пользу всем, кто хочет переползти с С на С++.
sergeeff
Цитата(Aprox @ Mar 14 2011, 22:42) *
Расскажу один назидательный случай


Если вы внимательно почитаете материалы форума, то про найденные вами "грабли" давно и внятно все было изложено.

Вам два совета:
- хороший тон - инициализация железа до входа в main();
- если уж хочется железо инициализировать в классах, никто не мешает объявить глобальные указатели на объекты, а в какой-либо low_level_init написать, что-нибудь типа:

Usart const *pUsart1;
....

void low_level_init(void)
{
pUsart1 = new Usart(1, 19200, 8, 1, 0); // пример чисто условный.
...
}

Есть еще варианты с singleton'ами, гарантирующими однократный вызов конструктора объекта.
Aprox
Цитата(sergeeff @ Mar 14 2011, 22:11) *
Вам два совета:
- хороший тон - инициализация железа до входа в main();
Почему? Я вспоминаю примеры профессиональных устройств c UCOS и С++, где задачи и железо при них создаются уже после входа в main(). Например, Ethernet модули фирмы NetBurner.
Цитата
- если уж хочется железо инициализировать в классах, никто не мешает объявить глобальные указатели на объекты, а в какой-либо low_level_init написать, что-нибудь типа:

Usart const *pUsart1;
....

void low_level_init(void)
{
pUsart1 = new Usart(1, 19200, 8, 1, 0); // пример чисто условный.
...
}

Есть еще варианты с singleton'ами, гарантирующими однократный вызов конструктора объекта.
Я пробовал с low_level_init. Для JTAG отладки программ ARM-ов - не годится. Проблема в том, что для JTAG отладки периферии требуется создать глобальные переменные-указатели на SFR-регистры. Иначе отладчик просто не видит периферию. По крайней мере, у меня с отладкой на кристаллах STR91xx именно так. И если в low_level_init произвести инициализацию этих указателей, то следующая за этим инициализация сегментов просто их стирает. В результате, отладчик улетает неизвестно куда.

Я по-прежнему считаю, что самый простой и надежный прием написания программ на C++ для ARMов- это вообще не касаться периферии до входа в main().


Сергей Борщ
Вставьте в low_level_init задержку чтобы отладчик успел остановить проц во время исполнения этой задержки.
QUOTE (Aprox @ Mar 15 2011, 10:26) *
Я пробовал с low_level_init. Для JTAG отладки программ ARM-ов - не годится. Проблема в том, что для JTAG отладки периферии требуется создать глобальные переменные-указатели на SFR-регистры. Иначе отладчик просто не видит периферию. По крайней мере, у меня с отладкой на кристаллах STR91xx именно так. И если в low_level_init произвести инициализацию этих указателей, то следующая за этим инициализация сегментов просто их стирает. В результате, отладчик улетает неизвестно куда.
Да ну, ерунда какая-то. Если речь идет об IAR. С STR91x не работал, а 71х прекрасно отлаживался без всяких указателей. И с чего бы отладчику куда-то улетать из-за неправильных указателей. Ну хотите указатели - инициализируйте их в main(). Периферия-то при чем?

QUOTE
Я по-прежнему считаю, что самый простой и надежный прием написания программ на C++ для ARMов- это вообще не касаться периферии до входа в main().
Ага, особенно если до входа в main инициализируются глобальные переменные, которые находятся во внешней S(D)RAM или создаются глобальные объекты, которые используют S(D)RAM, а она еще не включена.
Andy Mozzhevilov
Цитата(Aprox @ Mar 15 2011, 11:26) *
Я пробовал с low_level_init. Для JTAG отладки программ ARM-ов - не годится. Проблема в том, что для JTAG отладки периферии требуется создать глобальные переменные-указатели на SFR-регистры.

Создать именно в программе? Зачем там лишние сущности? Подключаете ddf файл для вашего контроллера, и смотрите всю периферию.
Цитата
Иначе отладчик просто не видит периферию. По крайней мере, у меня с отладкой на кристаллах STR91xx именно так.

По моему вы придумали для себя проблему, а потом ее решили, причем далеко не лучшим способом.
Во всяком случае сейчас посмотрел установленный у себя IAR и тут:
C:\Program Files\IAR Systems\Embedded Workbench 6.0\arm\config\debugger\ST\
находятся ddf файлы в том числе для STR91xx
Причем это все автоматически подключается, если вы в качестве целевого контроллера выберите именно ваш тип контроллера, а не generiс типа ARM7 или ARM9

Цитата
И если в low_level_init произвести инициализацию этих указателей, то следующая за этим инициализация сегментов просто их стирает. В результате, отладчик улетает неизвестно куда.

При желании и эту проблему можно обойти, но в вашем случае она надумана.

Цитата
Я по-прежнему считаю, что самый простой и надежный прием написания программ на C++ для ARMов- это вообще не касаться периферии до входа в main().

Это только один из способов, причем не факт, что самый надежный. Вы лишаете себя возможности создавать глобальные объекты классов с использованием периферии, при этом вручную вынуждены в определенной последовательности вызывать функции дополнительной инициализации объектов из main или откуда то еще, то есть совершаете дополнительное действие, которое можно забыть сделать.
Aprox
Цитата(Сергей Борщ @ Mar 15 2011, 13:04) *
Вставьте в low_level_init задержку чтобы отладчик успел остановить проц во время исполнения этой задержки.
А в боевом варианте задержку вручную потом убирать?
Цитата
Да ну, ерунда какая-то. Если речь идет об IAR. С STR91x не работал, а 71х прекрасно отлаживался без всяких указателей. И с чего бы отладчику куда-то улетать из-за неправильных указателей. Ну хотите указатели - инициализируйте их в main(). Периферия-то при чем?
Кристаллы STR91xx имеют уже готовую фирменную библиотеку драйверов периферийных устройств. Библиотека написана на C и требует запуска инициализации собственных внутренних переменных. Как понимаете, вызывать инициализацию такой библиотеки в low_level_init нельзя, потому что следующая за этим инициализация сегментов данных основной программы затрет внутренние переменные слинкованной библиотеки. Поведение программы становится непредсказуемым. Единственный выход- заниматься периферией только после входа в main().
Цитата
Ага, особенно если до входа в main инициализируются глобальные переменные, которые находятся во внешней S(D)RAM или создаются глобальные объекты, которые используют S(D)RAM, а она еще не включена.
Зачем нарываться на приключения? Создавайте объекты, взаимодействующие с периферией, во внутренней SRAM кристалла. Этой SRAM вполне достаточно.


Цитата(Andy Mozzhevilov @ Mar 15 2011, 13:10) *
Создать именно в программе? Зачем там лишние сущности? Подключаете ddf файл для вашего контроллера, и смотрите всю периферию.
По моему вы придумали для себя проблему, а потом ее решили, причем далеко не лучшим способом.
Во всяком случае сейчас посмотрел установленный у себя IAR и тут:
C:\Program Files\IAR Systems\Embedded Workbench 6.0\arm\config\debugger\ST\
находятся ddf файлы в том числе для STR91xx
Причем это все автоматически подключается, если вы в качестве целевого контроллера выберите именно ваш тип контроллера, а не generiс типа ARM7 или ARM9
Я работаю с IAR v4.30A. Там нет ddf заготовок для STR91xx. Использую установку Taget =ARM9663-E. Предложения типа перейти на новые версии IAR пока не рассматриваю, поскольку создана инфраструктура разработки, привязанная типом отладчика именно к указанной версии. Иными словами придется менять и отладчик, поскольку старый не понимает отладочной информации новых версий.
Цитата
Это только один из способов, причем не факт, что самый надежный. Вы лишаете себя возможности создавать глобальные объекты классов с использованием периферии, при этом вручную вынуждены в определенной последовательности вызывать функции дополнительной инициализации объектов из main или откуда то еще, то есть совершаете дополнительное действие, которое можно забыть сделать.
Я так не считаю. В моем варианте объекты могут использовать периферию. Надо только в этих объектах предусмотреть метод Старт_Периферии() и вызывать его сразу по входу в main(). Поскольку этот метод есть неотъемлемая часть класса, то забыть чего-либо в периферии вряд ли получиться. Кроме того, это пожалуй единственный прием работать с готовыми библиотеками драйверов периферии, написанными на C.
Andy Mozzhevilov
Цитата(Aprox @ Mar 15 2011, 14:20) *
Я работаю с IAR v4.30A.

Сочувствую.

Цитата
Там нет ddf заготовок для STR91xx.

Это текстовый файл, его можно сделать самостоятельно. А возможно он просто подойдет от более свежих версий.

Цитата
Предложения типа перейти на новые версии IAR пока не рассматриваю, поскольку создана инфраструктура разработки, привязанная типом отладчика именно к указанной версии. Иными словами придется менять и отладчик, поскольку старый не понимает отладочной информации новых версий.

Как то печально совсем у вас все. Что за такая уникальная инфраструктура, которая работает только с одной версией отладчика? Вы себя вяжете сами по рукам и ногам, работая на версии 4-летней давности.

Цитата(Aprox @ Mar 15 2011, 14:20) *
Библиотека написана на C и требует запуска инициализации собственных внутренних переменных. Как понимаете, вызывать инициализацию такой библиотеки в low_level_init нельзя, потому что следующая за этим инициализация сегментов данных основной программы затрет внутренние переменные слинкованной библиотеки.

Для этого есть такой способ для low_level_init():
1. Зайти в low_level_init
2. Вызвать из нее функцию статической инициализации iar
3. Вызвать функцию инициализации библиотек периферии
4. Выйти из low_level_init с кодом 0 - return 0;
Код возврата 0 означает, что startup должен пропустить статическую инициализацию (во всяком случае до версии IAR 5.50.5)
После этого вы в конструкторах глобальных объектах получаете доступ к уже проинициализированным библиотекам.

Сергей Борщ
QUOTE (Aprox @ Mar 15 2011, 13:20) *
А в боевом варианте задержку вручную потом убирать?
обрамить в #ifdef NDEBUG.
QUOTE (Aprox @ Mar 15 2011, 13:20) *
Кристаллы STR91xx имеют уже готовую фирменную библиотеку драйверов периферийных устройств. Библиотека написана на C и требует запуска инициализации собственных внутренних переменных.
Сочувствую.
QUOTE (Aprox @ Mar 15 2011, 13:20) *
Создавайте объекты, взаимодействующие с периферией, во внутренней SRAM кристалла. Этой SRAM вполне достаточно.
"Отучаемся говорить за других". Вам достаточно. Если внешнюю память предусмотрели - она кому-то нужна.
QUOTE (Aprox @ Mar 15 2011, 13:20) *
Кроме того, это пожалуй единственный прием работать с готовыми библиотеками драйверов периферии, написанными на C.
Нет, не единственный. Можно переписать cstartup.
Aprox
Цитата(Andy Mozzhevilov @ Mar 15 2011, 15:19) *
Как то печально совсем у вас все. Что за такая уникальная инфраструктура, которая работает только с одной версией отладчика? Вы себя вяжете сами по рукам и ногам, работая на версии 4-летней давности.
Да, я понимаю. Но заняться обновлением инфраструктуры пока совершенно нет времени.
Цитата
Для этого есть такой способ для low_level_init():
1. Зайти в low_level_init
2. Вызвать из нее функцию статической инициализации iar
3. Вызвать функцию инициализации библиотек периферии
4. Выйти из low_level_init с кодом 0 - return 0;
Код возврата 0 означает, что startup должен пропустить статическую инициализацию (во всяком случае до версии IAR 5.50.5)
После этого вы в конструкторах глобальных объектах получаете доступ к уже проинициализированным библиотекам.
Плохо понимаю, если отключить статическую инициализацию сегмента данных, то в самом приложении на СPP писать какую либо инициализацию глобальных переменных станет уже бесполезно? Как же тогда будут созданы глобальные объекты?

Вообще, мне кажется, что проблема упирается в случай, когда проект состоит из модулей частью на СPP и частью на С.


Цитата(Сергей Борщ @ Mar 15 2011, 17:01) *
Нет, не единственный. Можно переписать cstartup.

Хорошо, не единственный. Но зато самый простой и ненапряжный. Потому, что просто используешь куски программы на С, которая раньше стопроцентно работала, а не вязнешь в тонкостях low_level_init и прочих cstartup.
Andy Mozzhevilov
Цитата(Aprox @ Mar 15 2011, 17:49) *
Да, я понимаю. Но заняться обновлением инфраструктуры пока совершенно нет времени.
Плохо понимаю, если отключить статическую инициализацию сегмента данных, то в самом приложении на СPP писать какую либо инициализацию глобальных переменных станет уже бесполезно? Как же тогда будут созданы глобальные объекты?

Ее не надо отключать, ее вызов надо переместить. То есть вы ее можете вызвать из своей low_level_init() (п.2 моих пояснений). Потом вы вызываете инициализацию своей библиотеки, и поскольку вызов инициализации библиотеки происходит после статической инициализации, то все корректно. Это все равно что вызвать init библиотеки в первой строчке main() программы на Си. После выхода из low_level_init() с кодом возврата 0 стандартный startup iar пропустит вызов статической инициализации и вызовет сразу динамическую инициализацию глобальных объектов С++ (во всяком случае до 5.50.5, но ведь у вас вообще 4.30).
При этом вы в конструкторах этих объектов уже спокойно можете использовать вызов функций вашей библиотеки.

Цитата
Вообще, мне кажется, что проблема упирается в случай, когда проект состоит из модулей частью на СPP и частью на С.

Здесь нет никакой проблемы, модули С++ совершенно спокойно могут вызывать функции на Си. У меня в проектах на С++ используется куча сторонних библиотек на Си (для примера - lwip, fat_fs, ucos-ii, tlsf), и это не является проблемой.

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

В тонкостях то нужно разобраться один раз, сделать и отладить - цена вопроса - один рабочий день.
А вводить в объектах функции startObj и вызывать их - нужно повседневно.
По мне так лучше - полдня потерять, потом за 5 минут долететь. Не люблю рутинную ручную работу, изо дня в день наворачивать лишние строки кода только лишь потому, что библиотека неинициализирована до вызова конструкторов глобальных объектов.
sergeeff
Цитата(Andy Mozzhevilov @ Mar 15 2011, 20:08) *
В тонкостях то нужно разобраться один раз...


Абсолютно согласен. Никто не мешает startup файл организовать так, как тебе удобно и нужно, а не ребятам из отдела сопровождения компилятора X для процессора Y.
Aprox
Цитата(Andy Mozzhevilov @ Mar 15 2011, 19:08) *
Ее не надо отключать, ее вызов надо переместить. То есть вы ее можете вызвать из своей low_level_init() (п.2 моих пояснений). Потом вы вызываете инициализацию своей библиотеки, и поскольку вызов инициализации библиотеки происходит после статической инициализации, то все корректно. Это все равно что вызвать init библиотеки в первой строчке main() программы на Си. После выхода из low_level_init() с кодом возврата 0 стандартный startup iar пропустит вызов статической инициализации и вызовет сразу динамическую инициализацию глобальных объектов С++ (во всяком случае до 5.50.5, но ведь у вас вообще 4.30).
При этом вы в конструкторах этих объектов уже спокойно можете использовать вызов функций вашей библиотеки.
Спасибо, я попробую.
Цитата
В тонкостях то нужно разобраться один раз, сделать и отладить - цена вопроса - один рабочий день.
А вводить в объектах функции startObj и вызывать их - нужно повседневно.
По мне так лучше - полдня потерять, потом за 5 минут долететь. Не люблю рутинную ручную работу, изо дня в день наворачивать лишние строки кода только лишь потому, что библиотека неинициализирована до вызова конструкторов глобальных объектов.
Резон конечно есть,- лишняя сущность только во вред. Еще раз спасибо, буду пробовать.
Andy Mozzhevilov
Обращайтесь, если что. Можно в аську.
Aprox
Цитата(Andy Mozzhevilov @ Mar 16 2011, 11:36) *
Обращайтесь, если что. Можно в аську.
Спасибо. Потыркался целый день, искал кого куда и в какой момент вызывать - сильно утомился. Решил, как обычно- "лучшее враг хорошего" и остановился на своем варианте- инициализировать периферию и C-библиотеки после входа в main(). Так проще прикладнику, которому важнее сделать работающую вещь, чем погрязнуть в тонкостях стартапов. У меня работает, а больше ничего и не надо.

Вы говорили , что ucos инициализируют в low_leve_init. Я вспоминаю, когда года четыре назад возился с модулями Ethernet фирмы NetBurner, - там ucos инициализировалась вызовом Start_UCOS() сразу после входа в main(). Также инициализировался и TCP/IP стек вызовом Start_Enet(), тоже после main(). Все они написаны на C. А приложение пользователя писалось на C++. Компилятор был GCC. Собственно, те четырехлетние воспоминания и спасли меня от мучений с реал-тайм JTAG отладкой на IAR.
Andy Mozzhevilov
Цитата(Aprox @ Mar 17 2011, 20:28) *
Вы говорили , что ucos инициализируют в low_leve_init. Я вспоминаю, когда года четыре назад возился с модулями Ethernet фирмы NetBurner, - там ucos инициализировалась вызовом Start_UCOS() сразу после входа в main().


Я говорил, что я так делаю. Как делают другие, не знаю.
Если uCOS инициализировать стандартным способом, т.е. вызовом из main(), то в ПО на С++ теряется возможность создавать объекты, использующие сервисы uCOS в конструкторах объектов, объявленных глобально, т.е. необходимо каждый раз прибегать к вызову дополнительной функции инициализации.
С моей точки зрения, если внутри объекта к примеру используется семафор, логично, чтобы этот семафор создавался (выделялся) в конструкторе объекта. В этом случае после создания объекта он готов к использованию без дополнительных вызовов. Это как раз и решается нужной организацией стартапа (в части low_level_init()).
Поэтому способ, который я описал, он полностью рабочий и проверенный на практике.
Конечно, если ПО написано полностью на Си, то эти заморочки ни к чему.
Aprox
Цитата(Andy Mozzhevilov @ Mar 18 2011, 07:37) *
Если uCOS инициализировать стандартным способом, т.е. вызовом из main(), то в ПО на С++ теряется возможность создавать объекты, использующие сервисы uCOS в конструкторах объектов, объявленных глобально, т.е. необходимо каждый раз прибегать к вызову дополнительной функции инициализации.
С моей точки зрения, если внутри объекта к примеру используется семафор, логично, чтобы этот семафор создавался (выделялся) в конструкторе объекта. В этом случае после создания объекта он готов к использованию без дополнительных вызовов. Это как раз и решается нужной организацией стартапа (в части low_level_init()).
Вспоминаю, что в примерах C++ приложений для тех модулей с UCOS и TCP/IP стеком, объекты вообще не применялись. Это были примеры, как использовать готовые библиотеки на C. Мне потребовалось только дописать свои задачи управления I2C, SPI и UART, связать их средствами UCOS. Инициализация периферии происходила в момент создания задач через UCOS, т.е. после main(). Честно говоря, я не понимаю, почему это плохо и неидейно.
Andy Mozzhevilov
Цитата(Aprox @ Mar 18 2011, 11:16) *
Вспоминаю, что в примерах C++ приложений для тех модулей с UCOS и TCP/IP стеком, объекты вообще не применялись. Это были примеры, как использовать готовые библиотеки на C. Мне потребовалось только дописать свои задачи управления I2C, SPI и UART, связать их средствами UCOS. Инициализация периферии происходила в момент создания задач через UCOS, т.е. после main(). Честно говоря, я не понимаю, почему это плохо и неидейно.

Значит вы писали софт на Си, причем здесь С++?
Aprox
Цитата(Andy Mozzhevilov @ Mar 18 2011, 11:24) *
Значит вы писали софт на Си, причем здесь С++?
Не совсем. Внутри задач я создавал и использовал объекты. Например строки. Правда, они не были глобальными и создавались методом new при запуске каждой задачи UCOS. Сейчас же, как я понимаю, вся проблема в том, что программные модули взаимодействуют напрямую, через общие глобальные объекты.
sergeeff
Цитата(Aprox @ Mar 18 2011, 15:10) *
Не совсем. Внутри задач я создавал и использовал объекты. Например строки. Правда, они не были глобальными и создавались методом new при запуске каждой задачи UCOS. Сейчас же, как я понимаю, вся проблема в том, что программные модули взаимодействуют напрямую, через общие глобальные объекты.


А кто вам сказал, что обычные объекты можно "запросто" использовать в многозадачных OS? Если объявлены глобальные объекты, совместно используемые в разных задачах (потоках), вам предстоит самому обеспечивать синхронизацию доступа к таким объектам. Для того и сделаны всякие там мьютексы, семафоры, критические секции. Вы почитайте книги про это, очень полезно.
Aprox
Цитата(sergeeff @ Mar 18 2011, 21:28) *
А кто вам сказал, что обычные объекты можно "запросто" использовать в многозадачных OS? Если объявлены глобальные объекты, совместно используемые в разных задачах (потоках), вам предстоит самому обеспечивать синхронизацию доступа к таким объектам. Для того и сделаны всякие там мьютексы, семафоры, критические секции. Вы почитайте книги про это, очень полезно.
Наверное неверно понимаете. Я писал, что сейчас работаю с проектом, в котором нет многозадачной real-time OS и поэтому взаимодействие программных модулей приходится производить через глобальные объекты. Разговор же про OS происходил по другому поводу- что в случае с многозадачной OS нет нужды городить глобальные объекты и, соответственно, не возникает проблем в С++ со "стартапами" периферии. Пример, в задаче управления UART, вся инициализации необходимой периферии производится в начале процедуры, которую назначили "задачей". Там же, внутри процедуры вызываются конструкторы объектов, например класса string. Получаем локальные, не глобальные объекты, которые созданы после main() и после всех стартапов. А уж передавать эти обьекты, вернее указатели на них, другим задачам, действительно можно через семафоры, очереди OS и прочие фифо.
sergeeff
Цитата(Aprox @ Mar 19 2011, 13:30) *
Наверное неверно понимаете.


Как пишите, так и понимаю...
MALLOY2
Цитата
2. Вызвать из нее функцию статической инициализации iar


Можно по подробней что это за функция что то в мануале не нашел ?
Andy Mozzhevilov
Цитата(MALLOY2 @ Mar 23 2011, 16:18) *
Можно по подробней что это за функция что то в мануале не нашел ?

Точку входа можно найти, прошагав в отладчике стартап. Обычно с iar можно найти исходники статической и динамической инициализации, которые используются в startup.
Завтра на работе посмотрю точно, что я вызываю в low_level_init()
Andy Mozzhevilov
Код
// __iar_data_init -  функция из библиотеки, выполняющия фазу static segment initialization
// Для разных версий функции инициализации сегмента данных вызываются по разным именам
// __VER__ - предопределенное макро в IAR, см. документацию на компилятор
#if __VER__ <= 5011000
/* Для IAR 5.11 */
extern __interwork  void __iar_data_init(void);
/* Для IAR 5.20 и выше */
#elif __VER__< 5050005
extern __interwork  void __iar_data_init2(void);
#elif __VER__<= 6010001
// Версия Iar с 5.50.5 по 6.10.1
extern void * __iar_cstart_call_ctors(void *ptr);
extern __interwork void __iar_data_init3 (void);
#else   // Версия Iar выше 6.10.1
#error Untested init sequence for this compiler version
#endif
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.