Когда необходимо использовать блок radio строго определенным образом, сохраняя при этом соединение BLE, или когда нужно выполнить некую задачу, которая не должна быть прервана активностью radio, Вы можете настроить таймслот (timeslot). Таймслот это период времени от 100 мкс до 128 сек, в течение которого SoftDevice обеспечит для пользователя полный контроль над радиотрактом или другими периферийными устройствами.
Для экспериментов с таймслотами понадобится следующее:
• Keil 5.20 или более свежий. • Плата разработчика nRF52 DK (или nRF51 DK). • S132 SoftDevice (S130 для nRF51). • nRF5 SDK 11 или более свежий.
Приложение запрашивает таймслот, длительность которого может быть от 100 мкс до 100 мс. Более длинные таймслоты могут быть предоставлены путем запросов расширений (timeslot extension), максимум до 128 сек.
Тайслоты запрашиваются в пределах сессии, которая может состоять из нескольких таймслотов. Разные типы сессий и сценариев использования timeslot можно увидеть в примерах использования timeslot, которые находятся в Infocenter (см. также главу 10 "Multiprotocol support" руководства SoftDevice Specification [2]).
Когда запрашивается timeslot, приложение должно указать SoftDevice использовать high или normal приоритет для timeslot. Для большинства приложений во избежание конфликтов следует использовать приоритет normal. Если у Вас приложение, которое очень критично по времени выполнения кода, как например проприетарный radio-протокол или реализация OneWire [3], то используйте приоритет high.
Существует 2 типа запросов: выполнить как можно быстрее (earliest possible) и в течение заданного времени (given time). Тип earliest possible предназначен для однократных или реактивных событий, и он предоставляет timeslot так быстро, как это только возможно. Тип given time предназначен для повторяющихся событий, и он предоставляет таймслот через некоторое время после начала предыдущего таймслота.
Когда timeslot был предоставлен, приложение получает доступ к модулям RADIO, TIMER0, CCM, AAR и PPI (каналы 14-15) на время таймслота.
[События и действия для таймслотов]
Существуют следующие функции для использования таймслотов:
Параметр p_radio_signal_callback это указатель на функцию обратного вызова, которая будет принимать на входе сигнал NRF_RADIO_CALLBACK_SIGNAL_xxx от системы поддержки таймслота.
p_radio_signal_callback(NRF_RADIO_CALLBACK_SIGNAL_TYPE_START) будет вызвана, когда начнется формирование таймслота. Начиная с этого момента периферийные устройства NRF_RADIO и NRF_TIMER0 свободно доступны для приложения.
p_radio_signal_callback(NRF_RADIO_CALLBACK_SIGNAL_TYPE_TIMER0) будет вызвана всякий раз, когда происходит прерывание NRF_TIMER0.
p_radio_signal_callback(NRF_RADIO_CALLBACK_SIGNAL_TYPE_RADIO) будет вызвана всякий раз, когда произойдет прерывание NRF_RADIO.
Функция p_radio_signal_callback() будет вызвана с самым высоким приоритетом прерывания (ARM interrupt priority level 0). Подразумевается, что никакая из API-функций SoftDevice (начинающаяся с префикса sd_*) не может быть вызвана из тела p_radio_signal_callback().
Возвращаемые значения sd_radio_session_open:
NRF_ERROR_INVALID_ADDR указатель p_radio_signal_callback является недопустимым указателем на функцию. NRF_ERROR_BUSY Если сессия не может быть открыта. NRF_ERROR_INTERNAL Если новая сессия не может быть открыта из-за внутренней ошибки. NRF_SUCCESS Успешное открытие сессии.
Любой текущий таймслот radio будет завершен перед закрытием сессии. Если был запланирован таймслот radio, когда сессия закрыта, то этот таймслот будет отменен. Приложение не может считать сессию закрытой, пока не получит событие NRF_EVT_RADIO_SESSION_CLOSED.
Возвращаемые значения sd_radio_session_close:
NRF_ERROR_FORBIDDEN Если сессия не была открыта. NRF_ERROR_BUSY Если сессия в настоящий момент закрывается. NRF_SUCCESS Сессия была успешно закрыта.
Тип запроса определяется полем p_request->request_type, и может быть либо NRF_RADIO_REQ_TYPE_EARLIEST, либо NRF_RADIO_REQ_TYPE_NORMAL. Первый запрос в сессии всегда должен быть типа NRF_RADIO_REQ_TYPE_EARLIEST.
Для запросов с обычным приоритетом (NRF_RADIO_REQ_TYPE_NORMAL) время начала таймслота указывается полем p_request->distance_us относительно начала предыдущего таймслота.
Слишком малая величина p_request->distance_us приведет к событию NRF_EVT_RADIO_BLOCKED. К такому же событию приведут таймслоты, запланированные слишком близко друг к другу.
Для дополнительной информации по планированию, дистанции и длительности тамслотов см. SoftDevice Specification [2].
Если не найдена возможность организовать первый таймслот radio в течение 100 мс после вызова этой функции, то таймслот не будет запланирован, и будет отправлено событие NRF_EVT_RADIO_BLOCKED. При получении этого события приложение может попытаться снова запланировать первый таймслот radio.
Успешное планирование таймслота будет в случае запуска callback-функции nrf_radio_signal_callback_t с параметром NRF_RADIO_CALLBACK_SIGNAL_TYPE_START (см. выше описание функции sd_radio_session_open). Неудачный запрос приведет к генерации события NRF_EVT_RADIO_BLOCKED, см. описание событий SoC (NRF_SOC_EVTS).
Джиттер во времени запуска таймслотов radio будет +/- NRF_RADIO_START_JITTER_US мкс. У вызова nrf_radio_signal_callback_t(NRF_RADIO_CALLBACK_SIGNAL_TYPE_START) существует латентность относительно указанного начала таймслота radio, но это не влияет на реальное время начала таймслота.
NRF_TIMER0 сбрасывается в начале таймслота radio, и тактируется от частоты 1 МГц, вырабатываемой от высокочастоного источника тактов (16 МГц). Если p_request->hfclk_force_xtal == true, генерация высокой частоты гарантируется от внешнего кварцевого резонатора.
Во время таймслота radio код SoftDevice не обращается ни к периферийному устройству NRF_RADIO, ни к периферийному устройству NRF_TIMER0.
Параметры sd_radio_request:
[in] p_request Указатель на параметры запроса таймслота.
Возвращаемые значения sd_radio_request:
NRF_ERROR_FORBIDDEN Сессия не открыта, или сессия не в состоянии ожидания (не в IDLE). NRF_ERROR_INVALID_ADDR Если указатель p_request недопустимый. NRF_ERROR_INVALID_PARAM Если параметры p_request недопустимы. NRF_SUCCESS Запрос таймслота успешный.
Timeslot events. В дополнение к этим вызовам есть единственный обработчик сигнала, который вызывается через диспетчер событий системы (system event dispatcher), это соответствует следующим случаям (событиям):
NRF_EVT_RADIO_SESSION_IDLE - у сессии больше нет запланированных таймслотов. Если сработало это событие, то приложение завершит сессию. Обратите внимание, что в этот момент можно запросить новые таймслоты (подробнее об этом далее).
NRF_EVT_RADIO_SESSION_CLOSED - сессия закрыта и захваченные для неё ресурсы освобождены.
NRF_EVT_RADIO_BLOCKED - запрошенный таймслот запланировать не удалось, из-за конфликта с другими активностями в системе. Приложение должно запросить новый таймслот либо как можно раньше, либо в следующей normal-позиции.
NRF_EVT_RADIO_CANCELED - запланированный таймслот был отменен активностью с более высоким приоритетом. Приложение должно запросить новый таймслот.
NRF_EVT_RADIO_SIGNAL_CALLBACK_INVALID_RETURN - последнее значение, возвращенное обработчиком сигнала, содержит недопустимые параметры. В Вашем приложении должен сработать assert.
Timeslot signals. Адрес timeslot-обработчика передается как аргумент в функцию, которая открывает radio-сессию. Обработчик таймслота будет обслуживать следующие случаи:
NRF_RADIO_CALLBACK_SIGNAL_TYPE_START - запуск таймслота. В этот момент получает доступ к периферийным устройствам на длительность таймслота. Перед тем, как начать инициализировать свой таймслот, Вы должны использовать таймер (например TIMER0), который запускает событие 100 мкс около того до истечения таймслота. TIMER0 запускается автоматически с нуля изнутри SoftDevice в начале таймслота, и конфигурируется для тактирования от 1 МГц. Если Вам не удастся завершить работу до окончания таймслота, то приложение упадет в hardfault.
NRF_RADIO_CALLBACK_SIGNAL_TYPE_RADIO - прерывание Radio, подробнее см. описание 2.4GHz radio на сайте Infocenter.
NRF_RADIO_CALLBACK_SIGNAL_TYPE_TIMER0 - прерывание таймера. В этом месте корректно завершается работа таймера, который был вами настроен ранее. Здесь нужно выполнить деинициализацию и подготовиться к возврату активности SoftDevice. Также в этом месте также можно запросить следующий таймслот.
NRF_RADIO_CALLBACK_SIGNAL_TYPE_EXTEND_SUCCEDED - последнее расширение сессии выполнено успешно. Обычно в этом месте не нужно предпринимать никаких действий.
NRF_RADIO_CALLBACK_SIGNAL_TYPE_EXTEND_FAILED - последнее расширение сессии потерпело неудачу, попробуйте заново запланировать свой таймслот.
Обработчик таймслота возвратит некоторые параметры callback-а, которые используются в SoftDevice. Их использование мы рассмотрим позже.
NRF_RADIO_SIGNAL_CALLBACK_ACTION_NONE - Ничего не делать.
NRF_RADIO_SIGNAL_CALLBACK_ACTION_END - Сигнализирует о завершении текущего события выполняемого таймслота. SoftDevice может возобновить свои активности. Обычно используется для завершения свободного текущего таймслота.
NRF_RADIO_SIGNAL_CALLBACK_ACTION_REQUEST_AND_END - сигнализирует о событии завершения текущего таймслота, запрашивает новый таймслот перед сигнализацией о том, что SoftDevice может возобновить свои активности.
NRF_RADIO_SIGNAL_CALLBACK_ACTION_EXTEND - попытка расширения выполняющегося таймслота.
Обработчик таймслота выполняется с самым высоким уровнем приоритета прерывания 0, поэтому Вы не можете вызвать функции с более низким приоритетом.
[Настройка таймслота]
Код таймслота часто довольно большой, обычно его базовый уровень составляет порядка 150 строк. Однако код даже может быть значительно больше, когда Вы добавите в таймслот свой функционал. По этой причине лучше всего добавлять таймслоты в отдельном файле, а не в основной модуль main.c.
Дальнейшие описываемые шаги производятся в Keil µVision V5.34, с SDK v12.3.0 (nRF5_SDK_12.3.0_d7731ad), с использованием PCA10040 DK (чип nRF52832 QFAA).
1. Откройте проект, который Вы хотите изменить. В этом примере автор [1] использует в качестве основы проект ble_app_template из SDK (нужно открыть ble_app_template_pca10040_s132.uvprojx, который находится в папке ble_peripheral\ble_app_template\pca10040\s132\arm5_no_packs примеров SDK).
Это простейший пример периферийного устройства BLE (сервера), который обладает только базовым функционалом. Если сейчас проект скомпилировать (F7) и запрограммировать в память чипа (F8), то на плате DK начнет мигать светодиод LED1, сигнализируя о начале выдачи в эфир advertising-пакетов BLE. Можно открыть приложение nRF Connect [5] и подключиться к этому устройству по имени Nordic_Template. Светодиод LED1 начнет гореть непрерывно, и nRF Connect покажет базовые атрибуты устройства Nordic_Template.
2. Выполните правый клик на папке приложения (Application) и выберите Add New Item to Group 'Application'...
3. Выберите C File, дайте имя файла timeslot.c, кликните Add.
Далее нам нужно добавить в этот модуль некий код. В следующем примере кода будет запрос сессии и переключение состояния LED4 в начале каждого таймслота. Нам также надо поменять целевую ножку, которая будет переключаться, вызовом nrf_gpio_pin_toggle(n), в зависимости от используемого DK. Для зажигания светодиода на nRF51 DK (PCA10028) используйте ножку 24, для nRF52 DK (PCA10040) ножку 20.
Конфигурации таймслотов можно увидеть в функциях request_next_event_earliest(), configure_next_event_earliest() и configure_next_event_normal(). Здесь мы настраиваем таймслоты, которые используют нормальный приоритет и гарантированное использование кристаллов, что необходимо для использования radio. Обратите внимание, что мы настраиваем прерывание таймера непосредственно перед окончанием таймслота, чтобы приложение знало, что оно должно завершить активность таймслота.
/**@brief Обработчик сигналов таймслота (Timeslot signal handler)
*/
voidnrf_evt_signal_handler(uint32_t evt_id)
{
uint32_t err_code;
switch (evt_id)
{
case NRF_EVT_RADIO_SIGNAL_CALLBACK_INVALID_RETURN:
// Здесь не нужна никакая реализацияbreak;
case NRF_EVT_RADIO_SESSION_IDLE:
// Здесь не нужна никакая реализацияbreak;
case NRF_EVT_RADIO_SESSION_CLOSED:
// Здесь не нужна никакая реализация, session endedbreak;
case NRF_EVT_RADIO_BLOCKED:
// Провалимся дальше...case NRF_EVT_RADIO_CANCELED:
err_code = request_next_event_earliest();
APP_ERROR_CHECK(err_code);
break;
default:break;
}
}
/**@brief Обработчик событий таймслота (Timeslot event handler)
case NRF_RADIO_CALLBACK_SIGNAL_TYPE_RADIO:
signal_callback_return_param.params.request.p_next =NULL;
signal_callback_return_param.callback_action = NRF_RADIO_SIGNAL_CALLBACK_ACTION_NONE;
break;
case NRF_RADIO_CALLBACK_SIGNAL_TYPE_TIMER0:
// Прерывание таймера - делаем корректный shutdown - планируем следующий таймслот
configure_next_event_normal();
signal_callback_return_param.params.request.p_next =&m_timeslot_request;
signal_callback_return_param.callback_action = NRF_RADIO_SIGNAL_CALLBACK_ACTION_REQUEST_AND_END;
break;
case NRF_RADIO_CALLBACK_SIGNAL_TYPE_EXTEND_SUCCEEDED:
// Здесь не нужна никакая реализацияbreak;
case NRF_RADIO_CALLBACK_SIGNAL_TYPE_EXTEND_FAILED:
// Попытка запланировать новый таймслот
configure_next_event_earliest();
signal_callback_return_param.params.request.p_next =&m_timeslot_request;
signal_callback_return_param.callback_action = NRF_RADIO_SIGNAL_CALLBACK_ACTION_REQUEST_AND_END;
break;
default:// Здесь не нужна никакая реализацияbreak;
}
return (&signal_callback_return_param);
}
Теперь надо добавить путь поиска до созданного заголовка в проект.
4. Перейдите в меню Projects -> Options for Target (Alt+F7).
5. Выберите закладку C/C++, добавьте в Include Paths путь до файла заголовка timeslot.h. В нашем примере это папка ..\arm5_no_packs.
6. Добавьте в файл main.c подключение заголовка timeslot.h:
#include "timeslot.h"
7. Нужно добавить обработчик сигнала (signal handler) к диспетчеру системных событий (system event dispatcher). Для этого в тело подпрограммы диспетчера sys_evt_dispatch добавьте вызов nrf_evt_signal_handler (изначальный код может выглядеть немного иначе):
staticvoidsys_evt_dispatch(uint32_t sys_evt)
{
pstorage_sys_event_handler(sys_evt);
ble_advertising_on_sys_evt(sys_evt);
// Эта строка была добавлена:nrf_evt_signal_handler(sys_evt);
}
8. В тело функции main нужно добавить инициализацию таймслота непосредственно перед входом в бесконечный цикл main:
timeslot_sd_init();
Скомпилируйте и прошейте код проекта в свой DK. Убедитесь, что в DK запрограммирован S130/132 SoftDevice. Если все в порядке, то будет наблюдаться периодическое мигание LED4, и устройство будет выдавать пакеты радиооповещения (advertising) как Nordic_Template. Если подключиться к устройству (например, с помощью приложения nRF Correct), то это никак не повлияет на функциональность миганий LED4.
[Запрос расширения интервала]
Можно запросить расширение (extension) формируемого таймслота. Это расширение может быть больше 200 мкс, и не может быть кумулятивно больше чем 128 сек. Следующий пример показывает создание расширения интервала:
case NRF_RADIO_CALLBACK_SIGNAL_TYPE_TIMER0:
// Прерывание таймера - попытка увеличить (расширить) длительность таймслота:
signal_callback_return_param.params.extend.length_us = m_slot_length;
signal_callback_return_param.callback_action = NRF_RADIO_SIGNAL_CALLBACK_ACTION_EXTEND;
break;
case NRF_RADIO_CALLBACK_SIGNAL_TYPE_EXTEND_SUCCEEDED:
// Расширение было успешным, сброс таймера (конфигурация остается допустимой,// поскольку длительность слота та же самая):
NRF_TIMER0->TASKS_CLEAR =1;
break;
case NRF_RADIO_CALLBACK_SIGNAL_TYPE_EXTEND_FAILED:
// Неудача расширения длительности, планирование нового таймслота// в самое ближайшее (earliest) время:
configure_next_event_earliest();
signal_callback_return_param.params.request.p_next =&m_timeslot_request;
signal_callback_return_param.callback_action = NRF_RADIO_SIGNAL_CALLBACK_ACTION_REQUEST_AND_END;
break;
[Пример чтения датчика температуры DS18B20]
Для примера использовался код из проекта [6]. С помощью тайм-слотов реализация протокола оказалась довольно простой. Код чтения датчика добавляется в case-ветку NRF_RADIO_CALLBACK_SIGNAL_TYPE_START обработчика событий таймслота radio_callback (добавленный код показан жирным шрифтом):
/**@brief Обработчик событий таймслота (Timeslot event handler)
Примечание: необязательно делать вызов функции ds18b20_setResolution(12), потому что по умолчанию DS18B20 работает с разрешающей способностью измерения температуры в 12 бит.
Отладочный вывод в RTT результатов чтения DS18B20:
[Ссылки]
1. Setting up the Timeslot API site:devzone.nordicsemi.com. 2. SoftDevice Specification S132 SoftDevice v3.0 site:infocenter.nordicsemi.com. 3. iButton: описание протокола, электронный замок на ключах-таблетках. 4. Running micro-ESB concurrently with BLE site:devzone.nordicsemi.com. 5. nRF Connect site:nordicsemi.com. 6. sigurdnev / Nordic-DS18B20 site:github.com. 7. 220209nRF5x-timeslot-OneWire-sdk12_3_0.zip - примеры кода с использованием Timeslot API и реализацией OneWire на nRF5x.