Первое знакомство с nRF52832, его оригинальным SDK и средой разработки SEGGER. Мигалка светодиодом Bluetooth

nRF52832 популярный и доступный микроконтроллер, однако информации по нему на русском совсем немного, да и в зарубежных интернетах не до жиру. Спасает Nordic DevZone и Google. В Ардуино есть поддержка данного МК, но для взрослых проектов обычно используют поддержку производителя.

В данной статье я хочу рассказать свой подход к работе с данным МК. Небольшое предупреждение: я бы не советовал брать nRF52832 как первый в своей жизни МК, если вы неплохо знаете STM32 (или другой ARM МК) то вам будет проще работать с nRF52832. Я бы даже советовал для перехода с восьмибитных МК на nRF52832 освоить STM32 как первый в своей жизни ARM МК.

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

Вступление

Не стану подробно рассматривать установку SEGGER Embedded Studio. Скачивается с официального сайта и устанавливается (в пути к корневой папке не должно быть кириллицы в названиях папок! ), для некоммерческого использования с МК фирмы Nordic бесплатна, сейчас даже не требует регистрации и получения лицензии. Так же скачиваем SDK для nRF52832, я использую версию 17.1.0. Автоматически выбраны для скачивания несколько SoftDevice. Создаем папку в которой будем работать с SDK. Важно, чтобы путь к папке и название папки не содержали кириллицы, иначе проект не будет компилироваться. В рабочую папку помещаем скаченный архив и «распаковываем здесь». В папке появляется несколько архивов, из них «распаковываем здесь» архив nRF5_SDK_17.1.0_ddde560. С этого момента можно приступать к работе.

Аппаратная часть

Мы будем работать с примерами для отладочных плат, поэтому можно использовать готовую плату (например pca10040) или сделать свою как это сделал я на базе модуля с AliExpress

. В таком случае потребуется подключить 4 светодиода и 5 кнопок, SWD интерфейс и UART для отладки.

// LEDs definitions for PCA10040
#define LEDS_NUMBER    4

#define LED_START      17
#define LED_1          17
#define LED_2          18
#define LED_3          19
#define LED_4          20
#define LED_STOP       20

#define BUTTONS_NUMBER 4

#define BUTTON_START   13
#define BUTTON_1       13
#define BUTTON_2       14
#define BUTTON_3       15
#define BUTTON_4       16
#define BUTTON_STOP    16

#define RX_PIN_NUMBER  8
#define TX_PIN_NUMBER  6

Кнопки подтянуты к »+» питания и при нажатии замыкаются на землю. Пятая кнопка — RESET подключается к ноге P0.21. Примеры с Bluetooth не будут работать без кнопок, а если ошибиться и после запуска МК будет переходить в сон, то прошить его без кнопки RESET будет невозможно. Для исключения зависания при работе с UART лучше подтянуть RX к »+» питания.

В качестве программатора применяю J-Link, сделанный из BluePill. Инструкцию по сборке можно найти в Google.

Проверяем

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

Для проверки собираем и запускаем пример мигалки светодиодом. Надо открыть файл проекта по очень сложному пути в папках SDK. (Эту тайну я познал благодаря видео на YouTube)

b87f44416fc252bbe6ce060f65885fb3.png

Желтая стрелка указывает на папки с проектом, красная — на тип отладочной платы, зеленая — на тип используемого SoftDevice. Синяя стрелка указывает на файл проекта, который необходимо открыть. Запустится SEGGER. Нажимаем Build → Build Solution, затем Debug → Go. После записи программы в МК запускаем пример и светодиоды должны весело замигать

Начинаем начинать!

Хоть некоторые считают, что проекты надо собирать самому, но в данном случае количество библиотек в SDK огромно, и те из них, которые работают с BLE для меня представляют черный ящик, да и исходники SoftDevice закрыты. После очень долгих переделок проекта главной парадигмой программирования nRF52832 для меня стало выбрать наиболее подходящий пример и делать из него. Я хочу рассказать как сделать BLE UART устройство.

В среде радиолюбителей широко распространены Bluetooth UART модули вроде HC-05, HM-10 и другой зоопарк китайских поделок. Для этих модулей уже созданы Android приложения, да и работа в терминале удобна и проста. Поэтому я приведу пример мигалки светодиодом, управляемой через Serial Bluetooth Terminal.

b0bcafffafde0d65d94c6d08686bd5e8.png

Откроем папку ble_peripherial, и скопируем папку с примером ble_app_uart в созданную ранее папку (показана стрелкой). Это все необходимо чтобы уровень корня проекта не менялся и не терялась связь с библиотеками. Открываем и запускаем наш подопытный проект. Можно убедиться в работоспособности и подключить USB-UART преобразователь и соединиться с устройством в программе Serial Bluetooth Terminal с телефона.

d78405f73da27fb404074bf32e63bfc8.jpg

А вот при попытке соединиться с устройством в меню Bluetooth телефона вас постигнет первое серьезное разочарование. Данный пример не поддерживает соединение.

20d23b9824d4f9da8ba14ae4e324d460.jpg

Решение этой проблемы достойно отдельной статьи. Я не стану описывать как я в данный проект добавлял поддержку соединения, потому что простым и верным решением будет добавление поддержки ble_nus в пример, поддерживающий подключение.

Можно уже на данном этапе почувствовать себя крутым программистом и поменять имя устройстваМожно уже на данном этапе почувствовать себя крутым программистом и поменять имя устройства

Начнем с мигалки светодиодом на аппаратном таймере. В файле main (в котором очень много строк и можно заблудиться), добавляем макрос для создания таймера:

APP_TIMER_DEF (m_led_timer_id);

Находим функцию static void timers_init(void) и добавляем в нее создание таймера, функция будет выглядеть так:

static void timers_init(void)
{
    ret_code_t err_code = app_timer_init();
    APP_ERROR_CHECK(err_code);

    err_code = app_timer_create(&m_led_timer_id, APP_TIMER_MODE_REPEATED, timer_led_event_handler);
    APP_ERROR_CHECK(err_code);
}

И создадим две функции, одна запускает таймер, вторая является обработчиком прерывания:

/**@brief Function for initializing the timer module.
 */
static void timers_start(void)
{
    ret_code_t err_code;
  
    err_code =  app_timer_start(m_led_timer_id, APP_TIMER_TICKS(500), NULL);
    APP_ERROR_CHECK(err_code); 
}

void timer_led_event_handler(void* p_context)
{
    bsp_board_led_invert(3);
}

Остается вызвать функцию timers_start(); перед переходом МК в бесконечный цикл, в котором МК уходит в сон и просыпается по прерываниям. Запускаем и четвертый светодиод начинает мигать с периодом 1 секунда.

Теперь добавим работу с BLE. Заставим при мигании светодиодом еще и отправлять данные в BLE терминал. Для этого немного доработаем функцию обработчика прерывания:

void timer_led_event_handler(void* p_context)
{   
    ret_code_t err_code;
    uint8_t buff [20];
    static uint8_t count;

    bsp_board_led_invert(3);

    sprintf (buff, "led_invert %d\n", count);

    uint16_t length = strlen (buff);
                        err_code = ble_nus_data_send(&m_nus, buff, &length, m_conn_handle);
                        if ((err_code != NRF_ERROR_INVALID_STATE) &&
                            (err_code != NRF_ERROR_RESOURCES) &&
                            (err_code != NRF_ERROR_NOT_FOUND))
                        {
                            APP_ERROR_CHECK(err_code);
                        }

    count ++;
}

Подключаемся через терминал и видим, что имя устройства изменено и телефон принимает данные. (При изменении имени устройства могут возникнуть проблемы с подключением, если такое случается, то надо удалить данные о соединении Bluetooth и подключиться снова).

Вот он, первый успех в покорении Bluetooth!Вот он, первый успех в покорении Bluetooth!

Осталось сделать управление. Находим функцию обработки прерывания от ble_nus и вандалим ее! Заменим отправку данных по UART на управление аппаратным таймером:

static void nus_data_handler(ble_nus_evt_t * p_evt)
{

    if (p_evt->type == BLE_NUS_EVT_RX_DATA)
    {
        uint32_t err_code;

        NRF_LOG_DEBUG("Received data from BLE NUS. Setting App_Timer.");
        NRF_LOG_HEXDUMP_DEBUG(p_evt->params.rx_data.p_data, p_evt->params.rx_data.length);
        
        if (p_evt->params.rx_data.p_data[0] == '0')
        {
        bsp_board_led_invert(2);
        err_code =  app_timer_stop(m_led_timer_id);
        APP_ERROR_CHECK(err_code);
        }
        else if (p_evt->params.rx_data.p_data[0] == '1')
        {
        err_code =  app_timer_start(m_led_timer_id, APP_TIMER_TICKS(500), NULL);
        APP_ERROR_CHECK(err_code);
        }
    }
}

Теперь если отправить 0 через терминал, то таймер остановится, а если отправить 1, то снова запустится.

И вот он, настоящий триумф человека над машиной!И вот он, настоящий триумф человека над машиной!

Я делал устройство для работы с готовым приложением LK8000, и тут меня ждало второе разочарование: даже сделав устройство подключаемым, не удалось получить данные, потому что LK8000 сделан для работы с BLE UART модулями и у них другие сервисы и характеристики BLE, про это напишу позже.

Послесловие

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

Сергей Авдонин, схемотехник.

© Habrahabr.ru