Как в iar arm получить coff или elf файл

Обновлено: 04.07.2024

Данная среда поддерживает большое количество микропроцессоров и микроконтроллеров построенных на базе архитектуры ARM разных версий. Полный список можно посмотреть на их сайте Т.к. это серьезная среда разработки, то она поддерживает различные отладочные интерфейсы и средства сопряжения как производства самой IAR так и сторонних компаний, имеет встроенный отладчик, симулятор и т.д.

Но по причине отсутствия у меня какого либо девайса для внутрисхемной отладки рассказать я про все это не могу. А пользоваться симулятором как-то в голову даже не приходило. Я по старинке, пишу, заливаю в контроллер и смотрю что происходит. (Зато их есть у меня. И я вам скоро выдам пример того, какой это рулез. прим. DI HALT)

Есть мнение, что компилятор С/С++ у IAR один из самых лучших, но за это я не ручаюсь, хотя кое какие мои сравнения с Keil uVision v3 показали его превосходство.

И здесь есть 2 варианта

  • 1. Полнофункциональная версия с ограничением использования в 30 дней.
  • 2. Версия без ограничения по времени использования но генерирующая код не более 32Кб. Под наши ковыряния хватит с лихвой.

Обе версии, кроме того имеют следующие ограничения:

  • Они не включают исходный код библиотек.
  • Они не включают поддержку MISRA C (что это такое, к сожалению не знаю).
  • Имеют ограниченную техническую поддержку.
  • Версия с ограничением кода в 32Кб не имеет поддержки расширенной отладки (что это такое, к сожалению не знаю)

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

После установки можно приступать к созданию проекта. Запускаем IAR Embedded Workbench и видим следующее окно:


Выбираем в меню:

Project->Create New Project

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

и жмем кнопочку ОК



Создался новый проект содержащий только один файл main.c который содержит только одну пустую функцию main(). А вы хотели большего? Дальше все ручками.

На данном этапе желательно нажать на кнопку с изображением трех дискеток или через меню

EWARM попросит ввести имя WorkSpace (воркспейс может содержать множество проектов) и не мудрствуя лукаво я назвал его также LEDTest.

В отличие от Keil’a EWARM не попросил указать целевое устройство и все остальное, поэтому лезем в свойства проекта и начинаем его настраивать.

По аналогии с Microsoft Visual Studio EWARM позволяет создавать произвольное число конфигураций и по умолчанию создает в проекте 2 конфигурации Debug и Release, что подразумевает 2 набора параметров, одни для компиляции кода содержащего отладочную информацию и другой для кода без нее. Выбираем наиболее подходящую для себя.

Я, например, сразу выставляю Release и настраиваю только ее, но это дело вкуса и наличия или отсутствия у вас средств отладки.

Итак, идем в меню

Либо нажимаем Alt+F7, либо тыкаем правой кнопкой мыши на корне дерева слева и выбираем в меню Options. Появляется окно настроек проекта.

Первая категория – General Options
Здесь желательно выбрать ядро для которого нужно откомпилировать код или указать конкретное устройство. По умолчанию указано ядро ARM7TDMI. Переключаем радиобуттон на Device, нажимаем на кнопку справа от поля ввода и в списке выбираем

, где Y соответствует семейству имеющегося у вас микроконтроллера. Я выберу ST STM32F10xxE.


Далее по вкладкам и категориям буду пробегаться кратко и без картинок т.к. вкладок много и не все они важны для нас на данном этапе. На существенных остановлюсь подробно.

Output (вывод)
Здесь указываем что мы хотим получить навыходе, исполняемую программу или библиотеку. Оставляем без изменения – Executable. Также здесь можно прописать пути куда ложить откомпилированную программу/библиотеку, объектные файлы и файлы листингов. Меняем, если душа того просит.

Library Configuration (конфигурация runtime библиотеки языка С). Это тема отдельной телепередачи :-) но сейчас можно смело поставить None и пройти дальше.

Library options (опции стандартной библиотеки языка С)
Здесь настраивается работа функций printf и scanf. Вернее поддержка различных ключей строки форматирования. Ниже кратко расписано какая опция что поддерживает, а более подробно можно прочитать в документации идущей в комплекте с EWARM. Поддержка более сложных ключей форматирования увеличивает размер кода т.к. обработчики строки форматирования разные по сложности реализации. В данном проекте нам это не важно т.к. данными функциями мы пользоваться не будем. А в последущием я освещу данный вопрос подробнее.

MISRA-C: 2004 и MISRA-C: 1998. Настройки расширений MISRA-C. Что это такое, я честно не знаю. :-)

C/C++ Compiler (настройки компилятора С/С++)
Здесь настраивается поддержка расширений языков С/С++, режимы оптимизации компилятора, генерация отладочной информации, пути к инклудам и т.д. Здесь пока можно ничего не трогать.

Assembler
Соответственно настройки языка ассемблера. Здесь пока можно ничего не трогать.

Output Converter (конвертация вывода)
Вот это нам нужно. Дело в том, что по умолчанию EWARM генерирует исполняемый файл в формате ELF, что какбы намекает, что Unix и прочие линуксы IAR’у не чужды.

Но нам то они ни к чему, поэтому смело тыкаем галку Generate additional output (генерировать дополнительный выходной файл) и в списке Output format (формат выходного файла) выбираем подходящий для себя, вернее для используемого вами программатора, формат.

Выбор особо не велик и реально для большинства будет состоять из двух вариантов: Intel extended, в простонародье именуемый HEX или binary. Я лично пользуюсь вариантом binary. Здесь же, если того требуют ваши религиозные убеждения, можно задать имя выходного файла отличающееся от дефолтного.


Build Actions (действия при сборке)
Здесь можно указать команды которые нужно выполнить перед сборкой или после. Поступаем аналогично предыдущему пункту.

Категория Linker (линковщик)
Вот и добрались до второго, после указания целевого устройства, важного момента! Ибо от линковщика, вернее его настроек, очень сильно зависит запуститься ваш проект в микроконтроллере или нет т.к. именно линковщик располагает код и различные таблицы (например векторов прерываний) по нужным адресам.


Дело в том, что как я уже сказал выше, данный файл является заготовкой для всей архитектуры Cortex, поэтому если вы его измените, а потом захотите создать проект для другого контроллера, того-же NXP LPC17XX, то его опять придется редактировать уже под этот процессор. Тут есть 3 варианта решения:

  • Сказать себе, что кроме STM32F меня ничего не интересует и отредактировать данный файл.
  • Скопировать данный файл в той-же папочке где он лежит (а лежит он, как можно догадаться, в папке диск:путь куда установили EWARM\arm\CONFIG\) во что-то типа STM32F10XXX.icf и редактировать его.
  • Скопировать его в папочку с проектом переименовав во что-то типа STM32F10XXX.icf и редактировать его.

Итак, выбираем вариант себе по душе (я лично пользуюсь 3-им вариантом а путь прописываю так:

В появившемся окошке в первой вкладке Vector Table задаем адрес таблицы векторов прерываний. Что это такое, для тех кто не в курсе, я не буду раскрывать. (Я тоже не скажу :), т.к. все уже сказано в разделе про AVR. Тут все точно также, только векторов больше. прим DI HALT)

Адрес может быть либо 0х00000000 либо 0х08000000. Я предпочитаю ставить 0х08000000 т.к. он указывает на начало внутренней флеш памяти, а адрес 0х00000000 может мэпиться на флешку а может и нет, в зависимости от состояния входов BOOT в момент инициализации контроллера, но это нужно уже курить даташит на устройство.


У меня ее 512Кб, а у вас может быть 32, 64, 128, 256. Т.е. адрес окончания этой области памяти вычисляете сами. Для меня он будет равен 0x0807FFFF (адрес начала 0x08000000 + размер флеша (512*1024) – 1 в шестнадцатеричном формате). Для вас это может быть 0x08007FFF, 0x0800FFFF, 0x0801FFFF и т.д. Желательно указывать точный размер чтобы полученная прошивка не превысила этот размер, а так линковщик ругнется в случае чего. Но нам это не грозит пока. Аналогично заполняем поля для RAM, зная из чтения даташита, что она начинается с адреса 0x20000000 и посчитав где она закончится.


Если ошибетесь в этих адресах, особенно начальных, например нолик пропустите или лишний напишите, то программа просто не будет работать.

Вкладка Stack/Heap Sizes (размеры стека и кучи)
Параметры говорящие сами за себя.
Стек нужен для передачи параметров функциям, сохранения точек возврата из них и т.д. Сильно большим его делать не имеет смысла т.к. зря будет расходоваться ОЗУ, а если сделать сильно маленьким, то может не хватить (особенно если будет использоваться много вложенных функций). Поставим его равным 0x200 т.е. 512 байт. В нашем проекте этого более чем достаточно.
Куча – это часть ОЗУ выделенная для функций работы с памятью языка С/С++ таких как malloc, оператор new и т.д. В данном проекте мы их использовать не планируем, поэтому ставим 0.


Все, нажимаем кнопочку Save.

Категория Debuger (отладчик)
Здесь настраиваются аппаратные средства внутрисхемной отладки либо отладка в симуляторе. Как я уже говорил, аппаратных средств у меня нет, а симулятором я не пользуюсь. Поэтому рассказать тут особо ничего не могу.

С облегчением жмем кнопку Ок справа-внизу окошка, и применяем выбранные параметры.

Теперь можно смело нажать кнопку F7 или в меню

и откомпилировать наш проект.

В папочке которую вы указали для выходных файлов программы (если ничего не меняли, то это будет, в зависимости от выбранной конфигурации, папка Debug/exe либо Release/exe в папке с проектом) увидим 2 файла. Один с раширением .out и второй .bin или .hex, в зависимости от того, что вы указали в качестве дополнительного выходного файла.

Все, наша первая программа готова! Можно прошивать ее в контроллер и она заработает! Не верите? А вы попробуйте.

На этом позвольте закончить мою первую статью т.к. она и так получилась не маленькой. А к написанию более функционально насыщенной программы мы перейдем на следующем нашем шаге.

Спасибо. Вы потрясающие! Всего за месяц мы собрали нужную сумму в 500000 на хоккейную коробку для детского дома Аистенок. Из которых 125000+ было от вас, читателей EasyElectronics. Были даже переводы на 25000+ и просто поток платежей на 251 рубль. Это невероятно круто. Сейчас идет заключение договора и подготовка к строительству!

А я встрял на три года, как минимум, ежемесячной пахоты над статьями :)))))))))))) Спасибо вам за такой мощный пинок.

42 thoughts on “ARM. Учебный курс. IAR EWARM. Создание проекта.”

можно еще через встроенный USB по DFU прошивать без использования всяких преобразователей;)

Для создания эльфов нам потребуются: IAR, includes, _cpp.C, _cpp2.cpp - где все это взять? смотрим здесь и здесь. Пример эльфа HelloWorld - смотрим во вложениях.

И так приступим:

1) Открываем файл HelloWorld.eww в папке (файл находится в папке C:\IAR\Embedded Workbench 4.0 Evaluation\ARM\HelloWorld\ - если вы все сделали правильно как в этой теме). Происходит запуск IAR и появляется такое вот окошко:



2) Кликаем два раза левой кнопкой мыши (ЛКМ) на main.c. У вас должно окно IAR выглядеть так:


3) Вся работа с кодом эльфа происходит в окошке main.c:

4) Давайте просмотрим код нашего эльфа HelloWorld и посмотрим какая строчка за что отвечает:

u16 timer; // ID таймера
int lamp=0; // текущая яркость лампы
BOOK * HWBook;

int TerminateElf(void * ,BOOK* book)
FreeBook(book);
return(1);
>

typedef struct
BOOK * book;
>MSG;

int ShowAuthorInfo(void *mess ,BOOK* book)
MSG * msg = (MSG*)mess;
MessageBox(0x6fFFFFFF,STR("Hello Wordl!\n\nExample elf.."),0, 1 ,5000,msg->book);
return(1);
>

const PAGE_MSG HW_PageEvents[]@ "DYN_PAGE" = ELF_TERMINATE_EVENT , TerminateElf,
ELF_SHOW_INFO_EVENT , ShowAuthorInfo,
0,0
>;

void onTimer (u16 unk , void * data)
// перезапускаем таймер
Timer_ReSet(&timer,500,onTimer,0);
// переключаем лампу
SetLampLevel(lamp^=0x10);
>
>

void onCloseHWBook(BOOK * book)
if (book)
Timer_Kill(&timer);
StatusIndication_ShowNotes(0x6FFFFFFF);
SetLampLevel(0);
SUBPROC(elf_exit);
>
>

BOOK * CreateHWBook()
HWBook= new BOOK;
CreateBook(HWBook,onCloseHWBook,&base_page,"Example",-1,0);
return(HWBook);
>

int main (void)
CreateHWBook();
// выводим текст в "заметки" на StandBy
StatusIndication_ShowNotes(STR("Hello World!"));
// запускаем таймер на 0.5 сек.
timer=Timer_Set(500,onTimer,0);
return(0);
>

5) Символами "//" мы можем комментировать строчки в самом коде эльфа.

6) Постараемся провести разбор каждой строчки:

- "инклуды" - файлы Lib_Clara.h и Dir.h из которых берутся функции для нашего эльфа. По умолчанию находятся здесь - C:\IAR\Embedded Workbench 4.0 Evaluation\ARM\Include\ (папка куда вы установили IAR).

Файлы Lib_Clara.h и Dir.h можно самостоятельно просмотреть, открыв их блокнотом.

должны быть в каждом эльфе! Из них берутся функции, как к примеру эта:

u16 timer; // ID таймера
int lamp=0; // текущая яркость лампы
BOOK * HWBook;

также должны быть в каждом эльфе. Функция запускает таймер на определенное время, по истечении которого будет выполнено необходимое действие (более подробно =timer&s[]=set]здесь).

int TerminateElf(void * ,BOOK* book)
FreeBook(book);
return(1);
>

typedef struct
BOOK * book;
>MSG;

int ShowAuthorInfo(void *mess ,BOOK* book)
MSG * msg = (MSG*)mess;
MessageBox(0x6fFFFFFF,STR("Hello Wordl!\n\nExample elf.."),0, 1 ,5000,msg->book);
return(1);
>

const PAGE_MSG HW_PageEvents[]@ "DYN_PAGE" =ELF_TERMINATE_EVENT , TerminateElf,
ELF_SHOW_INFO_EVENT , ShowAuthorInfo,
0,0
>;

void onTimer (u16 unk , void * data)
// перезапускаем таймер
Timer_ReSet(&timer,500,onTimer,0);
// переключаем лампу
SetLampLevel(lamp^=0x10);
>
>

void onCloseHWBook(BOOK * book)
if (book)
Timer_Kill(&timer);
StatusIndication_ShowNotes(0x6FFFFFFF);
SetLampLevel(0);
SUBPROC(elf_exit);
>
>

BOOK * CreateHWBook()
HWBook= new BOOK;
CreateBook(HWBook,onCloseHWBook,&base_page,"Example",-1,0);
return(HWBook);
>

отвечают за поддержку эльфа Букменеджером (BookManager). Если этих строк нее будет - вы не увидите свой эльф в Букменеджере и не сможете "убить" эльф Букменеджером. Остановимся поподробнее:

MessageBox(0x6fFFFFFF,STR("Hello Wordl!\n\nExample elf.."),0, 1 ,5000,msg->book);

отвечает за информацию о названии и создателе эльфа ("About") в Букменеджере. Смотрим скриншот с экрана телефона:


Если мы хотим изменить информацию о создателе эльфа - правим строку Hello Wordl!\n\nExample elf.

\n\n - отвечает за перенос строчки

К примеру, напишем так: My Elf!\n\n© vodoo999

Строчка теперь выглядит так:

MessageBox(0x6fFFFFFF,STR("My Elf!\n\n(c) vodoo999"),0, 1 ,5000,msg->book);

После компиляции и запуске эльфа на телефоне мы получим в Букменеджере следующую надпись:


- отвечают за терминацию ("убийство") нашего эльфа Букменеджером. Должны быть всегда в коде эльфа! Иначе мы не сможем выгрузить наш эльф из памяти телефона (поможет только перезагрузка).

отвечает за создание "книги" (Book), а также и за поддержку Букменеджера:


Смотрим на скриншот:


int main (void)
CreateHWBook();
// выводим текст в "заметки" на StandBy
StatusIndication_ShowNotes(STR("Hello World!"));
// запускаем таймер на 0.5 сек.
timer=Timer_Set(500,onTimer,0);
return(0);
> Функция main (void) самая главная и в неё обычно вставляют обработчики клавиш и событий, функции которые необходимо запустить при старте эльфа и другие.

(с) arban
- лучше я бы не написал.

Т.к. у нас эльф простенький - в нем присутствует только одна функция -

StatusIndication_ShowNotes(STR("Hello World!"));

Как я уже писал - функция StatusIndication_ShowNotes(STR("Hello World!")); отвечает за создание и показ заметок на ГЭ с вашим текстом (в нашем примере - по умолчанию стоит текст Hello World!).

Вот что мы видим после запуска эльфа на нашем ГЭ:


Мы можем менять текст Hello World! на какой угодно:
к примеру на такой - Я написал эльф! Ура!

Получаем такой текст на ГЭ:


11) Теперь самое главное - мы после, всех проделанных операций, в программе IAR в окошке main.c должны получить следующий текст:

u16 timer; // ID таймера
int lamp=0; // текущая яркость лампы
BOOK * HWBook;

int TerminateElf(void * ,BOOK* book)
FreeBook(book);
return(1);
>

typedef struct
BOOK * book;
>MSG;

int ShowAuthorInfo(void *mess ,BOOK* book)
MSG * msg = (MSG*)mess;
MessageBox(0x6fFFFFFF,STR("My Elf!\n\n(c) vodoo999"),0, 1 ,5000,msg->book);
return(1);
>

const PAGE_MSG HW_PageEvents[]@ "DYN_PAGE" = ELF_TERMINATE_EVENT , TerminateElf,
ELF_SHOW_INFO_EVENT , ShowAuthorInfo,
0,0
>;

void onTimer (u16 unk , void * data)
// перезапускаем таймер
Timer_ReSet(&timer,500,onTimer,0);
// переключаем лампу
SetLampLevel(lamp^=0x10);
>
>

void onCloseHWBook(BOOK * book)
if (book)
Timer_Kill(&timer);
StatusIndication_ShowNotes(0x6FFFFFFF);
SetLampLevel(0);
SUBPROC(elf_exit);
>
>

BOOK * CreateHWBook()
HWBook= new BOOK;
CreateBook(HWBook,onCloseHWBook,&base_page,"HelloWorld",-1,0);
return(HWBook);
>

int main (void)
CreateHWBook();
// выводим текст в "заметки" на StandBy
StatusIndication_ShowNotes(STR("Я написал эльф! Ура!"));
// запускаем таймер на 0.5 сек.
timer=Timer_Set(500,onTimer,0);
return(0);
>


12) Приступаем к компиляция (созданию эльфа). Для того чтобы программа IAR скомпилировала ваш эльф, из строчек которые вы написали в окошке main.c, необходимо нажать клавишу "F7" на вашей клавиатуре. Если вы все сделали правильно, то окно IAR должно выглядеть так (после нажатия "F7"):


Обращаем внимание на окошко Messages:
если вы все сделали правильно то кол-во ошибок и предупреждений будет "0" -


13) Наш созданный эльф будет лежать в папке Exe (C:\IAR\Embedded Workbench 4.0 Evaluation\ARM\HelloWorld4\Release\Exe\) и будет иметь название main.elf.

Для того, чтобы изменить название эльфа (не main.elf, а к примеру - Hello_World_main.elf) перед компиляцией (нажатием "F7") необходимо в IAR нажать сочетание клавиш "Alt+F7", в Category выбрать Linker, поставить галочку Override default и написать имя своего будущего эльфа:


Повторюсь, после нажатия "F7" (компиляции, создания эльфа) - ваш созданный эльф будет лежать в папке по умолчанию C:\IAR\Embedded Workbench 4.0 Evaluation\ARM\HelloWorld4\Release\Exe и иметь название Hello_World_main.elf

Спасибо arban - за инструкцию, UltraShot, arban, Alex_E - за помощь.

PS: В следующем уроке мы поговорим о том, как добавить в наш эльф код-защиту от многократного запуска (запуск нескольких копий) эльфа.

[ Редактировано vodoo999 в 5.11.09 00:01 ]

"Valetudo est bonum optimum"

K800 => K810 R8BA024 CID 53

vodoo999,
А суть этого фака научить юзеров менять копирайты в исходниках. )))
Для написание эльфов самый полезный мануал находится в wiki. А розжовывать новичкам хелов ворд - я думаю не стоит. Им все это нужно самим понять иначе толку не будет. w580 R8BE001
WMR: R168151148681
WMU: U112893933179
WME: E123439764501
WMZ: Z161980978444 vodoo999,
А суть этого фака научить юзеров менять копирайты в исходниках. )))
Для написание эльфов самый полезный мануал находится в wiki. А розжовывать новичкам хелов ворд - я думаю не стоит. Им все это нужно самим понять иначе толку не будет.

я постарался как можно подробнее описать))) копирайты я вроде не нарушал

yr4ik - завтра подправлю Урок, спасибо

[ Редактировано vodoo999 в 5.11.09 00:35 ]

"Valetudo est bonum optimum"

K800 => K810 R8BA024 CID 53

void onCloseHWBook(BOOK * book)
if (book)
Timer_Kill(&timer);
StatusIndication_ShowNotes(0x6FFFFFFF);
SetLampLevel(0);
SUBPROC(elf_exit);
>
>

Эти строки отвечают за терминацию ("убийство") нашего эльфа Букменеджером.

u16 timer; // ID таймера
int lamp=0; // текущая яркость лампы
BOOK * HWBook;

также должны быть в каждом эльфе.

Я бы так не сказал что они должны быть в каждом эльфе. Это переменные. В каждом эльфе они задаются по своему. Например int lamp=0; - это глобальная переменная нужна лиж для установки а также проверки яркости лампы. BOOK * HWBook; - ожвучили HWBook как BOOK .

[ Редактировано yr4ik в 5.11.09 00:24 ]

w580 R8BE001
WMR: R168151148681
WMU: U112893933179
WME: E123439764501
WMZ: Z161980978444

Фига себе, это же как раз то что доктор прописал. Спасибо, vodoo999


Добавлено 5.11.09 17:22

С вашего разрешения влезу в эту тему с парой глупых вопросов.

1. Если эльфы пишутся на C, то что в проекте делают файлы с расширением *.cpp?


2. Что это за либы под main.c? Как они там оказались, ведь в коде мы инклудили только Lib_Clara.h и Dir.h?

3. Я в svn находил проекты в которых отсутствует main.c, и вообще файлы с этим расширением. Интересно будет почитать о том как устроены они.

4. Что это за полезный мануал, который находится в wiki? Я не встречал на него ссылки, поделитесь upd: нашел

ПС к эльмакерам которые против разжевывания hello world: к сожалению мемберы у которых возникает желание написать что нибудь полезное возможно видят Си впервые в жизни (как например я), и очень трудно самостоятельно разобраться во всех тонкостях, приходится открывать темы "по созданию эльфов" и наслаждаться пятидесятистраничным флеймом.
Например мне до сих пор невдомек, почему в эльфе Hello World рассматривается работа с фонариком

[ Редактировано xaBaz в 5.11.09 17:23 ]

[ Редактировано xaBaz в 5.11.09 17:23 ]

[ Редактировано xaBaz в 5.11.09 21:18 ]

1. Если эльфы пишутся на C, то что в проекте делают файлы с расширением *.cpp?

Эльфы пишутся на C++. Хотя можно выбрать язык в настройках проекта

Что это за либы под main.c? Как они там оказались, ведь в коде мы инклудили только Lib_Clara.h и Dir.h?

Lib_Clara.h инклудит ещё множество файлов, они тут тоже показываются

3. Я в svn находил проекты в которых отсутствует main.c, и вообще файлы с этим расширением. Интересно будет почитать о том как устроены они.

main.c не особо и нужен. Функцию-энтрипоинт можно выбрать в настройках проекта.

Для новичков полезны шаблоны эльфов из темы "Создание эльфов". С их помощью можно автоматически всё настроить в проекте именно так, как надо. Остаётся только код писать.

UltraShot, yr4ik - стоит ли мне продолжать или уроки не совсем "правильные"? Скажем так, я пока что не достиг достаточного уровня в эльфостроении - постарался расписать элементарщину, но многие и такому рады (xaBaz). Ведь начинать надо с простого и потихонечку усложнять процесс. Еще один урок PS: В следующем уроке мы поговорим о том, как добавить в наш эльф код-защиту от многократного запуска (запуск нескольких копий) эльфа.

- честно не так уж там много кода и до урока не дотягивает. Вопрос ребром - продолжать или нет? - продолжение следует

xaBaz писал:

Например мне до сих пор невдомек, почему в эльфе Hello World рассматривается работа с фонариком

- это какой например? если ты про урок от arban - то эльфа, типа HelloWorld - первые (самые простые на которых учат и объясняют) попытки в эльфостроении и программировании в целом. arban показал как можно создать простой эльф фонарик - потому и HelloWorld (я так понял - если не правильно - поправьте).

[ Редактировано vodoo999 в 7.11.09 21:46 ]

"Valetudo est bonum optimum"

K800 => K810 R8BA024 CID 53

int isMyElfBook(BOOK * book) <
//return 0==strcmp(book->xbook->name,BookName);
if(!strcmp(book->xbook->name,BookName)) return(1);
return(0);
>

В первой части статьи был рассмотрен процесс разработки программ для микроконтроллеров AVR в инструментальной среде Eclipse. В конце первой части представлена общая схема разработки программ для ARM в Eclipse с помощью GNU Tool chain.

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

Итак, процесс разработки программ в Eclipse для ARM микроконтроллеров с помощью GNU Tool chain изображен на следующей схеме.


Из схемы видно, что исходные файлы на языках С и Assembler компилируются с помощью arm-none-eabi-gcc. Компилятор arm-none-eabi-gcc может осуществлять вызов ассемблера arm-none-eabi-as, компоновщика arm-none-eabi-ld, поэтому в нашем Makefile вместо них будет указан arm-none-eabi-gcc.

В результате компиляции получается один или несколько объектных файлов. Далее компоновщик arm-none-eabi-ld выполняет компоновку объектных модулей в памяти микроконтроллера. Для микроконтроллеров с архитектурой ARM код программы может находиться в разных областях памяти( FLASH, SRAM и т.д. ).

На размещение кода и данных внутри микроконтроллера можно влиять путем создания файла сценария компоновки. Сценарий компоновки передает линкеру адрес и размер доступных в микроконтроллере объемов памяти, предписывает компоновщику размещение секций программы по конкретным адресам .

Чтобы написать скрипт компоновки необходимо иметь перед глазами карту памяти используемого микроконтроллера. Для большинства микроконтроллеров ARM можно найти готовый файл сценария.

После компоновки создается один файл исполняемого образа программы в формате elf .

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

Для этого необходимо выполнить преобразование утилитой arm-none-eabi-objcopy в один из доступных для прошивки форматов ( bin , intel hex, motorola S-records и т.д. ).

Из полученного после компоновки elf- файла можно получить листинг кода программы на языке ассемблера с помощью утилиты arm-none-eabi-objdump.

Makefile на нашей схеме изображен отдельно , в нем находиться предписание для утилиты make для сборки всего проекта.

Стартовый код написан на языке ассемблера. Его вовсе не обязательно разрабатывать самостоятельно, поскольку для всех поддерживаемых GNU Tool chain микроконтроллеров он входит в состав инструментальной среды. В большинстве случаев стартовый код поставляется в виде откомпилированного объектного модуля, который в процессе компоновки вашего проекта присоединяется к нему.

Однако мы рассмотрим более детально стартовый код для различных архитектур ядра ARM.

Далее изображен пример стартового файла crt0.S микроконтроллера компании NXP LPC2138 с архитектурой ядра ARM7. Будем рассматривать этот код частями сверху-вниз.

. global _ etext @ ;инициализированные данные в ROM( FLASH ) . global _ _ bss _ start @ ;начало инициализированных данных (bss-секция) в RAM . set UND _ STACK _ SIZE , 0x00000004 @ ;Размеры стеков различных режимов . equ I _ BIT , 0x80 @ ;установка I бита прерываний IRQ

В этой части кода выполняются объявления. Директива ассемблера (GNU-ASM) .global делает указанный после директивы символ глобальным, это значит, что данный символ можно будет использовать в других модулях программы.

Рассмотрим следующий фрагмент кода:

Директива .text указывает на начало сегмента кода, все следующие за ней директивы будут размещены в секции «text».

Далее при помощи директивы .arm указывается, что будет использован набор команд ARM.
Для процессоров ARM7 всего существует два набора команд — ARM и Thumb. Второй режим включает набор 16-разрядных команд и предназначен для увеличения плотности кода программы.

Директива .code 32 указывает на 32-разрядный код , а директива .align указывает на выравнивание кода по 16-ти разрядам (двум байтам). Метка _boot является глобальной (.global) и указывает на начало функции (.func _boot ).

Далее расположена таблица векторов прерываний. Директива b _start выполняет переход на адрес _start , а все остальные директивы загружают в счетчик команд адреса обработчиков.

В этом варианте обработчики содержат бесконечный цикл. При необходимости можно организовать функцию-обработчик соответствующего вектора прерывания.

. size _ boot , . - _ boot @ ;размер кода функции _boot равен разнице текущего адреса и адреса _boot

Теперь объявляем три глобальные метки, по которым можно перейти на следующий код. Код программы всегда начинается с функции _start.

Далее выполняем инициализацию указателей вершин стеков для всех режимов работы процессора ARM7.

Величина стека задана в скрипте компоновщика( далее будет описан ).

В расположенном выше фрагменте кода мы перешли в режим «Undefined Instruction», запретив прерывания установкой I_BIT и F_BIT. Потом загрузили в SP адрес вершины стека из R0. Вычли из текущего значения в R0 размер стека режима «Undefined Instruction» и получили в R0 адрес вершины стека для следующего режима.
Аналогичным образом выполняется инициализация для всех остальных режимов.

Следующая инициализация необходима, если код предназначен для загрузки в FLASH память микроконтроллера.

Заполняем секцию .bss нолями:

И , наконец, выполняем передачу управления функции main.

Подсчет размера функции _start и ее окончание.

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

Для процессорного ядра Cortex-M3 стартовый код вообще является необязательным. В «Кортексах» вся программа может быть написана на языке программирования С.

При компиляции описанного выше стартового кода необходимо указать компилятору использовать режим ARM, а точнее ничего не указывать, так как по-умолчанию код компилируется для режима ARM.
Раздельная компиляция ARM и Thumb исходных кодов, находящихся в разных файлах, является специфической особенностью компилятора GCC.

Для того, чтобы откомпилировать исходный текст программы в режиме Thumb необходимо указать компилятору ключ -mthumb. Если в проект входят файлы, откомпилированные в ARM и Thumb режимах и между ними выполняется вызов функций , то необходимо также указать компоновщику ключ -mthumb-interwork, который позволит осуществлять переход между режимами ARM и Thumb во время выполнения кода.

При сборке программы из объектных модулей компоновщик использует скрипт компоновки, в котором указывается размещение секций внутри памяти микроконтроллера. Если этот скрипт явно не указывать , то будет использоваться встроенный по-умолчанию в компоновщик скрипт.

Для подключения собственного файла скрипта необходимо указать компоновщику с помощью опции -T . название скрипта. Имя файла и расширение для компоновщика не имеют значения. Пример :

arm - none - eabi - ld - T script . link - o main . out main . o

Компоновщик вырабатывает исполняемый файл с настраиваемыми адресами. Эти адреса устанавливаются каждый раз при загрузке модуля в память. Каждый раздел описывается двумя адресами — виртуальным адресом (VMA) и загружаемым в память адресом (LMA). Первый предназначен для внутреннего использования, а второй указывает на расположение памяти, куда будет загружен раздел. Процесс определения адреса модуля называется его размещением.

Компоновщик объединяет созданные компилятором объектные файлы в один объектный файл, который содержит разделы( секции ). Каждый раздел имеет свое имя и размер. Секции с одинаковыми именами объединяются в один раздел. Разделы могут содержать исполняемый код, инициализированные значения данных , неинициализированные данные, для которых указывается только имя раздела и размер.

Рассмотрим пример сценария компоновщика для микроконтроллера LPC2138 ( ARM7 ).

Ключевое слово MEMORY используется для присвоения имен блокам адресного пространства. В данном примере указывается местоположение и размер памяти микроконтроллера. Внутренней FLASH памяти присваивается название rom, а внутреннее ОЗУ микроконтроллера поименовано как ram. Атрибуты памяти (rx) указывают на то, что содержимое памяти может читаться и выполняться, аналогичным образом атрибуты (rw) указывают на возможность чтения и записи в данной области памяти.

Ключевое слово SECTIONS указывает на то, что далее будет описана карта распределения памяти для скомпонованного объектного модуля. Точка указывает на текущий адрес, то есть строка
. = 0x00000000 устанавливает текущий адрес в абсолютный со значением 0x00000000. После установки текущего адреса он будет автоматически увеличиваться при добавлении элементов в выходной файл.

Оператор .text : < >помещает начало секции .text выходного файла по текущему адресу. В скобках указываются элементы, которые будут включены в раздел .text выходного файла. Можно указать конкретные имена входных файлов ( компонуемых объектных файлов), в данном примере указаны все ( оператор * ) имена входных файлов.

Строка >rom указывает на то, что данный раздел будет помещен в именованную область памяти rom (то есть в FLASH память ).

Наименования основных секций для компоновщика являются предопределеннымы. В секции .text находиться код программы, также там могут находиться константы (.rodata). Секция .data предназначена для размещения данных , а в секции .bss находятся инициализированные нулевым значением данные. Туда попадают,например, статические переменные в языке С, которые только объявлены, но должны содержать нулевое начальное значение. Поэтому заполнение секции .bss нулевыми значениями производиться в стартовом коде перед вызовом функции main.

Именованные разделы размещаются в том порядке, в котором они указаны. Раздел .bss в нашем примере будет находиться сразу после раздела .data, поскольку для него не указан начальный адрес.

Компоновщику, по большому счету, все-равно на каком языке программирования написан программный модуль, скомпилированный в объектный и используемый в качестве входного . Вы можете использовать при компоновке объектные модули, написанные на языках программирования С и Assembler, как это делается при подключении стартового кода. В большинстве случаев такая необходимость не возникает, поскольку объема памяти и производительности современных микроконтроллеров достаточно для применения языка С.

Все программы из пакета ARM Tool chain( в качестве ARM Tool chain может выступать пакет Sourcery G++, Yagarto и т.д ) имеют интерфейс командной строки. Можно последовательно вызывать из командной строки компилятор, компоновщик, утилиты для преобразования и прошивки исполняемого файла. А можно записать всю последовательность вызовов в Makefile, запустить утилиту make и наблюдать за результатами выполнения команд, перечисленных в Makefile.

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

Можно ли при компиляции библиотек управлять тем, какие функции будут экспортированы?
Понятно, что спецификатор static для того и предназначен, но как быть если исходный код библиотеки размещен в нескольких *.с файлах, с определенной иерархии вызовов, при этом в библиотеке должны быть функции только из одного *.с фала?

P.S.: При написании dll под Win проблема решалась с помощью __declspec(dllexport). Может что-то есть такое и в IAR?

__________________
Помощь в написании контрольных, курсовых и дипломных работ здесь

IAR Подключение своиз библиотек
Стою в ступоре. Помогите. Начал ковырять IAR и тут же затык. Проблема с подключением собственно.

Переделка кода из IAR в код "любителей библиотек"
Сколько пишу код - не могу понять, как писать код с помощью готовых библиотек. Вот есть такой.


Компиляция библиотек boost
Здраствуйте. Извините что пишу не в тот подфорум, но тут больше вероятность что кто-то даст ответ в.

создай H файл для внешней связи

Добавлено через 8 минут

Не совсем понял совет. Для того чтобы подключить скомпилированную библиотеку к другому проекту, естественно создается заголовочный файл с описанием экспортируемых функций. Но даже если я в нем укажу только те объекты, которые мне нужны, в самой библиотеке останутся все остальные объекты. Проблема в едином пространстве имен. Скажем, в библиотеке используется файл, куда вынесены все вспомогательные функции, например crc_calc(), естественно, экспортировать ее не требуется, и в заголовочный файл ее прототип не пойдет. Однако же в самом файле библиотеке она будет содержаться в публичном виде, а не в приватном (если в терминах с++), и линкер основного проекта имеет право ее задействовать. Это может привести к серьезным и плоходиагностируемым ошибкам. Блин, кажется пора и для микроконтроллерам переходить на плюсы .

Я системный программист и часто для того, чтобы передать железяку программисту прикладному удобно все взаимодействие с железом оформить в виде dll (если win) или so (если линь.) А они уже в своих красивостях используют мой отлаженный код, не вдаваясь в подробности как там все устроено. Если интересно как это делать, могу дать ссылку на репозиторий. Часть проектов открыты под BSD (но в силу специфичности никому не нужны ) Однако же в самом файле библиотеке она будет содержаться в публичном виде, а не в приватном (если в терминах с++), и линкер основного проекта имеет право ее задействовать. библиотека это уже скомпилированый объектный файл и соглашение имен в стиле Си уже не действуют
или ты библиотеку в исходных кодах поставляешь7 это динамическая библиотека, а так же в вин есть и статические библиотеки это есть lib библиотека это уже скомпилированый объектный файл и соглашение имен в стиле Си уже не действуют
или ты библиотеку в исходных кодах поставляешь7

Че-то я не до понимаю.
После компиляции библиотеки в IAR на выходе появляется *.a файл. Он составляется из нескольких объектных файлов .о, которые компилируются независимо друг от друга из соответствующих файлов *.с.
Утилита сборки (в IAR это iarchive) ничего не знает о том, какие функции из файлов типа *.о являются публичными, а какие приватными, также она ничего не знает о заголовочном файле, где я опишу прототипы публичных функций для использования библиотеки *.а в другом проекте. В результате в файле *.а все объекты доступны из вне и могут быть использованы линкером.

P.S.: У IAR нашел жуткий способ как решить эту проблему: Hiding symbols in a library using isymexport

Что-то я не понял, о каких "приватных" функциях идет речь, в контексте языка Си, но проблема в целом вполне интересна ).

Вот что можно добавить:
1) Насчет библиотеки и "расшаренных имен функций". При линковке объектников в отдельности или библиотеки в целом, есть несколько вариантов. Компилер может воткнуть всё из объектников и библиотеки, может удалить с помощью функции оптимизации неиспользуемые функции (часто впихивается весь объектник, даже если из него вызывается только одна функция). Это все зависит от продвинутости линкера. Некоторые линкеры, поговаривают, могут даже выкинуть некоторые куски кода, получая константы на этапе линковки, не говоря уже об оптимизации, например, коротких/длинных переходов, веенеров и т.д. Все это давно уже доступно.

Кстати, из-за длинных/коротких переходов - на этапе линковки иногда даже последовательность файлов как они "укладываются" в память - тоже влияет на код. (ходят слухи, что некоторые шибко умные компилеры и это умеют).

2) В СИ расшареные функции имеют обычное объявление, без никаких приставок. Если же вы хотите что-то "припрятать", в рамках библиотеки, но не хотите это использовать - можно объявить "static inline" функцию, и в библе она не появится.

ЗЫ: вообще, комбинация static и inline (always inline) дает оч. много разных вариантов, которые зависят тогда уже как от кода, библиотеки, так и, в основном варианте - от компилятора. И для библиотек это очень актуальный вопрос.

Насчет источников комментов - это GCC в основном. Возможно, это не относится к IAR, но тем не менее, имеет общие черты.

Добавлено через 6 минут

я уже об этом озвучил чуть выше, но уточню - на сегодня из-за мощных оптимизаторов, объектник представляет собой разве что "остов" программы на асме. Потому что переходы в нем обозначены (ссылок-то все равно нет, там нули). И после только окончательной линковки с оптимизацией вы получите "застывший код". Потому что оптимизатор запросто может перетасовать применяемые регистры в функциях, поменять функции переходов, вызовов и даже флаги условий. Но получив наконец-то *.elf, в коде "все успокаивается", не смотря на то, что и *.elf можно использовать в линковке с другими проектами, и брать из него имена объектов.

Читайте также: