Портирование кода IAR на AVR GCC |
Добавил(а) microsin |
Язык C был изначально разработан с целью портирования кода. Имеется два вида портирования - перенос приложения на другую платформу (другая операционная система и/или процессор), и перенос приложения на другой компилятор. Портирование на другой компилятор может быть усложнено, когда программа относится ко встраиваемым системам (embedded system - приборы и автоматика, построенные на микроконтроллере). Например, стандарт языка C странным образом не определяет правила для декларирования и определения обработчиков прерывания (Interrupt Service Routine, ISR). Разные компиляторы это делают по-разному, и используют разные регистры микроконтроллера, а также используют нестандартные структуры языка. В этой статье описаны некоторые методы и указания по портированию программ для AVR (код firmware) с проприетарного компилятора IAR (IDE проектирования IAR Embedded Workbench for AVR) на тулчейн GNU (AVR GCC, на платформе Windows это пакет WinAVR). Имейте в виду, что статья может оказаться неполным набором указаний по такому портированию. Здесь и далее перевод руководства "Porting From IAR to AVR GCC" с сайта www.nongnu.org. [Регистры AVR] Заголовочные файлы, описывающие адресное пространство регистров ввода/вывода (IO header files), содержат мнемонические идентификаторы для всех имен регистров и имен бит для каждой конкретной модели микроконтроллера. IAR имеет индивидуальные файлы заголовков для каждого процессора, и они должны быть подключены к коду, когда в нем используются имена регистров. Название подключаемого заголовочного файла часто связано с моделью применяемого микроконтроллера. Например: #include < iom169.h >
Примечание: IAR не всегда использует те же самые имена регистров или бит, которые определены в даташите Atmel для микроконтроллеров AVR. AVR GCC также имеет индивидуальные заголовочные файлы для каждого процессора. Однако реальный тип процессора указывается как опция командной строки компилятора (опция -mmcu=тип_микроконтроллера). Это часто делается через переменные makefile, как например MCU=atmega32. Такой вариант реализации позволяет Вам подключать всегда один и тот же заголовочный файл для любого типа процессора: #include < avr/io.h >
Примечание: прямой слеш в имени файла avr/io.h для разделения подпапок может использоваться и в дистрибутивах тулчейна для Windows, и это является рекомендуемым методом для подключения файла заголовка IO (несмотря на то, что в именах путей Windows изначально использовался обратный слеш \). Использование прямого слеша / предпочтительнее, так как обратный слеш используется для указания символов типа \0, \n, \r и т. п. Компилятор (из опции командной строки -mcu) знает тип процессора, и может с помощью одного вышеуказанного файла подключить тот индивидуальный заголовочный файл IO, который действительно необходим. Достоинство такого метода в том, что Вам нужно указать только один стандартный заголовочный файл avr/io.h, и можете проще портировать Ваше приложение на другой тип процессора без необходимости корректировки всех модулей для подключения нового файла заголовка IO. Тулчейн AVR (WinAVR + библиотека avr-libc) пытается точно придерживаться оригинальных названий регистров и бит, которые можно найти в даташите AVR. Может быть некоторое несоответствие между именами регистров в IO заголовках IAR по сравнению с IO заголовками AVR GCC. [Обработчики прерывания ISR (Interrupt Service Routines)] Как уже упоминалось, язык C не имеет собственного стандарта для определения ISR. Так что у каждого компилятора может оказаться свой собственный способ, как это нужно делать. IAR декларирует ISR так: #pragma vector=TIMER0_OVF_vect __interrupt void MotorPWMBottom() { // код обработчика прерывания } В AVR GCC Вы декларируете ISR так: ISR(PCINT1_vect) { // код обработчика прерывания } AVR GCC использует макрос для определения ISR. Этот макрос требует подключения файла заголовка: #include < avr/interrupt.h >
Имена разных векторов прерываний можно найти в индивидуальном файле заголовка IO, который должен подключаться через файл avr/io.h. Примечание: имена векторов прерываний в AVR GCC были изменены для соответствия именам векторов в IAR. Это значительно упрощает портирование приложений с IAR на AVR GCC. [Встроенные в язык подпрограммы, соответствующие командам ассемблера (Intrinsic Routines)] IAR имеет некоторые подпрограммы intrinsic, такие как __enable_interrupts(), __disable_interrupts(), __watchdog_reset(). Эти функции intrinsic компилируются в специальные коды команд ассемблера AVR (SEI, CLI, WDR). Имеются эквивалентные макросы в AVR GCC, однако все они не размещены в одном заголовочном файле. AVR GCC имеет sei() для замены __enable_interrupts() и cli() для __disable_interrupts(). Оба эти макроса размещены в файле заголовка avr/interrupts.h. Также AVR GCC имеет макрос wdt_reset(), который заменяет __watchdog_reset(). Однако весь программный интерфейс сторожевого таймера (Watchdog Timer API) в AVR GCC можно найти в заголовочном файле avr/wdt.h. [Переменные, размещенные в памяти Flash] Язык C не был разработан для Гарвардской архитектуры процессоров, в которой заданы разные адресные пространства памяти (отдельно для кода и отдельно для переменных). Это означает, что нужны некоторые нестандартные пути для определения переменных, которые размещены в области памяти программ (для AVR это память Flash). IAR использует нестандартное ключевое слово для декларирования переменной в памяти программ: __flash int mydata[] = .... AVR GCC использует атрибуты переменной для достижения того же самого эффекта: int mydata[] __attribute__((progmem)) Примечание: см. руководство пользователя GCC (GCC User Manual) для дополнительной информации по атрибутам переменной. Библиотека avr-libc предоставляет удобный макрос для атрибута переменной, размещаемой во Flash: #include < avr/pgmspace.h > . . . int mydata[] PROGMEM = .... Примечание: макрос PROGMEM раскрывается в атрибут переменной progmem. Этот макрос требует подключения заголовочного файла avr/pgmspace.h. Это канонический метод для определения переменной в области памяти программ. Чтобы прочитать данные из flash, используются макросы pgm_read_*(), также заданные в файле avr/pgmspace.h. В этом файле определены все макросы, которые нужны для работы с памятью программ (Flash). Есть также метод для определения переменных в памяти программ, который является общим для двух компиляторов (IAR и AVR GCC). Создайте заголовочный файл, который имеет следующие определения: #if defined(__ICCAVR__) // IAR C Compiler #define FLASH_DECLARE(x) __flash x #endif #if defined(__GNUC__) // GNU Compiler #define FLASH_DECLARE(x) x __attribute__((__progmem__)) #endif Этот кусок кода проверяет версию компилятора (какой компилятор используется - IAR или GCC) и определяет макрос FLASH_DECLARE(x), который будет декларировать переменную в памяти программ, используя подходящий метод для каждого компилятора. Вы можете использовать его так: FLASH_DECLARE(int mydata[] = ...); [Функция main() без возврата значения (Non-Returning main)] Декларирование main() без кода возврата в IAR делается так: __C_task void main(void) { // код программы } Чтобы выполнить то же самое в AVR GCC, сделайте следующее: void main(void) __attribute__((noreturn)); void main(void) { // код программы } Примечание: см. руководство пользователя GCC (GCC User Manual) для дополнительной информации по атрибутам функции. В AVR GCC нужно задать прототип для main(), где будет указано, что функция main() имеет тип "noreturn". Затем определите main() как обычно. Имейте в виду, что возвращаемый тип для функции main() теперь будет void. [Резервирование регистров] Компилятор IAR позволяет пользователю зарезервировать регистры общего назначения от r15 и ниже путем использования опций компилятора и следующих ключевых слов синтаксиса: __regvar __no_init volatile unsigned int filteredTimeSinceCommutation @14; Эта строка блокирует r14 для исключительного использования в Вашем коде под именем переменной "filteredTimeSinceCommutation". Это означает, что компилятор не может использовать этот регистр для генерации общего кода. Чтобы выполнить то же самое в AVR GCC, сделайте следующее: register unsigned char counter asm("r3"); Обычно таким способом можно использовать регистры от r2 до r15. Примечание: не резервируйте r0 или r1, которые используются внутри компилятора для временного хранения данных и для нулевой величины. Резервирование регистров не рекомендуется в AVR GCC, так как это выводит эти регистры из-под контроля компилятора, что может ухудшить качество генерируемого объектного кода (который, кстати говоря, и так хуже кода, генерируемого компилятором IAR). Так что используйте резервирование регистров на собственный страх и риск. |