Прошивка ARM Cortex M3 на примере STM32 и LPC1300. STM32 — это очень просто Я хочу установить дополнительную защиту, но я боюсь, что вы считаете мою суперсекретную программу перед установкой защиты



В своем проекте я использую микроконтроллер STM32F103C8 и фреймворк stm32duino . Этот клон Ардуино предлагает специальный бутлоадер, который позволяет заливать прошивку через USB, без использования внешних компонентов типа ST-Link или USB-UART переходника.

Сегодня мне понадобилось поработать с голым контроллером из-под CooCox и без stm32duino. Но вот в чем проблема. Даже простая моргалка лампочкой влитая через этот бутлоадер не работает.

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

Я ничего не имею против ST-Link и других отладчиков. Но в моем готовом устройстве его не будет, но точно будет USB. Почему бы сразу не заложить возможность обновлять прошивку через USB? Лично я нахожу этот способ удобным. тем более что все равно у меня уже подключен шнурок по которому идет питание и USB Serial.

Давайте посмотрим как работает бутлоадер. Для начала на примере контроллеров AVR. Почему я о нем вспомнил? Я переходил с Arduino и подсознательно ожидал такого же поведения. Но в STM32 оказалось все по другому. Потому хочу рассказать о разнице этих двух микроконтроллеров.

Итак. В микроконтроллерах AVR ATMega под бутлоадер можно зарезервировать некоторое количество памяти ближе к концу флеша. С помощью fuse битов можно регулировать с какого адреса будет стартовать программа. Если бутлоадера нет - программа стартует с адреса 0x0000. Если бутлоадер есть - он запускается с некоторого другого адреса (скажем, в ATMega32 с 0x3C00, если размер бутлоадера выбран 2к).


Когда бутлоадер сделал свои дела он передает управление основной программе с адреса 0x0000. Т.е. программа всегда стартует с адреса 0x0000. Компилятор и линковщик работают с учетом того, что код будет находится в начале адресного пространства.

В микроконтроллерах STM32 все не так. Все программы стартуют с адреса 0x0800000. Бутлоадер не является чем-то таким особенным. Это такая же программа, которая стартует с того же самого начального адреса. В процессе работы бутлоадер может принять прошивку (через USB или UART, считать с флешки, принять со спутника, достать из подпространства, whatever...) и записать ее по адресам выше чем находится сам загрузчик. Ну и, конечно же, в конце своей работы передать управление основной программе.


Так вот при компиляции прошивки нужно знать куда же бутлоадер запишет прошивку и соответствующим образом скорректировать адреса.

На этом с теорией все. Переходим к практике. Ниже пошаговая инструкция как прикрутить USB загрузчик к микроконтроллерам серии STM32F1xx, а может быть и к некоторым другим тоже.

Есть, правда, некоторые ограничения по схемотехнике. Тут я, к сожалению, не силен. ЯТП нужен подтягивающий резистор 1.5к для порта PA12 (он же USB D+). Это позволяет загрузчику в нужные моменты времени подключаться и отключаться от USB.

  • Теперь микроконтроллер готов ппрошиваться через USB загрузчик. Но ведь еще нужно саму прошивку подправить. А сделать нужно 2 вещи:
    • Указать линкеру стартовый адрес. В CooCox это делается в настройках проекта, вкладка Link, раздел Memory Areas, Адрес IROM1 Start Address. Бутлоадер занимает первые 8 килобайт, значит стартовый адрес прошивки будет 0x0800000 + 0x2000 = 0x08002000. Поле Size, наверное, тоже стоит уменьшить на 8к.
    • Где нибудь вначале программы перед инициализацией периферии сделать вызов

      NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x2000);

  • Заливатор прошивки можно взять из проекта stm32duino . В директории tools ищите скрипт под называнием maple_upload. Я пользовал только виндовую версию - maple_upload.bat.
  • Запускать так:

    "maple_upload.bat" COM20 2 1EAF:0003 "Path\To\Firmware.bin"
    Вместо COM20 нужно подставить свой порт куда прицепился микроконтроллер.

    Заливатор штука очень нежная, относительных путей не любит. так что путь к прошивке нужно указывать полностью.

    1EAF:0003 - это VID и PID

    2 - это параметр AltID, который указывает что прошивку нужно заливать по адресу 0x08002000 (читать ).

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

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

    На этом все. Надеюсь моя статья прольет свет на то, как работает загрузчик в STM32 и как можно загружать прошивку через USB порт. К сожалению порог вхождения по прежнему высок, но вдруг кому-то моя статья поможет его преодолеть.

    Некоторые из микроконтроллеров stm32 поддерживают USB DFU протокол (их список можно посмотреть в app note AN3156), в такие МК прошивку можно заливать через обычный USB, используя например DFuSe demo от ST, либо опенсурсный dfu-util. С этим вариантом все понятно и описывать его я не буду.

    Для тех же МК (в частности и того, который используется в плате BluePill — STM32F103C8T6), которые обделены поддержкой DFU, так или иначе нужен программатор, например ST-Link V2 Mini

    Распиновка устройства:

    Подключается к плате просто:

    ST-Link STM32F103C8T6 3.3V --- 3.3V GND --- GND SWDIO --- DIO SWCLK --- DCLK

    Также нужна утилита ST-Link Utility, скачать ее можно с официального сайта st.com — ссылка . При первом подключении неплохо было бы обновить firmware самого программатора. Выбираем ST-LINK -> firmware update, если доступна более свежая firmware, то будет что-то подобное:

    Выбираем Yes >>>>, прошивка обновляется.

    Далее открываем собственно файл с прошивкой, и выбираем Target -> Connect. В окне состояния утилиты появится информация и вашем МК — это значит, что программатор подключен корректно и утилита может контактировать с МК. Пример:

    Затем нужно сделать полную очистку чипа, выбираем Target -> Erase Chip

    В случае, если например, моя прошивка из поста контроллере педалей и кнопок уже была залита и ее нужно обновить или перезалить, то программатор не сможет так просто подключиться к плате (потому что я использую пины SWD как обычные GPIO). В этом случае есть два варианта:

    1. переставить оба желтых джампера. Плата в этом случае загрузится у вас сразу во внутренний бутлоадер
    2. можно сделать так называемый Connect under Reset. Для него последовательность будет такая:
    • в ST-Link Utility выбираем Target -> Settings
    • в Reset Mode выбираем Hardware Reset
    • нажимаем и держим кнопку Reset на плате
    • нажимаем OK в ST-Link Utility
    • отпускаем кнопку Reset на плате

    PS. Если у вас есть плата для разработчиков SMT32F4Discovery, то она уже имеет в себе программатор и его также можно использовать для прошивки другой платы. В данном случае нужно использовать разъем SWD у STM32F4Discovery и убрать обе перемычки CN3. Разъем SWD имеет следующую распиновку:

    Допустим вы выпустили на рынок своё устройство и опасаетесь того, что кто-то начнёт его копировать… Действительно - достаточно подключиться программатором к нашему устройству и считать прошивку, например, через утилиту ST-Link Utility (Target Connect ).

    Конечно можно просто оторвать ножки микроконтроллера… но разработчики ST Microelectronics предлагают альтернативу получше. У всех МК есть система защиты (read out protection). Суть её крайне проста - если в специальном регистре (Option bytes) установлено определённое значение - то возможность отладки и считывания прошивки отключается. В таком режиме у вас также пропадёт возможность перепрошивки МК. Разумеется, эту защиту можно отключить, поменяв значение в регистре Option bytes, однако в таком случае память программы будет затёрта, а значит её никто не сможет скопировать.

    Поменять значение в регистре можно при помощи всё той же ST-Link Utility, Target Option Bytes… Read out protection ENABLE .


    Отключить, соответственно, можно аналогичным способом. Это не совсем удобно, если устройств много. Можно включить защиту программно.

    Для реализации защиты удобно воспользоваться стандартной библиотекой периферии, а конкретно stm32f10x_flash.c содержит необходимую нам функцию. Сам регистр, а также работа с флеш-памятью описана в документе . Однако перед этим вспомним как реализована защита многократного включения заголовочных файлов. Мы использовали такие директивы препроцессора как:

    • #define - директива указывает препроцессору заменять строку в файле, однако если аргумент (то что стоит в конце) не указывается сам идентификатор (то что по середине) остётся в системе и может быть проверен другими директивами (т.е. можно написать условие, при котором определённый код будет выполняться или наоборот);
    • #ifndef - директиву можно прочитать как «если не определенно», однако нам больше подойдёт другая #ifdef - «если определенно»;
    • #endif - директива указывающая конец условия.

    Создадим идентификатор, который будет говорить, что данная сборка финальная. Пока данная строка закомментирована - код помещённый между #ifdef и #endif выполняться не будет.

    //#define RELEASE

    При конечной сборке достаточно просто раскомментировать строчку и прошить устройство.

    #ifdef RELEASE // code here #endif

    А теперь к самой сути. Мы уже разбирались как работать с внутренней флеш-памятью микроконтроллера. Перед записью битов защиты необходимо разблокировать доступ к памяти, затем необходимо произвести нужные операции и снова заблокировать доступ. Получается следующее:

    #ifdef RELEASE #warning "Protection is ON. Debug is OFF" if (FLASH_GetReadOutProtectionStatus() == RESET) { FLASH_Unlock(); FLASH_ReadOutProtection(ENABLE); FLASH_Lock(); } #endif

    Сегодня мы будем защищать прошивку на stm32 от считывания кул-хакерцами. Дабы не тянуть резину, вот кусок кода:

    #ifdef NDEBUG if (FLASH_GetReadOutProtectionStatus() == RESET) { FLASH_Unlock(); FLASH_ReadOutProtection(ENABLE); FLASH_Lock(); } #endif

    Как не сложно догадаться, кусок использует библиотеку от stm. Такой кусок кода использовать очень удобно - контроллер сам устанавливает свою защиту при первом запуске, а первый запуск легко организовать после прошивки. Вот она, свобода от тирании фьюзов!

    Да, я знаю, насчет холивора насчет «библиотека vs непосредственное обращение к регистрам». Моя позиция тут такова - если нужна большая скорость, или осталось совсем мало памяти, но нужно непосредственное обращение к регистрам. Если и скорость не важна и памяти много, лучше использовать библиотеку - так программа пишется быстрее и получается более читаемая.

    Снять защиту
    Контроллер то мы защитили. Но вот беда - нужно поправить нашу программу, а при установленной защите ничего с контроллером сделать нельзя - ни прочитать ни записать. Как снять защиту? Сразу предупреждаю - ребята из ST все сделали правильно и вместе с защитой уничтожается вся прошивка.

    Для снятия защиты нам понадобится программа st-link utility. Скачать ее можно .

    Запускаем программу и выбираем вот этот пункт, либо просто жмем ctrl-b:

    В поле «состояние защиты от чтения выбираем выключено». И нажимаем кнопку Apply:

    Вместе с битом защиты стирается и вся память:

    Защищайтесь, мы за безопасный эмбед.

    В последнее время на хабре появилось много статей по STM32 (). В комментариях неоднократно упоминается сложность STM32 по сравнению с AVR. Эта тема особенно влияет на новичков, которые хотят начать изучение микроконтроллеров, и, видя такое мнение, выбирают для изучения AVR. Давайте разберемся, так ли сложен этот зверь - STM32?

    Для этого выберем недорогой вариант платы и напишем прошивку в десяток-другой байт (да-да, мигание светодиодом в 2 килобайта сродни «Hello world» в сотни килобайт x86 для неумех). Также научимся писать программы на любом языке программирования для STM32.

    Вступление

    Какой тип микроконтроллеров изучать? Этот вопрос, по-моему, аналогичен вопросам типа «Какой язык программирования изучать?», «Какой иностранный язык учить?». ИМХО, изучать нужно тот, который нужнее в данную минуту, для данной задачи. Когда знаешь что-то одно, изучение второго дастся намного легче, а на счет третьего и не задумаешься.

    Итак, в чем же сложность STM32? Наиболее часто звучит мнение о сложности программирования его периферии. Количество и тип периферии STM32 и AVR примерно одинаков. Конфигурирование ее также не сильно отличается. Так в чем же сложность? В микроконтроллерах STM32 всю периферию нужно предварительно включать. Вот и вся сложность.

    Я сравниваю AVR с общественными зданиями: все двери нараспашку, везде мониторы сверкают рекламой и свет горит, а STM32 с личным домом: хочешь телевизор посмотреть - включи сначала, потом переключай каналы, захотел пи-пи - открой дверь и включи там свет, руки помыть - открой воду, и так далее. Не верите? Убедимся вместе.

    Обзор платы

    Я выбрал самую дешевую плату из предложенных на aliexpress (рисунок выше). Чуть дороже $2, 180 рублей в декабре 2015. На борту минимальная обвязка: два кварцевых резонатора - высокочастотный на 8МГц и часовой на 32.768Гц, кнопка «сброс», два джампера выбора режима загрузки, пара светодиодов - на питание и на ножке PC13 и набор разъемов: microUSB, отладочный, две гребенки для всех выводов микроконтроллера.

    Дешевле только купить все детали, сделать самому плату и спаять. Чем шить и отлаживать? Если есть ST-LINK, то лучше им, нет - не беда, есть еще несколько вариантов, например через USB-USART переходник, нет и его - можно напрямую через USB, правда нужно самому написать драйвер для такого случая, никто пока не озаботился. ST-LINK достаточно дешев, да и входит во все платы серии DISCOVERY. Вот и я воспользовался таким.

    Подключаем питание, светодиод весело мигает, плата исправна. Скачиваем и устанавливаем программу-программатор (масло-масляное) «STM32 ST-LINK Utility» (все программы и документы берем на сайте производителя). Пытаемся считать прошивку… Программа защищена от чтения. Видимо, недаром все говорят о сложности написания программ для STM32, даже китайцы защитили эту сверхсложную программу от взлома. Или там спрятана закладка-вирус? Разбираться не будем, снимаем защиту и получаем девственно чистый микроконтроллер STM32F103C8T6.

    Первая программа

    Давайте тоже помигаем светодиодом, сделаем, так сказать, реверс-инжиниринг в уме родной прошивки. Чем? Чтобы не городить споры по выбору среды разработки, я это сделаю в родной Visual Studio Community. Мне кажется, для Windows лучше для мужчины нет.

    Как там программа мигания для ардуины? Конфигурируем ножку на выход и в цикле переключаем ее с нуля на единицу и обратно.
    А как будет выглядеть она же для STM32? Намного сложнее. Сначала включим свет в комнате конфигурации ножек микроконтроллера, а затем «Конфигурируем ножку на выход и в цикле переключаем ее с нуля на единицу и обратно». Я понимаю, сложно… Но мы справимся.

    В документе «RM0008. Reference Manual» на наш микроконтроллер посмотрим карту памяти для нужных нам регистров.

    - Пойдем простым и логическим ходом.
    - Пойдем вместе.

    1. Включим тактирование порта C (наш светодиод висит на ножке 13 порта C). Смотрим документ. Нужный нам регистр RCC_ABP2ENR (переводим: регистр сброса и тактирования - вторая низкоскоростная шина периферии). Адрес порта - 0x40021018, нужный бит IOPCEN (порт ввода-вывода C - бит разрешения) четвертый - 0x00000010.

    Отступление

    У микроконтроллеров все как у взрослых процессоров. Есть высокоскоростная шина AHB aka «Северный мост» и низкоскоростная APB aka «Южный мост». Сам процессор микроконтроллера умеет все для ускорения работы: имеет предвыборку команд, конвейер выполнения команд. Нет кеша, но процессор не намного быстрее памяти, и чтение-запись в память успевает выполняться за один такт. Так что, можно сказать, вся память микроконтроллера - это один большой кеш. Ладно-ладно, не один и не большой. Два маленьких кеша.
    Вся периферия отображена (маппирована) на адресное пространство. По сравнению с x86 нет команд in-out, но и Intel оставил их только для совместимости, сейчас они практически не используются.


    2. Сконфигурируем ножку на вывод. Смотрим документ. Нужный нам регистр GPIOC_CRH (переводим: регистр порта ввода-вывода C - конфигурационный регистр для старшей половины ножек). Адрес порта - 0x40011004, за конфигурацию каждой ножки отвечают 4 бита, значение для переключения ножки на выход - 0001b, для ножки 13 значение - 0x00100000.

    3. Как переключить логическое значение на ножке. Смотрим документ. Нужный нам регистр GPIOC_ODR (переводим: регистр порта ввода-вывода C - регистр вывода данных). Адрес порта - 0x4001100С, его значение напрямую выводится в ножки микроконтроллера, для ножки 13 значение - 0x00002000. Все готово для написания программы (не забыть выложить проект на github):

    Int main(void) { *((int*)0x40021018) = 0x00000010; // RCC_APB2ENR = RCC_APB2ENR_IOPCEN *((int*)0x40011004) = 0x00100000; // GPIOC_CRH = MODER_OUTPUT_13 while(1) { *((volatile int*)0x4001100C) ^= 0x00002000; // GPIOC_ODR ^= BIT_13 int i; for (i=1000000; i>0; i--) ; } } extern int _eram; __attribute__ ((section(".isr_vector"))) int g_pfnVectors = { (int)&_eram, // начальное значение стека (int)main // Reset Handler };
    С векторами прерываний, надеюсь все понятно? Мы используем только два из них, поэтому незачем занимать память пустышками. Все остальные прерывания включаются программно, не включали - значит они никогда не сработают. Исключение - третий вектор HardFault, если случилось - микроконтроллер неисправен или сбоит, для простых проектов (не космос-авиация, не медицина) можно не обрабатывать.

    Это учебный проект, конечно следует оформить все адреса как символические константы в отдельный h-файл с большим количеством дефайнов, как это сделано в CMSIS. Можно взять их и приспособить для своих нужд. Для компиляции использую gcc, прошивка с помощью «STM32 ST-LINK Utility». Прошивка заняла 56 байт (привет, ассемблер).

    Еще отступление

    Еще одно утверждение о сложности STM32 - мало документации на русском языке. Спорно. Необходимы только два документа - Datasheet и Reference Manual на нужный микроконтроллер. Язык на котором он написан сложно назвать английским. Я изучал язык по непереведенным игрушкам, уровень английского остался на том же уровне, но даташиты я читаю без проблем, незнакомые термины понятны из контекста.



    Вроде много получилось, тогда на сегодня все.

    Во второй половине расскажу о программировании STM32 на любом языке программирования.

    Теги: STM32, микроконтроллеры-это-просто, ардуино-не-надо

    В продолжение темы:
    Интернет-подсказки

    Кто-то мечтает найти свою любовь, кто-то обожает общаться с новыми людьми и ищет друзей по всему миру. Именно для таких предоставлено приложение Topface – знакомства и...

    Новые статьи
    /
    Популярные