пятница, 25 марта 2016 г.

Знакомство с микроконтроллерами ARM Cortex M на примере LPC1114

В последнее время всё большую популярность набирают микроконтроллеры построенные на основе архитектуры ARM. Есть мнение, что эра восьмиразрядных микроконтроллеров подходит к концу, так как им трудно тягаться в производительности с 32-разрядными ARM Cortex M-ядрами. Более того, ARM-чипы уже опережают 8-битных конкурентов не только по соотношение цена/производительность, но и просто по цене.

Архитектура процессоров ARM и семейство Cortex M

Понятие "архитектура ARM" объединяет довольно большое семейство микроархитектур 32 и 64-разрядных RISC-процессоров. Различные микроархитектуры имеют свои особенности и свои области применения. Поэтому при первоначальнои поиске информации нужно понимать, что "ARM ARMу рознь". В этом посте речь пойдёт о микроархитектуре ARM Cortex-M, ориентированной на микроконтроллеры, и у неё, конечно, есть свои особенности, которые надо учитывать при первоначальном поиске информации. Например, процессоры ARM могут поддерживать два набора инструкций: 32-битные инструкции ARM и 16-битные Thumb. При этом ARM Cortex-M поддерживает только Thumb инструкции. Кроме того, процедура начальной загрузки, которую мы будем тут рассматривать, отличается для микроконтроллеров Cortex-M от таковой для большинства других ARM-микроархитектур.

Микроконтроллеры Cortex-M в свою очередь делятся на несколько семейств в зависимости от ядра. Семейство на самых простых ядрах Cortex-M0 - микроконтроллеры предназначенные ситуаций, где самый важный фактор - цена. Как правило они имеют не столь богатую периферию как Cortex-M3/M4/M7. Лично мне кажется, что чем меньше навротов тем проще изучение (хотя бы потому, что datasheet короче, и досконально изучить микроконтроллер гораздо проще). С другой стороны, реализация мигания светодиодом очень похожа и для LPC1114 (Cortex-M0) и для какого-нибудь STM32F4 (Cortex-M4F). Впрочем, хотя микроконтроллеры ARM Cortex-M от разных производителей хотя и имеют одинаковое "ядро", в периферии могут иметь большое количество различий, тонкостей и своих граблей. Поэтому не стоит недооценивать опыт работы с конкретным микроконтроллером и вдумчивое чтение документации.

На рынке есть большое количество отладочных плат с этими микроконтроллерами, что сильно облегчает изучение и создание прототипов устройств. Для создания своих собственных плат может потребоваться оборудование и определённые навыки. Тем не менее это вполне под силу многим энтузиастам. Пользователь habrahabr.ru Михаил Сваричевский (@BarsMonster) убедительно показывает, что создание своей платы с микроконтроллером STM32F100C4T6B вполне поддаётся лазерно-утюжной технологии, но для многих (для меня в том числе) даже это - черезчур.

С некоторых пор Cortex-M0 микроконтроллеры существуют и в DIP-корпусах, что на мой взгляд делает создание своих собственных плат доступным даже абсолютным новичкам.
Мне известно по крайней мере о двух таких чипах: LPC810 (DIP8) и LPC1114 (DIP28). Недавно я приобрёл второй чип LPC1114. На мой взгляд это идеальный микроконтроллер для знакомства с семейсвом Cortex M. Сейчас цена eBay на них $4.50 (недёшево).

Обзор регистров и инструкций Thumb

В архитектуре ARM есть 12 регистров общего назначения. Они так и называются: R0-R12. Также имеется 2 регистра-указателя стека MSP (Main Stack Pointer) и PSP (Process Stack Pointer). Один из этих указателей отображается на регистр R13 (SP) (Stack Pointer). R13 и SP - просто взаимозаменяемые названия. В зависимости от бита 1 в регистре CONTROL SP будет соответсвовать MSP (0) или PSP (1). Link Register (LR) или R14 используется при вызове процедур и прерываний. В нём содержится адрес возврата. Program Counter (PC) или R15 - указатель текущей команды. Program Status Register (PSR).

Программа кодируется с помощью 2-байтных инструкций Thumb

Регистры общего назначения R0-R12 делятся на LO-регистры (R0-R7) и HI-регистры (R8-R12). Многие команды Thumb работают только с LO-регистрами. Согласно документации, значение может передаваться из из LO-регистра в HI-регитр и из HI-регистра в LO-регистр, с помощью специального варианта инструкции MOV. Значения в HI-регистрах могут сравниваться с значениями в LO-регистрах с помощью инструкции CMP.  Значения в HI-регистрах могут так же добавляться к значениям в LO-регистрах с помощью инструкции ADD.

Задействуем периферию

В стандартный набор периферии микроконтроллеров могут входить UART (универсальный асинхронный приёмопередатчик, тот что лежит в основе компьютерных COM-портов), шина I2C, шина SPI и, разумеется GPIO (General Purpose I/O или вводы-выводы общего назначения). Могут входить так же ADC (АЦП, аналого-цифровой преобразователь), DAC (цифро-аналоговый преобразователь), реализованный аппаратно PWM (ШИМ, широтно-импульсная модуляция). Внешние утстройства управляются с помощью регистров (не путать с регистрами процессора R0-R15), которые отображаются на определённые области в памяти.

Так, например, в LPC1114 по адресу 0x50008000 находится регистр GPIO0DIR, который определяет, какие ножки GPIO0 будут работать как вводы, а какие - как выводы. А по адресу 0x50003ffc находится регистр GPIO0DATA, с помощю которого можно установить значение на выводах (записывая значения в соответствующие биты регистра), или, соответственно, считать значения на тех ножках GPIO0, которые настрены как ввводы.

То есть в отличие от архитектуры x86 в ARM нет аналогов инструкций IN/OUT. Вместо записи значения в порт нужно просто записать значение по определённому адресу так же как мы бы записывали его в память. Это, кстати, позволяет при работе с внешними устройствами из языка C/С++ использовать только лишь адресную арифметику и указатели.

В процессоре LPC1114 (как и во многих других) можно "включать" и "выключать" периферию, оптимизируя таким образом потребляемую мощность. Как я понял, подключение/отключение блоков периферии производится с помощью подключения/отключения тактирования для этих блоков. Это делается установкой соответствующего бита в регистре SYSAHBCLKCTRL (по адресу 0x40048080). В нашем примере мы устанавливаем 6-й и 16-й биты, включая таким образом GPIO (6-й бит) и IOCON Block (блок конфигурации воода-вывода).

Начальная загрузка процессора ARM Cortex M. 

Процедура начальной загрузки одинакова по крайней мере для контроллеров Cortex-M0/3/4. Процессор считывает по адресу 0x00000000 начальное значение указателя стека и инициализирует регистр MSP (который сразу после загрузки отображается на регистр R13) Затем, начиная с адреса 0x00000004 находится таблица исключений (таблица прерываний). Самое первое прерывание - Reset. Это значит, что по адресу 0x00000004 лежит адрес кода, который начнёт выпонятся сразу после сброса микроконтроллера. Вооружившись этими знаниями, а так же таблицей комманд Thumb и манулом к LPC1114 я покажу как реализовать мигание светодиодом - аналог "Hello world" в мире микроконтроллеров.

Реализация мигания светодиодом на ассемблере

Хорошая новость о разработке на ассемблере для ARM заключается в том, что можно обойтись совсем без ассемблера, и написать всю прошивку, начиная от начальной заргрузки и заканчивая общением с периферией на языке C или даже C++. Однако для знакомства с платформой написание программ на ассемблере будет очень полезным упражнением. С этого и начнём. В мире ARM популярны как минимум два транслятора ассемблерного кода GNU AS и ARMASM, и синтаксис у них отличается. Здесь рассмотрен GNU AS.


Инструменты

  Я буду использовать набор утилит для сборки (toolchain) GNU и коммандную строку. Это позволит в максимально явном виде показать каждый этап сборки.

Сборка проекта

Сначала оттранслируем ассемблерный файл, получив на выходе объектный файл *.o :

PATH_TO_GNU_TOOLCHAIN_\arm-none-eabi-as.exe -mcpu=cortex-m0 blink-lpc1114.s -o blink-lpc1114.o

В итоге должен получиться файл blink-lpc1114.o . Следующий шаг - компоновка, или, в просторечии - линковка. Для того чтобы всё правильно слинковалось нам понадобится правильный скрипт компоновки.
Используя этот скрипт можно скомпоновать объектный файл в ELF-файл:

PATH_TO_GNU_TOOLCHAIN/arm-none-eabi-gcc -nostartfiles -Tblink-lpc1114.ld blink-lpc1114.o -o blink-lpc1114.elf

Должен сгенерироваться файл blink-lpc1114.elf . Наконец, последний шаг - конвертация ELF-файла в BIN-файл, который будет из себя представлять двоичные данные в том виде, в котором они будут лежать во флеш-памяти микроконтроллера. Это делается с помощью утилиты objcopy:

PATH_TO_GNU_TOOLCHAIN/arm-none-eabi-objcopy.exe -O binary blink-lpc1114.elf blink-lpc1114.bin
В результате получим файл blink-lpc1114.bin.

Прошивка контроллера

Здесь я немного "срезал угол". Я приобрёл чип LPC1114 вместе с mbed-совместимой платой. Она подключается к USB-разъёму, и определяется как файловая система (этакая небольшая флешка). На неё можно просто скопировать bin-файл. После перезагрузки плата "поймёт", что есть новая версия прошивки для микроконтроллера и "зальёт" бинарник в его флеш-память. Остаётся убедиться, что всё работает.


Без ткой платы LPC1114 прошивается через UART с уровнями 3.3 В. Существуют по крайней мере две утилиты для прошивки: lpc21isp (opensource) и flashmagic (бесплатная, но исходники, вроде, закрыты). Если использовать компьютерный COM-порт, то необходимо использовать адаптер уровней (COM-порт выдаёт уровни +12/-12 вольт). Я планирую попробовать использовать плату Raspberry Pi и её UART-выводы, которые как раз выдают 3.3 В, и преобразование не требуется. Я надеюсь испытать этот метод на практике в ближайшем будущем и напишу о результатах в этом блоге.

Собираем схему. Немного об электрических характеристиках LPC1114

Нам достаточно одной ножки, чтобы подключить светодиод. Из datasheet'а я понял, что среди всех "стандартных" GPIO-ножек выделяется PIO0_7 (28-я ножка микросхемы), которая служит "выводом с большим током" (High-current output driver), и может выдавать аж 20мА, что вполне достаточно для подключения светодиода через ограничивающий ток резистор. С остальными выводами ситуация такая: если через вывод вытекает ток равный 4мА, то напряжение на этом выводе падает на 0.4В, то есть выходное сопротивление этих выводов равно 0.4В/4мА = 100Ом. При этом суммарный потребляемый микроконтроллером ток (часть из которого будет выходить через выводы GPIO) не должен превышать 100мА. В общем, к другим ножкам тоже можно подключить светодиоды. Я собрал вот такую схему на макетной платe:


Плата завелась без проблем

Я использовал плату Arduino Uno в качестве источника питания на 3.3В

Заключение

Я думаю, что мне удалось показать, что работа с микроконтроллерами ARM Cortex M не обязательно сложна как с программной так и со схемотехнической точек зрения. Надеюсь, этот пост поможет кому-то вьехать в разработку устройств на этой архитектуре. В следующих постах я планирую написать про использование языка C и заголовочных файлов, предоставляемых производителем контроллера для более быстрой и удобной разработки.

Ссылки


LPC111x/LPC11Cxx User manual

Simple ARM example for LPC1114

Programming the LPC1114

STM32F4: GNU AS: Программирование на ассемблере (Часть 1)

Комментариев нет:

Отправить комментарий