В статье я хотел бы описать шаги на пути к написанию прошивки для микроконтроллеров stm32 без использования специальных сред разработки типа keil, eclipse и тому подобных. Я опишу подготовку прошивки с самых основ, начиная с написания загрузчика на ассемблере, скрипта для линкера и заканчивая основной программы на C. В коде на C буду использовать заголовочные файлы из CMSIS. Редактор кода может быть любым на ваш вкус, vim, emacs, блокнот, все что угодно. Для сборки проекта буду использовать утилиту make. Итак, начнем!

Почему так сурово, спросите вы. Во-первых, чтобы что-то хорошо освоить, необходимо начинать с основ. Я не хочу, чтобы мой читатель бездумно щелкал клавишами клавиатуры набирая текст очередной супер-программы для устройства, не понимая, как работает устройство. Stm32 гораздо более сложный микроконтроллер по сравнению, например с atmega8 — atmega328 (микроконтроллером, установленным на самой популярной плате серии arduino). Во-вторых, я люблю сам разбираться в любом деле с нуля, и можно сказать, данная статья — это заметки для меня в будущем, чтобы открыть и вспомнить некоторые нюансы.

Да, я забыл еще сказать, что разработку буду вести под Linux. Подойдет любой дистрибутив, например, у меня это Arch Linux. Для ubuntu процесс установки необходимых утилит я постараюсь описать в следующих частях. Можете попробовать Windows, MacOS, но для этого вам самим придется разобраться, как установить необходимые утилиты для компиляции и прошивки.

Первое, что вам нужно сделать, это приобрести плату для разработки на основе контроллера stm32f103. У меня это blue pill:

image

Еще одна вещь, необходимая для старта, это программатор st-link:

image

Плату blue pill и программатор я приобрел на aliexpress, заплатив 200 руб. за все вместе.
Второе, что необходимо сделать, это скачать набор для компиляции кода под arm GNU GCC.

Для arch linux необходимо поставить пакет gcc-arm-none-eabi:

yaourt -Syy arm-none-eabi-gcc

Далее нам понадобится утилита st-link для работы с одноименным программатором st-link2:

yaourt -Syy stlink

Теперь давайте попробуем подключить нашу плату к компьютеру через программатор.

Соединяем программатор с платой blue pill в таком порядке:

  • Подключить к пину GND (ground — земля, пина два, возьмите любой, например, 4-й) на программаторе провод (желательно следовать некоторым стандартам, для земли используйте черный или синий) и подключите к пину на плате подписанному GND;
  • Подключить пин SWCLK (clock — синхронизация) на программаторе к пину SWCLK на плате;
  • Подключить пин SWDIO (IO — ввод/ввод) на прогамматоре к пину SWIO на плате;
  • И наконец, пин 3,3V на программаторе соедините с пином 3,3V на плате.

Пока все очень просто. Теперь подключаем программатор в USB порт компьютера, открываем терминал и проверяем, что устройство успешно определилось в системе:

dmesg

image

При этом на самой плате загорится два диода, один красный должен гореть постоянно, что сигнализирует о том, что питание подается, второй зеленый, должен мигать. Это работает прошивка по-умолчанию.

Теперь давайте проверим характеристики нашей демо-платы. Для этого в терминале запускаем команду st-info из установленного до этого пакета stlink:

image

На выбор можем посмотреть:

--version — текущая версии утилиты st-info
--flash — выведет информацию о размере flash-памяти программ микроконтроллера, в моем случае это 0x10000 (65536 байт)
--sram — объем статической памяти — 0x5000 (20480 байт)
--descr — описание — F1 Medium-density device
--pagesize — размер страницы памяти — 0x400 (1024 байт)
--hla-serial — "\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x31"
--probe — Found 1 stlink programmers
serial: 303030303030303030303031
openocd: "\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x31"
flash: 65536 (pagesize: 1024)
sram: 20480
chipid: 0x0410
descr: F1 Medium-density device

Из важного для нас — размер flash-памяти и размер статической памяти, а также стоит запомнить что у нас устройство Medium-density.

Не следует начинать разработку без документации под рукой. Во-первых следует скачать с официального сайта Reference Manual. В нем полное описание всей периферии, регистров периферии микроконтроллера. Во-вторых, скачиваем Programmer Manual по той же ссылке. В нем узнаете о микропроцессоре семейства контроллеров STM32F10xxx/20xxx/21xxx/L1xxxx Cortex-M3, его архитектуре, наборе команд.

Далее разберем, с чего вообще начинается исполнение программы на микроконтроллере.

  1. Наш микроконтроллер stm32f103c8 сразу после включения начинает считывать по адресу 0x08000000 (для удобства чтения я буду делить тетрады пробелом — 0x0800 0000) значение для регистра SP. SP (Stack pointer) — регистр указателя стека (стр. 15 Programmer Manual). Стек начинается с конца доступной RAM-памяти и растет “вверх”;
  2. По адресу 0x0800 0004 считывает значение в регистр PC — Program counter. Это значение — адрес точки входа в нашу основную программу, другими словами по адресу 0x0800 0004 flash должен лежать адрес C — функции main(), определенной нами далее;
  3. Микроконтроллер начинает выполнение программы.

Чтобы вычислить начальное расположение стека (значение для SP регистра), обратимся к мануалу Reference Manual на стр. 65. Там указано, что RAM начинается с адреса 0x2000 0000. Ранее мы определили, что у микроконтроллера объем RAM 20480 байт:

0x2000 0000 + 0x5000 = 0x2000 5000

То есть по адресу 0x0800 0000 мы должны поместить значение 0x2000 5000.
По адресу 0x0800 0004 мы должны положить указатель на начало нашей программы. Каждый указатель имеет размер 4 байта, значит следующий адрес за 0x0800 0004 во flash памяти будет 0x0800 0004 + 4 = 0x0800 0008. Это значение и необходимо поместить по адресу 0x0800 0004.

Так будет выглядеть начальный участок нашей прошивки:

+-------------+-------------+
| Адрес flash |  Значение   | 
+-------------+-------------+
| 0x0800 0000 | 0x2000 5000 |
| 0x0800 0004 | 0x0800 0008 |
+-------------+-------------+

Теперь об одной особенности микроконтроллеров stm32. Дело в том, что формат команд для stm32 должен быть в Thumb представлении вместо стандартного ARM. Это значит, что при указании указателей мы должны прибавлять 1. Запомните это правило.

Хватит теории, пора переходить к практике. Надеюсь, вы еще не спите. Открывайте ваш любимый редактор кода, будем писать начальный файл для запуска нашего контроллера. Мы начнем с startup файла и он будет написан на ассемблере. Это будет единственный раз, когда я заставляю вас писать на скучном ассемблере, зато вы начнете понимать и “чувствовать” устройство изнутри.

Пишем в самом начале:

@stm32f103 

Это комментарий на языке ассемблера, каждый комментарий начинается с символа @.
Далее указываем директивы ассемблеру

.syntax unified 

@тип команд для stm32 - Thumb!

.thumb             

@семейство процессора микроконтроллера cortex-m3 
@(в этом можно убедиться из мануала)

.cpu cortex-m3 


И далее коротенькая bootstrap-программа:

@указатель на вершину стека. Пишем без пробелов!
@.equ директива ассемблера это почти
@тоже что и define в C или на худой конец
@думайте, что это обычное присваивание переменной
.equ StackPointer 0x20005000

@.word - указываем, что здесь машинное слово - 4 байта
@по сути отсюда (0x0800 0000) процессор
@начинает свою работу после включения
.word StackPointer

@”кладем” указатель на начало основной программы.
@Reset в данном случае - метка, адрес точки входа.
@не забываем о том, что у нас набор команд Thumb,
@поэтому к указателю прибавляем единицу
.word Reset + 1

@метка Reset. Здесь мы встречаем первую, настоящую и
@единственную команду, которая нам понадобится на
@протяжении всего руководства. Это команда B -
@безусловный переход в системе команд ARM,
@аналог JMP в ассемблере для x86, или простыми
@словами goto в языках более высокого уровня.
@Аргумент команды B - это адрес безусловного перехода, в нашем случае мы пока
@указываем метку Reset, тем самым заводим процессор в бесконечный цикл.
Reset: B Reset

Программу целиком вы можете скачать по ссылке https://bit.ly/2rc7bcf
Сохраните ее под названием bootstrap.s.

А теперь давайте скомпилируем и прошьем нашу плату.

Прежде всего я покажу, как скачать прошивку по-умолчанию с вашей платы, ту, которая мигает светодиодом. Вдруг когда-нибудь пригодится.

Снова вставляем программатор с подключенной платой в usb и запускаем в терминале Linux команду:

st-flash read ./default.bin 0x08000000 0x10000

Здесь мы указываем, что хотим прочитать в файл default.bin flash-память начиная с адреса 0x08000000 и размером 0x10000 (64K), то есть всю flash-память.

st-flash — утилита для работы с прошивкой микроконтроллера, полное описание ее читайте в терминале: st-flash --help.

После этого проверим, что прошивка корректно считалась. Загрузим ее вновь, перезаписывая старую.

st-flash  write ./default.bin 0x08000000

Что означает записать default.bin в flash память контроллера начиная с адреса 0x08000000.

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

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

arm-none-eabi-as -o bootstrap.o bootstrap.s 

Здесь мы компилируем наш исходный файл в объектный код. Это еще не готовая прошивка, годная для заливки в микроконтроллер. Нам необходимо еще “указать” куда, по каким адресам размещать нашу программу. Этим занимается компоновщик. Мы воспользуемся самым популярным компоновщиком LD, который входит в поставку пакета arm-none-eabi-gcc. Подробнее о компоновщике и описание скрипта для компоновщика ld я расскажу в следующей части, когда мы перейдем к автоматической сборке нашей супер-простой прошивки. А пока просто скачайте этот маленький файл stm32f103.ld https://bit.ly/2HXIydu, и выполните команду:

arm-none-eabi-ld -o main.elf -T stm32f103.ld bootstrap.o

Этой командой мы компонуем наш объектный файл с помощью скрипта stm32f103.ld, на выходе получаем elf файл.

Чтобы окончательно подготовить исполнимый elf файл к прошиванию, выполним последнюю команду:

arm-none-eabi-objcopy main.elf main.bin -O binary

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

Итак, наша первая программа для контроллера stm32 готова! Прошиваем!

st-flash write ./main.bin 0x08000000

Поздравляю! Теперь микроконтроллер обречен на вечное выполнение безусловного перехода. До следующей встречи!

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


  1. Serge78rus
    29.04.2018 21:48

    --sram — объем статической памяти — 0x5000 (4096 байт)
    Вы описАлись:
    0x5000 — (20480 байт)


    1. DrAndyHunter Автор
      29.04.2018 21:57

      Поправил, спасибо!


      1. Serge78rus
        29.04.2018 22:46

        Файл stm32f103.ld, на который есть ссылка в статье, тоже содержит строку

        ram: org = 0x20000000, l = 4K
        За статью спасибо, ждем продолжения.


  1. Goron_Dekar
    29.04.2018 22:00

    Осталось расписать файл stm32f103.ld


  1. Mirn
    30.04.2018 07:57
    +1

    извиняюсь но ассемблер лучше не использовать:
    1. в gcc есть атрибуты которые позволяют разместить что угодно в заданной секции линковки.
    2. в ld файле можно нужным секциям задать KEEP чтоб он не выкидывал оптимизацией например контекст точки входа (адрес входа и стека) и всё что указано в пункте 1
    3. весь gcc компилятор с си и его линкер сам умеет добавлять единичку к указателям на код.
    4. использование по назначению тулчейна и его знание это то к чему новичков следует приучать сразу.
    5. писать на ассемблере такие тривиальные вещи в 21 веке это как сдать чертёж дома в автокаде но фасад выполнить на каменных скрижалях. Зачем для 1% прошивки использовать совсем другой язык с другой парадигмой в добавок когда есть современная альтернатива?
    6. нередко новички начитавшись статей где используется асм и прочий низкоуровневый устаревший тулчейн, вместо изучения электроники и программирования, бросают все силы в прокачку умений низкоуровневой оптимизации, которая часто ненужна, не к месту, не читабельная и не поддерживается и самое плохое — бесполезно или даже вредна быстродействию/размеру.
    7. «зато вы начнете понимать и “чувствовать” устройство изнутри» ах как бы хотелось научиться понимать устройство изнутри чтоб ктонибудь разобрал такие вещи как ДМА, виды памяти и оптимизация быстродействия переносом в IDTM память например, свои загрузчики, защита памяти и всё то что действительно является устройством, а не эти базовые вещи архитектуры любого проца темболее которые одноразово сделаешь и забудешь. А вот ДМА, события, прерывания и это у разных мк разные и знать реально важно иначе зачем брать именно такой мощный МК. А про USB и всякие Ethernetы темболее.

    Я не противник асма, но он просто необходим для чтения и понимания того что не так сделал компилятор, понимания ботлнеков и тд. Но он не для написания. Иногда мне кажется что было бы лучше если компилятор с ассемблера не поставлялся в стандартном пакете GCC вообще даже в сборках для нужд электроники.
    Традиционно предлагаю челендж для защитников асма: например сделать на асме быстрый загрузчик для СТМ32Ф4 в 4кбайта. Ну или свой вариант но только чур практически ценный всем. Надоело спорить с мигателями светодиодов и хеллоувордчиками — спору нет ваш асм на такой банальности лучше но толку от этого нет.
    Извиняюсь если показалось грубо, но об этом надо говорить.
    Ребята, давайте изучать электронику, программирование и программирование электроники! А асм оставим на потом, когда упрёмся лбом в быстродействие когда оптимизировали алгоритмы и данные и даже электронику до последнего и не хватает всего 10 — 20% быстродействия.


    1. Goron_Dekar
      30.04.2018 08:23

      Ассемблер всё равно придётся использовать. Правда, в виде вставок, но без него никак.
      К сожалению, даже С/С++ не дают всех тех возможностей, которые иногда нужны.
      Например, переключить задачу в RTOS, или сделать тело прерывания, отрабатывающее за 10 тактов (включая переход в прерывание).
      Но тех, кто пишет на нём логик или инициализацию надо карать. И самая страшная для них кара — поддержка собственных проектов.


      1. dimaviolinist
        30.04.2018 10:28
        +2

        Я, вот как не фантазирую, никак не могу себе представить себе необходимость «Ассемблер всё равно придётся использовать» на двухдолларовой платке на 72 МГц. Да даже если там компилятор в прерывании выдаст на 10 тактов больше кода, чем на чистом asm.


        1. Mirn
          30.04.2018 16:27

          он про всё тело прерывания в 10 тактов… что странно как то и возможно ли?
          Мне как то пришлось сделать ножко дрыгание без дма частотой в 500килогерц (без дма потому что выдача на output в текущем прерывании зависила от инпута от предыдущего) и то «этот костыль» жрал 50% CPU на 168мегагерцах хоть и был буквально в 5 примитивных строк на Си.

          И вообще если встала задача в оптимизации до 10 тактов то надо менять архетектуру, или подумать над задачей и подходом в целом, тут полюбому проблема не в софте а уровнем гораздо выше.

          В добавок современные процы сложные и инструкции могут выполняться не за гарантированное время, а раз прерывание, то явно будет работа с устройствами, а там вообще хаос. Ну получишь ты 5-10 инструкций, но гарантий если похорошему подумать точно не будет.


        1. Dima_Sharihin
          30.04.2018 16:42

          Хорошо, как поменять указатель стека из Си?


          1. Mirn
            30.04.2018 16:51

            Ну в моём случае загрузчика и задачи вызова основной программы:
            при помощи чтения документации на основу из основ всех ARM CortexMx, она CMSIS называется.
            и использовании инстрикт-функции __set_MSP(newSP);
            внутри naked функции, т.е. той в теле которой нет никаких приготовлений с стеком что очень важно иначе она испортит новый стек и сдвинет его.

            __attribute__( ( naked ) )
            void jump_main(uint32_t stack, uint32_t func)
            {
            	__set_MSP(stack);
            	(*(void (*)())(func))();
            }
            

            а её вызов
            void main_start()
            {
            ...
            	send_str("CONTEXT OK\r\r");
            	usart_deinit();
            	RCC_DeInit();
            	jump_main(boot_from[0], boot_from[1]);
            }

            целиком в github.com/Mirn/Boot_F4_fast_uart/blob/master/src/sfu_commands.c
            начиная с 229 строки


            1. Dima_Sharihin
              30.04.2018 16:56

              1. CMSIS далеко не у всех вендоров МК используется
              2. __set_MSP(uintptr_t addr) — не intrinsic, а обычная Сишная функция, написанная на ассемблере

              /**
               * @brief  Set the Main Stack Pointer
               *
               * @param  topOfMainStack  Main Stack Pointer
               *
               * Assign the value mainStackPointer to the MSP 
               * (main stack pointer) Cortex processor register
               */
              void __set_MSP(uint32_t topOfMainStack) __attribute__( ( naked ) );
              void __set_MSP(uint32_t topOfMainStack)
              {
                __ASM volatile ("MSR msp, %0\n\t"
                                "BX  lr     \n\t" : : "r" (topOfMainStack) );
              }

              Что-то я не вижу здесь отсутствия ассемблерного кода


      1. Mirn
        30.04.2018 16:32
        +1

        Ассемблер всё равно придётся использовать. Правда, в виде вставок, но без него никак.

        Да полностью во всём согласен, но это явно придёт не сразу и мне кажется что начинать «с самых основ» кодить на асме странно. Разве что как специфическое хобби, но основы ли это? Мне кажется что странно делать статью для начинающих с очень сложного подхода без особой надобности.


        1. videochel
          01.05.2018 08:43
          -2

          в древние времена программировать на CPU приучали как раз на асме. Тогда получался инженер. Но это правда приучали технарей, инженеров железячников. А программистам этого не понять.
          У них другой путь. Научиться С++, а потом покупать готовые платки и прошивать их на С.
          И после этого говорить, что «они работают с электроникой» )
          Не с электроникой вы работаете, а с работой тех, кто работал с электроникой. А вы программист.


          1. Mirn
            01.05.2018 09:23
            +1

            Но бизнесу всё равно кто кем называется. Бизнесу и обществу нужен результат и далеко не идеальный а в меру рабочий результат за разумные деньги. Мне вот ковыряться с ассемблером надоело ещё в 2000ых. Да в начале 90ых это было круто, особенно для МК когда ничего кроме асма не было, а в времена i51 и первых пик микро вообще порой на опкодах фигачили (завод закрытый и инета не было вместе с компиляторами) — но повторять такое садомазо не хочу.
            А хочу пользоваться современными средствами разработки и прочесть на новый GCC грёбанную документацию (RTFM) потому что есть семья, другие хобби и просто жизнь вне инженерии и ИТ.

            Другой довод против асма:
            написать простейшую программу вида:
            >Введите своё имя: Иван
            >Привет Иван!
            Но на асме и на японском или другом требующем сложного способа ввода. Да можно вызывая просто стандартный IME. Т.е. базовые вещи для двух третей населения планеты выраждаются на асме в лютый дрочь, что согласитесь как минимум не уместно в 21 веке.


    1. avost
      30.04.2018 10:05
      +3

      Простите, что задаю этот вопрос, но без него никак. Вы ведь программирование микроконтроллеров (и программирование вообще) начали с написания вашего высокоскоростного оптимизированного загрузчика в 4 кб? Я в программировании лет 20, паял раньше разное, спекки и тд. Но статьи по программированию контроллеров удручают. Либо ардуино подход, либо решение сложных узкоспециализированных задач. Но первый, при низком пороге вхождения, не даёт понимания что происходит, а второй, да, вызывает восхищение автором, умявшим загрузчик в 4к, но тоже не дающий понимания — вероятно, это очень круто, но, опять же, чёрт возьми, что происходит!?
      Да, можно разобраться самому, но у многих и своя сложная работа, семья и тд. На мой взгляд, автор выбрал очень правильный стиль изложения. У меня, например, в кои-то веки возникло непреодолимое желание дождаться продолжения и вытащить, наконец, из ящика "голубую таблэтку" :). Что до ассемблера — мне не показалось, что автор собирается использовать его в дозах больше, чем необходимо. Будем надеятся, что действительно не показалось.
      А автору — пейши ещё! (пошёл ставить кросстулы) ;)


      1. Mirn
        30.04.2018 16:39

        Что до ассемблера — мне не показалось, что автор собирается использовать его в дозах больше, чем необходимо.

        Хех. Мне показалось что он его использует слишком мало, я бы сказал что меньше всех.
        Обычно для CortexMx пишется на асме полноценный стартап код на асме, т.е. таблица прерываний с контекстом сброса, стек, инициализация озу и иногда даже RCC и PLL что оправдано иначе это всё на том же SPL жрёт сразу с ходу почти 8 кбайт, что многовато для младших МК.
        Либо его не используют вообще обходясь средствами GCC.
        Иногда его используют чууучть-чуть как это делается в Кокосе чисто чтоб память иницилизировалась не за 100-200мс а побыстрее, только лишь циклы очистки bss и копирования .data.
        А автор вроде хотел избавиться совсем от него, но вот пару машинных слов пришлось вот так вот выкручиваться. Чтож интересно свежо и здорово, мне понравилось. А писал я предостережение для новичков чтоб знали что можно сделать красивше.


    1. Koyanisqatsi
      30.04.2018 10:28
      +3

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


      1. dernuss
        30.04.2018 10:52

        А как продолжать, вопроса не должно возникнуть, как мне кажется…


    1. DrAndyHunter Автор
      30.04.2018 11:19
      +3

      Очень хороший комментарий для меня. Я постараюсь в будущем (надеюсь не далеком) учесть пожелания рассказать о dma, прерываниях и т. п.
      Что касается ассемблера, в следующих частях я уже не вернусь к нему, так как писать на нем особенно для новичка, это мучение. Но зато хорошо тренирует память и умение писать на ассемблере.


      1. Mirn
        30.04.2018 16:18

        чтоб не быть голословным и для примера как голым си обойтись и сделать маленький хеллоу ворлд совсем без асма и для понимания как это делается, вот ссылка
        habr.com/post/274901/#comment_8738493
        эдакий минимальный статап без использования инициализации ОЗУ и нормальной переферии типа PLL, надеюсь эта рыба будет полезной.

        а асм если разбирать то на примере дизасма Си кода — это порой очень важно чтоб понимать почему реалтайм критический участок кода внезапно перестал работать как надо. Там кстати много реально интересного подчерпнуть можно, и как в Кортекс М делается быстрое деление и как делаются частичные инлайны, и почему в некоторых случаях переферия не так работает, и влияние оптимизаций (как вы можите словить в простейшей проге переполнение 65к байтного стека например после loop unrool) и прочее и прочее. Да за 10 лет с стм32 чего только не было… всегда кучу времени экономило заглядывание в дизасм листинг примитивной программы тестовой, после локализации проблемы и выделения глючной части в отдельный проект.
        Но вот самому чего то на асме монстрячить — ну вот не разу даже мысли не возникло, извините я наверное уникальный такой да и надо было в 99% случаях просто сделать в срок проект.


    1. onborodin
      30.04.2018 12:06
      +1

      извиняюсь но ассемблер лучше не использовать

      Для целей разработки — минимально, осознанно, в силу необходимости, не более чем в виде редких вставок.
      В целях (само)обучения — обязательно, в достаточном объеме до четкого понимания.


      Но вряд ли авторы статей на хабрах претендуют быть авторами целевых платных курсов, с соотвествующими объемами работ на разработку материалов, методики и отвественностью за результат обучения.
      Поэтому как есть, в их частных альтруистких целях, жастофан, не судить, и так далее.


    1. DrAndyHunter Автор
      01.05.2018 16:40
      +1

      > в gcc есть атрибуты которые позволяют разместить что угодно в заданной секции линковки.
      > в ld файле можно нужным секциям задать KEEP
      >gcc компилятор с си и его линкер сам умеет добавлять единичку к указателям

      Изучил неспеша, почитал документацию к gcc, и в итоге переписал загрузку на C. Спасибо еще раз!


  1. ZyXI
    30.04.2018 09:19
    +1

    Ещё я бы убрал возню с main.bin. St-link нормально поддерживается openocd, а gdb умеет отправлять на устройство ELF файлы. (Дамп памяти тоже можно gdb сделать, но я не знаю, как делать это эффективно.) Зачем записывать с st-flash, если можно сразу познакомить с gdb, тем более что отладчик всё равно понадобится для реальной разработки?


    1. onborodin
      30.04.2018 11:42

      gdb умеет отправлять на устройство ELF файлы.

      гм, может я что-то пропустил в жизни, но дебагер ни разу не умеет отправлять на устройство, в силу того что в нем нет такого кода вообще, и это осуществляет, к примеру, openocd + stlink свисток как агент отладчика.
      Картинки тут.
      https://stackoverflow.com/questions/38033130/how-to-use-the-gdb-gnu-debugger-and-openocd-for-microcontroller-debugging-fr


      1. ZyXI
        30.04.2018 19:01

        Openocd предоставляет сервер, связывающийся по протоколу удалённой отладки. Gdb умеет отправлять прошивку серверу, которые отправляет её дальше на устройство, но т.к. с пользователем в этом случае взаимодействует именно gdb, а openocd просто запускается один раз где?то в фоне, то это «gdb отправляет файлы».


    1. onborodin
      30.04.2018 11:49

      PS
      Собственно, вся операция ELF to binary, длится доли секунды
      arm-eabi-objcopy -O binary $elf_file $bin_file
      Можно вписать в Макеfile, можно в скрипт


      1. ZyXI
        30.04.2018 19:16

        Вопрос не в том, сколько она длится. Вопрос, зачем оно вообще нужно? Кроме того, bin?файл это один непрерывный кусок данных без каких?либо смещений. Что означает, что во?первых, если там будут разрывы (а они будут если сказать компоновщику распихать секции по разным регионам памяти), то вы будете отправлять лишние нули. И, во?вторых, смещение должны указать вы, что является абсолютно бесполезным копированием магических чисел. И зачем говорить «отправь файл на устройство по адресу X», если можно просто сказать «отправь файл на устройство»?


        1. ZyXI
          30.04.2018 19:21

          И, кстати, gdb ещё и напишет вам размер прошивки при выполнении команды load. Получение его из ELF — это ещё одна лишняя команда. А bin файл может содержать лишние нули.


          1. onborodin
            30.04.2018 22:00

            если не ошибаюмь по памяти, gdb при отправке в таргет делает конверсию elf2bin на лету, в чип передается готовый образ.


  1. onborodin
    30.04.2018 10:28

    Ловите заготовки
    github.com/onborodin/stm32lcd
    github.com/onborodin/stm32lc2
    Ну и для f4 заготовка
    github.com/onborodin/stm32f4tm
    Не жалко.
    Все для opencm3 + newlib, писал просто в редакторе mcedit.
    В коде есть микро косячки (типа сбудуна назначил точку входа main() вместо reset_handler(), так оно и жило какое-то время), и не все оптимально, но это для примера не принципиально.


  1. mksma
    30.04.2018 10:28

    Спасибо за статью. У меня возник вопрос по поводу адресов. Вы пишите:

    Каждый указатель имеет размер 4 байта, значит следующий адрес за 0x0800 0004 во flash памяти будет 0x0800 0004 + 4 = 0x0800 0008. Это значение и необходимо поместить по адресу 0x0800 0004.

    Судя по таблице векторов прерываний 0x0800 0004 – адрес reset вектора, а 0x0800 0008 – адрес NMI вектора. Непонятно зачем размещать адрес NMI вектора в reset векторе?


    1. DrAndyHunter Автор
      30.04.2018 11:12

      Да, вы абсолютно правы. По-хорошему необходимо описать всю таблицу векторов прерываний, но я не хотел, чтобы мой читатетель уснул на пятом абзаце объяснений векторов прерываний.


  1. maxfox
    30.04.2018 12:04

    О, это просто замечательный материал, очень надеюсь на продолжение. Совершенно не понимаю, почему все книги/статьи/руководства по программированию МК начинаются с описания IDE, которые нажатием пары магических кнопок делают что-то, о чем мне пока (вообще?) знать не положено… Ведь моя задача — не достижение конкретного результата, я хочу понять, что происходит и как это работает. Почему все эти горе-писатели пытаются оградить меня от этого знания?
    В общем, жду продолжения, очень радует ваш подход, тем более что я тоже использую Arch Linux как рабочее окружение.


  1. silovi4
    30.04.2018 12:38

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


  1. neitri
    30.04.2018 15:12

    --pagesize — размер страницы памяти — 0x400 (256 байт)
    0x400 =1024
    256=0x100
    какой размер страницы?


    1. DrAndyHunter Автор
      30.04.2018 16:32

      Да что же я с цифрами то везде ошибся. Конечно 1024. Поздно вечером нельзя писать и считать ничего. Спасибо, поправлю!


  1. mkc
    30.04.2018 18:49

    Пишите обязательно ещё! В идеале показать инициализацию периферии на асме и связь потом с С листингом)


    1. DrAndyHunter Автор
      30.04.2018 18:51

      Связь с C кодом, если я вас правильно понял, очень простая: нужно заменить Reset на main в безусловном переходе.


  1. Mike-M
    30.04.2018 20:33

    Начало интересное. Ждем продолжения.
    Предлагаю показать инициализацию в двух вариантах: на ассемблере и на С.