Серия Bluetooth SoC nRF52 от Nordic Semiconductor сочетает в себе мощный процессор ARM Cortex с современными возможностями подключения Bluetooth и BLE.

Nordic предоставляет обширный SDK и пример кода для обновления прошивки nRF52 из различных источников. Один из вариантов — использовать ПК с последовательным подключением и инструмент под названием nrfutil. С помощью nrfutil вы указываете файл zip с новой прошивкой и последовательным интерфейсом, а инструмент — в сотрудничестве с загрузчиком на nRF52 — выполняет фактическое обновление прошивки.

В недавнем клиентском проекте nRF52 был подключен ко второму микроконтроллеру, который имел доступ к SD-карте. Запуск nrfutil на основе Python на таких контроллерах для обновления nRF52 обычно невозможен; мы долго искали решение, более подходящее для микроконтроллеров, но ничего не нашли, поэтому решили сами создать минимальную реализацию.

Мы создали небольшую библиотеку fwu, которую представляем в этом сообщении блога и которую вы можете использовать в качестве отправной точки в своем собственном проекте (см. или репозиторий github, https://github. com/classycodeoss/nrf52-dfu). Библиотека реализует подмножество протокола Nordic DFU и может быть интегрирована в хост-приложение на втором MCU для обновления прошивки nRF52 через загрузчик nRF52.

На следующей диаграмме показаны различные компоненты, участвующие в обновлении микропрограммы:

Вверху находится система (плата, устройство), которая содержит nRF52 и второй MCU. На nRF52 работает старая версия приложения App V1, которую мы хотели бы обновить до более новой версии. nRF52 и второй MCU подключены через последовательный канал. Кроме того, у второго MCU есть способ получить обновление прошивки в виде двоичных данных, например. с помощью SD-карты, второго последовательного канала, HTTP-соединения, карты памяти и т. п.

Внизу рисунка находится среда разработки. Разработчик создает новую версию приложения, App V2, которую мы хотели бы запрограммировать на nRF52 через второй MCU.

В следующих разделах мы представляем

  • две версии небольшого демонстрационного приложения, старая версия и новая версия;
  • загрузчик для nRF52, который поддерживает обновление прошивки через последовательное соединение;
  • библиотека fwu, которая лежит в основе этого поста в блоге, работает на втором микроконтроллере и координирует обновление микропрограммы с загрузчиком на nRF52;
  • демонстрационное хост-приложение, которое позволяет запускать библиотеку fwu на стандартном настольном компьютере вместо второго MCU и показывает, как интегрировать и использовать библиотеку;
  • преобразователь для создания файлов заголовков из файла встроенного ПО для включения в демонстрационное хост-приложение.

Соответствующий код доступен в нашем git-репозитории.

Демонстрационное приложение

Версия 1 нашего демонстрационного приложения делает две вещи:

  • Он заставляет светодиод мигать 3 раза, делает небольшую паузу и снова начинает мигать.
  • Он отслеживает вход GPIO и, если обнаруживает сигнал на входе, переключается на загрузчик для получения обновления прошивки.

Версия 2 точно такая же, но позволяет светодиоду мигать 4 раза вместо 3. Таким образом мы можем убедиться, что обновление прошло успешно.

Если вы хотите попробовать наш пример, вам нужно изменить наш образец кода для вашей конкретной платы. Файл main.c демонстрационного приложения содержит в начале два #define, которые определяют сигналы GPIO для светодиода и кнопки.

Чтобы скомпилировать демонстрационное приложение, сначала предоставьте nRF52 SDK и набор инструментов на верхнем уровне структуры каталогов в следующих двух папках:

  • SDK/nRF5_SDK_15.2.0_9412b96
  • toolchain/gcc-arm-none-eabi-7–2018-q2-обновление

Вы можете загрузить SDK и набор инструментов с веб-страниц Nordic и ARM:

Код был протестирован с конкретными версиями, перечисленными выше, но его легко перенести на другие версии.

После загрузки и разархивирования набора инструментов и SDK вам необходимо применить патч, который мы предоставляем в каталоге верхнего уровня (подробности см. «https://devzone.nordicsemi.com/f/nordic-qa/37120/nrfx_uart- ошибка"):

$ patch -p0 < nrfx_uart.patch

Кроме того, нам понадобится библиотека uECC:

$ pushd sdk/nRF5_SDK_15.2.0_9412b96/external/micro-ecc
$ export GNU_INSTALL_ROOT="$PWD/../../../../toolchain/gcc-arm-none-eabi-7-2018-q2-update/bin/"
$ git clone https://github.com/kmackay/micro-ecc.git
$ make -C nrf52hf_armgcc/armgcc
$ make -C nrf52nf_armgcc/armgcc
$ popd

После обновления main.c вы сможете собрать приложение:

$ cd 01_Demo_App
$ make clean
$ make v1

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

Загрузчик

Для поддержки процесса обновления прошивки на nRF52 мы реализуем небольшой загрузчик на основе модулей Nordic nrf_bootloader и nrf_dfu.

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

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

Скомпилируйте загрузчик с помощью этих команд и запрограммируйте его во флэш-память. («make flash_hex» использует nrfjprog для управления целью; отрегулируйте, как требуется для вашей цели):

$ cd 02_Bootloader
$ make clean
$ make
$ make hex
$ make flash_hex

Если все работает нормально, вы должны увидеть сообщение «@@BOOTLOADER» от загрузчика на последовательной консоли, указывающее, что загрузчик готов принять приложение.

Создайте пакет DFU для ранее скомпилированной версии 1 приложения и отправьте его в загрузчик с помощью стандартного инструмента python nrfutil от Nordic (скрытого за вызовом «make dfu»):

$ cd 01_Demo_App
$ cd dfu_zip
$ make clean
$ make v1
$ make dfu  <-- replace the serial device in the Makefile first!

После перезагрузки вы должны увидеть схему мигания светодиода версии приложения 1 (3 вспышки, пауза, 3 вспышки и т. д.)

Библиотека fwu и хост-приложение DFU

Как только загрузчик nRF52 готов принять прошивку, хост-приложение DFU может инициировать обновление. Для этого он может импортировать нашу библиотеку, модуль fwu (fwu.h/fwu.c). Этот модуль отвечает за связь с загрузчиком и реализует протокол Nordic DFU. Мы предоставляем пример хост-приложения, которое вы можете скомпилировать и запустить на настольном компьютере и которое демонстрирует, как использовать модуль fwu.

Интерфейс fwu определен в файле fwu.h:

// First function to call to set up our internal state.
void fwuInit(TFwu *fwu);
// Execute the firmware update.
void fwuExec(TFwu *fwu);
// Call regularly to allow asynchronous processing to continue
// and to let us detect timeouts.
EFwuProcessStatus fwuYield(TFwu *fwu, uint32_t elapsedMillisec);
// Call after data from the target has been received.
void fwuDidReceiveData(TFwu *fwu, uint8_t *bytes, uint8_t len);
// Inform FWU module that it may send maxLen bytes of data.
void fwuCanSendData(TFwu *fwu, uint8_t maxLen);

Модуль fwu необходимо настроить с помощью fwuInit, прежде чем его можно будет использовать. В fwuInit хост-приложение настраивает структуру данных TFwu, которую передает в fwuInit:

sFwu.commandObject = gFirmwareDat;
sFwu.commandObjectLen = sizeof(gFirmwareDat);
sFwu.dataObject = gFirmwareBin;
sFwu.dataObjectLen = sizeof(gFirmwareBin);
sFwu.txFunction = txFunction;
sFwu.responseTimeoutMillisec = 5000;
// Prepare the firmware update process.
fwuInit(&sFwu);
// Start the firmware update process.
fwuExec(&sFwu);

Обратите внимание, что во время инициализации необходимо предоставить полные двоичные BLOB-объекты. Модификация для потоковой передачи больших двоичных объектов оставлена ​​читателю в качестве упражнения ;-)

Необходимо обеспечить четыре вещи:

  • Указатель и длина метаданных прошивки (gFirmwareDat)
  • Указатель и длина двоичного файла прошивки (gFirmwareBin)
  • Функция обратного вызова для отправки байтов на nRF52
  • Тайм-аут ответа в миллисекундах, чтобы определить, как долго ждать ожидаемых ответов от nRF52.

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

Последовательная связь между хост-приложением и nRF52

Хост-приложение реализует функцию обратного вызова TX со следующей сигнатурой:

void (*FTxFunction)(struct SFwu *fwu, uint8_t *buf, uint8_t len);

Модуль fwu передает информацию о буфере данных и длине этой функции обратного вызова и ожидает, что хост-приложение передаст эти данные на nRF52. Чтобы избежать переполнения передатчика, модуль fwu будет отправлять только тот объем данных, который ранее был разрешен хост-приложением с помощью функции fwuCanSendData.

// Can send 4 chars... (On a microcontroller, you'd use the 
// TX Empty interrupt or test a register.)
fwuCanSendData(&sFwu, 4);

Всякий раз, когда хост-приложение получает данные от nRF52, оно должно пересылать эти данные в библиотеку fwu с помощью функции fwuDidReceiveData.

// Data available? Get up to 4 bytes... (On a microcontroller, 
// you'd use the RX Available interrupt or test a register.)
uint8_t rxBuf[4];
uint8_t rxLen = readData(rxBuf, 4); // access serial port
if (rxLen > 0) {
    fwuDidReceiveData(&sFwu, rxBuf, rxLen);
}

Отслеживание времени ожидания и состояния

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

fwuYield возвращает текущее состояние процесса обновления, которое может быть…

  • FWU_STATUS_UNDEFINED, если статус неизвестен,
  • FWU_STATUS_FAILURE, если обновление прошивки не удалось, или
  • FWU_STATUS_COMPLETION, если обновление прошивки успешно завершено.

В качестве альтернативы хост-приложение может проверить поля processStatus и responseStatus в структуре TFwu:

// Overall process status code
EFwuProcessStatus processStatus;
// Response status code
EFwuResponseStatus responseStatus;

processStatus соответствует возвращаемому значению fwuYield.

responseStatus содержит дополнительную информацию об ошибках (время ожидания, несоответствие идентификатора ping, ошибка контрольной суммы и т. д.) и особенно полезно после того, как fwuYield вернул FWU_STATUS_FAILURE.

Предоставление двоичного файла микропрограммы и метаданных хост-приложению

Модуль fwu ожидает 2-байтовых массива в вызове инициализации, один для двоичного файла встроенного ПО и один для метаданных. То, как хост-приложение на втором микроконтроллере получает эти данные, очевидно, зависит от вашего варианта использования. Например, данные могут поступать с SD-карты или могут предоставляться через второй последовательный интерфейс или даже по беспроводной сети.

Мы предоставляем небольшой инструмент командной строки, fwconvert, который принимает в качестве входных данных стандартный двоичный файл прошивки nRF52 и создает два файла заголовков, которые содержат массивы двоичных файлов (gFirmwareBin) и метаданных (gFirmwareDat). Два заголовочных файла можно использовать для предоставления данных прошивки нашему демонстрационному хост-приложению. fwconvert должно быть легко модифицировать для создания другого вывода, например. файл для сохранения на SD-карту.

Чтобы попробовать это, скомпилируйте версию 2 приложения, сгенерируйте ZIP-файл DFU, извлеките файлы BIN и DAT из ZIP-файла и запустите fwconvert для создания больших двоичных объектов:

$ cd 01_Demo_App
$ make clean
$ make v2
$ cd dfu_zip
$ make clean
$ make v2
$ cp app_dfu_package.zip /tmp
$ pushd /tmp
$ unzip app_dfu_package.zip
$ popd

$ cd ../..
$ cd 05_Firmware_Converter
$ gcc fwconvert.c
$ ./a.out /tmp/nrf52832_xxaa.bin dfu_firmware_bin.h gFirmwareBin
$ ./a.out /tmp/nrf52832_xxaa.dat dfu_firmware_dat.h gFirmwareDat

Два заголовочных файла, dfu_firmware_bin.h и dfu_firmware_dat.h, теперь можно скомпилировать в демонстрационное приложение хоста DFU:

$ cd ..
$ cd 04_Demo_Host_Application
$ cp ../05_Firmware_Converter/dfu_firmware_bin.h .
$ cp ../05_Firmware_Converter/dfu_firmware_dat.h .
$ make

Результатом является приложение командной строки, fwu, которое включает в себя большие двоичные объекты и библиотеку, а также код для доступа к последовательному интерфейсу. Войдите в режим DFU на цели, нажав кнопку. Светодиод должен перестать мигать, а загрузчик должен быть готов к приему обновленного приложения. Запустить фву:

$ ./fwu "/dev/tty.usbserial-DN009NQG" 57600  <-- configure for your serial device

Через некоторое время вы должны увидеть сообщение об успешном обновлении, а светодиод должен мигать по схеме v2 (4 вспышки, пауза, 4 вспышки и т. д.)

Как работает модуль fwu?

Основная часть модуля fwu — это большой конечный автомат, который координирует команды для отправки на nRF52 в соответствии со скандинавским протоколом DFU.

Например, как только хост-приложение инициирует обновление с помощью функции fwuExec, модуль fwu отправляет команду PING на цель nRF52, чтобы узнать, отвечает ли она и готов к прошивке. Если проверка связи не удалась, следующий вызов fwuYield вернет FWU_STATUS_FAILURE.

За успешной командой ping следует обмен настройками квитанции, размером блока передачи, выбором объекта команды INIT и т. д. — подробности см. в коде fwu.c и документации по протоколу Nordic DFU.

В дополнение к основному конечному автомату fwu.c содержит второй, меньший по размеру конечный автомат, который отслеживает, отправляет ли модуль в данный момент данные или получает данные, или находится в режиме ожидания.

Вывод

Протокол Nordic DFU обеспечивает простой способ обновления приложений nRF5 в полевых условиях. Мы представили библиотеку C для выполнения таких обновлений с дополнительного процессора или микропроцессора. Вы можете скачать библиотеку из нашего репозитория github, https://github.com/classycodeoss/nrf52-dfu.

На данный момент библиотека реализует только часть протокола. Например, частично завершенное обновление прошивки восстановить пока невозможно; обновление необходимо перезапустить с самого начала. Кроме того, необходимо сразу предоставить полные двоичные BLOB-объекты новой версии приложения. В некоторых случаях будет полезен потоковый подход.

Мы надеемся, что библиотека станет полезной отправной точкой в ​​ваших собственных проектах, и будем рады вашим отзывам в разделе комментариев!