ARM и специфичный для компиляторов код

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

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

Ниже приведен пример кода для некоторых ARM-компиляторов.


#if defined (__CC_ARM)
    /* Код для компилятора Keil */
#elif defined (__ICCARM__)
    /* Код для компилятора IAR */
#elif defined ( __GNUC__ )
    /* Код для компилятора GNU */
#elif defined (__TASKING__)
    /* Код для компилятора TASKING */
#else
    /* Неизвестный компилятор */
    #error Unknown compiler
#endif

STM32CubeMX по умолчанию отключает SWD для серии F1

STM32CubeMX — это генератор исходного кода для микроконтроллеров STM32. Позволяет настроить периферию с помощью графического интерфейса.

Если создать проект в STM32CubeMX, выбрать микроконтроллер серии F1 и сгенерировать исходный код, не производя никаких настроек, то этот код отключит интерфейсы программирования JTAG и SWD. Если такую программу скомпилировать и прошить, то в следующий раз прошить микроконтроллер будет затруднительно.

То есть новичок, делающий первую программу для STM32F1, гарантировано окажется в тупиковой ситуации.

Проблема проявляется только для микроконтроллеров серии F1. Для проверки использовался STM32CubeMX последней (4.17.0, на 13.11.2016) версии.

Проблема несколько раз упоминалась (раз, два, три) в 2015 году. И STM даже обещали (по третьей ссылке) ее исправить, но не исправили.

Код, отвечающий за отключение JTAG и SWD, находится в файле stm32f1xx_hal_msp.c в функции HAL_MspInit(), которая вызывается функцией HAL_Init(), и выглядит следующим образом:

 /**DISABLE: JTAG-DP Disabled and SW-DP Disabled
*/
__HAL_AFIO_REMAP_SWJ_DISABLE();

Чтобы избежать этой проблемы, нужно в STM32CubeMX на вкладке Pinout в дереве настроек найти пункт Configuration -> Peripherals -> SYS -> Debug и из выпадающего списка выбрать подходящее значение:

  • No Debug — значение по умолчанию, отключает отладочные интерфейсы JTAG и SWD.
  • Serial Wire — отключает JTAG, включает SWD, который использует только два вывода, подходит для программатора ST-Link.
  • JTAG (4 pin) — включает JTAG и SWD, используется 4 вывода (без NJTRST).
  • JTAG (5 pin) — включает JTAG и SWD, используется 5 выводов (с NJTRST), что соответствует состоянию микроконтроллера после сброса.

Как прошить микроконтроллер с отключенными JTAG и SWD

Если SWD и JTAG отключились, то не все потеряно. Уверен, что есть проекты в которых это даже необходимо.

Для начала нужно создать прошивку, которая бы не отключала SWD и JTAG.

Вариант 1

Вариант работает при прошивке через SWD с помощью ST-LINK/V2 (ST-LINK второй версии). Через JTAG тоже можно, но с ST-LINK, у которого версия прошивки V2J15Sx или новее.

В программе STM32 ST-LINK utility в настройках нужно выбрать режим (Mode) «Connect Under Reset» (подключение при сбросе). Этот режим позволяет подключиться к микроконтроллеру до начала выполнения программы.

Чтобы прошить микроконтроллер, нужно на вывод сброса микроконтроллера (NRST) подать низкий уровень (или соединить вывод с землей), запустить прошивку и сразу убрать низкий уровень с NRST.

Возможно, чтобы вручную не дергать NRST, можно соединить его с одноименным вывод ST-LINK и произвести еще какие-то настройки STM32 ST-LINK utility. Но автор эту идею не проверял. Если вы знаете, напишите в комментариях.

Вариант 2

Если у вас нет ST-LINK/V2, то микроконтроллер можно прошить через USART с помощью программы STM32 Flash loader demonstrator. Для этого понадобится переходник USB-UART, который создаст на компьютере виртуальный COM-порт.

Во флеш STM32 есть системная область, в которую на заводе зашит специальный загрузчик. Чтобы запустить выполнение программы загрузчика, нужно подать на ноги BOOT0 и BOOT1 необходимые уровни (согласно документации) и запустить микроконтроллер.

В Application note AN2606 можно уточнить какие USART с какими выводами использует загрузчик, а также необходимое состояние выводов BOOTx.

Развертывание среды разработки для STM32

Большая часть статьи — это текстовое описание видео: 

В статье показано, как с помощью бесплатных кроссплатформенных инструментов развернуть среду разработки для микроконтроллеров STM32.

Программы и библиотеки, о которых будет идти речь, уже объединены в готовые бесплатные среды разработки, например CooCox CoIDE или SW4STM32. Первая только для Windows, а вторая для множества операционных систем, но ссылки для скачивания появляются только после регистрации.

Мы соберем свою среду, чтобы лучше ее понимать и контролировать.

Читать далее Развертывание среды разработки для STM32

Приоритеты прерываний в Cortex-M и их настройка в STM32Cube

STM32Cube — это набор библиотек фирмы STM для своих микроконтроллеров (STM32). Эти библиотеки пришли на смену библиотекам SPL.

Микроконтроллеры STM32 сделаны на базе ядра ARM Cortex-M. Это ядро служит основой микроконтроллеров и у других производителей.

Ядро поддерживает прерывания, которые могут прерывать не только основную программу, но и друг друга, либо не прерывать, что определяется их приоритетом.

Приоритет прерывания зависит от 3 факторов:

Читать далее Приоритеты прерываний в Cortex-M и их настройка в STM32Cube

Начало работы с STM32 в Keil MDK-ARM

Статья посвящается Пановой Ксении.

Все картинки в этой статье кликабельны.

STM32 — это семейство 32-разрядных микроконтроллеров фирмы STMicroelectronics.

Микроконтроллеры содержат микропроцессорное ядро ARM, точнее ARM Cortex-M. Это ядро присуще не только микроконтроллерам STM32, оно существует само по себе, и на его основе выпускается множество микроконтроллеров от разных производителей.

Keil MDK-ARM (произносится «Кеил эм-ди-кей арм») — это среда разработки для микроконтроллеров с ядром ARM Cortex-M.

Читать далее Начало работы с STM32 в Keil MDK-ARM

Переполнение при отсчете времени в STM32Cube

Ранее я писал об отсчете времени в STM32Cube.

STM32Cube считает время в миллисекундах. Время хранится в беззнаковой целочисленной 32-битной переменной:

static __IO uint32_t uwTick;

Это означает, что через 49 суток 17 часов 2 минуты 47 секунд и 295 миллисекунд произойдет переполнение этой переменной.

Хорошо, если устройство не может так долго работать, например, гарантировано сядет батарейка. Но, что если устройство должно работать непрерывно месяцами?

Время используется библиотекой в операциях ввода/вывода для организации таймаутов. Кроме того, библиотека предоставляет функцию задержки HAL_Delay().

Если переполнение обрабатывается неправильно, то мы получим неправильные таймауты и задержки.

Посмотрим, на код, отвечающий за обработку времени:

файл stm32***_it.c:

void SysTick_Handler(void)
{
  HAL_IncTick();
}

файл stm32***_hal.c:

static __IO uint32_t uwTick;

__weak void HAL_IncTick(void)
{
  uwTick++;
}

__weak uint32_t HAL_GetTick(void)
{
  return uwTick;
}

__weak void HAL_Delay(__IO uint32_t Delay)
{
  uint32_t tickstart = 0;
  tickstart = HAL_GetTick();
  while((HAL_GetTick() - tickstart) < Delay)
  {
  }
}

Обработчик прерывания SysTick_Handler() вызывается каждую миллисекунду, и вызывает функцию HAL_IncTick(), которая просто увеличивает uwTick на единицу (инкрементирует). Функция HAL_GetTick() возвращает текущее время в миллисекундах; эта функция обильно используется библиотекой следующим образом:

uint32_t tickstart = HAL_GetTick();
...
if((HAL_GetTick()-tickstart) >= Timeout)
{ ... }

Операция сравнения времени довольно проста. Аналогичная операция используется в функции HAL_Delay().

Видите обработку переполнения? И я не вижу. А она есть.

Рассмотрим функцию HAL_Delay(). Для начала ситуацию без переполнения.

Допустим:

Delay = 100,
tickstart = 1000.

Разница

HAL_GetTick()-tickstart

будет расти от 0 до 100. Цикл будет прерван, когда HAL_GetTick() вернет 1100:

1100 - 1000 = 100

100 < 100 = ложь,

Теперь рассмотрим ситуацию с переполнением.

Допустим:

Delay = 100,
tickstart = 4294967290
.

Максимальное число в беззнаковой 32-битной переменной равно 4294967295. То есть до переполнения остается всего 5 инкрементов. При 6-м инкременте HAL_GetTick() вернет 0.

Что тогда вернет операция

HAL_GetTick()-tickstart

то есть, что будет если из 0 вычесть 4294967290?

Обе переменные беззнаковые и целочисленные. И операция вычитания вернет 6. А дальше, когда HAL_GetTick() возвратит 1, операция вычитания вернет 7.

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

Важно, что такое поведение стандартизировано и должно реализовываться любым компилятор C и C++. Можете ознакомиться с обсуждением этого вопроса на Stack Overflow.

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

Вывод

Обработка времени в библиотеке STM32Cube происходит корректно, несмотря на то, что переменная uwTick, хранящая время, может переполнится.

Отсчет времени в STM32Cube

STMicroelectronics выпускает библиотеку STM32Cube для своих микроконтроллеров. Эта библиотека пришла на смену Standard Peripherals Library и содержит новые особенности.

Одна из особенностей — это отсчет времени. Благодаря ему библиотека может устанавливать время ожидания в операциях ввода/вывода.

Время считается в миллисекундах и хранится в беззнаковой целочисленной 32-битной переменной:

static __IO uint32_t uwTick;

Максимальное значение, которое может принимать эта переменная, равно 2^{32}-1. Что соответствует 49 суткам 17 часам 2 минутам 47 секундам и 295 миллисекундам.

Можно убедится:

49\cdot24\cdot60\cdot60\cdot1000+17\cdot60\cdot60\cdot1000+2\cdot60\cdot1000+47\cdot1000+295=\newline=4294967295

2^{32}-1=4294967295

Пользователю предоставляются две полезные функции:

  • uint32_t HAL_GetTick (void)
    Возвращает время в миллисекундах.
  • void HAL_Delay (uint32_t Delay)
    Создает задержку на заданное количество миллисекунд.

Отсчет времени запускается при инициализации библиотеки, вызовом функции HAL_Init().

Для отсчета времени используется таймер SysTick, который встроен в ядро Cortex-M. Функция HAL_Init() настраивает таймер так, чтобы он генерировал прерывание каждую миллисекунду. В обработчике прерывания SysTick (в функции SysTick_Handler()) должен быть вызов функции HAL_IncTick(). Эта функция увеличивает счетчик времени на единицу.

Приоритет прерывания SysTick задается определением TICK_INT_PRIORITY в файле stm32*_hal_conf.h. Чтобы функция HAL_Delay() работала в обработчиках других прерываний, требуется чтобы приоритет этих прерываний был меньше, чем у SysTick.

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

Инструменты для разработки под ARM-микроконтроллеры

Список бесплатных кроссплатформенных инструментов для разработки под ARM-микроконтроллеры:

  1. Eclipse IDE for C/C++ Developers
    интегрированная среда разработки (чтобы русифицировать, следует установить Babel (Вавилон)),
  2. GNU Tools for ARM Embedded Processors
    инструменты для сборки из исходного кода: компилятор C/C++, сборщик и т. д.,
  3. GNU ARM Eclipse Plug-ins
    плагин для Eclipse, объединяющий предыдущие два инструмента,
  4. CMSIS
    низкоуровневая библиотека для процессоров ARM Cortex-M, скачивание доступно после регистрации.
  5. Библиотеки для конкретных микроконтроллеров, которых тысячи, например:

STM32 Communication peripheral application library (CPAL)

CPAL — это библиотека предоставляющая высокоуровневый программный интерфейс к I2C микроконтроллеров STM32.

Существует две версии библиотеки: CPAL (CPAL v1) и CPAL v2.

CPAL v1 поддерживает следующие микроконтроллеры:

  • STM32F10x
  • STM32F2xx
  • STM32L1xx

CPAL v2 поддерживает следующие микроконтроллеры:

  • STM32F0xxxx
  • STM32F3xxxx

CPAL v2 входит в SPL (Standard Peripherals library) соответствующих микроконтроллеров.

CPAL v1 предоставляется отдельно.

Документация:

Подключение CPAL v1 к проекту

  1. Из библиотеки в свой проект скопировать два файла:
    • STM32_I2C_CPAL_V1.1.0/Libraries/STM32_CPAL_Driver/inc/cpal_conf_template.h
    • STM32_I2C_CPAL_V1.1.0/Libraries/STM32_CPAL_Driver/src/cpal_usercallback_template.c
  2. Переименовать скопированные файлы, убрав из имени «_template».
  3. Необходимо указать следующие пути поиска подключаемых фалов:
    • STM32_I2C_CPAL_V1.1.0/Libraries/STM32_CPAL_Driver/devices/stm32f10x
    • STM32_I2C_CPAL_V1.1.0/Libraries/STM32_CPAL_Driver/devices/stm32f2xx
    • STM32_I2C_CPAL_V1.1.0/Libraries/STM32_CPAL_Driver/devices/stm32l1xx
    • STM32_I2C_CPAL_V1.1.0/Libraries/STM32_CPAL_Driver/inc
    • путь к файлу cpal_conf.h
  4. Добавить следующие модули в сборку:
    • STM32_I2C_CPAL_V1.1.0/Libraries/STM32_CPAL_Driver/src/cpal_hal.c
    • STM32_I2C_CPAL_V1.1.0/Libraries/STM32_CPAL_Driver/src/cpal_i2c.c
    • Из каталога STM32_I2C_CPAL_V1.1.0/Libraries/STM32_CPAL_Driver/devices:
      • /stm32f10x/cpal_i2c_hal_stm32f10x.c
      • /stm32f2xx/cpal_i2c_hal_stm32f2xx.c
      • /stm32l1xx/cpal_i2c_hal_stm32l1xx.c
    • cpal_usercallback.c
  5. Где нужно использовать библиотеку, подключить файл cpal_i2c.h:
    #include "cpal_i2c.h"

Продолжение следует…