Как создать прототип BLE-приложения на STM32WB с помощью STM32CubeWB
18 февраля 2020
Александр Шауэрман (Алтайский край)
Выпустив на рынок двухъядерную беспроводную систему на кристалле STM32WB, компания STMicroelectronics интегрировала его в хорошо известную экосистему STM32Cube, что позволило применять при разработке привычный по STM32 инструментарий.
В современной электронике практически для каждой технической идеи находится готовое решение. Чем универсальнее это решение, тем больше шансов, что идея дойдет до потребителя в виде серийного продукта. Компания STMicroelectronics хорошо чувствует эту тенденцию и предлагает чрезвычайно гибкие инструменты, воплощающие эти идеи. Вот одно из решений ST для беспроводных персональных сетей.
STM32WB – это система-на-кристалле: два микропроцессорных ядра (ARM Cortex-M4F и ARM Cortex-M0+) и приемопередатчик радиочастоты (RF) объединены в одном корпусе. Первое ядро (CPU1) реализует приложение пользователя, второе ядро (CPU2) управляет приемопередатчиком и выполняет программный стек протокола связи.
Благодаря тому что большая часть стека реализуется программно на сопроцессоре CPU2, STM32WB поддерживает целый спектр различныx технологии беспроводной связи: Bluetooth® Low Energy 5.0 (BLE), OpenThread, 802.15.4 MAC, ZigBee 3.0 или любой другой проприетарный протокол на базе стандарта IEEE 802.15.4. При этом со стороны основного процессора и приложения пользователя весь стек выполняется независимо, как если бы это было реализовано полностью аппаратно.
В данной статье мы акцентируем свое внимание на технологии BLE. В отличии от классического Bluetooth, BLE в первую очередь ориентирован на устройства с автономным питанием. Передача данных ведется короткими пакетами по необходимости. В задачах, где нет необходимости в работе с потоковыми данными, устройство BLE большую часть времени может находиться в спящем режиме. Кроме того, в двухъядерной архитектуре сетевой сопроцессор может работать автономно и не требует участия основного приложения на CPU1 для поддержания низкопотребляющего соединения, что совместно с технологией производства процессоров STMicroelectronics позволяет реализовать сверхмалое потребление.
Для понимания технологии BLE нужно уметь оперировать множеством терминов и аббревиатур. Большинство из них имеет иностранное (английское) происхождение. Для терминов, имеющих устоявшийся русский перевод, будем использовать русскоязычный эквивалент. Чтобы помочь читателю разобраться в теме, в этой статье для аббревиатур будем приводить англоязычную расшифровку и дословный перевод. Для тех, кто уже знаком с технологией BLE, такой подход предупредит проблему двойного толкования, а для тех, кто только знакомится, послужит основой и направлением для самостоятельного поиска информации.
Для того чтобы непростая технология Bluetooth® Low Energy стала доступна широкому кругу разработчиков, STMicroelectronics интегрировал семейство STM32WB в хорошо известную экосистему STM32Cube. Таким образом весь привычный инструментарий STM32, начиная с программатора и заканчивая интегрированной средой разработки, доступен для работы с новым семейством и позволяет в короткие сроки создать прототип и выпустить серийное устройство на рынок.
В статье мы познакомимся с принципами создания встраиваемого программного обеспечения для STM32WB и с помощью инструментария STM32Cube создадим демонстрационный проект BLE с нуля.
Библиотека STM32 CubeWB
STM32Cube
STM32Cube™ – это набор инструментов, призванных облегчить жизнь разработчикам. STM32Cube™ поддерживает все семейство микроконтроллеров STM32, и, конечно же, STM32WB.
STM32Cube™ состоит из двух основных компонентов:
- STM32CubeMX – это лицо проекта, графический инструмент начальной конфигурации. Он генерирует С-код инициализации периферии процессора и базовые настройки проектов для популярных IDE, уменьшает порог входа в экосистему ST, делает семейство STM32 дружелюбным для новичков.
- Комплексная платформа программного обеспечения для каждой серии STM32. Например, для микроконтроллеров STM32WB предназначен пакет STM32CubeWB [1], который включает в себя:
- STM32Cube™ HAL, HAL (Hardware Abstraction Layer) – слой абстракции встраиваемого программного обеспечения. Слой позволяет легко переносить код как между микроконтроллерами одного семейства, так и между семействами. HAL доступен для всей встроенной периферии;
- LL (Low-layer APIs), также как и HAL – слой аппаратной абстракции, но более легковесный. Обеспечивает более высокую скорость работы с аппаратной периферией и меньший объем исполняемого кода, чем HAL, но требует от программиста высокой квалификации и глубокого знания аппаратной платформы. К тому же, LL доступен не для всей периферии;
- набор программного обеспечения промежуточного уровня (Middle ware), такого как USB Device, STMTouch (STM32 touch sensing library), FatFS and FreeRTOS, STM32_WPAN (Bluetooth® Low Energy 5.0, Open Thread, 802-15-4 MAC, ZigBee 3.0);
- обширный набор примеров для всех вышеуказанных компонентов.
Итак, STM32CubeWB – это пакет программного обеспечения, предназначенный для работы на микроконтроллерах семейства STM32WB. Пакет включает в себя все необходимые для разработки приложений на этом семействе программные компоненты. STM32CubeWB совместим с кодогенератором STM32CubeMX. Пакет включает в себя API (Application Programming Interface – интерфейс программирования) для работы с аппаратной периферией (LL и HAL).
Библиотеки HAL и LL опубликованы под свободной open-source-лицензией BSD. Лицензирование каждого компонента Middle ware индивидуально и определяется непосредственно автором или правообладателем, однако все ПО из набора Middle ware поставляются на бесплатных, удобных для пользователя, условиях лицензии:
- CMSIS-RTOS – адаптация FreeRTOS™ с открытым исходным кодом;
- полный стек USB-устройств, поддерживающий множество классов – Audio, HID, MSC, CDC и DFU;
- STMTouch – библиотека для работы с сенсорными панелями;
- STM32_WPAN – фреймворк для разработки беспроводных персональных сетей по технологии Bluetooth® Low Energy (BLE) 5.0, OpenThread.;
- файловая система FAT, базирующаяся на open source-решении FatFS.

Рис. 1. Архитектура STM32CubeWB
Встраиваемое программное решение STM32CubeWB построено вокруг трех независимых взаимодействующих уровней, показанных на рисунке 1 [1].
Уровень 0
Этот уровень подразделяется на три компонента:
- BSP (Board Support Package) – набор драйверов, ориентированных на определенную отладочную плату;
- слой аппаратных абстракций (HAL):
- драйверы периферии HAL;
- драйверы периферии LL.
- примеры использования периферийных устройств.
BSP предлагает набор API для работы с внешними по отношению к микроконтроллеру периферийными компонентами, расположенными на плате, такими как жидкокристаллический индикатор (ЖКИ), кнопки, светодиоды, аудиокодек или карта памяти microSD™. BSP включает в себя две части:
- Компонент. Это драйвер внешнего (по отношению к STM32) устройства на плате. Драйвер компонента предоставляет определенный API для драйвера BSP и может быть перенесен на другую плату.
- BSP-драйвер. Это и есть непосредственно драйвер устройства. Он связывает компонент с определенной платой, реализует процедуры взаимодействия API с аппаратным обеспечением.
Модульная архитектура BSP позволяет с легкостью перенести компоненты на любуюдругуюплату, просто реализовав низкоуровневые функции.
Библиотека HAL представляет собой высокоуровневый функционально ориентированный API. Вся непростая работа с MCU и периферией скрыта от пользователя. HAL упрощает создание приложений пользователя, давая готовые к применению процессы. К примеру, для интерфейсов коммуникации (I2C, UART, SPI и других) этот API реализует функции инициализации, функции управления передачей данных в синхронном режиме через опрос флагов либо в асинхронном режиме через прерывание или DMA. При этом функции обрабатывают ошибки, возникающие в процессе коммуникации.
Драйверы HAL можно разделить на две категории:
- Общий API. Драйверы этой категории обеспечивают общие для всего семейства STM32 функции.
- Расширенный API. Функции периферии, специфичной для конкретного семейства STM32 или даже конкретного микроконтроллера с уникальной специализированной периферией.
Библиотека LL – это низкоуровневая альтернатива HAL на уровне регистров. LL очень хорошо оптимизирована, но ее портируемость хуже чему HAL. Применение этого API требует от программиста глубоких знаний архитектуры MCU и спецификации периферии. Драйверы LL обеспечивают быстрый и легкий слой, который ближе к аппаратному обеспечению чем HAL. В отличие от HAL, API LL не предоставляются для периферийных устройств, где оптимизированный доступ не является основной функцией, или для тех, которые нуждаются в тяжелой конфигурации ПО и/или сложного стека верхнего уровня.
Перечислим особенности драйверов LL:
- набор функций для инициализации периферийных устройств. Параметры инициализации, как правило, передаются в функцию через структуру данных;
- набор функций, используемых для заполнения структур данных инициализации значениями по умолчанию;
- набор функций для деинициализации периферийных устройств. Регистры записываются исходными значениями по умолчанию;
- набор встраиваемых (inline) функций для прямого и атомарного доступа к регистрам;
- полная независимость от HAL и возможность использования LL как совместно с HAL, так и в автономном режиме (без HAL);
- полный охват поддерживаемых периферией функций. Это означает, что если какой-либо аппаратный блок, поддерживаемый LL, что-либо умеет, то это «что-то» однозначно можно сделать с помощью LL.
В данном уровне ПО содержатся примеры небольших проектов, построенных на периферии STM32 с использованием только ресурсов HAL, LL и BSP.
Уровень 1
Промежуточное ПО (Middle ware) – это набор библиотек, охватывающий FatFS, FreeRTOS™, USB Device, STMTouch (STM32-библиотека сенсорного ввода), и ПО STM32_WPAN. Горизонтальное взаимодействие компонентов данного слоя осуществляется посредством вызова API-интерфейсов объектов, в то время как вертикальное взаимодействие с низкоуровневыми драйверами осуществляется через специальные обратные вызовы и статические макросы, реализованные в интерфейсе вызова библиотечной системы. К примеру, FatFS реализует драйвер дискового ввода-вывода для доступа к Flash-памяти microSD™.
Большинство компонентов Middle ware (FatFS, FreeRTOS™, USB Device, STMTouch) характерно для всего семейства STM32 и их рассмотрение выходит за рамки этой статьи. Но обратим внимание на уникальный для STM32WB компонент, отвечающий за организацию беспроводной связи – STM32_WPAN.
Итак, STM32_WPAN включает в себя следующие компоненты:
- Сертифицированный стек BLE 5.0:
- канальный уровень;
- интерфейс-хост-контроллер HCI (Host Controller Interface);
- L2CAP (Logical Link Control and Adaptation Protocol – протокол управления логическим подключением и адаптацией);
- ATT (Attribute Protocol) – протокол атрибутов;
- SM (Security Manager) – менеджер безопасности;
- GAP (Generic Access Profile) – профиль общего доступа;
- GATT (Generic Attribute Profile) – профиль общих атрибутов.
- Сертифицированный стек 802.15.4Thread® v1. Стек представлен в виде проекта с открытым исходным кодом OpenThread. Стек поддерживает две конфигурации:
- FTD (FullThreadDevice). Применяется для ThreadLeader (маршрутизатор, управляющий другими маршрутизаторами), маршрутизатора и конечного устройства (исключая граничный маршрутизатор);
- MTD (Minimal Thread Device). Используется для конечного устройства MED (Minimal End Device) и спящего конечного устройства SED (Sleepy End Device).
- 15.4 MAC – уровень управления доступом к среде. Этот API базируется на официальном стандарте IEEE802.15.4-2011. API поддерживает работу узла в качестве полнофункционального устройства (Full Function Devices, FFD), в роли координатора сети или узла с ограниченным функционалом (Reduced Function Device, RFD).
Каждый компонент промежуточного программного обеспечения поставляется с одним или несколькими примерами (также называемыми «приложениями»), демонстрирующими, как с ним работать. Кроме того, показаны примеры интеграции, использующие несколько компонентов промежуточного программного обеспечения в одном проекте.
Уровень 2
Этот уровень состоит из одного слоя: набора демонстрационных проектов, базирующихся на компонентах промежуточного ПО и компонентах нижнего уровня HAL или BSP. Эти проекты реализуют комплексный функционал какой-либо демонстрационной или отладочной платы. Например, демонстрационный проект «Adafruit_LCD_1_8_SD_Joystick» запускается на плате Nucleo-68 из набора P-NUCLEO-WB55 с подключенной платой расширения, на которой установлен TFT-дисплей, джойстик и SD-карта.
Пакет STM32CubeWB
Пакет STM32CubeWB можно скачать с сайта STMicroelectronics в виде zip-архива либо установить через менеджер встроенного программного обеспечения STM32CubeMX. На мой взгляд, второй вариант более предпочтителен, так как пакет загрузится в автоматическом режиме и установится в специально выделенную для этого директорию в рабочем каталоге STM32CubeMX. Для этого в меню «Help» находим «Embedded Software Packet Manager», в окне менеджера выбираем семейство микроконтроллеров, в нашем случае это STM32WB, активируем нужную версию пакета, нажимаем кнопку «Install Now» и ждем, пока программа все сделает за нас. После завершения установки пакет в распакованном виде находится в директории c:\Users\username\STM32Cube\Repository\. Кстати, для удаления пакета или его устаревшей версии достаточно снять соответствующую галочку в списке.
Структура пакета STM32CubeWB в распакованном виде показана на рисунке 2.

Рис. 2. STM32CubeWB. Дерево каталогов
Для каждой платы из набора P-NUCLEO-WB55 в пакет включена директория Projects, содержащая коллекцию приложений, примеров и демонстраций [2]. Здесь следует разобраться в терминологии. В документации STMicroelectronics примеры классифицируются в зависимости от уровня, к которому они относятся (рисунок 1). Примеры на нулевом уровне, основанные на HAL, называются просто «примеры» («Examples»). Если примеры основаны на слое LL, то называются «Examples_LL», если в проекте используется LL совместно с HAL, то – «Examples_MIX» (рисунок 3).

Рис. 3. STM32CubeWB. Примеры
На уровне 1 примеры называются «приложения», или, по-английски, «Applications». Такие приложения представлены для всех компонентов промежуточного ПО, скажем, директории Thread и Zigbee содержат примеры применения соответствующих технологий, а папка BLE содержит типовые случаи использования стека BLE 5.0. Например, проекты BLE_p2pClient и BLE_p2pServer демонстрируют возможности компонента BLE для связи в режиме «точка-точка», проекты BLE_HealthThermometer, BLE_BloodPressure, BLE_HeartRate, BLE_Hid показывают, как можно использовать стандартные профили SIG [3], проекты с постфиксом «_ota» позволяют обновлять прошивку удаленно по радио (ОТА – Over-the-air, подробнее в AN5247 [4]). Примечателен проект BLE_Thread, где реализована одновременная работа Thread и BLE.
Примеры уровня 2 называются «демонстрациями», или «Demonstrations».
Назначение любого примера можно узнать из файла readme.txt, расположенного в корневой директории проекта.
Встраиваемое программное обеспечение микроконтроллеров STM32WB55
Основные принципы
Итак, микроконтроллер STM32WB55 состоит из двух процессорных ядер. Первое ядро CPU1 – это ядро общего назначения Cortex®-M4, которое отвечает за профили Bluetooth и выполняет приложение пользователя, то есть реализует основной алгоритм работы устройства. Программа на CPU2 (Cortex®-M0+) в реальном времени выполняет все операции смарт-системы Bluetooth, контролирует физический уровень BLE (PHY), управляет протоколом безопасности, выполняет шифрование и дешифрование пакетов данных. Взаимодействие между сопроцессорами осуществляется через контроллер межпроцессорной связи (IPCC). Контроллер, используя разделяемую физическую память SRAM2, реализует интерфейс связи по типу «mailbox».
На рисунке 4 показана карта памяти программ микроконтроллера.

Рис. 4. Распределение Flash-памяти STM32WB55
Стек на CPU2 работает автономно и не требует участия приложения CPU1 для поддержания соединения, выполнения сканирования или рассылки объявлений. При такой архитектуре большую часть рабочего времени CPU1 может находиться в спящем режиме, просыпаясь лишь на запрос от внешнего устройства, таймера или CPU2, и тем самым обеспечивая системе сверхмалое потребление.
Безопасный доступ к общим ресурсам гарантируется с помощью аппаратных семафоров (HSEM).
Для последующего практического освоения STM32WB55 сформулируем следующие принципы:
- весь программный код, работающий на CPU2, поставляется STMicroelectronics в виде зашифрованного двоичного файла;
- с точки зрения разработчика CPU2 и все, что на нем выполняется, представляет собой «черный ящик» с определенным протоколом взаимодействия;
- весь код, работающий на CPU1, поставляется в виде исходного кода. Программист может как использовать готовые компоненты промежуточного уровня STM32CubeWB в своем проекте, так и создать эти компоненты сам;
- исполняемый код для CPU1 и CPU2 располагается в различных областях единой памяти. Доступ CPU1 к области памяти CPU2 ограничен;
- связь между процессорами осуществляется специальным контроллером IPCC через общую оперативную память.
Архитектура стека BLE
Перед тем как мы самостоятельно создадим рабочий проект BLE, рассмотрим структуру программного стека:
- ACI (Application Command Interface) – интерфейс, с помощью которого приложение, запущенное на CPU1, управляет работой стека, исполняемого на CPU2, и взаимодействует с ним;
- IPCC (Inter Processor Communication Controller) – специальный периферийный модуль микроконтроллера, который обеспечивает обмен данными между двумя сопроцессорами;
- SMP (Security Manager Protocol) используется для сопряжения и распределения ключей шифрования (AES128). Все команды SMP передает по специальному выделенному каналу L2CAP;
- L2CAP (Logical Link Control and Adaptation Protocol) – уровень, который отвечает за инкапсулирование протоколов верхнего уровня в пакеты BLE. На этом уровне выполняется сегментация пакетов: пакеты с верхнего уровня разбиваются на более мелкие, адаптированные к среде передачи данных, а пакеты с нижнего уровня собираются в большие пакеты для верхнего. Благодаря L2CAP верхние уровни стека могут оперировать пакетами данных длинною до 64 кбайт;
- HCI (Host Controller Interface) – интерфейс между Controller (Link Layer и PHY) и Host (верхние уровни стека);
- DTM (Direct Test Mode) – специальный интерфейс для управления тестированием на нижнем уровне. DTM обеспечивает валидацию физического уровня передачи и, в конечном счете, гарантирует качество соединения.
- GAP (Generic Access Profile) – уровень, контролирующий процессы рассылки объявлений (advertising) и сопряжения (connections). Определяет взаимодействие двух устройств;
- GATT (Generic Attribute Profile) – профиль общих атрибутов. Это обязательный профиль с общими спецификациями отправки и приема данных;
- ATT (Attribute Protocol) – протокол атрибутов.
На рисунке 5 показана архитектура стека BLE применительно к микроконтроллеру STM32WB55.

Рис. 5. Архитектура ПО BLE
Последние три слоя стоит рассмотреть подробнее. Совместимость между различными устройствами Bluetooth достигается за счет профиля Bluetooth. Профиль – это совокупность функций, определяющих назначение устройства. Профили определяют функции каждого уровня в системе Bluetooth, начиная от физического; профили определяют вертикальное взаимодействие между слоями, а также одноранговые взаимодействия определенных слоев между устройствами. Кроме того, поведение приложения и форматы данных также определяются профилем. Когда два устройства соответствуют всем требованиям профиля, можно говорить, что устройства совместимы.
В стеке Bluetooth определен базовый профиль, который должны реализовывать все устройства Bluetooth – это GAP. Этот профиль устанавливает основные требования к устройству. Например, для классического Bluetooth (BR/EDR) он задает параметры радиотракта, диспетчера соединений, L2CAP и функциональность протокола обнаружения служб; для BLE он определяет физический уровень, уровень соединения (Link layer), L2CAP, SMP, протокол атрибутов (ATT) и профиль общих атрибутов (GATT). В GAP описывается поведение и методы обнаружения устройств, способы установления соединений, протоколы безопасности и аутентификации.
Профиль GATT определяет структуру, в которой содержатся такие элементы как сервисы и характеристики. Профиль состоит из одной или нескольких служб (сервисов), необходимых для выполнения сценария использования устройства. Сервис состоит из характеристик, или ссылок на другие сервисы. Сервис – это контейнер для логически связанных элементов данных. Например, сервис Device Information Service предоставляет различную информацию об устройстве. Характеристики – это логически связанные элементы данных в рамках одного сервиса. Например, строка серийного номера или информации о производителе (из Device Information Service). Характеристика состоит из типа, значения, некоторых свойств, разрешений и дескрипторов (опционально). Дескрипторы либо предоставляют дополнительные сведения, либо позволяют настроить поведение, связанное с характеристикой, например, включить уведомления.
GATT определяет, как два BLE-устройства обмениваются данными между собой.
Настройка своего BLE-приложения в STM32CubeMX
Для практического исследования возможностей экосистемы STM32Cube при создании приложений на STM32WB организуем обмен данными между двумя устройствами по BLE в режиме «точка-точка».
В качестве сервера данных в нашем эксперименте будем использовать Nucleo-68 из набора P-NUCLEO-WB55 [5]. Плата обладает встроенным программатором-отладчиком ST-LINK, это позволит нам загружать исполняемый код непосредственно из IDE и, при необходимости, выполнять пошаговую отладку. На плате имеются три светодиода разных цветов, их удобно использовать для индикации текущего состояния устройства и визуализировать команду с клиента. Еще одним достоинством платы Nucleo-68 можно считать то, что последовательный порт микроконтроллера USART1 может быть подключен к преобразователю USB-UART отладчика ST-LINK и в операционной системе отображается как виртуальный COM-порт. Это позволит без каких-либо дополнительных аппаратных затрат выводить в терминал диагностические сообщения. Данные для клиента будем генерировать с помощью тактовой кнопки.
В качестве клиента для подключения к плате по Bluetooth нам понадобится смартфон или планшетный компьютер с утилитой ST BLE Sensor, ее можно установить из стандартного источника приложений Google Play или App Store.
Я привык работать в IDE, основанных на Eclipse, поэтому для своих проектов я выбираю Atollic TrueSTUDIO for STM32. Безусловно, подойдет любая другая среда, например IAR™ или Keil®.
Хорошее правило: перед началом работы с новым семейством микроконтроллеров нужно обновить до актуального состояния все связанные программные приложения. В настоящий момент у меня на компьютере установлена STM32CubeMX версии 5.4.0, пакет STM32CubeWB версии 1.3.0 и Atollic TrueSTUDIO версии 9.3.0.
Шаг первый. Здравствуй, мир!
Запускаем STM32CubeMX и создаем новый проект. Указываем целевой микроконтроллер семейства STM32WB. Если для экспериментов выбрана отладочная плата, то целесообразно создать проект для существующей платформы (вкладка «Board Selector», рисунок 6), это позволит автоматически инициализировать периферию платы (на вопрос «Initialize all peripherals with their default Mode?» отвечаем утвердительно).

Рис. 6. Создание нового проекта. STM32CubeMX
В результате все светодиоды и кнопки подключены, генераторы HSE и LSE настроены, USART1 и USB активны (рисунок 7). В ближайших экспериментах USB нам не понадобится, его можно отключить.
Я рекомендую в имена линий GPIO, отвечающих за светодиоды, добавить информацию о цвете. Переименуем светодиоды: LD1 в LED_B, LD2 в LED_G, LD3 в LED_R.
В дальнейшем нам понадобится USART1 в режиме DMA. В разделе «Connectivity» выбираем пункт «USART1». Если проект был создан для отладочной платы Nucleo-68 и принята конфигурация по умолчанию, то для последовательного порта выбраны выводы, подключенные к виртуальному COM-порту отладчика, если вы самостоятельно выполняете начальную конфигурацию, то нужно выбрать для TX и RX выводы PB7 и PB6 соответственно. На вкладке «Parameter Settings» можно произвести типовые настройки USART, значения по умолчанию (115200 Бод, 8 бит данных, 1 стоп-бит) нас устраивают. На вкладке «DMA Settings» активируем DMA2-контроллер, добавляем для запроса USART_TX канал Channel 4, направление «Memory To Peripheral» (рисунок 8).
На вкладке «Project Manager» (рисунок 9) вводим название проекта. Выбираем тип IDE. Проверяем, что путь к пакету STM32CubeWB указан корректно, и запускаем генерацию кода («Generate Code»). Теперь можно импортировать проект в TrueSTUDIO.
Чтобы программа что-то делала в цикле while(1) функции main() (файл main.c), добавляем задержку и переключаем светодиод:
HAL_GPIO_TogglePin(LD1_GPIO_Port, LD1_Pin);
HAL_Delay(100);
Здесь и далее для инициализации и управления периферией микроконтроллера мы будем использовать библиотеку HAL.
Позволю себе рекомендацию: в редакторе кода вашей IDE установите опцию «заменить табуляцию пробелами», а размер табуляции сделайте равным двум (как правило, по умолчанию стоит 4). Это сделает стиль вашего кода соответствующим стилю кода STM32Cube.
Перед основным циклом выполним однократный вывод в консоль в режиме DMA:
uint8_t str[] = "Hello World!\r\n";
HAL_UART_Transmit_DMA(&huart1, str, sizeof(str));
Важно отметить что для того чтобы «ужиться» с автоматическим генератором кода в рамках одного файла исходника, весь пользовательский код нужно вставлять между фразами, которые начинаются с /* USERCODEBEGIN… и заканчиваются на /* USERCODEEND…, в противном случае при следующей регенерации проекта все ваши наработки будут потеряны.
Перед запуском проекта на отладочной плате убедимся, что плата к этому готова.Установим перемычку на JP1 в положение 7-8, это подаст питание на микроконтроллер с ST-LINK; на JP5 все перемычки должны быть установлены, это позволит ST-LINK подключиться к микроконтроллеру. Особое внимание обратим на перемычки RX-TX этого разъема. Эти джамперы соединят порт USART1 с виртуальным COM-портом отладчика. Стоит отметить, что все эти перемычки установлены на плате по умолчанию. На плате Nucleo-68 два разъема USB, один соединен непосредственно с STM32WB55, второй – с ST-LINK. В нашей работе плату нужно подключать через разъем для ST-LINK (рисунок 10). Подробно об отладочном наборе можно почитать в статье, посвященной отладочному набору P-NUCLEO-WB55 [5].

Рис. 10. Nucleo-68, подготовка к работе
Компилируем и запускаем проект. Подключаемся программой эмулятора терминала к последовательному порту STLink Virtual COM Port. Убеждаемся, что в терминал выводится фраза приветствия, а синий светодиод мигает на плате. Если приветствия нет, то рекомендую в настойках проекта STM32CubeMX на вкладке «Advanced Settings» проверить порядок вызова функций инициализации: инициализация DMA должна выполняться раньше инициализации UART.
Шаг второй. Добавляем стек BLE
Для того чтобы добавить Middle ware STM32_WPAN, должен быть активирован интерфейс приемопередатчика RF (раздел «Connectivity»), разрешена работа часов реального времени RTC (раздел «Timers»), разрешены аппаратные семафоры HSEM (раздел «System Core»). В соответствующих пунктах «Pinout&Configuration» проставляем галочки «Activated».
Аппаратный семафор нужен для защищенного доступа к общей для двух процессоров периферии. Никакая конфигурация для HSEM на настоящий момент пользователем не предусмотрена, она полностью управляется промежуточным программным обеспечением. Просто будем иметь в виду, что они существуют и используются для управления доступом к ресурсам, разделяемыми сопроцессорами CM0+ и CM4F. Блок RTC нужен промежуточному ПО для программных таймеров и режимов малого энергопотребления (Low Power Modes, LPM). В этом пункте настройки со стороны пользователя не требуются.
После активации требуемых аппаратных блоков в разделе Middle ware становится доступным пункт STM32_WPAN, и можно активировать BLE. В этом разделе выбираем тип приложения (BLE Application Type): «Server Profile», разрешаем «Custom P2P Server», как это показано на рисунке 11.

Рис. 11. Настройка STM32_WPAN. STM32CubeMX
При желании можно изменить локальное имя устройства (максимум 7 символов). Других настроек в этом разделе пока не требуется.
Отметим, что автоматически в разделе «Utilities» активируются SEQUENCER и TINY_LPM – это утилиты для управления приложением, о них мы поговорим позже.
Настало время настроить тактирование. Переходим на вкладку «Clock Configuration». Наша цель – для критически важных узлов задать опорный тактовый сигнал от высокоточных внешних резонаторов:
- для внешнего резонатора HSE устанавливаем значение 32 МГц (если это не выполнено до сих пор);
- для линии SYSCLK в качестве источника сигнала устанавливаем HSE_SYS;
- на линии HCLK1 и HCLK2 устанавливаем частоту 32 МГц;
- для RTC выбираем источник тактирования LSE;
- для RFWKP выбираем LSE.
Генерируем проект. На рисунке 12 показано дерево файлов проекта. Цветом выделены файлы, пришедшие в проект с STM32_WPAN.
Обратим внимание на три файла, с ними в дальнейшем нам придется плотно работать:
- app_entry.c – инициализация транспортного уровня BLE, конфигурация STM32_WPAN Middle ware, дополнительные аппаратные функции;
- app_ble.c – инициализация стека BLE и управление соединением (рассылка объявлений, режим сканирования и так далее);
- p2p_server_app.c – управление приложением P2P-сервера.
Для того чтобы запустить стек BLE, достаточно модифицировать два файла: src/main.c и src/stm32wbxx_it.c. Для этого в файле main.c добавляем планировщик заданий – секвенсор (Sequencer). В секции «Includes» добавляем ссылку на заголовочный файл секвенсора:
/* Private includes ––// USER CODE BEGIN Includes / #include "stm32_seq.h" /
USER CODE END Includes */
В бесконечный цикл функции main() добавляем:
UTIL_SEQ_Run( UTIL_SEQ_DEFAULT );
Файл stm32wbxx_it.c содержит обработчики прерываний. В этом файле нужно добавить функции обработчиков прерывания для контроллера межпроцессорного взаимодействия (IPCC). В секцию «USER CODE BEGIN Includes» добавляем заголовочный файл:
/* USER CODE BEGIN Includes */
#include "app_common.h"
/* USER CODE END Includes */
В секцию «USERCODE BEGIN 1» добавляем обработчики:
/**
* @brief This function handles RTC wake-up interrupt through EXTI line 19.
*/
void RTC_WKUP_IRQHandler(void)
{
HW_TS_RTC_Wakeup_Handler();
}
/**
* @brief This function handles IPCC RX occupied interrupt.
*/
void IPCC_C1_RX_IRQHandler(void)
{
HW_IPCC_Rx_Handler();
}
/**
* @brief This function handles IPCC TX free interrupt.
*/
void IPCC_C1_TX_IRQHandler(void)
{
HW_IPCC_Tx_Handler();
}
При старте устройства сначала запускается процессорное ядро CPU1, инициализируется периферия и HAL-окружение, затем настраивается системный канал связи (Transport Layer, TL) с CPU2 (рисунок 13). Транспортный уровень состоит из двух компонентов: это разделяемая память SDRAM2 и межпроцессорный контроллер связи IPCC.

Рис. 13. Транспортный уровень
После настройки TL CPU1 возвращается в фоновый цикл «while» и ожидает уведомления от CPU2 о том, что он готов принять системную команду и продолжить инициализацию стека. Пока уведомления нет, CPU1 может выполнять любые другие действия, не связанные с работой радиотракта (CPU2). Этот алгоритм запуска не зависит от того, с каким стеком работает CPU2 (BLE, OpenThread или ZigBee) и является универсальным для приложений на STM32WB.
Итак, когда CPU2 готов принимать системные команды, он отправляет уведомление для CPU1. Пользовательское приложение получает это уведомление с помощью функции «APPE_SysUserEvtRx()» (файл app_entry.c) и запускает процедуру инициализации стека «APP_BLE_Init()» (файл app_ble.c). В этой функции инициализируется транспортный уровень BLE, интерфейс HCI, настраивается BLE-стек на CPU2 (начиная от выходной мощности передатчика радиочастоты и заканчивая параметрами GAP и GATT) и, в завершение, запускается режим посылки объявлений – advertising. Длительность advertising задается константой «INITIAL_ADV_TIMEOUT» и по умолчанию составляет 60 секунд, после чего отправка объявлений прекращается. Для управления advertising используются функции:
- Adv_Request() – инициализация, настройка и запуск рассылки;
- Adv_Cancel() – остановка рассылки.
Компилируем проект. Загружаем бинарный код в память микроконтроллера. На этом этапе наше устройство должно быть видно в утилите ST BLE Sensor при поиске (рисунок 14). К нему можно подключиться, но никакие действия выполняться не будут даже при подключении – плата не проявляет никакой активности.

Рис. 14. Поиск устройств. ST BLE Sensor
Шаг третий. Подключаем трассировку
Минимальную информацию о состоянии устройства можно получить с помощью светодиодов (у нас на плате их целых три!). Чтобы мы могли видеть, что нечто происходит, будем мигать зеленым светодиодом каждый раз, когда у нас случается радиособытие. В файле app_ble.c находим функцию «SVCCTL_App_Notification()» – это обработчик событий, поступающих от радиостека CPU2. В функции находим обработку события «RADIO_ACTIVITY_EVENT» и добавляем код:
/* USER CODE BEGIN RADIO_ACTIVITY_EVENT*/
HAL_GPIO_WritePin(LED_G_GPIO_Port, LED_G_Pin, GPIO_PIN_SET);
HAL_Delay(5);
HAL_GPIO_WritePin(LED_G_GPIO_Port, LED_G_Pin, GPIO_PIN_RESET);
/* USER CODE END RADIO_ACTIVITY_EVENT*/
С помощью красного светодиода покажем статус подключения. В функции «SVCCTL_App_Notification()» находим обработку события «EVT_LE_CONN_COMPLETE» (соединение установлено) и добавляем включение светодиода:
/* USER CODE BEGIN HCI_EVT_LE_CONN_COMPLETE */
HAL_GPIO_WritePin(LED_R_GPIO_Port, LED_R_Pin, GPIO_PIN_SET);
/* USER CODE END HCI_EVT_LE_CONN_COMPLETE */
В обработчике события «EVT_DISCONN_COMPLETE» (соединение разорвано) погасим светодиод:
/* USER CODE BEGIN EVT_DISCONN_COMPLETE */
HAL_GPIO_WritePin(LED_R_GPIO_Port, LED_R_Pin, GPIO_PIN_RESET);
/* USER CODE END EVT_DISCONN_COMPLETE */
Загружаем бинарный код проекта в Nucleo-68. При старте программы зеленый светодиод начнет быстро мигать. При подключении к устройству с помощью утилиты STM BLE Sensor включится красный светодиод, при отключении он погаснет. Если у устройства нет активного соединения, то через некоторое время (60 секунд) рассылка оповещений будет прекращена, а зеленый светодиод прекратит мигать.
Редактируя файл app_ble.c, вы должны были заметить, как много в программном коде встречается человеко-ориентированных текстовых сообщений с описанием текущего статуса или событий, например:
APP_DBG_MSG("\r\n\r** DISCONNECTION EVENT WITH CLIENT \n");
или
APP_DBG_MSG(" \r\n\r");APP_DBG_MSG("** STOP ADVERTISING ** \r\n\r");
Макрос «APP_DBG_MSG()» может выводить сообщение в терминал. По умолчанию этот функционал отключен, однако этот механизм может быть очень полезен для понимания внутренней архитектуры стека BLE и программы в целом. Давайте с помощью STM32CubeMX включим и настроим трассировку нашего приложения.
На первом шаге мы уже настроили USART1 для работы в режиме DMA и убедились в его работоспособности. Теперь научим STM32_WPAN работать с ним. Для этого на вкладке «Configuration» разрешаем работу USART1 в качестве интерфейса отладки. Параметры CFG_HW_USART1_ENABLED и CFG_HW_USART1_DMA_TX_SUPPORTED устанавливаем в «Enabled», а для параметра CFG_DEBUG_TRACE_UART выбираем «hw_uart1». Теперь управлением USART1 будет заниматься стек BLE, инициализацию USART1 в функции «main()» нужно деактивировать, для этого в STM32CubeMX на вкладке «Project Manager» в панели «Advanced Settings» запрещаем генерацию вызова «MX_USART1_UART_Init» (галочка установлена) и саму функцию делаем глобальной (галочка снята), как показано на рисунке 15. Не забываем из функции «main()» удалить наше приветствие миру.
После того как USART1 выбран в качестве интерфейса отладки, нужно задать параметры трассировки. Включаем трассировку в службах BLE (параметр CFG_DEBUG_BLE_TRACE) и в приложении (параметр CFG_DEBUG_APP_TRACE).
В проект будут добавлены файл hw_uart.c с функциями передачи данных по последовательному порту, а в файл hw_conf.h будут прописаны константы с параметрами USART1.
Для того чтобы трассировка заработала, в функцию «APPE_Init()» (файл app_entry.c) нужно добавить функцию инициализации отладчика:
/* USER CODE BEGIN APPE_Init_1 */
Init_Debug();
/* USER CODE END APPE_Init_1 */
Теперь все сообщения макроса «APP_DBG_MSG()» будут выводиться в терминал рисунок 16.

Рис. 16. Трассировка приложения. Вывод терминала
Если параметр CFG_DEBUG_TRACE_FULL установить в Enabled, то к выводу добавится информация о местоположении сообщения в исходном файле: имя файла, имя функции, номер строки (рисунок 17).

Рис. 17. Трассировка приложения. Подробный вывод
В большинстве случаев такой подробный вывод избыточен и значительно замедляет восприятие событий.
Шаг четвертый. Управляем светодиодом
Приложение для телефона STM BLE Sensor ищет определенные функции, описанные в Device ID пакетов advertising, чтобы включить различные экраны. Если мы сейчас подключимся к нашему BLE-устройству, то запустится режим «Led Control» (рисунок 18). На экране мы видим колокольчик «No alarm received» для функции сигнализации и большую кнопку со светодиодом для управления светодиодом на плате. На данный момент все эти функции не поддерживаются нашим сервером.

Рис. 18. Сервер приборов. ST BLE Sensor
Когда мы создали проект, мы включили не стандартный профиль, определенный Bluetooth SIG, а проприетарный, разработанный STMicroelectronics, в нем определены свои сервисы и характеристики с 128-битными уникальными идентификаторами (Universally Unique Identifier, UUID). В данном профиле определен компонент P2P-сервер [6]. Приложение P2P-сервера удобно использовать для демонстрации связи BLE в режиме «точка-точка». Сервер P2P действует как периферийное устройство и предоставляет сервисы и характеристики, показанные в таблицах 1 и 2.
Таблица 1. Сервисы и характеристики P2P-сервера
Группы | Сервисы | Характеристики | Размер | Режим | UIID |
---|---|---|---|---|---|
LED button control | P2P service | – | – | – | 0000FE40-cc7a-482a-984a-7fed5b3e58f |
– | WRITE | 2 | Чтение/запись | 0000FE41-8e22-4541-9d4c-21edae82ed19 | |
– | NOTIFY | 2 | Уведомление | 0000FE42-8e22-4541-94dc-21edae82ed19 |
Таблица 2. Спецификация характеристик P2P-сервера
WRITE | NOTIFY | ||||
---|---|---|---|---|---|
Октет (байт), LSB | Имя | Значение | Октет (байт), LSB | Имя | Значение |
0 | Выбор устройства |
0x01: P2P server 1, 0x02: P2P server 2, 0x0x: P2P server x, 0x00: All | 0 | Выбор устройства |
0x01: P2P server 1, 0x02: P2P server 2, 0x0x: P2P server x |
1 | Управ. светодиодом | 0x00 LED OFF, 0x01 LED ON, 0x02 Thread switch | 1 | Состояние кнопки |
0x00 Switch OFF, 0x01 Switch ON |
Таким образом, наш сервер GATT имеет две характеристики P2P_WRITE и P2P_NOTIFY: первая служит для управления с главного устройства светодиодом на плате сервера, вторая позволяет передавать сообщения на главное устройство, например, сигнализировать о нажатии кнопки по инициативе сервера.
На рисунке 19 показана процедура обмена данными между клиентом и сервером P2P.

Рис. 19. Обмен сообщениями P2P «клиент-сервер»
Функция «P2PS_STM_App_Notification()» в файле p2p_server_app.c содержит обработчики событий, поступающих из стека BLE на уровне GATT, нас интересует событие «P2PS_STM_WRITE_EVT», возникающее, когда пришли данные на P2P_WRITE. Состояние светодиода описывает второй байт данных (таблица 2). Добавим проверку значения и включим (или выключим) светодиод:
/* USERCODEBEGINP2PS_STM_WRITE_EVT */
if(pNotification->DataTransfered.pPayload[1] == 0x01)
{
HAL_GPIO_WritePin(LED_B_GPIO_Port, LED_B_Pin, GPIO_PIN_SET);
APP_DBG_MSG("– P2P APPLICATION SERVER : LED BLUE ON\n\r");
} else
{
HAL_GPIO_WritePin(LED_B_GPIO_Port, LED_B_Pin, GPIO_PIN_RESET);
APP_DBG_MSG("– P2P APPLICATION SERVER : LED BLUE OFF\n\r");
}
/* USER CODE END P2PS_STM_WRITE_EVT */
Теперь, если подключиться к серверу P2P с помощью ST BLE Sensor, можно включать и выключать голубой светодиод на плате Nucleo-68. Благодаря сообщениям в макросе «APP_DBG_MSG()» в терминал выводится информация о событии (рисунок 20).

Рис. 20. Включение и выключение светодиода. Вывод терминала
Шаг пятый. Уведомления
Для передачи информации в инициативном порядке с сервера на клиент используется система уведомлений. В качестве события для уведомления выберем факт нажатия кнопки SW1. Первым делом в STM32CubeMX настроим системное прерывание от линии ввода PC4, к которой подключена кнопка. В «Pinout view» находим этот вывод и назначаем функцию внешнего прерывания GPIO_EXT4 (рисунок 21).

Рис. 21. Выбор функционала вывода. STM32CubeMX
В настройках GPIO (раздел «System Core») для вывода PC4 устанавливаем название линии SW1 (имя удобно выбрать в соответствии с позиционным обозначением на принципиальной схеме), активируем внутренний подтягивающий резистор (опция «Pull-up»), выбираем задний (спадающий) фронт для срабатывания прерывания («Fallingedge»), как показано на рисунке 22.

Рис. 22. Настройка GPIO. STM32CubeMX
На вкладке «NVIC» разрешаем прерывание, устанавливаем галочку в строке «EXTI line4 interrupt».
После генерации проекта в файле main.h добавилось описание порта SW1, а в файле stm32wbxx_it.c появился обработчик прерывания «EXTI4_IRQHandler()».
Возникает вопрос, в каком файле должна быть размещена функция обратного вызова обработчика прерывания «HAL_GPIO_EXTI_Callback(GPIO_Pin)»? Дело в том, что эта функция может быть объявлена только в одном месте, а служит для обработки всех внешних прерываний, в том числе и никак не связанных с BLE. Для таких общих для всего проекта задач промежуточное программное обеспечение создало файл app_entry.c. В секцию «USER CODE FD»добавляем:
/* USER CODE BEGIN FD */
void HAL_GPIO_EXTI_Callback( uint16_t GPIO_Pin )
{
switch (GPIO_Pin)
{
case SW1_Pin:
APP_DBG_MSG("-- INTERRUPT: SW1 PUSHED\n\r");
break;
default:
break;
}
return;
}
/* USERCODEENDFD */
Пока наше событие ничего не делает, а только выводит сообщение о самом себе. Этого достаточно, чтобы проверить работоспособность прерывания (рисунок 23).

Рис. 23. Нажатие кнопки. Вывод в терминал
За отправку уведомлений отвечает GATT-характеристика P2P_NOTIFY. Для отправки уведомлений служит функция «P2PS_STM_App_Update_Char()», однако мы не можем вызвать эту функцию из контекста прерывания GPIO, так как это заблокирует выполнение всего стека. Вызов должен быть асинхронным, для этого нам понадобится диспетчер, или, иначе, планировщик заданий.
Классические архитектуры встраиваемого программного обеспечения микроконтроллеров (прошивки) базируются на конечных автоматах (КА, или FSM — Finite-state machine). При определенном уровне сложности КА перерастает в операционную систему, но в простейших случаях достаточно программной обертки для АК. STMicroelectronics при реализации сетевого стека WPAN в качестве диспетчера задач использует секвенсор. Его исходный код расположен в двух файлах: stm32_seq.c и stm32_seq.h (директория «Utilites», рисунок 12). Код секвенсора универсальный: напрямую никак не связанный со стеком BLE, он может быть использован в проекте на любом микроконтроллере STM32. Подробное описание секвенсора можно найти в «Application note AN5289» [6]. Нам же для продолжения вполне достаточно уметь работать с тремя функциями. Первую функцию «UTIL_SEQ_Run( UTIL_SEQ_DEFAULT )» мы добавили в бесконечный цикл функции «main()» на втором шаге. Это функция-планировщик запускает ожидающие выполнения процессы, входной параметр этой функции – маска, где каждый бит связан с определенным процессом. Для того чтобы функция стала процессом, она должна быть зарегистрирована с помощью «UTIL_SEQ_RegTask()». Например, при инициализации стека BLE в качестве процессов зарегистрированы функции «hci_user_evt_proc()» (обработка событий) и «Adv_Cancel()» (завершение режима объявлений). Для того чтобы планировщик запустил процесс, он должен быть активирован функцией «UTIL_SEQ_SetTask()», активированная функция выполнится только один раз.
Создадим функцию-обертку для «P2PS_STM_App_Update_Char()» и зарегистрируем ее как процесс секвенсора. В секцию кода пользователя PFP добавляем прототип функции:
/* USERCODEBEGINPFP */
static void P2PS_Send_Notification( void );
/* USER CODE END PFP */
В секцию FD добавляем тело функции:
/* USER CODE BEGIN FD */
void P2PS_Send_Notification(void)
{
APP_DBG_MSG("– P2P APPLICATION SERVER : INFORM CLIENT BUTTON 1 PUSHED \n\r");
P2PS_STM_App_Update_Char(P2P_NOTIFY_CHAR_UUID, 0x00);
return;
}
/* USER CODE END FD */
В нашем коде функция «P2PS_STM_App_Update_Char()» всегда шлет значение ноль. При желании можно организовать счетчик нажатия кнопки и слать в уведомлении номер текущего нажатия.
В функции инициализации «P2PS_APP_Init()» регистрируем процесс:
/* USER CODE BEGIN P2PS_APP_Init */
UTIL_SEQ_RegTask( 1<< CFG_TASK_SW1_BUTTON_PUSHED_ID, UTIL_SEQ_RFU,
P2PS_Send_Notification );
/* USER CODE END P2PS_APP_Init */
Обратим внимание, что идентификатор процесса «CFG_TASK_SW1_BUTTON_PUSHED_ID» был заранее зарезервирован Middle ware для регистрации события нажатия кнопки (файл app_conf).
В функцию обратного вызова обработчика прерывания добавляем активацию процесса отправки:
...
case SW1_Pin:
APP_DBG_MSG("– INTERRUPT: SW1 PUSHED\n\r");
UTIL_SEQ_SetTask( 1<<CFG_TASK_SW1_BUTTON_PUSHED_ID, CFG_SCH_PRIO_0);
break;
...
Если никаких ошибок не допущено, то проект должен успешно собираться.

Рис. 24. Уведомление о нажатии кнопки. ST BLE Sensor
Загружаем прошивку в память микроконтроллера. При нажатии кнопки SW1 в утилите ST BLE Sensor загорается красным уведомление в виде колокольчика «Buttonpressed» (рисунок 24). На рисунке 25 показан соответствующий вывод в терминал.

Рис. 25. Отправка уведомлений. Вывод в терминал
На этом этапе можно доверить читателю самостоятельное исследование возможностей STM32WB в приложениях BLE. Исходные коды и сам проект нашей демонстрации можно найти в репозитории на GitHub.
Заключение
Хотелось бы в заключение написать, что технология беспроводной связи проста и легка в освоении, но это будет лукавством. Протокол BLE непрост, он состоит из множества уровней, каждый из которых требует от инженера-программиста детальной проработки. Тем не менее, STMicroelectronics проделала огромную работу в направлении популяризации беспроводных технологий – в хорошо знакомую разработчикам экосистему микроконтроллеров STM32 интегрировала поддержку стека. В результате программист, который уже работал с STM32Cube, увидит привычное окружение, не будет тратить время на рутинную инициализацию периферии и изучение новой для него IDE, а сконцентрирует свое внимание на беспроводном стеке и, собственно, самом приложении.
Микроконтроллеры серии STM32WB могут стать основой широкого спектра IoT-устройств, а BLE – удобный интерфейс взаимодействия между устройствами на дистанциях нескольких десятков метров.
Литература
- Getting started with STM32CubeWB for STM32WB Series. STM User manual UM2550.
- STM32Cube MCU Package examples for STM32WB Series. STM Application note AN5155.
- Bluetooth Generic Attributes. BluetoothSIG.
- Over-the-air application and wireless firmware update for STM32WB Series microcontrollers. STM Application note AN5247.
- Шауэрман А.А. Отладочные плата P-NUCLEO-WB55 для STM32WB55 – возможности и особенности.
- Building wireless applications with STM32WB Series microcontrollers. STM Application note AN5289.
- STM32WB workshop MOOC.
Наши информационные каналы