Программирование ARM: решение проблем, FAQ IAR EW ARM: устранение ошибок компиляции Sat, March 25 2017  

Поделиться

нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.

IAR EW ARM: устранение ошибок компиляции Печать
Добавил(а) microsin   

На этой страничке решил собрать ошибки, возникающие при компиляции, линковке, отладке в IDE IAR Embedded Workbench for ARM, и методы их устранения.

Ошибка произошла из-за того, что я вынес в отдельный файл pins.c определения составных переменных:

//[Файл pio.h] 
// Вычисление размера массива элементов Pin
// Массив должен быть локальным, (не указателем),
// иначе вычисление будет некорректным.
#define PIO_LISTSIZE(list) (sizeof(list) / sizeof(Pin))
//[Файл board.h]
// Столбцы опроса клавиатуры ( захват ввода )
#define PIN_KBMATRIX_COL_0 {1 << 0, AT91C_BASE_PIOA, AT91C_ID_PIOA, PIO_INPUT, PIO_PULLUP}
#define PIN_KBMATRIX_COL_1 {1 << 1, AT91C_BASE_PIOA, AT91C_ID_PIOA, PIO_INPUT, PIO_PULLUP}
#define PIN_KBMATRIX_COL_2 {1 << 2, AT91C_BASE_PIOA, AT91C_ID_PIOA, PIO_INPUT, PIO_PULLUP}
#define PIN_KBMATRIX_COL_3 {1 << 3, AT91C_BASE_PIOA, AT91C_ID_PIOA, PIO_INPUT, PIO_PULLUP}
#define PINS_KBMATRIX_COLS PIN_KBMATRIX_COL_0, PIN_KBMATRIX_COL_1, PIN_KBMATRIX_COL_2, PIN_KBMATRIX_COL_3
//[Файл pins.c]
#include "board.h"

const Pin pinsCol[] = {PINS_KBMATRIX_COLS};
//[Файл pins.h]
extern const Pin pinsCol[];
//[Файл main.c]
#include "pins.h"#include ... // Вот тут компилятор ругался на PIO_LISTSIZE, // Error [Pe070]: incomplete type is not allowed for (i=0; i < PIO_LISTSIZE(pinsCol); i++) { ...

Ошибка [Pe070] возникала потому, что макрос PIO_LISTSIZE не мог вычислить sizeof(list). Решение: в pins.h указать размер массива. Связано с неудобствами - нужно следить за синхронизировать pins.c и pins.h. Устранение ошибки:

//[Файл pins.h]
extern const Pin pinsCol[4];

На языке C одиночный оператор может состоять из нескольких строк текста. Принято, что не последняя строка такого оператора должна завершаться обратным слешем (\).

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

Error[Pe008]: missing closing quote C:\папка\модуль.c 19
Error[Pe007]: unrecognized token C:\папка\модуль.c 23
Error[Pe065]: expected a ";" C:\папка\модуль.c 23

Пример оператора с ошибкой и без ошибки:

multistring_operator.png

Пропущенный слеш нужно искать в строке перед второй ошибкой.

Долго не мог разобраться, почему возникала ошибка Error[Pe147]: declaration is incompatible with "TParams const __data папка_проекта\vars.c 22 paramDefault" (declared at line 22). У меня была задана структура TParams как тип:

typedef __packed struct _Params{
    u32 paramseed;
    u32 versionARM;
    ...
    u8 lblstate[15];
    u16 CRC;} TParams;

Грешил даже на то, что структура назначалась как переменной, так и константе:

TParams param;const TParams paramDefault = {
    0xAA55AA55,
    0x00000000,
    ...
    LABEL_DEFAULT,
    0x0000};

Оказалось все проще (хотя сразу не разобрался потому, что компилятор не указывал на это место) - я просто забыл ключевое слово const в хедере, где предварительно объявлялись param и paramDefault. Было там так (это ошибочно):

extern TParams param;extern TParams paramDefault;

А для исправления ошибки должно быть так:

extern TParams param;extern const TParams paramDefault;

Иногда после продолжительных правок трудно найти место, где пропустили точку с запятой. Например, компилятор неожиданно выдает ошибки на синтаксис в файле kbmatrix.h, который Вы даже не исправляли:

Remark[Pe082]: storage class is not first C:\asm\testproject\include\kbmatrix.h 25
Error[Pe065]: expected a ";" C:\asm\testproject\include\kbmatrix.h 25
Error[Pe757]: variable "KeyEventCallback" is not a type name C:\asm\testproject\include\kbmatrix.h 54
Error[Pe757]: variable "KeyEventCallback" is not a type name C:\asm\testproject\include\kbmatrix.h 67
Error while running C/C++ Compiler

Для того, чтобы найти действительное место ошибки, нужно найти все включения файла kbmatrix.h - перед ним наверняка будут другие включаемые заголовки, в которых допущена ошибка. Запускаем общий поиск по всему проекту, для чего жмем Ctrl+F и в строке поиска указываем kbmatrix.h. Переключатель "Look in" должен стоять на "Project files and all include files".

missing_semicolon01.PNG

После поиска получим список файлов, которые включают директивой #include файл kbmatrix.h:

C:\asm\testproject\kbmatrix.c 4 #include "kbmatrix.h"
C:\asm\testproject\include\pins.h 3 #include "kbmatrix.h"
C:\asm\testproject\pins.c 6 #include "kbmatrix.h"
----------
Found 3 instances. Searched in 270 files.

В одном из этих найденных файлов (kbmatrix.c, pins.h, pins.c), перед #include "kbmatrix.h" включаются другие файлы, в которых допущена ошибка. Теперь место пропущенной точки с запятой найти легко. В моем примере ошибка была в исправленном файле mytypes.h. Вот содержимое файла pins.h (он был в списке найденных), где добавлялся заголовок mytypes.h с ошибкой:

#include "settings.h"
#include "mytypes.h"
#include "kbmatrix.h"
..

Просмотрев список включаемых файлов, перед #include "kbmatrix.h", я увидел файл, который исправлял - mytypes.h, в нем как раз и была ошибка (пропущена точка с запятой после определения типа структуры).

Возможно, что пропущено словечко void в параметрах при определении функции. Пример:

extern unsigned char DBGU_GetChar ();unsigned char DBGU_GetChar (){
    while ((AT91C_BASE_DBGU->DBGU_CSR & AT91C_US_RXRDY) == 0);
    return AT91C_BASE_DBGU->DBGU_RHR;}

Исправить нужно так:

extern unsigned char DBGU_GetChar (void);unsigned char DBGU_GetChar (void){
    while ((AT91C_BASE_DBGU->DBGU_CSR & AT91C_US_RXRDY) == 0);
    return AT91C_BASE_DBGU->DBGU_RHR;}

Пример лога ошибок:

Error[Pe020]: identifier "FILE" is undefined C:\myproject\precompiled-IAR-libs\at91lib\peripherals\dbgu\dbgu.c 82 
Error[Pe020]: identifier "stdout" is undefined C:\myproject\precompiled-IAR-libs\at91lib\peripherals\dbgu\dbgu.c 84 
Error[Pe020]: identifier "stderr" is undefined C:\myproject\precompiled-IAR-libs\at91lib\peripherals\dbgu\dbgu.c 84 
Error[Pe020]: identifier "FILE" is undefined C:\myproject\precompiled-IAR-libs\at91lib\peripherals\dbgu\dbgu.c 103 
Error[Pe020]: identifier "stdout" is undefined C:\myproject\precompiled-IAR-libs\at91lib\peripherals\dbgu\dbgu.c 141 
Error while running C/C++ Compiler 

Исправить можно, поменяв конфигурацию используемых стандартных библиотек. Откройте свойства проекта (Options...) -> General Options -> Library Configuration -> Library: поставьте Full.

Проблема в том, что в блоке памяти [0x00100000-0x001002db] находится код, до которого не могут достать ассемблерные команды короткого перехода, находящиеся в ассемблерном коде (обычно в других модулях). В этом случае нужно править ассемблерный код, чтобы переходы были длинными, либо переписать код с ассемблера на C (тогда компилятор сам подставит нужные команды). Другое решение указать компилятору генерировать код для режима процессора не thumb, а arm (Свойства проекта (Options...) -> General Options -> C/C++ Compiler -> Processor mode -> Arm).

Чтобы сгенерировать карту памяти и статистику линковки (это поможет найти проблему), откройте свойства проекта (Options...) -> General Options -> Linker -> List -> Поставьте галочки на "Generate linker map file" и на "Generate log file", а также все галочки ниже на опциях лог-файла. Лог-файл см. в файле имя_конфигурации\List\имя_проекта.log, а карту распределения памяти в файле имя_конфигурации\List\имя_проекта.map.

Предупреждение возникало при попытке вызвать из кода на C функцию abs, пример:

Pa = ((float)valU/100000) * ((float)valI/100000) * abs(cos(RadianAngle));

Дело в том, что на простом C (не C++) нельзя распознать прототип функции abs по типу переменной. Только для C++ можно автоматически подставить нужный прототип abs (для разных типов аргументов на C++ имеются разные реализации abs). В данном случае в параметре была переменная типа float, а определение abs подразумевало в параметре целый тип. На самом деле на C нужно напрямую вызывать abs с нужным функционалом по отдельному имени, здесь подойдет fabsf:

//Pa = ((float)valU/100000) * ((float)valI/100000) * abs(cos(RadianAngle));
Pa = ((float)valU/100000) * ((float)valI/100000) * fabsf(cos(RadianAngle));

Пример кода, который генерирует предупреждение Pa091:

unsigned char lblstyle, set, clear;
 
clear = 0x80;
set   = 0x02;
lblstyle &= ~clear;   //тут предупреждение Pa091
lblstyle |= set;

Причина ошибки в том, что оператор инверсии ~ делает приведение типа unsigned char к типу signed int перед инверсией. Для того, чтобы пропало предупреждение, нужно перед инверсией дополнительно вставить ключевое слово unsigned:

unsigned char lblstyle, set, clear;
 
clear = 0x80;
set   = 0x02;//lblstyle &= ~clear;
lblstyle &= ~(unsigned)clear;
lblstyle |= set;

Такая ошибка возникает при логических операциях с константами из перечисления (которые определены через enum). Вот пример кода, который генерирует предупреждение Pe188:

typedef enum{
   MODE_MASK              = 0x00FF,
   TEST                   = 0x4000,
   MODE_INIT              = 0x2000,
   MODE_FIRMWARE          = 0x1000,
   ..
   MODE_LOAD_VARS         = 0x00FE}MODE;
 
MODE appmode = MODE_LOAD_VARS | MODE_INIT;           //Warning[Pe188]!

Причина предупреждения в том, что результатом логической операции будет целочисленный тип, а не тип enum. Исправить можно, если явно указать, что результат операции будет иметь тип перечисления:

MODE appmode = (MODE)(MODE_LOAD_VARS | MODE_INIT);   //OK

Когда 2 заголовочных файла ссылаются друг на друга директивой #include, то могут возникнуть ошибки Error[Pe020]: identifier "имя_идентификатора" имя_файла.h номер строки is undefined (упомянутый идентификатор не найден). К примеру, в заголовочный файл headerA.h подключен файл headerB.h, и также к файлу headerB.h подключен файл headerA.h - тогда блок защиты от повторного включения заголовка [2] не даст видимости всех нужных имен, определенных в первом подключенном заголовке.

////////////////////////////////////////////
// Содержимое заголовка headerA.h
#ifndef __HEADERA__
#define __HEADERA__  #include "headerB.h" //Здесь определения идентификаторов, некоторые
// из которых должны быть видны в заголовке
// headerB.h. ..
#endif //__HEADERA__

////////////////////////////////////////////
// Содержимое заголовка headerB.h
#ifndef __HEADERB__
#define __HEADERB__  #include "headerA.h"  //Здесь определения идентификаторов, некоторые
// из которых должны быть видны в заголовке
// headerA.h. ..
#endif //__HEADERB__

Для того, чтобы исправить эту ситуацию, требуется создать третий заголовок headerC.h, который будет содержать общие для headerA.h и headerB.h определения, и подключить headerC.h в headerA.h и headerB.h, чтобы они не ссылались друг на друга.

////////////////////////////////////////////
//Содержимое заголовка headerA.h
#ifndef __HEADERA__
#define __HEADERA__  //#include "headerB.h"
#include "headerC.h"  //Все идентификаторы, которые должны быть видны
// в headerB.h, перенесены в заголовок headerC.h. .. #endif //__HEADERA__
////////////////////////////////////////////
// Содержимое заголовка headerB.h
#ifndef __HEADERB__
#define __HEADERB__  //#include "headerA.h"
#include "headerC.h"  //Все идентификаторы, которые должны быть видны
// в headerA.h, перенесены в заголовок headerC.h. .. #endif //__HEADERB__

Постоянно достают предупреждения компилятора IAR о неправильной строке форматирования (printf, sprintf, sscanf): "IAR EWB ARM: Remark[Pe181]: argument is incompatible with corresponding format string conversion".

Избавиться от таких предупреждений поможет правильный выбор опции форматирования вывода printf. Для подробной информации см. [3].

  • Функция printf, аргумент u8 (unsigned char), строка формата %u -> надо поменять формат на %i.
  • Функция printf, аргумент u16 (unsigned short), строка формата %u -> надо поменять формат на %i.
  • Функция sscanf, аргумент u8 (unsigned char), строка формата %u или %i -> надо поменять формат на %hu, а тип аргумента на u16 (unsigned short).

Ошибка может возникнуть, если неправильно определена переменная структуры, например:

    //Здесь будет Warning[Pe1000]: a storage class ..
    struct myStructVariable
    {
        char var1;
        int  var2;
    };

Исправить предупреждение Pe1000 можно, если правильно указать экземпляр переменной структуры:

    struct
    {
        char var1;
        int  var2;
    }myStructVariable;

[140110]

Вопрос: мне нужно кастомизировать вывод на собственное устройство отображения (LCD). Все сделал, как написано в документации IAR - определил внешнюю функцию MyLowLevelPutchar, раскомментировал функцию __write, но почему-то при вызовах printf и putchar вывод на LCD не происходит. Функция __write не вызывается, и код, который я написал в теле функции MyLowLevelPutchar, не работает. В чем проблема?

Ответ: Вы все сделали правильно, но кроме этого в некоторых версиях IAR (например 4.20) необходимо особым образом выбрать конфигурацию библиотек DLIB. Это делается через свойства проекта General Options -> Library Configuration -> Library. Попробуйте из выпадающего списка выбрать вариант Normal, и если не заработает, то попробуйте выбрать Full. У меня иерархия вызовов в IAR версии 4.20 работала почему-то следующим образом: если выбрать Full, то последовательность вызова получается printf -> .. -> putchar -> fputc, и в этом варианте ничего не работало (код в MyLowLevelPutchar не вызывается). Если выбрать Normal, то тогда цепочка вызовов printf -> .. -> putchar -> __write, и в этом случае перенаправление вывода работает (потому что в функция __write вызывает пользовательский код из MyLowLevelPutchar).

[140312]

Такая ошибка возникает, когда Вы перенесли проект в другую папку, но перед запуском отладки не сделали очистку и не перекомпилировали проект. В отладочных файлах запомнились старые абсолютные пути до фалов исходного кода, которые теперь не соответствуют реальным. Чтобы исправить ошибку, выберите пункт меню Project -> Clean, после чего запустите отладку. Проект скомпилируется заново, и теперь отладка запустится без ошибки.

[140515]

Для больших проектов время компиляции может составлять большую проблему. Есть несколько советов, как можно уменьшить время компиляции.

1. Убедитесь, что в каждом заголовочном файле у Вас есть защитные заголовки, например:

#ifndef __HFILE_H#define __HFILE_H
   /* ... */#endif

Подробнее про защитные заголовки см. [4]. Кроме того, проверьте файлы исходного кода на лишние подключения заголовков директивой #include. Этот фактор значительно влияет на скорость компиляции. 

2. Создайте предварительно скомпилированные библиотеки, куда поместите редко изменяемые модули: BSP (Board Support Package, пакет поддержки платы разработчика), стеки протоколов и т. д. Это нужно делать периодически для кода, который остается неизменным, и который не нужно перекомпилировать каждый раз, когда что-то меняется в коде приложения. Подробнее см. [5].

3. Запретите генерацию файлов листинга компилятора (Options -> C/C++ Compiler -> List).

4. Если некоторые файлы находятся на сетевом диске (даже если это RAM-диск на сервере), попробуйте сделать их локальными, т. е. скопировать на жесткий диск рабочей станции, и компилировать оттуда. Если это ускорит компиляцию, то значит имелись проблемы, связанные с сетевой файловой системой (NFS/Samba).

5. Если совет 4 помог (при использовании локального диска компиляция ускорилась), то может помочь размещение на локальном диске только объектных файлов и файлов листинга. Для этого измените настройки Project -> Options -> General Options -> Output. Сконфигурируйте пути "Object files" и "List files" так, чтобы они были размещены не на сети, а локально.

6. Если при компиляции бывают случаи, что процесс сильно замедляется или среда IAR зависает, то возможно, что в этот момент происходит сканирование рабочих файлов антивирусом. Такой случай может привести также к сообщениям о запрещенном доступе (denied permission), об истечении таймаута.

Для того, чтобы исключить проверку антивирусом папок IAR, нужно добавить в исключения антивируса следующие папки:

• Инсталляционный каталог IAR (например, это может быть папка C:\Program Files\IAR Systems\Embedded Workbench 5.4).
• C:\Program Files\Common Files\IAR Systems
• C:\Documents and Settings\ USERNAME \Application Data\IAR Embedded Workbench

Здесь USERNAME означает имя (логин пользователя), под которым Вы работаете в системе Windows. Полное имя до этой папки может зависеть от версии операционной системы Windows.

7. Создайте RAM-диск, и попробуйте компилировать проект на нем. Будьте осторожны, поскольку при пропадании питания или зависании компьютера вся Ваша работа потеряется! Как промежуточный вариант, можно на RAM-диск сохранять только объектные файлы, файлы листинга и выходные файлы, для этого в свойствах проекта General Options -> Output поменяйте пути до выходных рабочих директорий Output directories.

Самое простое средство для создания RAM диска - бесплатная программа SoftPerfect RAM Disk site:softperfect.com.

[161019]

При попытке компиляции выскакивают совершенно неожиданные ошибки - невозможно удалить файл, который либо вообще не используется в проекте, либо находится в каталоге установки IAR. Например, у меня появлялась вот такая ошибка, когда я попробовал сменить конфигурацию Release на Debug:

Building configuration: guilibprj - Debug 
Updating build tree... 
Failed to delete C:\Program Files (x86)\IAR Systems\Embedded Workbench 5.4\ 
arm\inc\wchar.h 
 
0  file(s) deleted. 
Updating build tree... 
 
Build aborted.

Очевидно, что это полный бред - зачем очистке понадобилось вдруг удалять файл wchar.h, который относится к стандартным библиотекам, да и еще находится в каталоге установки среды разработки IAR Embedded Workbench?..

Причина ошибки заключается в том, что произошла рассинхронизация конфигураций Release и Debug. Конфигурация Debug давно не использовались, в то время как конфигурацию Release вносились изменения. Похоже, что глючит обработчик списка зависимостей, причем могут происходить даже еще более неприятные глюки, вплоть до удаления рабочих заголовочных файлов проекта (поэтому чаще делайте бэкапы!).

Решение проблемы заключается в просмотре обоих конфигураций и внесение корректных изменений в конфигурацию, где происходит эта ошибка. Особенно внимательно следует проверить дополнительные пути поиска подключаемых файлов, а также наличие предварительно определенных переключающих макросимволов (Project -> Options -> C/C++ Compiler -> Preprocessor).

Довольно часто появляющаяся ошибка в разных версиях IAR. Разработчики борются с этой ошибкой, и с переменным успехом. Обычно проблема решается, если несколько раз выполнить операцию очистки (Project -> Clean). Иногда помогает перезапуск IAR + выполнение очистки проекта.

Еще одна из причин такой ошибки - в проекте имеется 2 подключенных исходных файла, компиляция которых приводит к генерации одного и того же объектного файла. Например, есть две версии одного и того же модуля - один на языке C (с расширением *.c), и другой оптимизированный, на ассемблере (с тем же именем, но с расширением *.s). Решение проблемы очевидно - нужно исключить из процесса компиляции один из этих файлов.

Чтобы переименовать workspace, но сохранить при этом в нем те же самые имена проектов, что и были раньше, просто переименуйте файл *.eww.

Поскольку среда разработки IAR EWB не предоставляет прямого функционала для специального переименования проекта, требуется обходной путь для этого действия. Когда я переименовываю workspace и связанный с ним проект, то поступаю следующим образом:

1. Делаю копию Template.ewp (это файл проекта) и переименовываю копию, скажем, в NewProject.ewp.
2. Открываю файл workspace Template.eww.
3. Выбираю Project -> Add Existing Project...
4. Выбираю NewProject.ewp и кликаю Open.
5. Выбираю закладку "Template" в нижней части списка файлов Workspace (соответствует старому проекту).
6. Делаю правый клик на старом проекте и выбираю Remove.
7. Закрываю IAR EWB, он запросит сохранить workspace, сохраняю.
8. Переименовываю файлы старого workspace *.eww, *.ewd в NewProject.eww, NewProject.ewd (и также файл *.dep, если он у Вас есть).
9. Удаляю Template.ewp.

После этого Вы сможете открыть NewProject.eww как полностью переименованное workspace и полностью переименованный проект. Если Вы хотите только переименовать проект, и оставить старое имя для workspace, то выполните только шаги 1..7.

См. также:

Renaming in IAR Embedded Work Bench site:stackoverflow.com
IAR EW ARM: как перенести проект в другую папку
Расширения файлов IAR для процессоров ARM

[Ссылки]

1. Что такое lvalue и rvalue?
2. IAR EW ARM: применение #ifndef и #define для разрешения конфликтов включаемых файлов (#include).
3. IAR EWB ARM: форматированный вывод printf библиотеки DLIB.
4. IAR EW ARM: применение #ifndef и #define для разрешения конфликтов включаемых файлов (#include).
5. IAR: создание библиотеки

 

Добавить комментарий


Защитный код
Обновить

Top of Page