Но оказалось, что все мои опасения были беспочвенны, ведь существует готовая библиотека, решающая эти и многие другие проблемы, например проблему отсутствия драйверов к периферии ядра микроконтроллера (USB, I2C, SPI и все остальное). Эта библиотека называется CMSIS - Cortex Microcontroller Software Interface Standard. Она стандартизирована и, как правило, производитель сам пишет свою версию этой библиотеки для производимого им микроконтроллера. Микроконтроллер, естественно, должен принадлежать к семейству Cortex'ов.
В библиотеке CMSIS содержатся следующие хорошие вещи:
Открываем LPCXpresso IDE и приступаем...
Для начала, надо импортировать библиотеку CMSIS, поставляемую вместе со средой разработки. Как это сделать - описано в руководстве по LPCXpresso для начинающих. Ссылка на него - в конце статьи.
После импорта главное окно IDE должно выглядеть примерно так:
Процесс создания нового проекта описан в уже упомянутом руководстве для начинающих. Увы, но руководство видимо устарело и охватывает не все опции мастера создания проектов в IDE. Некоторые, наиболее важные вещи я опишу здесь.
Во-первых, если вы пошли по пути, предложенному авторами руководства, и создали новый Workspace, то для начала нужно заново импортировать в него библиотеку CMSIS.
Во-вторых, вдобавок к опции включения CMSIS в проект, добавлена опция использования CRP:
Что же это такое?
CRP - это защита от считывания кода, дабы злыекитайцы конкуренты не уперли прошивку. В даташите на LPC1768 говорится, что существуют три уровня защиты:
Теперь все готово - после нажатия на кнопку "Finish" мы получим готовый и уже открытый проект.
Попробуем теперь, ради теста, скомпилировать и запустить на плате наш новый проект. В "Quickstart Panel->Start here" жмем на кнопку "Build 'test1' [Debug]" (в квадратных скобках указана текущая конфигурация - Debug или Release - настраивается в настройках проекта). Если все было сделано верно, то мы получим скомпилированный бинарник, который всего лишь инкрементирует счетчик в бесконечном цикле.
Теперь прошьем его в плату. Для этого надо лишь нажать на кнопку "Debug 'test1' [Debug]" (чуть-чуть пониже предыдущей) и немного подождать. Подключенная к компьютеру плата помигает светодиодами и микроконтроллер приступит к выполнению фнукции main(), которое тут же будет прервано брекпойнтом. Продолжим выполнение программы и начнем наслаждаться бессмысленной тратой ресурсов...
Вот так - не нужно писать стартовый код и инициализировать вручную кучу устройств для запуска микроконтроллера!
К сожалению, библиотека CMSIS, поставляемая с LPCXpresso IDE, включает в себя лишь базовые модули - драйверов устройств, как в [1] там нет.
Конечно, есть возможность прикрутить к LPCXpresso вышеуказанную библиотеку через "Import Example project(s)" - архив с библиотекой в требуемом формате есть на сайте Code Red [5]. Но у меня попытки скомпилировать данную библиотеку не увенчались успехом, даже после ручной правки десятка исходных файлов...
Ручная компиляция библиотеки с драйверами устройств тоже не вполне удалась. Несмотря на заявленную поддержку инструментальных средств GNU, под ними имеется в виду лишь тулчейн от CodeSourcery. Как выяснилось, тулчейн поставляемый с LPCXpresso и arm-linux-eabi-* тулчейн из репов убунты не особо подходят - может с их помощью и можно скомпилировать HelloWorld с данной библиотекой, но у меня это не вышло - линковщик ругался на -lcs3 not found, причем полезной информации об этой ошибке в гугле - ноль байт.
Используем CodeSourcery G++ под Linux'ом
К сожалению, из всех редакций CodeSourcery G++ полностью бесплатной является лишь Sourcery G++ Lite Edition - свободная, не поддерживаемая разработчиками версия, имеющая в своем составе только лишь утилиты командной строки (компилятор, линковщик, отладчик и т.д.). Скачать ее можно по ссылке в конце поста [6].
Установка тулчейна не сложнее установки любой виндосовской программы (да-да, вы таки не ослышались!):
Теперь можно приступать к компиляции CMSIS с драйверами [1]. Чтобы скомпилировать ее под Linux'ом, а не под Windows'ом, придется сделать несколько телодвижений.
Во-первых, нужно поменять обратные слеши в путях файловой системы на прямые в следующих файлах:
Перед компиляцией библиотеки рекомендую залезть в файл makeconfig и поменять там путь к каталогу с деревом исходных кодов библиотеки (PROJ_ROOT), путь к каталогу с CodeSourcery тулчейном (GNU_INSTALL_ROOT) и версию тулчейна (GNU_VERSION) на актуальные в настоящий момент. Если хочется компилировать программы с использованием библиотеки CMSIS, то можно еще поправить слеши в строчке:
Теперь, когда у нас есть скомпилированная версия библиотеки CMSIS, с драйверами устройств внутри, можно попробовать написать более полезную программу, чем предыдущая. Мы будем зажигать светодиод!
Согласно схеме LPCXpresso из руководства пользователя, на плате есть светодиод, подключенный к порту P0[22]:
Для того, чтобы его зажечь, достаточно использовать вывод 22 порта №0 как вывод GPIO-порта, подтянутый к нулю. Затем нужно записать единицу в соответствующий регистр порта. С применением CMSIS, все это делается так:
Прошивать проект можно при помощи LPCXpresso IDE - другого способа работать со встроенным JTAG-отладчиком LPCXpresso Board в Linux'е я не нашел.
Делается это так - создается/открывается простейший проект (наподобие нашей первой программы), затем он компилируется, чтобы среда разработки не пыталась перекомпилировать его перед прошивкой.
В процессе компиляции видно, что прошиваемым в плату файлом является projectname.axf:
Этот файл нужно найти в файловой системе и подменить его ELF-файлом нашего проекта. Затем как обычно - Debug и Resume (F8).
Лезем под капот CMSIS
Основная часть Core Peripheral Access Layer (для Cortex-M3 естественно) содержится в файлах core_cm3.c и core_cm3.h. В них лежат объявления и определения различных структур данных, извлекаемых из регистров:
Ссылки
- Определения регистров, входящих в ядро ARM, для упрощения доступа к ним.
- Определения функций для работы с базовыми периферийными устройствами внутри микроконтроллера (например с таймерами).
- Также в нее могут входить функции для работы с остальными периферийными устройствами, которые производитель включил в микроконтроллер.
Использование данной библиотеки в программах для встраиваемой вычислительной техники позволяет достаточно просто комбинировать программы от различных производителей внутри одного кристалла - обращения к железу проходят через единый, стандартизированный интерфейс.
Также данная библиотека сохранит время и нервы программистам - во-первых нет необходимости писать заново (в тысячный раз) писать драйвер для работы какого-нибудь SPI-интерфейса, а во вторых можно без проблем использовать код из примеров от производителя или код других разработчиков, если конечно они тоже использует CMSIS.
Перейдем теперь от теории к практике.
Вначале, в качестве среды разработки я буду использовать основанную на Eclipse среду разработки, поставляемую вместе с платами LPCXpresso. Эта среда разработки привязывается к конфигурации компьютера и требует ключи активации. Но тем не менее все бесплатно, но работает ли (и работает ли полноценно) эта IDE без активации я не проверял.
Вначале, в качестве среды разработки я буду использовать основанную на Eclipse среду разработки, поставляемую вместе с платами LPCXpresso. Эта среда разработки привязывается к конфигурации компьютера и требует ключи активации. Но тем не менее все бесплатно, но работает ли (и работает ли полноценно) эта IDE без активации я не проверял.
Открываем LPCXpresso IDE и приступаем...
Для начала, надо импортировать библиотеку CMSIS, поставляемую вместе со средой разработки. Как это сделать - описано в руководстве по LPCXpresso для начинающих. Ссылка на него - в конце статьи.
После импорта главное окно IDE должно выглядеть примерно так:
Импорт CMSIS удался! |
Во-первых, если вы пошли по пути, предложенному авторами руководства, и создали новый Workspace, то для начала нужно заново импортировать в него библиотеку CMSIS.
Во-вторых, вдобавок к опции включения CMSIS в проект, добавлена опция использования CRP:
Что же это такое?
CRP - это защита от считывания кода, дабы злые
- На первом уровне запрещается доступ к чипу через JTAG и допускается лишь частичное изменение Flash-памяти чипа (нельзя менять содержимое нулевого сектора) с использованием лишь определенного набора ISP команд.
- На втором уровне защиты доступ к чипу через JTAG по прежнему запрещен, при этом допускается лишь полная запись/стирание Flash-памяти с использованием сильно ограниченного набора ISP команд.
- На третьем уровне доступ к чипу запрещен и через JTAG и через ISP! Менять содержимое Flash-памяти можно только при помощи IAP (In Application Programming - когда программа, залитая в чип, сама меняет содержимое Flash-памяти) или же запросив восстановление работоспособности ISP у пользовательского приложения через UART0.
Теперь все готово - после нажатия на кнопку "Finish" мы получим готовый и уже открытый проект.
Попробуем теперь, ради теста, скомпилировать и запустить на плате наш новый проект. В "Quickstart Panel->Start here" жмем на кнопку "Build 'test1' [Debug]" (в квадратных скобках указана текущая конфигурация - Debug или Release - настраивается в настройках проекта). Если все было сделано верно, то мы получим скомпилированный бинарник, который всего лишь инкрементирует счетчик в бесконечном цикле.
Компиляция завершена успешно! |
Бессмысленная трата ресурсов в самом разгаре |
К сожалению, библиотека CMSIS, поставляемая с LPCXpresso IDE, включает в себя лишь базовые модули - драйверов устройств, как в [1] там нет.
Конечно, есть возможность прикрутить к LPCXpresso вышеуказанную библиотеку через "Import Example project(s)" - архив с библиотекой в требуемом формате есть на сайте Code Red [5]. Но у меня попытки скомпилировать данную библиотеку не увенчались успехом, даже после ручной правки десятка исходных файлов...
Ручная компиляция библиотеки с драйверами устройств тоже не вполне удалась. Несмотря на заявленную поддержку инструментальных средств GNU, под ними имеется в виду лишь тулчейн от CodeSourcery. Как выяснилось, тулчейн поставляемый с LPCXpresso и arm-linux-eabi-* тулчейн из репов убунты не особо подходят - может с их помощью и можно скомпилировать HelloWorld с данной библиотекой, но у меня это не вышло - линковщик ругался на -lcs3 not found, причем полезной информации об этой ошибке в гугле - ноль байт.
Используем CodeSourcery G++ под Linux'ом
К сожалению, из всех редакций CodeSourcery G++ полностью бесплатной является лишь Sourcery G++ Lite Edition - свободная, не поддерживаемая разработчиками версия, имеющая в своем составе только лишь утилиты командной строки (компилятор, линковщик, отладчик и т.д.). Скачать ее можно по ссылке в конце поста [6].
Установка тулчейна не сложнее установки любой виндосовской программы (да-да, вы таки не ослышались!):
Next, Next, Next, Finish! |
Во-первых, нужно поменять обратные слеши в путях файловой системы на прямые в следующих файлах:
- LPC1700CMSIS/makefile
- LPC1700CMSIS/makesection/makeconfig
- LPC1700CMSIS/makesection/makerule/common/make.rules.environment
- LPC1700CMSIS/Drivers/source/makefile
Во-вторых, необходимо поменять строчку #include "lpc17xx.h", на #include "LPC17xx.h" - юниксовые файловые системы критичны к регистру в именах файлов. Менять надо файлы:
- LPC1700CMSIS/Drivers/include/lpc17xx_pinsel.h
- LPC1700CMSIS/Drivers/include/lpc17xx_clkpwr.h
В третьих, надо убрать префикс $(TOOLS_PATH)/ из путей к стандартным юниксовым утилитам (всякие ls, mv, cp) в файле make.rules.environment.
Для тех, кому лень менять все это ручками, я сделал небольшой патч (см. ссылки в конце). Применять его следует командой:
patch -p0 -i CMSIS_LPC1700-linux.patchв родительском каталоге каталога LPC1700CMSIS.
Перед компиляцией библиотеки рекомендую залезть в файл makeconfig и поменять там путь к каталогу с деревом исходных кодов библиотеки (PROJ_ROOT), путь к каталогу с CodeSourcery тулчейном (GNU_INSTALL_ROOT) и версию тулчейна (GNU_VERSION) на актуальные в настоящий момент. Если хочется компилировать программы с использованием библиотеки CMSIS, то можно еще поправить слеши в строчке:
include $(PROJ_ROOT)\makesection\makerule\common\make.rules.ftypesв файле LPC1700CMSIS/makesection/makerule/example/makefile.ex.
Теперь, когда у нас есть скомпилированная версия библиотеки CMSIS, с драйверами устройств внутри, можно попробовать написать более полезную программу, чем предыдущая. Мы будем зажигать светодиод!
Согласно схеме LPCXpresso из руководства пользователя, на плате есть светодиод, подключенный к порту P0[22]:
Для того, чтобы его зажечь, достаточно использовать вывод 22 порта №0 как вывод GPIO-порта, подтянутый к нулю. Затем нужно записать единицу в соответствующий регистр порта. С применением CMSIS, все это делается так:
#include "lpc17xx_pinsel.h" #include "lpc17xx_gpio.h" int main() { PINSEL_CFG_Type led2_pin; led2_pin.Portnum = PINSEL_PORT_0; led2_pin.Pinnum = PINSEL_PIN_22; led2_pin.Funcnum = PINSEL_FUNC_0; led2_pin.Pinmode = PINSEL_PINMODE_PULLDOWN; led2_pin.OpenDrain = PINSEL_PINMODE_NORMAL; PINSEL_ConfigPin(&led2_pin); GPIO_SetDir(PINSEL_PORT_0, 0x00400000, 1); GPIO_SetValue(PINSEL_PORT_0, 0x00400000); while(1) {} return 0; }Все просто и код писать практически не нужно. Кстати, практически не нужно писать и Makefile! Можно воспользоваться всем тем, что нам предоставляет CMSIS:
EXDIRINC=. EXECNAME=LED_blink_LPC1768 CMSIS_INSTALL=/home/drag0n/Загрузки/lpc17xx.cmsis.driver.library/LPC1700CMSIS include $(CMSIS_INSTALL)/makesection/makeconfig include $(CMSIS_INSTALL)/makesection/makerule/example/makefile.ex clean: rm -f LED_blink_LPC1768* \ main.o .PHONY: clean
Прошивать проект можно при помощи LPCXpresso IDE - другого способа работать со встроенным JTAG-отладчиком LPCXpresso Board в Linux'е я не нашел.
Делается это так - создается/открывается простейший проект (наподобие нашей первой программы), затем он компилируется, чтобы среда разработки не пыталась перекомпилировать его перед прошивкой.
В процессе компиляции видно, что прошиваемым в плату файлом является projectname.axf:
test1.axf |
Лезем под капот CMSIS
Составляющие части библиотеки CMSIS |
/** @addtogroup CMSIS_CM3_SysTick CMSIS CM3 SysTick memory mapped structure for SysTick @{ */ /** @brief System Tick Timer (SysTick) register structure definition */ typedef struct { __IO uint32_t CTRL; /*!< Offset: 0x00 SysTick Control and Status Register */ __IO uint32_t RELOAD; /*!< Offset: 0x04 SysTick Reload Value Register */ __IO uint32_t CURR; /*!< Offset: 0x08 SysTick Current Value Register */ __IO uint32_t CALIB; /*!< Offset: 0x0C SysTick Calibration Register */ } SysTick_Type;масок и смещений:
/* SysTick Control / Status Register Definitions */ #define SysTick_CTRL_COUNTFLAG_Pos 16 /*!< SysTick CTRL: COUNTFLAG Position */ #define SysTick_CTRL_COUNTFLAG_Msk (1ul << SysTick_CTRL_COUNTFLAG_Pos) /*!< SysTick CTRL: COUNTFLAG Mask */ #define SysTick_CTRL_CLKSOURCE_Pos 2 /*!< SysTick CTRL: CLKSOURCE Position */ #define SysTick_CTRL_CLKSOURCE_Msk (1ul << SysTick_CTRL_CLKSOURCE_Pos) /*!< SysTick CTRL: CLKSOURCE Mask */ #define SysTick_CTRL_TICKINT_Pos 1 /*!< SysTick CTRL: TICKINT Position */ #define SysTick_CTRL_TICKINT_Msk (1ul << SysTick_CTRL_TICKINT_Pos) /*!< SysTick CTRL: TICKINT Mask */ #define SysTick_CTRL_ENABLE_Pos 0 /*!< SysTick CTRL: ENABLE Position */ #define SysTick_CTRL_ENABLE_Msk (1ul << SysTick_CTRL_ENABLE_Pos) /*!< SysTick CTRL: ENABLE Mask */ /* SysTick Reload Register Definitions */ #define SysTick_LOAD_RELOAD_Pos 0 /*!< SysTick LOAD: RELOAD Position */ #define SysTick_LOAD_RELOAD_Msk (0xFFFFFFul << SysTick_LOAD_RELOAD_Pos) /*!< SysTick LOAD: RELOAD Mask */ /* SysTick Current Register Definitions */ #define SysTick_VAL_CURRENT_Pos 0 /*!< SysTick VAL: CURRENT Position */ #define SysTick_VAL_CURRENT_Msk (0xFFFFFFul << SysTick_VAL_CURRENT_Pos) /*!< SysTick VAL: CURRENT Mask */ /* SysTick Calibration Register Definitions */ #define SysTick_CALIB_NOREF_Pos 31 /*!< SysTick CALIB: NOREF Position */ #define SysTick_CALIB_NOREF_Msk (1ul << SysTick_CALIB_NOREF_Pos) /*!< SysTick CALIB: NOREF Mask */ #define SysTick_CALIB_SKEW_Pos 30 /*!< SysTick CALIB: SKEW Position */ #define SysTick_CALIB_SKEW_Msk (1ul << SysTick_CALIB_SKEW_Pos) /*!< SysTick CALIB: SKEW Mask */ #define SysTick_CALIB_TENMS_Pos 0 /*!< SysTick CALIB: TENMS Position */ #define SysTick_CALIB_TENMS_Msk (0xFFFFFFul << SysTick_VAL_CURRENT_Pos) /*!< SysTick CALIB: TENMS Mask */ /*@}*/ /* end of group CMSIS_CM3_SysTick */а также различных вспомогательных функций:
static __INLINE void __enable_irq() { __ASM volatile ("cpsie i"); } static __INLINE void __disable_irq() { __ASM volatile ("cpsid i"); } static __INLINE void __enable_fault_irq() { __ASM volatile ("cpsie f"); } static __INLINE void __disable_fault_irq() { __ASM volatile ("cpsid f"); } static __INLINE void __NOP() { __ASM volatile ("nop"); } static __INLINE void __WFI() { __ASM volatile ("wfi"); } static __INLINE void __WFE() { __ASM volatile ("wfe"); } static __INLINE void __SEV() { __ASM volatile ("sev"); } static __INLINE void __ISB() { __ASM volatile ("isb"); } static __INLINE void __DSB() { __ASM volatile ("dsb"); } static __INLINE void __DMB() { __ASM volatile ("dmb"); } static __INLINE void __CLREX() { __ASM volatile ("clrex"); }Также, в этот же уровень входит несколько специфичных для каждого микроконтроллера файлов, поставляемых производителем. У CMSIS от NXP это:
- файл LPC17xx.h, который является главным заголовочным файлом для других уровней CMSIS (например для драйверов периферийных устройств). В нем определены регистры микроконтроллера, номера прерываний специфические для LPC17xx серии и т.д.
- файл startup_LPC17xx.s - ассемблерный файл со стартовым кодом. Есть несколько версий этого файла - для GCC, для IAR и для Keil'а.
Оставшиеся два уровня (Device Peripheral Access Layer и Access Functions for Peripherals) заключены внутри содержимого директории LPC1700CMSIS/Drivers. Исходный код драйвера к какому-нибудь устройству можно найти в файлах lpc17xx_peripheralname.c [.h]. В этих файлах содержатся определения для регистров и областей памяти периферии и вспомогательные функции для работы с этой периферией. Если необходимо работать с каким-нибудь из драйверов в своей программе, то достаточно подключить соответствующий заголовочный файл и внимательно прочитать его (или Doxygen-овскую документацию, чтобы разобраться как с ним работать).
Например, если нам необходимо работать с I2C, то нужно использовать файл lpc17xx_i2c.h и файл lpc17xx_pinsel.h - чтобы использовать соответствующие выводы микроконтроллера для шин SDA и SCL.
Также, в библиотеке CMSIS от NXP содержится каталог с настройками среды сборки - ./makesection. Есть еще и весьма полезные примеры - каталог ./Examples.
Всех интересующихся отсылаю к документации, поставляемой с библиотекой - ./LPC1700CMSIS/LPC1700 Peripheral Driver Library Manual.chm.
Ссылки
- CMSIS-Compliant библиотека для LPC17xx: GNU, Keil, IAR (lpc17xx.cmsis.driver.library.zip, 20.7 Мб)
- LPCXpresso IDE (Linux, Windows, для скачивания нужна регистрация)
- Руководство для начинающих по LPCXpresso IDE (lpcxpresso.getting.started.pdf, 3.0 Мб)
- Руководство пользователя по чипам LPC17xx (user.manual.lpc17xx.pdf, 4.9 Мб)
- Библиотека CMSIS для LPC17xx с драйверами периферийных устройств, пригодная для импорта в LPCXpresso IDE (lpc.cmsis.driver.library.zip, 10 Мб)
- Code Sourcery G++ Lite Edition 2010.09-51 for ARM EABI (~100 Мб)
- Code Sourcery G++ Lite Edition for ARM
- Патч для CMSIS-Compliant библиотеки для LPC17xx. С ним она компилируется в Linux'е. (CMSIS_LPC1700-linux.patch, 7669 байт)
- Программа, зажигающая светодиод на плате (LED_blink_LPC1768.tar.gz, 638 байт)