0,1
рейтинг
8 августа 2013 в 20:50

Разработка → ARM-ы для самых маленьких tutorial



Пару дней назад я опубликовал и потом внезапно убрал в черновики статью о плане написать про создание своей ОС для архитектуры ARM. Я сделал это, потому что получил много интересных отзывов как на Хабре, так и в G+.

Сегодня я попробую подойти к вопросу с другой стороны, я буду рассказывать о том, как программировать микроконтроллеры ARM на нарастающих по сложности примерах, пока мы не напишем свою ОС или пока мне не надоест. А может, мы перепрыгнем на ковыряние в Contiki, TinyOS, ChibiOS или FreeRTOS, кто знает, их там столько много разных и интересных (а у TinyOS еще и свой язык программирования!).

Итак, почему ARM? Возиться с 8-битными микроконтроллерами хотя и интересно, но скоро надоедает. Кроме того, средства разработки под ARM обкатаны долгим опытом и намного приятнее в работе. При этом, начать мигать светодиодами на каком-то «evaluation board» так же просто, как и на Arduino.



Небольшой экскурс в архитектуру


ARM продвигает замечательную архитектуру, которую успешно лицензирует, мне на самом деле сложно представить, в каком устройстве нет никакого присутствия продуктов этой компании. В вашем смартфоне гарантированно есть несколько ядер на базе архитектуры ARM. Еще парочка найдется в современном ноутбуке (и это даже не CPU, а так, сопутствующий контроллер какой-либо периферии), еще несколько – в автомобиле. Есть они и в других бытовых вещах: микроволновках и телевизорах.

Такая гибкость достигается тем, что в самом базовом варианте ядро ARM очень простое. Сейчас существуют три разновидности этой архитектуры. Application применяется в устройствах «общего назначения» – как основной процессор в смартфоне или нетбуке. Этот профиль самый навороченный функционально, тут есть и полноценный MMU (модуль управления памятью), возможность аппаратно выполнять инструкции Java bytecode и даже поддержка DRM-схем. Microcontroller – это полная противоположность профилю application, применяемая (внезапно!) для использования в микроконтроллерах. Тут актуально минимальное энергопотребление и детерминистическое поведение. И, наконец, real-time используется как эволюция профиля microcontroller для задач, где критично иметь гарантированное время отклика. Все эти профили получили реализацию в одном или нескольких ядрах Cortex, так, например, Cortex-A9 основан на профиле application и является частью процессора в iPhone 4S, а Cortex-M0 основан на профиле microcontroller.

Железки!



В качестве целевой платформы мы будем рассматривать работу с Cortex-M, так как она самая простая, соответственно, надо вникать в меньшее количество вопросов. В качестве тестовых устройств я предлагаю вам LPC1114 – MCU производства NXP, схему на котором можно собрать буквально на коленке (нет, правда, вам нужен только сам MCU, FTDI-кабель на 3,3 В, несколько светодиодов и резисторов). LPC1114 построен на базе Cortex-M0, так что это будет самый урезанный вариант платформы.



В качестве альтернативного варианта мы будем работать с платформой mbed, а конкретно, с моделью на базе LPC1768 (а значит, внутри там Cortex-M3, несколько более навороченный). Вариант уже не настолько бюджетный, но процесс заливки бинарников на чип и отладки упрощен максимально. Да и можно поиграться с самой платформой mbed (вкратце: это онлайн-IDE и библиотека, с помощью которой можно программить на уровне ардуины).

Приступим


Интересной особенностью современных ARM-ов является то, что их вполне реально программировать целиком на С, без применения ассемблерных вставок (хотя ассемблер не так уж и сложен, у Cortex-M0 всего 56 команд). Хотя некоторые команды в принципе не доступны из С, эту проблему решает CMSIS – Cortex Microcontroller Software Interface Standard. Это драйвер для процессора, который решает все основные задачи управления им.

Как же загружается процессор? Типична ситуация, когда он просто начинает выполнять команды с адреса 0x00000000. В нашем случае процессор несколько более умный, и рассчитывает на специально определенный формат данных в начале памяти, а именно – таблицу векторов прерываний:


Старт выполнения программы происходит следующим образом: процессор читает значение по адресу 0x00000000 и записывает его в SP (SP – регистр, который указывает на вершину стека), после чего читает значение по адресу 0x00000004 и записывает его в PC (PC – регистр, который указывает на текущую инструкцию + 4 байта). Таким образом начинает выполняться какой-то код пользователя, при этом у нас уже есть стек, указывающий куда-то в память (т.е., все условия для выполнения программы на С).

В качестве тестового упражнения мы будем мигать светодиодом. На mbed у нас их целых четыре, в схему с LPC1114 (далее — «доска») мы устанавливаем светодиод вручную.

Перед тем как непосредственно писать код, нам надо выяснить еще одну вещь, а именно – что где должно располагаться в памяти. Поскольку мы не работаем с какой-то «стандартной» ОС, то компилятор (вернее, компоновщик) не может узнать, где у него должен быть стек, где сам код, а где — куча. К счастью для нас, у семейства ядер Cortex стандартизированная карта памяти, что позволяет относительно просто портировать приложения между разными процессорами этой архитектуры. Работа с периферией, конечно, остается процессорозависимой.

Карта памяти для Cortex-M0 выглядит вот так:



(изображение из Cortex™-M0 Devices Generic User Guide)

У Cortex-M3 она, по сути, такая же, но несколько более детальна. Проблема тут в том, что у NXP есть свой, отдельный взгляд на этот вопрос, так что проверяем карту памяти в документации на процессор:



(изображение из LPC111x/LPC11Cxx User manual)

На самом деле, SRAM у нас начинается с 0x10000000! Вот так, одни стандарты, другие стандарты, а все равно надо тома документации листать.

Вооружившись этими знаниями, идем писать код. Для начала – таблица прерываний:

.cpu cortex-m0      /* ограничиваем ассемблер списком существующих инструкций */
.thumb

.word   _stack_base /* начало стека в самом конце памяти, стек растет вниз */
.word   main        /* Reset: с прерывания сразу прыгаем в код на С */
.word   hang        /* NMI и все остальные прерывания нас не сильно волнуют */
.word   hang        /* HardFault */
.word   hang        /* MemManage */
.word   hang        /* BusFault */
.word   hang        /* UsageFault */
.word   _boot_checksum /* Подпись загрузчика */
.word   hang        /* RESERVED */
.word   hang        /* RESERVED*/
.word   hang        /* RESERVED */
.word   hang        /* SVCall */
.word   hang        /* Debug Monitor */
.word   hang        /* RESERVED */
.word   hang        /* PendSV */
.word   hang        /* SysTick */
.word   hang        /* Внешнее прерывание 0 */
                    /* ... */

/* дальше идут остальные 32 прерывания у LPC1114 и 35 у LPC1768, но
   их нет смысла описывать, потому как мы их все равно не используем */

.thumb_func
hang:   b .         /* функция заглушка для прерываний: вечный цикл */

.global hang


Сохраним эту таблицу в boot.s. Тут, фактически, только одна ассемблерная вставка – функция hang, которая устраивает процессору бесконечный цикл. Все прерывания, кроме reset, указывают на нее, так что в случае непредвиденной ситуации процессор просто зависнет, а не пойдет выполнять непонятный участок кода.

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

Теперь напишем реализацию функции main:

#if defined(__ARM_ARCH_6M__)

/* Cortex-M0 это ARMv6-M, код для LPC1114 */

#define GPIO_DIR_REG  0x50018000  /* GPIO1DIR  задает направление для блока GPIO 1 */
#define GPIO_REG_VAL  0x50013FFC  /* GPIO1DATA задает значение для блока GPIO 1 */
#define GPIO_PIN_NO   (1<<8)      /* 8-й бит отвечает за 8-й пин */

#elif defined(__ARM_ARCH_7M__)

/* Иначе просто считаем что это LPC1768 */

#define GPIO_DIR_REG  0x2009C020  /* FIO1DIR задает направление для блока GPIO 1 */
#define GPIO_REG_VAL  0x2009C034  /* FIO1PIN задает значение для блока GPIO 1 */
#define GPIO_PIN_NO   (1<<18)     /* 18-й бит отвечает за 18-й пин */

#else

#error Unknown architecture

#endif

void wait()
{
  volatile int i=0x20000;
  while(i>0) {
    --i;
  }
}

void main()
{
  *((volatile unsigned int *)GPIO_DIR_REG) = GPIO_PIN_NO;

  while(1) {
    *((volatile unsigned int *)GPIO_REG_VAL) = GPIO_PIN_NO;
    wait();
    *((volatile unsigned int *)GPIO_REG_VAL) = 0;
    wait();
  }

  /* main() *никогда* не должен вернуться! */
}


У mbed первый светодиод подключен к порту GPIO 1.18, на доске мы подключили светодиод к GPIO 1.8. Одни и те же пины могут выполнять разные функции, эти по умолчанию работают именно как GPIO (General Purpose I/O – линии ввода/вывода общего назначения).

Код относительно прямолинеен, если держать под рукой LPC-шный User manual (один и второй). Для начала мы указываем режим работы GPIO через регистр GPIO_DIR_REG (у наших процессоров они в разных местах, да и вообще LPC1768 может работать с GPIO более эффективно), где 1 – вывод, 0 – ввод. Потом мы запускаем бесконечный цикл, в котором пишем в порт попеременно значения 0 и 1 (0 В и 3,3 В соответственно).

Функция для «паузы» у нас работает наугад, просто прокручивая относительно долгий цикл (volatile int не дает компилятору выоптимизировать этот цикл целиком).

Наконец, все это нужно правильно скомпоновать:

_stack_base = 0x10002000;
_boot_checksum = 0 - (_stack_base + main + 1 + (hang + 1) * 5);

MEMORY
{
   rom(RX)   : ORIGIN = 0x00000000, LENGTH = 0x8000
   ram(WAIL) : ORIGIN = 0x10000000, LENGTH = 0x2000
}

SECTIONS
{
   .text : { *(.text*) } > rom
   .bss  : { *(.bss*) } > ram
}


Сценарий компоновщика объясняет ему, где у нас флеш, где оперативная память, какие у них размеры (тут используются размеры для LPC1114, так как у LPC1768 всего больше, сдвиги, к счастью, идентичны). После определения карты памяти мы указываем, какие сегменты куда копировать, .text (код программы) попадает в флеш, .bss (статические переменные, которых у нас пока нет) – в память. Помимо этого мы задаем два символа, которые использовали в boot.s: _stack_base – указывает на вершину стека и _boot_checksum (спасибо Zuy за уточнение!) – записывает чексумму загрузчика. Чексумма рассчитывается по формуле: дополнительный код (2's compliment) от суммы полей выше (т.е. адреса стека, и всех прерываний до непосредственно чексуммы). Хотя утилиты для прошивки (см. далее) сами исправили бы чексумму на правильную, если бы мы прошивали бы код из самого приложения, то загрузиться снова мы бы уже не смогли.

Теперь у нас есть три файла: boot.s, main.c, mem.ld, пора это все скомпилировать и, наконец, запустить. В качестве тулчейна мы будем использовать GCC, позже, возможно, я покажу как делать то же с LLVM. Пользователям OS X я советую взять тулчейн у Linaro – в самом конце списка: Bare-Metal GCC ARM Embedded. Пользователям других ОС я советую взять тулчейн там же :-) (разве что гентушникам будет проще сэмержить crossdev и скомпилить GCC).

arm-none-eabi-as boot.s -o boot.o
arm-none-eabi-gcc -O2 -nostdlib -nostartfiles -ffreestanding -Wall -mthumb -mcpu=cortex-m0 -c main.c -o main-c0.o
arm-none-eabi-gcc -O2 -nostdlib -nostartfiles -ffreestanding -Wall -mthumb -mcpu=cortex-m3 -c main.c -o main-c3.o
arm-none-eabi-ld -o blink-c0.elf -T mem.ld boot.o main-c0.o
arm-none-eabi-ld -o blink-c3.elf -T mem.ld boot.o main-c3.o
arm-none-eabi-objdump -D blink-c0.elf > blink-c0.lst
arm-none-eabi-objdump -D blink-c3.elf > blink-c3.lst
arm-none-eabi-objcopy blink-c0.elf blink-c0.bin -O binary
arm-none-eabi-objcopy blink-c3.elf blink-c3.bin -O binary


Интересный момент тут — это отключение использования всех стандартных библиотек у GCC. Действительно, весь код, который попадет в итоговый бинарник – это код, который написали мы сами.

Вопрос: как компоновщик знает, куда надо засунуть таблицу прерываний? А он и не знает, там не написано :-). Он просто линкует подряд, начиная с нулевого адреса, так что порядок файлов (boot.o, потом main-c0.o) очень важен! Попробуйте слинковать наоборот или слинковать boot.o два раза и сравните вывод в lst-файле.

Хорошая идея – посмотреть на итоговый листинг (файл lst) или закинуть бинарник в дизассемблер. Даже если вы не говорите на ARM UAL, то чисто визуально можно проверить, что хотя бы таблица прерываний находится на своем месте:




Еще можно обратить внимание на забавный момент – GCC при компиляции под Cortex-M3 генерирует функцию wait() больше, чем в варианте под Cortex-M0. Правда, если включить оптимизацию то она вправит ему мозги.

Мигаем!


Все что нам осталось – залить бинарники на наши тестовые платформы. С mbed тут все максимально просто, просто скопируйте blink-c3.bin на виртуальную флешку и нажмите reset (на mbed). С доской все немного сложнее. Во-первых, для того, чтобы попасть в загрузчик, нам нужен резистор между GND и GPIO 0.1. Во-вторых, необходима программа для непосредственно прошивки. Можно использовать Flash Magic (Win, OS X), можно использовать консольную утилиту – lpc21isp:

lpc21isp.out -verify -bin /path/to/blink-c0.bin /dev/ftdi/tty/device 115200 12000


Процесс прошивки следующий:
  • ставим резистор между j5 и j7 (10 кОм подойдет);
  • нажимаем reset;
  • запускаем lpc21isp;
  • снимаем резистор;
  • нажимаем reset еще раз – запускается приложение.


Если у вас есть возможность запустить примеры на разных устройствах, вы заметите, что скорость мигания на них не идентична. Это связанно с тем, что у разных устройств разная частота ядра, соответственно, wait() они выполняют за разное время. В следующей части мы изучим вопросы осцилляции детальнее и сделаем четкий отсчет времени.

P.S. Отдельное спасибо хабраюзеру pfactum за то, что тратит время на исправление моих ошибок в тексте :-).

P.P.S. Просьба тем, у кого есть тестовая платформа на базе ARM – пишите в комментариях – какая. Я могу пересмотреть аппаратную базу для дальнейших статей.
Интересны ли реальные аппаратные демо?

Проголосовало 1140 человек. Воздержалось 225 человек.

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

Владимир Пузанов @farcaller
карма
182,7
рейтинг 0,1
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (64)

  • –24
    > потом внезапно убрал в черновики статью о плане написать про создание своей ОС для архитектуры ARM

    > я буду рассказывать о том, как программировать микроконтроллеры ARM на нарастающих по сложности примерах, пока мы не напишем свою ОС или пока мне не надоест.

    т.е. Вы планировали написать пост, продолжения которого может не быть потому, что Вам это надоело? Правильно что убрали в черновики — такие посты тут не надо. И так вон горы обещаний типа: «в следующем посте я расскажу поподробнее» и привет на долгие года.
    • +4
      Нет, я написал пост для которого у меня уже было две статьи продолжений. Но люди попросили больше конкретики, и я изменил концепцию (этот текст – это компоновка материала из первых двух частей, третья сейчас в переработке).
    • –1
      какой отрицательный отзыв
    • +22
      Я смотрю вы просто мастер написания постов. Спасибо за ваш бесценный опыт!
  • +1
    Итого, треть заинтересованных, пишите! А пролистывающие еще заинтересуются.

    Вопрос про RT. Насколько по-вашему реально сделать что-то вроде звукового FX-процессора на подобном?
    • +1
      Я, если честно, не сильно в теме RT, но мне кажется что тут лучше бы подошел FPGA. Есть подозрение, что у M-ок не хватит числодробилки, тут бы что-то с FPU (впрочем, у Cortex-M4 есть FPU).
      • 0
        ой, не туда
    • 0
      Для звукового процессора лучше использовать специализированные решения aka DSP или FPGA.
      • 0
        Лучше DSP. Работали как то над IP телефоном. Blackfin подороже TI шные обычно чуть дешевле. С Blackfin работать проще.
  • +6
    > ARM делает замечательные встраиваемые процессоры
    ARM не делает процессоры. ARM проектирует процессорные ядра и лицензирует их.
    • +1
      Да, конечно вы правы, обновил текст.
  • +2
    Почему по адресу 0х4 лежит 0х69, а main начинается с 0х68?
    • +2
      0-й бит адреса означает Thumb режим. Есть три набора инструкций: ARM (4-байтный), Thumb (2-байтный), Thumb2 (4-байтный, расширяет возможности Thumb). Cortex-Mx поддерживают только Thumb режим, так что в указателях младший бит всегда будет 1. В Cortex-Ax можно изменяя этот бит при прыжке перейти из ARM-режима в Thumb и наоборот.
      • 0
        Мне казалось, что Thumb2 — это 32-битные и 16-битные команды вперемешку, и Cortex-M поддерживает только этот режим.
        • 0
          Thumb2, насколько я помню, новых 16-битных команд не добавляет.

          Cortex-M0 поддерживает только Thumb (16-битные) и 6 команд из Thumb2. Cortex-M3 поддерживает Thumb/Thumb2 целиком.
          • +1
            Пришлось гуглить. Итак, Cortex-M3 поддерживает только Thumb-2. Просто Thumb-2 — это надмножество Thumb.

            infocenter.arm.com/help/topic/com.arm.doc.faqs/ka13089.html
            • 0
              В любом случае, фраза про то, что РС указывает на текущую инструкцию + 4 байта, не очень корректна. Скорее уж РС указывает на следующую команду.
              • +1
                Нет, там как раз +4 байта:



                (из ARMARMv7)
                • 0
                  Подождите. Вы говорите, что процессор «читает значение по адресу 0x00000004 и записывает его в PC (PC – регистр, который указывает на текущую инструкцию + 4 байта)». По адресу 0х4 у нас 0х69. Это не адрес текущей команды + 4, это адрес следующей команды!
                  • +2
                    Ох, я маленький кусок абзаца захватил значит :-)



                    При записи мы переходим по адресу, который записали.
                    При чтении мы получаем адрес инструкции, которая читает + 4.
                    • 0
                      Ок, спасибо за разъяснения. Стараюсь узнать наших конкурентов получше :)
    • 0
      thumb?
  • +2
    > Просьба тем, у кого есть тестовая платформа на базе ARM – пишите в комментариях – какая. Я могу пересмотреть аппаратную базу для дальнейших статей.

    PINBOARD II (STM32F103)
    • 0
      Добавлю сюда же HY-Mini STM32V (STM32F103)
    • 0
      STM32L-Discovery (STM32L152RB)
  • 0
    Спасибо, Отличная вводная статья… Правда в наших краях LPC почти не бывает… Тем более LPC1114… Учитывая её цену и исполнение в DIP'е, это должно быть раритетом… Из arm'ов мне доступно ti launchpad Stellaris Cortex-M4F и STM32F4-Discovery…
    • 0
      Как раз наоборот. В дипе более новый чипы они появились года два назад, не раньше.
      • 0
        раритет — это не в смысле старые, а в смысле фиг найдешь в продаже… Разве что в Дипе с их долбанутым ценником…
        • 0
          Интернет магазины вас спасут.
          • 0
            Я с ходу LPC1114FN28 не нашёл… В других форм-факторах на AliExpress'е за десять долларов пучок полно предложений…
            • 0
              На mouser.com есть только два ARM-а в DIP исполнении, это не самый популярный формат для этой архитектуры, но очень интересный для экспериментов :)
              • 0
                10 долларов 3 штуки и 120 долларов доставка… Тогда уж лучше в Чип&Дип… ))
            • 0
              Набросил Снежинскому идею, может быть скоро в Чипстере будет.
              • 0
                Будем надеяться… )
            • 0
              Если надо недорогая борда с ARM архитектурой на борту, обратите взор на Stellaris Launchpad от TI.
              За $10 (пересылка бесплатно) получаем 2 камня на борде + serial «кабель» 5V5 -> 3V2
              • 0
                Она у меня есть. Интересует возможность поиграться аппаратной частью на breadboard'е…
        • 0
          Все же, у слова раритет имеется немного другое значение.
          • 0
            Я знаю только одно. Полагаю у это слова с английским rare (редкий) одно происхождение… Скорей всего от латинского…
            • 0
              Неправильно выразился. Устоявшееся применение другое.
  • 0
    Да и еще есть интересный момент, что чтение из памяти(LDR) в микроконтроллерах на ядре Cortex-M0 должно быть только по выровненным адресам, т.е если мы считываем данные размером 4 байта(LDR), то адрес из которого читаем должен быть кратен 4, так же и для 2 байтавых переменных(LDRH) адрес должен быть кратен 2.
  • –3
    STM32VLDISCOVERY, но интереса у меня почти нет — и без ваших статей опыта в этом деле хватает.
    • +1
      Держите нас в курсе, обязательно!
  • 0
    Я бы кстати в рамках цикла статей «ARM для самых маленьких» с удовольствием почитал небольшой обзор-сравнение с другими RISC-архитектурами микроконтроллеров — MIPS, SPARC и т.д. Фраза «ARM продвигает замечательную архитектуру» выглядит как-то куце без упоминания о том, чем же так замечательна эта архитектура.
    • 0
      Записал идею, подумаю над этой темой.
  • +1
    Очень поддерживаю цикл статей такого плана, с удовольствием почитаю.

    Просьба тем, у кого есть тестовая платформа на базе ARM – пишите в комментариях – какая.

    У меня TI Stellaris launchpad — она сейчас стоит 10$ и дошла до Питера за 5 дней и бесплатно — по-моему отличный вариант.
    • 0
      Ко мне такая же борда едет, скорее всего добавлю ее как вариант.
  • 0
    Инетресный момент, что ваш собранный бинарник, если прошить его в таком виде как он есть в ROM микроконтроллера даже не запуститься.
    Вы используете LPC1114, стоит выделить место в статье и написать о порядке старта программы. Вы упустили очень важную особенность, на которую потом часто нарываются разработчики.
    • 0
      Какую именно, уточните пожалуйста? Я прошиваю собранные бинарники, и они работают. LPC не надо настраивать осциллятор, он по умолчанию стартует с внутреннего в достаточном объеме чтобы работать.
      • +3
        Судя по листингу дизасма, у вас по смещению 0x1C находится заглушка 0x45. В листинге boot.s у вас это место помечено, как RESERVED.
        Но, оно же зарезервированно с определенной целью.
        В соответствии с параграфом 26.3.3 документации на LPC111x там должна быть контрольная сумма первых 7-ми векторов, подсчитанная таким образом, чтобы сумма первых 8-ми векторов равнялась нулю. Если это условие не выполняется, встроенный в контроллер бутлоадер не будет считать прошивку валидной и никогда ее не запустит. Эта процедура одинаковая для многих cерий LPC контроллеров.
        Ну а запускаются ваши бинарники скорее всего потому, что программы, которыми вы прошиваете бинарник в контроллер на ходу его модифицируют и правят это место. Flash Magic это однозначно делает. Считайте ваш бинарник назад из контроллера и увидите, что он модифицирован.
        В итоге получается, что ваш бинарник сам по себе не валидный, а возможность его запуска зависит от сторонней утилиты для прошивки.
        • +1
          Хм! Это интересный момент, и вы правы, я его упустил. Доработаю, спасибо.
  • 0
    Интересно. Может, наконец брошу мелкие AVR и возьмусь за лежащие без дела демо-платы

    Просьба тем, у кого есть тестовая платформа на базе ARM – пишите в комментариях – какая.


    — Launchpad MSP430
    — Launchpad Stellaris

    Взяты в своё время по $4-6, когда была акция с распродажей и предзаказом, но как-то пока так и лежат без дела :(
  • +1
    #if defined(__ARM_ARCH_6M__)
    
    /* Cortex-M0 это ARMv6-M, код для LPC1114 */
    ...
    
    #else
    
    /* Иначе просто считаем что это LPC1768 */
    ...
    
    #endif
    

    Тут есть потенциальные грабли, когда в разросшийся код будет добавлятся новая платформа. Лучше сразу приучить себя писать конструкции вида:

    #if defined (__PLATFORM_1__)
    
        ...
    
    #elif defined (__PLATFORM_2__)
    
        ...
    
    #else
    
        #error "Please support new platform here"
    
    #endif
    
    • 0
      Да я так тоже думал, а потом что-то стало лень дописывать: what could possibly go wrong? :-) Но вы правы – ваш вариант надежнее, обновил.
  • +1
    > Вопрос: как компоновщик знает, куда надо засунуть таблицу прерываний? А он и не знает, там не написано :-). Он просто линкует подряд, начиная с нулевого адреса, так что порядок файлов (boot.o, потом main-c0.o) очень важен! Попробуйте слинковать наоборот или слинковать boot.o два раза и сравните вывод в lst-файле.

    Так делать нельзя. Для этого есть ключевое слово .section в gcc ассемблере. В линкерном скрипте первой строчкой вписываете эту секцию — желательно сразу с параметром KEEP.
    • 0
      Да, я упомяну это в следующей части. Сейчас мне хотелось показать что линкер по сути работает «в лоб».
      • +1
        Ровно до того момента, как включите оптимизацию
  • 0
    Просьба тем, у кого есть тестовая платформа на базе ARM – пишите в комментариях – какая.

    stm32vldiscovery и STM32F0DISCOVERY.

    stm32f0 серия уже есть TSSOP-20 корпусах ( ziblog.ru/2013/08/01/stm32f030-value-line.html и we.easyelectronics.ru/reptile/stm32f050f4-v-tssop20-otladochnaya-plata-i-testovyy-proekt.html )

    Цена на STM32F050F4P6 всего 29 руб в розницу!

    Ваша статья очень понравилась. Ждем продолжения. Если можно покажите как с LLVM сделать тоже самое.
    • 0
      Ну TSSOP не такая вроде уж и новость вроде, LPC811 тоже в TSSOP есть, но это не так интересно как DIP.

      LLVM будет позже (собственно уже есть, но следующая часть про осцилляторы, так что ему прийдется лежать части до третей).
  • 0
    Для меня stm32 легче купить, чем LPC. И когда выбирал микроконтроллер для хобби, не смог найти как прошивать LPC из linux. Поэтому и радуюсь что хоть tssop появился для stm32.
    C нетерпением жду продолжения серии статей (про осциляторы тоже очень интересно).
  • 0
    Спасибо большое за наводку на LPC1114. Заказал на таобао, теперь осталось месяц её ждать.

    >Интересной особенностью современных ARM-ов является то, что их вполне реально программировать целиком на С
    o_O Имею опыт с TI Launchpad MSP430, pic-контроллерами. Асм-код не писал никогда. Зато ни там ни там не писал таблиц прерывай. Смотрится страшно, магически и не понятно. Её нужно писать каждый раз?
    • 0
      нет, в либах лежат уже готовые, конечно же.
      по крайней мере с STM32 Mini в комплекте шло всё готовое
    • 0
      Наверно имелись в виду именно современные АРМЫ в противовес не современным.
      Для тех же ARM7TDMI и ARM9 нужно было писать стартап-файл на асме.
  • 0
    Большое спасибо за статью, очень интересно.

    Скажите, а как вы всему этому научились? Какую литературу посоветуете?
    • +1
      Я начинал с даташитов и ARMARM (референсный мануал по архитектуре). Уверен на 100%, есть более простые методы изучения этого, например та же ардуина (когда физические процессы становятся понятны, можно опуститься «ближе к железу», и смотреть как программа влияет на микроконтроллер).

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