Модули E73-2G4M04S на базе nRF52832
- Войдите на сайт для отправки комментариев
Вот здесь было небольшое обсуждение беспроводных модулей E73-2G4M04S на базе чипа Nordic Semi nRF52832, к которому я бы хотел вернуться, только уже здесь. Получил я один такой модуль с али и идея подпайки проводков отпала сразу же -- шаг контактных площадок мелковат. Проще показалось заЛУТить платку-адаптер, что и было сделано. Получилось в форме гаечного ключа, т.к. захотелось, чтобы штырьевые контакты по ширине помещались на беспаечную макетку и оставалось по одному ряду с каждой стороны для втыкания проводков.
HASL по-домашнему:
Запайка самого модуля:
Ну и стенд для первичных испытаний:
Оценка работоспособности производилась при помощи китайского клона ST-LINK v2 и среды разработки Keil uVision V5. Из списка примеров, которые идут с самим Keil-ом, был извлечен Blink, который сходу, тем не менее, загружаться не захотел. Гугление навело на рецепт поменять настройки в разделе программатора:
После чего стало возможным тереть, шить и отлаживать программу через программатор ST-LINK V2. Ну и блинк в итоге заработал.
О ходе дальнейших изысканий планирую дописывать тут по мере накопления материала.
Круто! И плата красивая!
-----
Не в тему, но большая просьба, гляньте, пожалуйста.
a5021. приветствую.
По вашей наводке заказал 2 таких модуля и тоже получил в эти выходные. Правда у меня пока ST-Linka нет, чтобы попробовать.
Плата отличная. В чем разводили?
Плату разводил в Протеусе, но, по моему, такую плату можно в чем угодно развести. Если кому лень будет разводить и протеус не смущает, могу выложить куда-нибудь данный проект в протеусе. Спрашивайте.
Без ST-Link V2 (или J-Link) к этой плате подступиться будет сложно. Теоретически, Arduino Primo Core тоже состоит главным образом из одного только чипа nRF52832 и для него существует бутлоадер, но простым способом сей бутлоадер можно зашить только с помощью Arduino Primo, который используется, как программатор. Так что даже если удастся подсунуть E73, как Arduino Primo Core, нужен еще и просто Primo, чтобы зашить. Такой путь лично мне не видится совсем простым.
Проще оказалось пойти на https://github.com/sandeepmistry/arduino-nRF5 и по инструкции оттуда добавить в Arduino IDE поддержку "Nordic Semiconductor nRF5 based boards". Этот пакет при установке затянул еще и поддержку программаторов ST-LINK, J-LINK, Black Magic Probe и CMSIS-DAP, т.ч. с вопросом "Чем зашиться?" ситуация упростилась.
Упростилась бы она и в случае отсутствия у меня ST-LINK, т.к. в вышеприведенном списке программаторов присутствует еще и Black Magic Probe, который на коленке легко делается из небезызвестной китайской платки BluePill (на базе STM32F103C8)
по методике изложенной здесь.
Сам я продолжаю пользоваться китайским клоном ST-LINK-а и здесь могу попутно заметить, что идея перешить его в J-LINK тут не прокатывает. Я попробовал, он без проблем перешивается, но из-за политики SEGGER-а, такой новообращенный J-LINK ни с чем, кроме процессоров ST Micro работать не желает. Поверхностное гугление способов преодоления данного затруднения не предлагает.
Не разобрался пока еще почему, но Arduino IDE никак не желает зашивать скетчи, если в качестве целевой платформы выбрать Generic NRF52, хотя это вроде бы самый короткий путь в моем случае. Ладно, фиг сним, выбрал платформу "RED BEAR BLEND 2". Эту зашивает с вот таким журналом сообщений:
Скетч использует 2 604 байт (0%) памяти устройства. Всего доступно 524 288 байт.
C:\Users\0123456\AppData\Local\Arduino15\packages\sandeepmistry\tools\openocd\0.10.0-dev.nrf5/bin/openocd.exe -d2 -f interface/stlink-v2.cfg -c transport select hla_swd; set WORKAREASIZE 0x4000; -f target/nrf52.cfg -c program {{C:\Users\0123456\AppData\Local\Temp\arduino_build_153375/BlinkWithoutDelayZerro.ino.hex}} verify reset; shutdown;
Open On-Chip Debugger 0.10.0-dev-00254-g696fc0a (2016-04-10-10:13)
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
debug_level: 2
0x4000
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
adapter speed: 10000 kHz
Info : Unable to match requested speed 10000 kHz, using 4000 kHz
Info : Unable to match requested speed 10000 kHz, using 4000 kHz
Info : clock speed 4000 kHz
Info : STLINK v2 JTAG v24 API v2 SWIM v4 VID 0x0483 PID 0x3748
Info : using stlink api v2
Info : Target voltage: 3.520420
Info : nrf52.cpu: hardware has 6 breakpoints, 4 watchpoints
nrf52.cpu: target state: halted
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x00000534 msp: 0x20010000
** Programming Started **
auto erase enabled
Info : nRF51822-QFN48(build code: B00) 512kB Flash
Warn : using fast async flash loader. This is currently supported
Warn : only with ST-Link and CMSIS-DAP. If you have issues, add
Warn : "set WORKAREASIZE 0" before sourcing nrf51.cfg to disable it
nrf52.cpu: target state: halted
target halted due to breakpoint, current mode: Thread
xPSR: 0x61000000 pc: 0x2000001e msp: 0x20010000
wrote 4096 bytes from file C:\Users\0123456\AppData\Local\Temp\arduino_build_153375/BlinkWithoutDelayZerro.ino.hex in 0.896679s (4.461 KiB/s)
** Programming Finished **
** Verify Started **
nrf52.cpu: target state: halted
target halted due to breakpoint, current mode: Thread
xPSR: 0x61000000 pc: 0x2000002e msp: 0x20010000
verified 2724 bytes in 0.588028s (4.524 KiB/s)
** Verified OK **
** Resetting Target **
shutdown command invoked
Не очень понятно о чем вот эта строка:
Info : nRF51822-QFN48(build code: B00) 512kB Flash
Чип, по идее, должен отображаться, как nRF52832.
Cоответствие ног в скетчах с ногами на E73 надо будет выяснять и составлять таблицу, т.к. попытки подрыгать ногами с последовательными номерами иногда приводят к тому, что дрыгаются ноги, которые находятся далеко друг от друга.
Пока попробовал компилировать самое простое:
Пин 13 оказался пином 5 на E73, но блинкает исправно. Посмотрел, какой код варится для того же millis(). По сравнению с атмеговским выглядит забавно:
Глаз цепляется за 64-битную арифметику. Ассемблерная команда umull перемножает два беззнаковых 32-битных числа с получением 64-битного результата. Время выполнения -- один такт. Внушает.
Раз уж тут засветился реалтаймовый счетчик NRF_RTC1->COUNTER, который увеличивается на 1 со скоростью 32768 раз в секунду, то подумалось, что можно блинк непосредственно по нему вычислять, не вызывая миллис:
Так тоже мигает. Опять интересно глянуть, во что компилятор превращает (NRF_RTC1->COUNTER / 32768 ) & 1:
По сути, вся арифметика выполняется единственной командой: ubfx r1, r1, #15, #1 -- взять из регистра R1 один бит начиная с 15-го, дополнить нулями слева до 32-х разрядов и поместить обратно в регистр R1. Красиво.
Соответствие пинов ардуино-совместимой платы RedBear Blend 2 пинам модуля E73-2G4M04S:
Таким образом, если хочется помигать светодиодом, который у Ардуино УНО висит на 13 пине, к E73-2G4M04S его надо цеплять на 5-ый контакт. В скетч стандартного блинка никаких изменений вносить не надо.
скажите пожалуйста, а чем Вы переносите тонер на текстолит ? ужбольно платка красивая получилась.
я остановился на глянцевом ломонде, но между мелких дорожек выковыривать его остатки замучался.
скажите пожалуйста, а чем Вы переносите тонер на текстолит ? ужбольно платка красивая получилась.
я остановился на глянцевом ломонде, но между мелких дорожек выковыривать его остатки замучался.
я такой пользуюсь
https://ru.aliexpress.com/item/10pcs-A4-Heat-Toner-Transfer-Paper-For-DIY-PCB-Electronic-Prototype-Mak/32638704954.html
Печатаю на пищевой фольге. Вырезаю отпечатанный рисунок и пришквариваю к стеклотекстолитовой заготовке утюгом. В таком виде и бросаю травиться. Сначала стравливается алюминий, потом следом и медь.
Чтобы было понятно, как фольга попадает в принтер, вот фотка того, что использовалось для изготовления данной платы.
Верхняя часть бумажного листа оборачивается фольгой, фольга фиксируется на обратной стороне и в таком виде оно опускается в принтер.
да, спасибо, встречал это способ на просторах инета, надо будет попробывать..
и еще, будьте любезны, а чем травите это дело ?
Лучший состав из мной испробованных -- соляная кислота + перекись водорода. В принципе, он же и в промышленности в основном используется. Мне он глянулся тем, что обеспечивает минимальный боковой подтрав. Если бы я взял состав на основе той же лимонной кислоты, скажем, то ровных дорожек, как на фотках, точно бы не получилось. Лимонка грызет края дорог, как рашпиль. Многократно в этом убеждался, когда экспериментировал.
хех, ну с "лимонкой" понятно, а где брать соляную кислоту и какой состав для травления ?
не гуглится ничего по этому поводу...
Я добывал соляную кислоту в промышленности и насколько знаю, частным лицам ее не продают.
Возвращаясь к теме этой ветки, информарцию о чипе, установленном в модуле, можно узнать не снимая крышку. Вот скетч, который считывает жестко прописанные внутри самого nRF5 данные о конфигурации:
У меня он показывает такое:
Не наврали, стало быть, китайцы. Установлена версия с 512к флеша и 64к ОЗУ.
Где-то хорошо отрекламировали данные модули. У продавца на али 552 заказа на данный момент. С учетом, что девайс весьма специфический, можно лишь предположить, что встроили его куда-то в опен-соурс проект, который довольно популярен. Вот только куда?
можно лишь предположить, что встроили его куда-то в опен-соурс проект, который довольно популярен. Вот только куда?
на MySensors.com огромная ветка про NRF52, в том числе и конкретно про эти модули.
Я сделал иначе. На raspberry pi установил openOCD (это система отладки для МК). Инструкции как установить и шить МК вот тут:
https://learn.adafruit.com/programming-microcontrollers-using-openocd-on...
https://gitlab.cba.mit.edu/pub/hello-world/nrf52/tree/master
https://github.com/ntfreak/openocd
Единственное, что при установке использовал не git clone git://git.code.sf.net/p/openocd/code openocd
а его зеркало git://repo.or.cz/openocd.git т.к. с вышеуказанного не качалось.
Далее установил настройки платы от adafruit https://github.com/adafruit/Adafruit_nRF52_Arduino для arduino IDE
Залил через raspberry pi загрузчик от adafruit. После этого можно шить наш МК через обычный переходник USB-UART предварительно законектив ножку DFU на GND.
У adafruit прицепом ставится куча примеров на все случаи. Пины соответсвуют.
До этого быстро набросал для пробы макетку sim800c. Подключил её к nrf52... всё работает как часы.
А кто-то пробовал принимать этим модулем с nrf24l01? Стандартными средствами(библиотеками работает), но приходит какая-то белеберда, вместо нужных цифр (
P/S Желтая бумага с али лучше всего переносит тонер, самый идеальный и простой вариант. https://ru.aliexpress.com/item/10PCS-A4-Sheets-Heat-Toner-Transfer-Paper-For-DIY-PCB-Electronic-Prototype-Mak/32571735391.html?spm=a2g0s.13010208.99999999.262.1nTfoS
А кто-то пробовал принимать этим модулем с nrf24l01? Стандартными средствами(библиотеками работает), но приходит какая-то белеберда, вместо нужных цифр (
Насчет приема не в курсе, но в обратную сторону пишут, что работает. Сам я не проверял, т.к. в данный момент интереснее покопаться с самим процессором. Радио пока на потом оставил.
мужики, мне на фоне вас стыдно. Я лугить не умею, поэтому подошел к вопросу по "рабоче-крестьянски" :)
Взял обычную одностороннюю макетку с шагом 2.54мм, напаял в нее два ряда по 7 стоек из проволоки 0.5мм на расстоянии 7 рядов друг от друга. Между этими рядами вставил модуль "брюшком" вверх - так паять удобнее, да и выводы подписаны. Стойки точно вошли в полукруглые выемки контактов. После этого обрезал проволоку по высоте модуля и запаял. Получилось надежно и вполне аккуратно(сорри за качество фото):
По краям поставил обычные гребенки контактов. Из нижнего ряда вывел только P0.06 P0.07 P0.08 для RX TX - три штуки, потому что не знал точно, какие надо - в разных источниках интернета указаны разные пины под Сериал. В итоге оказались 6 и 8 - как на картинке a5021, только при подключении UART на основе BMP почему-то RX TX оказываются наоборот.
Конечно, так я получил вывод только SWDIO, SWCLK. питания и RX|TX. а из GPIO - лишь каждого второго контакта - но для тестов мне пока хватит, а потом, может, и ЛУГ освою :)
Программатор сделал из платки STM32F103CBT6 "blue pill", как описано выше по ссылке, что давал a5021. Тоже не без проблем - позже может распишу - но в итоге получилось. Модуль прошивается, светодиодом мигает на 12 ноге (13-ю я не вывел, как можно видеть на фото).
Теперь интересно радио освоить - по мне так главный интерес этого модуля все же связзь, микроконтроллеров-то и без него навалом. Spa-sam - я не вполне понял из ваших сообщений - вы передавать-принимать уже пробовали? Можно подробностей - что за протокол, какой девайс на другой стороне, откуда брали примеры кода?
А вот это, как раз, в данном случае не имеет никакого значения. Главное получить более-менее удобный доступ к функционалу. Преимущество вашего варината в том, что он простой и доступный. Я считаю, что вышло неплохо.
Мож это на BMP наоборот? Распиновка ардуиновских плат зависит от содержимого массива g_ADigitalPinMap[], который определен в файле variant.cpp. Этот файл для каждой платы свой, но для той же ReadBear Blend2 TX и RX определены так:
Кстати сказать, назначение ног блокам периферии у nRF52832 весьма гибкое. Не знаю можно ли абсолютно любой периферии назначать абсолютно любые ноги, но для того же UART-а это практически так:
Какие числа в эти регистры запишешь, такие ноги и будут обслуживать соответствующие входы/выходы уарта.
Было бы интересно почитать.
На нордиковском форуме есть сообщения, подтверждающие возможность передачи данных между nRF52832 и nRF24. Код там вроде самый незамысловатый, примеры есть в SDK (папка examples\proprietary_rf\). Все, что начинается с аббревиатуры esb (Enchanced ShockBurst), имеет отношение к передаче данных в форматах NRF24. Подробнее на данный момент вряд ли могу рассказать, т.к. в этой части имею крайне поверхностные представления.
Распиновка ардуиновских плат зависит от содержимого массива g_ADigitalPinMap[], который определен в файле variant.cpp. Этот файл для каждой платы свой, но для той же ReadBear Blend2 TX и RX определены так:
Насколько я понял, этот файл определяет только номера пинов в среде ардуино, соответвующие "ногам" конкретной платы. На функции ног он никак не влияет.
В этом смысле для нашего модуля лучше бы подощел variant.cpp от "Generic NRF52" - там в массиве просто перечислены числа от 0 до 31, что, кмк, означает, что номера пинов в Ардуино будут соответсвовать выходам P0.xx с этим же номером
Generic NRF52 нам вообще не подходит и как раз по принчине того, что номера выводов перечислены сплошняком. С самого начала списка и не подходят, т.к. D0 и D1 не могут находиться на пинах P0.00 и P0.01 по той причине, что на последних висит кварц.
Продолжаю раскуривать потихоньку nRF5. На данный момент немного подразобрался с таким функционалом, как события и задания (EVENTS и TASKS). Фишка в том, что периферийные устройства в процессе функционирования генерируют разнообразные события (напр., изменение состояния пина, переполнение таймера, готовность результатов АЦП, опустошение буфера передачи и множество других). Данные события можно связывать с командами другим блокам периферии таким образом, что возникшее событие в одном периферийном блоке запусает задачу в другом блоке напрямую, т.е. без взаимодействия с центральным процессором.
Вот код, который моргает тремя светодиодами с частотой 1 герц, когда моргания каждого светодиода смещены по фазе на 120 градусов друг относительно друга:
Как можно видеть, процессор не выполняет никакого полезного кода в блоке loop(), тем не менее светики перемигиваются.
пины для подключения светиков -- P0.22, P0.23, P0.24
Работает это так: модуль часов реального времени RTC считает импульсы частотой 32768 герц. Три регистра сравнения этого модуля проинициализированы на 1/6 секунды, 1/3 и 1/2 соответственно. При совпадении значения счетчика с содержимым первого регистра сравнения, возникает событие, которое пинает первый пин, совпадение со вторым регистром пинает второй пин и с третьим разрядом все точно так же. Пины пинаются на переключение, т.е. был 0, стал 1 и наоборот. За одну секунду каждый пин включится и выключится по одному разу.
Насчет связи между NRF24L01 и NRF52832, на MySensors.Org пишут следующее:
Good news. Thanks to the work of @d00616 on making the ESB transport available, I'm getting very good range using the nRF52832 as a receiver and a pro mini with an inexpensive power amplified nRF24 as the sender, all at 2mbps.
Код при этом выкладывают такой:
Вроде бы простая процедура -- измерить температуру и считать ее значение в четвертях градуса из регистра NRF_TEMP->TEMP. Но не тут то было. Из-за довольно объемной эрраты, правильное считывание температуры должно выглядеть так:
Скетч измеряет температуру раз в пять секунд и выводит ее в консоль.
Есть у nRF52832 генератор случайных чисел -- штука простая и удобная. Далее привожу рецепт, как им можно пользоваться.
Для запуска генератора всего и нужно -- проинициализировать регистр вот таким образом:
После того, как генератор стартовал, в регистре результата NRF_RNG->VALUE в определенном темпе начинают появляться значения случайных чисел из диапазона 0..255 (8 бит). В принципе, генератору все равно, считываются эти значения или нет, новые значения замещают старые через определенные промежутки времени.
Момент, когда генерация нового случайного числа завершена, отображается записью единицы в NRF_RNG->EVENTS_VALRDY. Как только это произошло -- можно забирать случайное число из регистра результата. Для того, чтобы отслеживать появление следующего случайного числа, флажок имеет смысл каждый раз сбросывать обратно в ноль.
Когда генератор больше не нужен, имеет смысл его выключить:
В идеале, генератор случайных чисел должен обеспечивать дискретное равномерное распределение результатов. Так как идеального ничего нет, генератор случайных числе у nRF52832 имеет два режима -- быстрый и медленный. Быстрая генерация обеспечивает большее отклонение от равномерного распределения, медленная -- меньшее. Для большинства применений быстрой генерации должно быть достаточно. Если числа должны быть случайными "по максимуму", то есть смысл задействовать медленный режим. Пу умолчанию генератор включается в быстром режиме. Если нужно перевести его в медленный, значение регистра конфигурации надлежит сделать равным единице:
В быстром режиме генератор выдает результат в среднем за 30 микросекунд, в медленном за 120 микросекунд.
Вот скетч, демонстрирующий работу генератора случайных чисел:
В принципе, как управляться с генератором показано в строках 25-31. В цикле из генератора извлекаются восемь случайных байт, которые сохраняются в массиве r8_arr. Почти весь остальной код служит цели визуализации результатов -- массив распечатывается сначала поэлементно, потом в виде четырех шестнадцатибитных значений и в конце в виде двух тридцатидвухбитных значений, типа такого:
===============================
RND-0000006691: 57 b8 bd bd ef a4 b2 6b; b857 bdbd a4ef 6bb2; bdbdb857 6bb2a4ef
RND-0000006692: c5 8e fc 45 79 91 56 c0; 8ec5 45fc 9179 c056; 45fc8ec5 c0569179
RND-0000006693: 58 2e eb f8 ce 59 88 1e; 2e58 f8eb 59ce 1e88; f8eb2e58 1e8859ce
RND-0000006694: 0f ec b0 1a eb e1 24 80; ec0f 1ab0 e1eb 8024; 1ab0ec0f 8024e1eb
RND-0000006695: bb 01 21 92 49 63 9d 21; 01bb 9221 6349 219d; 922101bb 219d6349
RND-0000006696: d5 46 ed ed bd a2 c0 aa; 46d5 eded a2bd aac0; eded46d5 aac0a2bd
RND-0000006697: b2 97 92 39 15 6e 02 40; 97b2 3992 6e15 4002; 399297b2 40026e15
===============================
Нашел на гитхабе простые, понятные и наглядные примеры использования периферии nRF5. В принципе, можно сразу затягивать в Arduino IDE с минимальными правками. Для разбирательств, как оно работает -- самое оно.
В инстаграмме попалось:
Дошла очередь до таймеров. Их у nRF52832 пять. Таймеры весьма специфичные, если их сравнивать с AVR/PIC/STM32/MSP430. Первое, что бросается в глаза -- это отсутствие прямого доступа к счетчику. Счетчик наличествует, но ни считать из него, ни записать в него ничего нельзя. Можно только сбросить в ноль или через захват скопировать текущее значение в регистр захвата, откуда уже, собственно, прочесть.
Чтобы запустить любой таймер в скетче, нужно выполнить операцию присваивания вида:
где "x" может быть цифрой от 0 до 4, что соответствует номеру таймера.
После запуска таймера в конфигурации по умолчанию, счетчик начнет увеличивать свое значение на 1 через каждую микросекунду, пока не переполнится на значении 2^16=65536 (т.е. счетик 16-битный). После переполнения счетчик вновь начинает с нуля и цикл счета повторяется. Вызывать прерывание по переполнению счетчика таймеры не умеют, что опять сильно их отличает от таймеров других МК.
Осмысленно использовать таймер в таком режиме, на мой взгляд, можно только одним образом -- измерять время между событиями. Перед началом измерений следует сделать один захват счетчика, а после -- другой. Разница в значениях будет соответствовать измеренному времени в микросекундах. Нижеследующий пример измеряет, сколько длится вызов delay(10) на самом деле:
Инструкция в строке 08 инициирует захват счетчика в регистр захвата 0, а инструкция в строке 10 в регистр 1. Разница между значениями в регистрах захвата 1 и 0, как раз и равняется времени, затраченному на вызов функции в строке 09:
===
Elapsed time = 10042uS
Elapsed time = 10043uS
Elapsed time = 10043uS
===
Число регистров захвата у разных таймеров разное. TIMER0..TIMER2 имеют четыре регистра, а TIMER3 и TIMER4 шесть.
Измерения в вышеприведенном примере хорошо работают до тех пор, пока измеряемый промежуток не превышает 65536 микросекунд. Все, что дольше измеряться не будет. Как быть? Один из вариантов -- увеличить разрядность счетчика. В отличии от меги и прочих распространенных МК, у nRF52832 разрядность можно выбирать. 8, 16, 24 и 32 бит -- такую разрядность могут иметь регистры любого из пяти имеющихся таймеров.
За разрядность у нордика отвечает регистр NRF_TIMERx->BITMODE. Записывая туда значния 0, 1, 2 и 3, можно делать таймер 16-и, 8-и, 24-х и 32-х разрядным. Так, если к предыдущему скетчу дописать перед запуском таймера в функции setup() строчку
то таймер станет 32-разрядным и вышеописанным способом можно считать промежутки времени до 71,5 секунд с разрешением 1мкс. Правда, чтобы скетч работал правильно, нужно еще изменить приведение типа в строке 13 с uint16_t на uint32_t .
Другим способом использования таймера является режим сравнения. Регистры NRF_TIMERx->СC[y] называются регистрами захвата/сравнения потому, что помимо захвата, рассмотренного в предыдущем примере, они могут использоваться и для сравнения значений в них записанных с содержимым счетчика. В принципе, логика тут простая: записываем в CC[x] некое число и как только счетчик таймера становится этому числу равен, генерируется событие. Так как число регистров сравнения четыре или шесть в зависимости от используемого таймера, то можно регистрировать до четырех или шести событий за один период счета.
Несколько раньше в этой теме я писал, что в функционале блоков периферии присутствуют такие понятия, как события и задания (EVENTS и TASKS). Есть и связывающий оба этих понятия функционал под названием "сочетания" (SHORTCUTS или SHORTS). Применительно к таймерам, "сочетания" могут быть использованы для сброса таймера или его останова по событию, скажем, совпадения. Например, если в регистр сравнения занести число 100, а в регистр "сочетаний" внести задание "очистка по совпадению", то счетчик будет считать до 100, сбрасываться и так по кругу. Если активировать "сочетание" "остановка по совпадению", то счетчик один раз досчитает до ста и остановится. Вот примеры, как можно использовать эти сочетания.
1) Blink на таймере. а)Значение регистра сравнения выбирается равным 500 тысяч. Этим обеспечивается выставления флага "совпадение" через 0.5 секунды после начала счета. б) Конфигуригурируется "сочетание" "очистка по свопадению", что обеспечивает выставление флага через каждые 0.5 секунды.
2) delay() на таймере. Функция принимает значение задержки в миллисекундах. Это число умножается на 1000 и заносится в регистр сравнения таймера. Конфигурируются "сочетания" "остановка при совпадении" и "очистка при совпадении", т.е. одно событие запускает две задачи. Работа функции иллюстрируется на все том же Blink-е.
Твиттер принес ссылку на свежую статью, как хакнуть фитнесс-трекер X9. Это бы не было интересно, когда бы сей трекер ни был сделан на nRF52832, а хакали его с помощью Arduino IDE и библиотеки Sandeep Mistry's Nordic ArduinoCore.
Из прилагаемых к статье скетчей, помимо прочего, есть и пример передачи данных через BLE. Сам я успел просмотреть его лишь по диагонали, но как мне показалось, в качестве учебного пособия его вполне можно использовать.
a5021. сорри за молчание, неделю отсутсвовал. Спасибо, что делитесь опытом. Почитаю вечером
Запустил я скетч, который упоминал в сообщении №30 и что касается блютуза в версии BLE то оно вполне себе работает. С телефоном общается.
Попробовал передавать данные через bleSerial.print() -- данные на телефон приходят.
Попутно, кстати, выяснил, что существует целый набор мобильного софта от нордика для блютузов, который можно найти в гугл плее поиском по сочетанию "NRF".
Исходник, упоминавшийся в предыдущем посте, был усечен до минимума и вот, что от него осталось:
Вот так на телефоне выглядит то, что он шлет через блютуз:
Вспомнил, что неожиданно много времени потратил на поиски SoftDevice v2.0.1, под которую заточена BLE Peripherial. Более свежие вполне доступны, а с поисками именно этой пришлось повозиться. На всякий случай привожу ссылку на страницу загрузки.
Вспомнил, что неожиданно много времени потратил на поиски SoftDevice v2.0.1, под которую заточена BLE Peripherial. Более свежие вполне доступны, а с поисками именно этой пришлось повозиться. На всякий случай привожу ссылку на страницу загрузки.
a5021 - что-то я упустил, в какой момент и для чего Вам понадобилось SoftDevice ? - по моим наивным представлением - мы используем либо среду Ардуино, либо СофтДевайс, но не обе вместе...
SoftDevice попал в поле моего зрения еще в ссобщении №30. На этом и возникло любопытство посмотреть, как оно работает. Вообще, возможность использования SoftDevice появляется после установки библиотеки sandeepmistry/arduino-nRF5.
Теперь, если мы хотим прошить SoftDevice в nRF52832, всего и надо -- выбрать "Записать Загрузчик" в данном меню.
Другое дело, что использовать каким-нибудь простым образом этот SoftDevice будет затруднительно, т.к. все манипуляции с ним придется самостоятельно описывать. Что бы не было так страшно, можно библиотеку BLEPeripherial установить. Там и примеры и возможность подглядеть, как оно все должно работать.
В общем и целом, Arduino и SoftDevice вполне себе совместимы, но есть ряд ограничений, которые придется учитывать при разработке приложений. Речь о совместном использовании ресурсов МК пользовательским приложением и самим SoftDevice. Я глубоко не копал, но уже налетел на некий конфликт в этой области. Попытка использовать аппаратный генератор случайных чисел без оглядки на SoftDevice привела к тому, что телефон перестал подключаться к E73, хотя в блютуз-окружении это устройство присутствует. Для выяснения подробностей, почему так, надо смотреть SoftDevice API и возможно у меня когда-то дойдет до этого дело, но на данный момент это не первоочередная цель.
SoftDevice попал в поле моего зрения еще в ссобщении №30. На этом и возникло любопытство посмотреть, как оно работает. Вообще, возможность использования SoftDevice появляется после установки библиотеки sandeepmistry/arduino-nRF5.
Так вы с BLE (например скетч №33) работаете через SoftDevice? - просто из кода это никак не просматривается.
Если да - не встречали ли способов работать с блютуз без СофтДевайса? Просто из беглого просмотра сообщений на разных форумах создалось впечатление, что СофтДевайс - предмет для погружения на много дней, на что я пока ради одной платы как-то не готов....
Ардуиновская библиотека BLEPeripherial -- это лишь обертка над SoftDevice. Все протоколы BLE 4.2 реализуются внутри SoftDevice. Можно ли без SoftDevice ? Можно. Для этого, правда, придется написать свой стек протоколов согласно спецификации BLE 4.2. У нордиков это заняло порядка нескольких лет. Как по мне, перспектива не вдохновляет.
Насчет "погружения на много дней", тут больше зависит от того, что требуется. Чтобы начать передавать данные с E73 никаких "многих дней" вобщем-то и не нужно. Смотрим примеры, соображаем, как это употрибить в своих целях. Вместе с BLEPeripherial идет кучка примеров, откуда вполне возможно вытянуть простой функционал без заныривания в теории. Хочется чего-то большего -- есть SDK, в которой много всего и достаточно подробно, но придется погружаться. Способов, чтобы и "любой каприз" и без погружения, я не знаю.
Способов, чтобы и "любой каприз" и без погружения, я не знаю.
Да это и понятно, что "без труда и рыбку съесть..." не выйдет. :) Буду разбираться.
Спасибо, что делитесь.
Подбираюсь потихоньку к радио. Документация у нордиков, конечно, вещь в себе. Можно перечитать непонятное место несколько раз, но к пониманию не приблизиться ни на миллиметр. Это я о том, как пытался понять, о чем идет речь в "23.5 Data whitening" -- разделе манулала, касающегося нюансов функционирования радио. Нордики скупы на пояснения. По их словам, блок радио у nRF52832 умеет отбеливать данные и де-отбеливать (попытался представить на бытовом уровне, что может скрываться за понятием де-отбеливания, но не смог). Дальше даю точный перевод, описывающий сие "отбеливание" в мануале нордика:
Отбеливающее слово генерируется с помощью полинома X^7 + X^4 + 1, которое затем складывается по модулю 2 ("исключающее ИЛИ") с пакетом данных, который должен быть отбелен или де-отбелен.
Кто-нибудь может вот так сходу пояснить, о чем бы это могло говориться?
Я, может, еще долго медитировал бы над этим "де-отбеливанием", пытаясь постигнуть его кармическую сущность, но решил сделать проще и почитать, что об этом пишут другие производители радио-чипов. И вот у SiliconLabs попалось совершенно доходчивое описание, что же сам процесс отбеливания означает и для чего он нужен.
Выяснилось следующее: дешевые цифровые передатчики и приемники часто склонны к рассинхронизации при приемопередаче длинных последовательностей, состоящих из одного и того же логического уровня. Типа, десять единиц или двадцать нолей подряд. Из-за нестабильности задающего генератора, приемник может "увидеть" лишний разряд или, наоборот, пропустить значащий. В ситуации, если уровни меняются часто, даже нестабильный приемник умеет подстраиваться и синхронизироваться по самому потоку данных. Точнее, по переходам между логическими уровнями. И вот здесь умные головы додумались, что если на частую смену логических уровней в передаваемых данных рассчитывать нельзя, то можно им искусственно придать такое качество, применив математическую обработку данных перед отправкой. Отбеливание здесь видимо и означает, что с помощью математики, передаваемым данным придаются свойства белого шума, т.е. переходы из нуля в единицу и обатно обретают некоторую равномерность.
Теперь о том, какой смысл это "отбеливание" может иметь в практическом применении. Насколько я в курсе, в BLE оно используется по требованию самого стандарта и тут выбора никакого нет. Модули NRF24L01, напротив, про "отбеливание" ничего не знают и аппаратно его не умеют. Таким образом, если возникает нужда связывать NRF24 и NRF52, отбеливание у последних нужно выключать в обязательном порядке. И наоборот, при организации связи на одних только NRF52 отбеливание лучше использовать, тем более, что оно полностью аппаратное и никаких доп. расходов не влечет.
Здесь попутно возникла мысль про то, что, отбеливание хорошо бы применять и в случае, когда используется связь вида NRF24L01 <-> NRF24L01 или NRF24L01 <-> NRF52832. Когда нет возможности использовать аппаратную фичу, можно и в софте изобразить то же самое. По идее, это должно улучшать качество связи.
NRF52 может получать тактирование от внутреннего генератора или от внешнего кварца. В принципе, то же самое можно сказать и про мегу, но у этих МК имеется некоторая разница в подходах. У меги за источник тактового сигнала отвечают фьюзы и только с их помощью можно задать будет ли МК работать от кварца или внутреннего генератора. В случае с NRF52, МК всегда включается на внутреннем генераторе, а задействовать ли более стабильный внешний источник, решает программист, сочиняющий управляющую программу. Есть у NRF52 еще и генератор низкой частоты, от которого тактируются счетчики реального времени (RTC). Можно с их помощью отсчитывать время на глазок, если в качестве опорного используется внутренний генератор или более точно, когда задействован кварц. Казалось бы, ну что за ерунда и почему бы не использовать кварцованные генераторы всегда? Однако на деле выходит, что кроме плюсов у кварцованных генераторов есть и минусы. Основных минусов два -- повышенное энергопотребение и большее время выхода на режим стабильной генерации. Для устройств с микропотреблением -- это критично. Отсюда идея, оперативно выбирать источник тактового сигнала в зависимости от потребностей, выглядит вполне здравой. Надо -- включили кварцевую стабилизацию, не надо -- выключили. Теперь о том, как это может выглядеть в коде. Вот пример функции, вызов которой может переключить тактирование с внутренних на внешние генераторы:
Ну а это скетч целиком с выводом в консоль результатов:
Для проведения экспериментов по организации радиосвязи между nRF52832 и nRF24L01+, помимо имеющейся E73, по идее, необходимо еще некое устройство, куда бы можно было подключать nRF24L01+. Например, ардуино уно/про-мини или что-то наподобие. С другой стороны, ничто не мешает подключить nRF24L01+ непосредственно к E73, благо последняя -- это тоже ардуино, только в своей ипостаси. В этом случае эксперименты при радиобмене будут проводиться между компонентами одного устройства -- встроенным радио и подключаемым. Так оно и компактнее и писанины меньше.
nRF24L01+ имеет интерфейс подключения SPI, стало быть, на E73 нужно настроить один из имеющихся там преиферийных блоков SPI (SPI0-SPI3). Весьма кстати, что почти весь нужный здесь функционал обеспечивают настройки по умолчанию и по большому счету, нужно лишь распределить на какие ноги, какие сигналы выводить. Чтобы не вносить лишней путаницы, практичнее испльзовать сложившуюся практику подлкючения SPI-устройств, для чего "совместить" линии интерфейсов таким образом:
SPI E73 NRF24L01+
===============================
SS 10 (22) --- CSN 4
MOSI 11 (23) --- MOSI 6
MISO 12 (24) --- MISO 7
SCK 13 (25) --- SCK 5
===============================
9 (20) --- CE 3
8 (19) --- IRQ 8
в скобках у E73 приведена нумерация физических контактов. На всякий случай, распиновка nRF24L01+:
Теперь про инициализацию SPI у E73. Она незамысловата:
Дальше уже можно писать скетч опроса nRF24L01+ целиком:
Скетч считывает из nRF24L01+ первые десять регистров и выводит их содержимое в консоль. У меня получились вот такие значения:
===
NRF24L01+ REGISTERS: 0x8 0x3F 0x3 0x3 0x3 0x2 0xF 0xE 0x0 0x0
===
Если сравнить эти числа со значениями регистров по умолчанию из даташита, то они совпадают. Стало быть, SPI-интерфейс и висящий на нем внешний модуль работают нормально.
Немного о том, как использовать радио nRF52832 в режиме совместимости с NRF24L01+. Нюансы там имеются, но можно начать с самого простого. Определяем самые основные параметры, без которых совсем никак.
Скорость приема или передачи:
Два регистра, задающих формат пакета:
Адрес, используемый в радиообмене:
Параметры расчета контрольных сумм:
Вышеприведенный код -- есть некий минимум для nRF52, чтобы начать налаживать связь с nRF24.
Следующее пояснение будет о том, как блок радио у nRF52 обращается с принятыми данными и данными для отправки. Все, что блок радио должен знать о данных -- это указатель на буфер (массив) в памяти. Указатель -- это регистр, в который должн быть записан 32-битный адрес буфера. Например, так:
Как только рабочий буфер определен, блок радио может обращаться к нему напрямую, через механизм прямого доступа к памяти (DMA). Центральный процессор в этом не участвует и на такие обращения никак не отвлекается. С точки зрения выполняющейся программы, данные из переменной сами улетают в эфир или, наоборот, из эфира прилетают прямиком в переменную (буфер).
В принципе, буфер может быть переменной любого типа, массивом, структурой и т.п. Здесь лишь надлежит учесть, что первые два байта являются служебными и к передаваемым данным не относятся. Первый байт буфера содержит размер пакета данных в байтах, второй -- уникальный идентификатор этого пакета. Сам пакет данных начинается с третьего байта. В этом смысле, удобно представить буфер структурой вида:
С точки зрения организации связи между nRF52 и nRF24, максимальный объем передаваемых данных за раз не может превышать 32 байта. Это ограничение проистекает из характеристик nRF24L01+ (в то время, как два модуля nRF52 вполне могли бы пересылать друг-другу пакеты и существенно большего размера).
Массив pl[] в данном примере, тем не менее, имеет размер на один байт больший. Такой размер выбран на тот случай, если пейлоад представляет собой ASCIIZ-строку. Такая ситуация может возникать, если в буфер передачи напрямую печатать, например, с помощью функции sprintf(). Вывели строку через sprintf(), дернули передачу и строка улетела в эфир. Удобно.
Еще небольшое уточнение по первым двум байтам буфера. В поле len значащими являются младшие шесть битов. Это не удивительно, т.к. числа больше 32 туда записывать никакого смысла нет. В поле id значащих битов и того меньше -- только три. Младший бит там -- это флаг NO_ACK, а другие два бита -- это уникальный идентификатор пакета. Данный идентификатор используется главным образом при разрешении двусмысленных ситуаций, когда последовательно приняты два пакета с одинаковым содержимым. Если id у этих пакетов разные, то и пакеты, стало быть, уникальные. Если id одинаковые, то дубликат пакета нужно отбросить.
Прежде, чем начинать передачу данных между nRF52832 и nRF24L01+, надлежит сторону nRF24L01+ так же сконфигурировать в совместимый режим. Это достигается записью одного регистра в случае работы на передачу и записью значений в два регистра, если модуль должен работать на прием. В обоих случаях надо разрешить переменную длину пакета передаваемых данных:
Приемнику следует разрешить прием таких пакетов:
Трансивер у nRF24L01+ включается установкой первого бита (Power Control) в регистре CONFIG:
Нулевой бит в этом регистре отвечат за режим приема (бит установлен) или передачи (бит сброшен). На примере выше, трансивер включается в режиме передачи.
Не смотря на то, что трансивер включен, блок радио еще не работает. За включение радио отвечает вывод CE модуля. Переключение его из низкого положения в высокое приводит к тому, что модуль начинает передавать в эфир данные (если они были подготовлены заранее) или принимать их оттуда.
Оправку данных в модуль для передаче и извлечение данных из модуля при приеме имеет смысл оформить в виде отдельных процедур:
При поднятом уровне CE данные в эфир выдаются сразу же после окончания работы функции nrf24_write_payload().
Извлекающая принятые данные из модуля функция nrf24_read_payload() нуждается в параметре len, указывающем сколько байт должно быть получено. Уточнить объем принятых данных можно вызовом следущей функции:
Наконец два скетча, демонстрирующих работу модулей на прием и передачу. В первом nRF24L01+ передает, nRF52832 получает:
Во втором передача ведется в обратном направлении -- nRF52832 передает, nRF24L01+ получает:
Работоспособность обоих скетчей проверена. Результаты удовлетворительные.
nRF52832 умеет замерять уровень принимаемого с эфира сигнала. Такие измерения можно делать в любое время и любое количество раз, при условии, что радио находится в режиме приема. Измерение запускается просто:
и по времени занимает 0.25 микросекунды. По окончанию замера, из регистра NRF_RADIO->RSSISAMPLE можно забирать результат. Результат в регистре представлен без знака, хотя значение соответствует отрицательной величине. Т.е. если из регистра прочитано, к примеру, значение 86, то это означает, что сила измеряемого сигнала была равна -86dBm.
Измерения силы принимаемого сигнала можно автоматизировать через регистр сочетаний:
Данное выражение предписывает блоку радио производить измерения всякий раз, когда наступает событие "ADDRESS MATCH", т.е. непосредственно в процессе приема адресованного данному приемнику пакета. Это довольно удобно.
Работоспособность обоих скетчей проверена. Результаты удовлетворительные.
что с дальностью?
Это не самый простой вопрос, чтобы ответить на него односложно. С дальностью у nRF52 просто обязано все быть лучше, чем у nRF24. Большая мощность и лучшая чувствительность очень к тому располагают.
У меня по жилищу время от времени летают разные байтики через nRF24. Раньше о качестве связи можно было судить лишь на уровне проходит/непроходит. Теперь можно добавить кой-какую визуализацию:
RX: 8115392A16098060 -79dBm
RX: 8115392A16098060 -79dBm
RX: 8115392A16098060 -79dBm
RX: 8315392A16 -80dBm
RX: 8315392A16 -80dBm
RX: 8315392A16 -80dBm
RX: 8515392A16 -80dBm
RX: 8515392A16 -79dBm
RX: 8515392A16 -79dBm
RX: 4715392A16 -78dBm
RX: 4715392A16 -78dBm
RX: 4715392A16 -78dBm
RX: 4915392A16 -77dBm
RX: 4915392A16 -77dBm
RX: 4915392A16 -77dBm
RX: 4B15392A16 -81dBm
RX: 4B15392A16 -81dBm
RX: 4B15392A16 -81dBm
RX: 4D15392A16 -80dBm
RX: 4D15392A16 -80dBm
RX: 4D15392A16 -81dBm
RX: 4F15392A16 -78dBm
RX: 4F15392A16 -78dBm
RX: 4F15392A16 -78dBm
RX: 4115392A16098060 -83dBm
RX: 4115392A16098060 -83dBm
RX: 4115392A16098060 -83dBm
Передатчик (NRF24, 0dBm, 2mbit) и приемник (NRF52) находятся в разных помещениях и общаются через ж/б стену. По прямой между ними где-то 3,5м, плюс предметы интерьера, которые связи тоже не улучшают. Сеансы представляют собой тройную отправку пачки байтов через каждую минуту. Мощность сигнала гуляет. Конкретно на этом куске не видно, но когда сигнал ослабевает до -88дбм и ниже, начинаются выпадения из троек.
Для интереса можно будет попробовать включить на прием и nRF24 в параллель. Посмотреть, кто больше потеряет. Есть, правда, ощущение предопределенности этого эксперимента.
ps. кусок кода, который рисовал вышеприведенный журнал:
В примерах, которые я приводил до сего времени, интерфейс SPI у nRF52832 использовался в самом простом режиме, когда все действия с ним, по большому счету, сводятся к побайтовым записи и чтения регистра данных.
Во второй строке байт записывается в регистр данных для передачи, в пятой строке считывается из него после приема. По большому счету, все точно так же, как и на атмеге, разве что запись немного иная.
Разница в реализации SPI начинает проявляться, если у nRF52832 задействовать этот интерфейс в более продвинутом виде с использованием EasyDMA. EasyDMA здесь служит в качестве средства блоковой записи/чтения, когда данные поблочно выдаются в линию из буфера и так же поблочно считываются с линии в буфер.
Чтобы отличать простой режим от продвинутого, нордики один и тот же блок периферии обозвали по разному. Простой -- это SPI, а продвинутый -- SPIM или SPIS, в зависимости от того, мастер или слейв.
Инициализация периферийного блока SPIM во многом похожа на инициализацию SPI. Сначала распределяем ноги:
Скорость передачи:
А вот дальше возникает "разница". Необходимо указать, где находятся буферы и какими объемами писать/читать.
В данном случае чтение и запись предполагается производить блоками по 2 байта. Это связано с тем, что у NRF24L01, на примере работы с которым я и рассматриваю здесь функционал SPIM, большинство обменов -- двухбайтовые -- сначала передается номер регистра, а потом считывается или записывается значение.
К вышеприведенному коду стоит добавить, что переменные tx_buf и rx_buf глобальные и имеют формат простого массива.
Последнее действие -- включить интерфейс:
В этом месте я словил довольно увесистые подводные грабли, когда по легкомыслию записал в пробном коде
Лишь спустя довольно продолжительное время выяснилось, что единственное значение, которое можно записывать в NRF_SPIM0->ENABLE -- это число 7 и именно оно разрешает работу этого блока периферии. Все остальные значения работу запрещают. Неожиданный ход разработчиков чипа.
Немного о том, как происходит обмен: первым делом прижимается к земле линия CSN, что сообщает модулю NRF24L01, что даные, которые сейчас будут переданы через SPI, адресованы ему. Первый байт -- это номер регистра и действие с ним (запись/чтение), второй байт -- значение или пустышка в случае чтения. Синхронно NRF24L01 передает в обратном направлении свои данные. Вот так это может быть изображено в коде:
В качестве иллюстрации, как все вышеописанное работает, скетч, который раз в пять секунд считывает и печатает в последовательный порт содержимое тридцати регистров NRF24L01:
На приведенном в предыдущем сообщении примере довольно сложно увидеть какие-либо преимущества использования SPIM против SPI. Кода инициализации даже больше получается, что скорее минус, чем плюс. Однако, такое положение дел возникает только потому, что никакие продвинутые фичи еще не использовались и задача, по сути, подводилась к "предыдущему" решению, как иногда это принято в математике. Однако, более обширный функционал SPIM позволяет применять и новые, более интересные решения.
Как уже говорилось ранее, передачу данных через SPI предваряет понижение уровня на выводе CSN, а после того, как передача завершена, уровень этот нужно вернуть обратно в высокое состояние. Делать это приходится в коде всякий раз, когда случается передача:
И вот этот digitalWrite лично мне как-то действует на нервы. Хочется предпринять что-то, чтобы пин CSN автоматически понижался перед началом передачи и возвращался назад после ее окончания. Помнится пытался я придумать какую-нибудь реализацию этого механизма на STM32, но там мне не удалось выполнить это изящно. Зато удалось теперь на nRF52832 и вот каким образом:
1) У nRF52832 есть такой блок периферии, как GPIOTE. По сути, весь блок -- это надстройка над единственным пином, чтобы сделать этот пин событийно-управляемым, плюс умеющим самостоятельно генерировать события.
Событийно-управляемым -- это когда произошедшее в каком либо блоке периферии событие оказывает непосредственное влияние на состояние пина, причем, без участия ядра (ЦП). Закончился период преобразования АЦП, пин автоматически меняет полярность. Досчитал таймер до нужного значения, пин перещелкнулся. И тому подобное. Но здесь есть одно но: GPIOTE пином управлять может, но узнавать о событиях, произошедших где-то вне зоны его отвественности, нет. Для того, чтобы транслировать информацию о событиях между разными блоками и нужен п.2:
2) События между разными блоками периферии передаются через PPI -- Programmable peripheral interconnect -- матрицу коммутации периферийных устройств. Если мы хотим, чтобы какое-либо событие в одном блоке вызывало реакцию в другом, надо через элемент PPI соединить выход первого со входом второго. PPI здесь, если говорить условно, это что-то навроде веревки, за которую надо дергать или провод по которому бежит электрический сигнал.
Теперь собираем 1) и 2) вместе. Сначала инициализируем GPIOTE.
Строка 1 указывает, что используется регистр конфигурации элемена 0 блока GPIOTE. Всего таких элементов может быть 8.
Вторая строка предписывает, что пин работает, как выход. "Task" -- это исполнитель заданий, по задумке нордиков -- меняет состояние выхода по требованию со стороны. Бывает еще "Event" -- генератор событий в зависимости от состояния входа -- остылает требования другим блокам.
В строке 3 указывается, каким именно пином данный элемент GPIOTE управляет.
Строка 4 определяет, в каком состоянии должен пин находится изначально.
Стоит отметить, что с момента, когда пин "отдан" GPIOTE, обычным образом использовать его уже более невозможно.
У любого элемента блока GPIOTE есть несколько "входов", дергая за которые можно менять состояние пина. Выполнив такое присваивание:
пин будет переведен в низкое состояние, а при таком:
обратно в высокое.
Но дрыгание пином посредством манипуляций в коде -- это не совсем то, что в данном случае интересует. Нужно, чтобы пин дрыгался сам, исключительно от событий внути переиферии. Для этого нужно проложить связи через матрицу коммутаций PPI. В рамках этого примера нужно связать событие в блоке SPIM под названием "EVENTS_STARTED" с заданием элементу блока GPIOTE перевести пин в низкое состояние. В коде это будет выглядеть так:
После выполнения данного кода, всякий раз, когда будет отдаваться команда начать передачу по SPI (NRF_SPIM0->TASKS_START = 1;), пин CSN будет уходить вниз автоматически. Остается сделать то же самое, в обратном порядке, для завершения передачи. Блок SPIM, когда выполнил все задания, генерирует событие "EVENTS_END", которое и нужно "связать" с переводом CSN в высокое положение:
Последним штрихом, чтобы это все заработало, должно быть разрешение на работу каналов 0 и 1 PPI:
Наконец, пример, не использующий digitalWrite() для дерганья за CSN целиком: