© 2023 DiyTronic

Снова nRF51822 - пробуем запустить Bluetooth

В первой статье, посвящённой nRF51822 модулю и опытов над ним я запустил на этом модуле простейшую мигалку светодиодами. Но это вовсе не то для чего создавался этот модуль и главная цель это получение полноценного BLE устройства. Поэтому продолжаю эксперименты и попробую поднять на устройстве BLE.

Для начала было бы неплохо узнать, что у нас есть на борту. Зачем это нужно станет понятно в дальнейшем, а пока поверьте мне на слово — это важно. Поэтому в начале небольшое лирическое отступление и минутка теории.

Как узнать версию текущего softdevice-а, зашитого в устройство?

Идентификатор SoftDevice находится по адресу 0x300C. Попробуем извлечь его оттуда.

1
2
3
$ JLinkExe -device nrf51822 -if swd
J-Link>mem16 0x300C, 1
0000300C = 0067

По таблице находим что за SoftDevice соответствует этому идентификатору. Если возвращается пустое значение 0xFFFF значит SoftDevice не установлен.

SoftDevice FWID
S110 v7.1.0 0×005A
S110 v8.0.0 0×0064
S120 v2.0.0 0×0060
S130 v1.0.0 0×0067
S130 0.9alpha 0×005E
S130 v2.0.0-7.alpha 0×0078
S310 v2.0.0 0×005D
S132 v1.0.0-3.alpha 0×006D

Более подробно тут https://devzone.nordicsemi.com/question/3629/how-do-i-access-softdevice-version-string/? answer=3693#post-id-3693

Итого как оказалось я залил softdevice S130 версии 1. Собственно с SDK v10 поставляется именно она поэтому с виду вроде как всё в порядке.

Для более глубокого погружения в тему — вот спецификация на S130 v1.0.0. Уверен, придётся туда заглядывать ещё не раз.

Тип чипа

В предыдущей статье уже упоминал важность знания конкретной маркировки чипа. В связи с этим заснял свой экземпляр:

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

Адресация S130

Итак я нашёл как распределяется адресное пространство контроллера в случае использования SoftDevice S130 v1.

На этой схеме:

Параметр Значение Где найти
APP_CODE_BASE 0×1C000 Спецификация к SoftDevice
APP_RAM_BASE 0×20002800 Спецификация к SoftDevice
SizeOfProgMem 256k Определить по типу чипа
SizeOfRAM 16k Определить по типу чипа

И что же это нам даёт? А даёт это нам возможность корректно указать линковщику куда (в какие области памяти) размещать код. Softdevice передаёт управление программе по конкретному адресу, поэтому эти значения нужно указывать точно иначе код просто не запустится.

Конфигурация линкера

Чтобы упростить работу со всем этими областями памяти gcc предоставляет нам возможность указать все эти области, а так-же расположение секций кода в специальном файле, который потом можно скормить линковщику и тот в свою очередь раскидает код как надо. В примерах SDK это файлы с расширением ld.

Секция MEMORY

Здесь задаются значения областей памяти доступных коду программы. В нашем случае мы должны вычислить области флеша и памяти, свободные после заливки в память устройства кода softdevice. Значения по умолчанию можно найти в соответствующих файлах, расположенных в папке components/toolchain/gcc SDK.

Выглядит это примерно так:

1
2
3
4
5
MEMORY
{
FLASH (rx) : ORIGIN = 0x1c000, LENGTH = 0x24000
RAM (rwx) : ORIGIN = 0x20002800, LENGTH = 0x5800
}

Скажем прямо — сходу ничего непонятно — какие-то константы и откуда они взялись решительно неясно.

Как рассчитать эти значения

Поля ORIGIN берутся из спецификации к SoftDevice-у. Выше в таблицах указаны значения для S130 v1.

Для флеша

  • ORIGIN = APP_CODE_BASE = 0×1C000
  • LENGTH = < размер флеш памяти> — ORIGIN = 256k — 0×1C000

Для ОЗУ

  • ORIGIN = APP_RAM_BASE = 0×20002800
  • LENGTH = < размер ОЗУ> — (APP_RAM_BASE — 0×20000000) = 16k — 0×2800

Для ОЗУ нумерация адресов идёт не с нуля, поэтому и вычитаем начальный адрес.
Для ОЗУ так-же есть нюанс — ORIGIN не фиксирован и в ряде случаев должен быть пересчитан, но это уже нюансы которые описаны в документации.

В итоге для XXAA чипа и SoftDevice S130 v. 1 конфигурация памяти в файле линковщика будет выглядеть как:

1
2
3
4
5
MEMORY
{
FLASH (rx) : ORIGIN = 0x1c000, LENGTH = 256k - 0x1c00
RAM (rwx) : ORIGIN = 0x20002800, LENGTH = 16k - 0x2800
}

Секция SECTIONS

Здесь задаются секции кода. В файле components/toolchain/gcc/nrf51_common.ld (ну и соответствующие common файлы для других чипов) описаны стандартные секции кода, поэтому этот файл всегда включается в файл линковщика.

Часто встречающаяся секция fs_data и fs_data_out предназначена для хранения информации о привязке (bonding). Ну или для хранения любой другой информации используя API для хранилища если это вам нужно. Поэтому как правило нужна. Более подробно можно почитать в документации

Какой пример будем запускать?

Поковырявшись в примерах решил попробовать этот examples/ble_peripheral/ble_app_uart. Не скажу почему, но с другой стороны почему бы и нет?

Итак исходный код не трогаю, ибо он мне пока не интересен — тут бы готовый пример заставить работать, а не с кодом упражняться. В папке вижу подпапку pca10028 — это какая-то демоплата, но у меня такой нет, а есть ble400, просто копирую всю папку pca10028 в ble400 и далее буду упражняться на ней.

Заходим в неё и видим знакомые нам подпапки S110 и S130. Очевидно нам нужна S130 ибо выше всё уже посчитано для неё. Внутри сразу проходим в armgcc и видим знакомые нам Makefile и конфигурацию линковщика. Ну что — лопату в руки и поехали.

Правим Makefile

  1. Меняю имя проекта с ble_app_uart_s130_pca10028 на ble_app_uart_s130_ble400
  2. -DBOARD_PCA10028 на -DBOARD_CUSTOM
  3. Добавляю -D__HEAP_SIZE=512 к опциям CFLAGS и ASMFLAGS

Ну и собственно и всё. Последнее действие нужно т. к. при сборке выскочила ошибка region RAM overflowed with stack. В общем уменьшение HEAP до 515 решает проблему, но я больше склоняюсь к мысли переехать на SoftDevice S110, как менее ресурсоёмкий.

Линковщик

Выше уже все расписал, поэтому просто приведу свой файл.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/* Linker script to configure memory regions. */

SEARCH_DIR(.)
GROUP(-lgcc -lc -lnosys)

MEMORY
{
FLASH (rx) : ORIGIN = 0x1c000, LENGTH = 256k - 0x1c00
RAM (rwx) : ORIGIN = 0x20002800, LENGTH = 16k - 0x2800
}

SECTIONS
{
.fs_data_out ALIGN(4):
{
PROVIDE( __start_fs_data = .);
KEEP(*(fs_data))
PROVIDE( __stop_fs_data = .);
} = 0
}

INCLUDE "nrf5x_common.ld"

Сборка

Просто выполняем команду make

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ make
rm -rf _build
echo Makefile
Makefile
mkdir _build
Compiling file: app_button.c
Compiling file: app_error.c
...
Compiling file: system_nrf51.c
Compiling file: softdevice_handler.c
Compiling file: gcc_startup_nrf51.s
Linking target: nrf51422_xxac_s130.out
make[1]: вход в каталог «/home/roman/.Projects/nrf51/nRF51_SDK_10.0.0_dc26b5e/examples/ble_peripheral/ble_app_uart/ble400/s130/armgcc»
Preparing: nrf51422_xxac_s130.bin
Preparing: nrf51422_xxac_s130.hex

text data bss dec hex filename
21980 116 2260 24356 5f24 _build/nrf51422_xxac_s130.out

make[1]: выход из каталога «/home/roman/.Projects/nrf51/nRF51_SDK_10.0.0_dc26b5e/examples/ble_peripheral/ble_app_uart/ble400/s130/armgcc»

То, что выходной файл имеет неверное имя чипа и его тип не должно смущать, это всего-лишь наименование которое авторы SDK видимо ленятся менять. Поэтому я тоже поленюсь и оставлю как есть. Но тем не менее мы получили файл прошивки nrf51422_xxac_s130.hex и это прекрасно.

Прошивка

Давайте же наконец зальём наше творение в модуль и посмотрим как оно работает и работает ли вообще.
Расчехляю JLink, соединяю всё как надо и запускаю процесс прошивки. На всякий случай очищу память модуля и заново залью SoftDevice и свой код.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
$ JLinkExe -device nrf51822 -if swd
SEGGER J-Link Commander V4.84f ('?' for help)
Compiled May 9 2014 20:12:27
Info: Device "NRF51822_XXAA" selected (257 KB flash, 16 KB RAM).
DLL version V4.84f, compiled May 9 2014 20:12:24
Firmware: J-Link ARM V8 compiled Jul 17 2014 12:31:18
Hardware: V8.00
S/N: 158005115
Feature(s): RDI,FlashDL,FlashBP,JFlash,GDBFull
VTarget = 3.332V
Info: Found SWD-DP with ID 0x0BB11477
Info: Found Cortex-M0 r0p0, Little endian.
Info: FPUnit: 4 code (BP) slots and 0 literal slots
Found 1 JTAG device, Total IRLen = 4:
Cortex-M0 identified.
Target interface speed: 100 kHz
J-Link>erase
Erasing device (nRF51822_xxAA)...
Info: J-Link: Flash download: Total time needed: 5.947s (Prepare: 0.510s, Compare: 0.000s, Erase: 5.393s, Program: 0.000s, Verify: 0.000s, Restore: 0.043s)
Erasing done.
J-Link>loadfile ../../../../../../components/softdevice/s130/hex/s130_nrf51_1.0.0_softdevice.hex
Info: J-Link: Flash download: Flash programming performed for 2 ranges (110592 bytes)
Info: J-Link: Flash download: Total time needed: 16.710s (Prepare: 1.205s, Compare: 0.246s, Erase: 0.000s, Program: 14.484s, Verify: 0.022s, Restore: 0.751s)
J-Link>loadfile _build/nrf51422_xxac_s130.hex
Info: J-Link: Flash download: Flash programming performed for 1 range (22528 bytes)
Info: J-Link: Flash download: Total time needed: 4.623s (Prepare: 0.982s, Compare: 0.028s, Erase: 0.000s, Program: 3.104s, Verify: 0.004s, Restore: 0.502s)
J-Link>r
Reset delay: 0 ms
Reset type NORMAL: Resets core & peripherals via SYSRESETREQ & VECTRESET bit.
J-Link>g

Если кого-то пугают ужасные пути подскажу — в JLink-е есть автодополнение путей по клавише Tab.

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

Результат

В итоге попробовал просканировать эфир программой «nRF Connect» и собственно увидел в списке устройств Nordic_UART, что как бы намекает нам, что устройство функционирует и доступно для подключения.

Я считаю неплохой результат — по крайней мере ещё один шаг вперёд.

Источники

Комментарии