STM Урок 105. NRF24L01. Передаём данные. Часть 1



Продолжаем изучение модулей беспроводной передачи данных — NRF24L01 (NRF24L01+).

В уроке 103 мы с ними основательно познакомились, научились читать и писать их регистры, читать и писать буфер, и убедились в том, что мы действительно это умеем, на практике.

Теперь нам предстоит следующая задача — передача данных по воздуху через данные модули. Они, собственно, для этого и созданы.

Та схема, с которой мы работали в уроке 103, будет выступать у нас в качестве передатчика, а в качестве приёмника мы соберём другую схему, с которой мы познакомимся немного позднее. Сначала давайте пока займёмся передатчиком. Из проекта урока 103 с именем NRF24_REG мы создадим другой проект с именем NRF24_TX.

Откроем наш проект в генераторе проектов Cube MX и, абсолютно ничего в нём не трогая, сгенерируем проект для Keil. Откроем в нём проект, настроим программатор на автоперезагрузку, установим уровень оптимизации в 1, а также подключим к проекту файл нашей библиотеки NRF24.c.

Попробуем собрать наш проект. Если он нормально собрался, то зайдём в файл NRF24.c и сначала произведём там некоторые поправки.

Если модуль собирается работать в режиме передатчика, то адрес совпадающий с адресом передатчика должен находиться в канале обмена Pipe0, но никак ни в каком другом. Иначе передача будет мягко говоря затруднена. Это подтверждает вот такая блок-схема (нажмите на картинку для увеличения изображения)

 

 

Как мы можем видеть, адрес приёмника в каждом из 6 возможных передатчиков прописывается именно в Pipe0. А вот с приёмником всё по-другому. В нём уже адреса передатчиков прописываются каждый в своём канале обмена.

Поэтому в функции NRF24_ini поправим строки

 

NRF24_WriteReg(EN_AA, 0x01); // Enable Pipe0

NRF24_WriteReg(EN_RXADDR, 0x01); // Enable Pipe0

 

и вот эти

 

NRF24_Write_Buf(RX_ADDR_P0, TX_ADDRESS, TX_ADR_WIDTH);

NRF24_WriteReg(RX_PW_P0, TX_PLOAD_WIDTH); //Number of bytes in RX payload in data pipe 0

 

То есть мы в этих строках заменили единички на нолики.

Теперь перейдём в файл main.c и в бесконечном цикле тажке поменяем регистр

 

NRF24_Read_Buf(RX_ADDR_P0,buf1,3);

 

 

Соберём проект и проверим его работу в терминальной программе, конечно, прошив перед этим контроллер

 

 

У нас изменились значения регистров, приняв значение 0x01, что соответствует Pipe0, а также значения адресов TX и RX у нас по прежнему равны, что нам и требуется.

Идём дальше. Возвращаемся в файл NRF24.c и добавим там функцию перехода в режим передатчика ниже функции NRF24L01_RX_Mode

 

//------------------------------------------------

void NRF24L01_TX_Mode(uint8_t *pBuf)

{

  NRF24_Write_Buf(TX_ADDR, TX_ADDRESS, TX_ADR_WIDTH);

  CE_RESET;

  // Flush buffers

  NRF24_FlushRX();

  NRF24_FlushTX();

}

//------------------------------------------------

 

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

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

 

//------------------------------------------------

void NRF24_Transmit(uint8_t addr,uint8_t *pBuf,uint8_t bytes)

{

  CE_RESET;

  CS_ON;

  HAL_SPI_Transmit(&hspi1,&addr,1,1000);//отправим адрес в шину

  DelayMicro(1);

  HAL_SPI_Transmit(&hspi1,pBuf,bytes,1000);//отправим данные в буфер

  CS_OFF;

  CE_SET;

}

//------------------------------------------------

 

С кодом тела функции, надеюсь, всё понятно. Это обычная передача байтов в шину SPI по определённому адресу, только мы здесь перед передачей ещё опускаем на землю управляющую ножку, а по окончании её поднимаем обратно к высокому уровню.

 

 

В файле NRF24.h добавим ещё один макрос
 

#define STATUS 0x07 //'Status' register address

#define OBSERVE_TX 0x08 //'Transmit observe' register

 

Этот регистр мы уже изучали. Нам нужен будет из него показатель попыток повторных передач пакета.

Далее вернёмся в файл NRF24.c и добавим функцию передачи данных адресату после функции NRF24_Transmit

 

//------------------------------------------------

uint8_t NRF24L01_Send(uint8_t *pBuf)

{

  uint8_t status=0x00, regval=0x00;

  return regval;

}

//------------------------------------------------

 

и начнём писать её тело.

Вызовем сначала функцию перевода в режим передатчика

 

uint8_t status=0x00, regval=0x00;

NRF24L01_TX_Mode(pBuf);

 

Далее мы переведём модуль в режим передатчика, отключив соответствующий бит в конфигурационном регистре и разбудим модуль, если он спит, включением бита PWR_UP, ну и затем подождём минимум 130 микросекунд, как это предписывает нам техническая документаци.

 

NRF24L01_TX_Mode(pBuf);

regval = NRF24_ReadReg(CONFIG);

//если модуль ушел в спящий режим, то разбудим его, включив бит PWR_UP и выключив PRIM_RX

regval |= (1<<PWR_UP);

regval &= ~(1<<PRIM_RX);

NRF24_WriteReg(CONFIG,regval);

DelayMicro(150); //Задержка минимум 130 мкс

 

Теперь мы можем быть уверены в том, что модуль наш находится в режиме передатчика и готов передавать данные приёмнику.

Дадим команду модулю, которая перепишет определённое количество байтов из подготовленного нами временного буфера в буфер TX FIFO. Затем притянем ножку управления, подождём минимум 10 микросекунд и отпустим её обратно

 

DelayMicro(150); //Задержка минимум 130 мкс

//Отправим данные в воздух

NRF24_Transmit(WR_TX_PLOAD, pBuf, TX_PLOAD_WIDTH);

CE_SET;

DelayMicro(15); //minimum 10us high pulse (Page 21)

CE_RESET;

 

Подождём, когда данные передадутся, используя для этого состояние ножки INT, и установим соответствующие флаги, а также изменим состояние ножки, отвечающей за светодиод, при успешной передаче для наглядности

 

CE_RESET;

while((GPIO_PinState)IRQ == GPIO_PIN_SET) {}

status = NRF24_ReadReg(STATUS);

if(status&TX_DS) //tx_ds == 0x20

{

    LED_TGL;

NRF24_WriteReg(STATUS, 0x20);

}

else if(status&MAX_RT)

{

  NRF24_WriteReg(STATUS, 0x10);

  NRF24_FlushTX();

}

 

Считаем регистр, в котором хранится количество повторных попыток передачи, а также количество потерянных пакетов

 

  NRF24_FlushTX();

}

regval = NRF24_ReadReg(OBSERVE_TX);

 

Затем мы уходим в режим приёмника

 

  regval = NRF24_ReadReg(OBSERVE_TX);

  //Уходим в режим приёмника

  NRF24L01_RX_Mode();

  return regval;

}

 

Создадим на данную функцию прототип в заголовочном файле.

Вернёмся в файл NRF24.c и изменим в макросе количество байт в пакете

 

#define TX_PLOAD_WIDTH 5

 

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

В функции main() закомментируем переменную и добавим ещё несколько

 

//uint8_t dt_reg=0;

uint8_t retr_cnt, dt;

uint16_t i=1,retr_cnt_full;

 

Вернёмся в бесконечный цикл и скопируем 2 байта из адреса переменной обычного счётчика счётчика в буфер

 

HAL_Delay(1000);

memcpy(buf1,(uint8_t*)&i,2);

 

Так как на приёмнике будет восьмиразрядный светодиодный индикатор, то количество счёта повторов отправки ограничим до трех цифр

 

memcpy(buf1,(uint8_t*)&i,2);

if(retr_cnt_full>999) retr_cnt_full=999;

 

Будем надеяться, что у нас столько повторов не будет.

Скопируем два байта счётчика повторных передач из его адреса в следующие два байта нашего буфера

 

if(retr_cnt_full>999) retr_cnt_full=999;

memcpy(buf1+2,(uint8_t*)&retr_cnt_full,2);

 

Передадим данные приёмнику

 

memcpy(buf1+2,(uint8_t*)&retr_cnt_full,2);

dt = NRF24L01_Send(buf1);

 

Уберём ненужные нам биты из значения регистра

 

dt = NRF24L01_Send(buf1);

retr_cnt = dt & 0xF;

 

Проинкрементируем обычный счётчик и добавим количество повторов в счётчик повторов

 

retr_cnt = dt & 0xF;

i++;

retr_cnt_full += retr_cnt;

 

Также ограничим обычный счётчик

 

retr_cnt_full += retr_cnt;

if(i>=999) i=1;

 

Соберём код и прошьём контроллер. Только проверить мы сейчас ничего не сможем, так как у нас ещё нет работающего приёмника.

 

В следующей части занятия мы создадим и настроим проект для приёмника и проверим данных от передатчика приёмнику на практике.

 

 

Предыдущий урок Программирование МК STM32 Следующая часть

 

 

Модуль NRF24L01+ с антенной можно купить здесь NRF24L01+

Модуль NRF24L01+ без антенны можно купить здесь NRF24L01+

Адаптер для NRF24L01 можно купить здесь (5 штук) Адаптер для NRF24L01

Отладочную плату можно приобрести здесь Nucleo STM32F401RE

Отладочную плату STM32F103C8T6 можно приобрести здесь STM32F103C8T6

Программатор недорогой можно купить здесь ST-Link V2

Индикатор светодиодный восьмиразрядный с драйвером MAX7219

 

 

Смотреть ВИДЕОУРОК в RuTube (нажмите на картинку)

STM NRF24L01. Передаём данные

 

Смотреть ВИДЕОУРОК в YouTube (нажмите на картинку)

STM NRF24L01. Передаём данные

3 комментария на “STM Урок 105. NRF24L01. Передаём данные. Часть 1
  1. долго мучелся пока в функцию NRF24L01_RX_Mode(SPI_HandleTypeDef* SPIx) не добавил regval |= (1<<PRIM_RX); и поскокали битики . спс за уроки!

  2. Алекссей:

    regval = NRF24_ReadReg(OBSERVE_TX);
    на данном этапе кейл пишет, что «OBSERVE_TX» не определена, хотя действие выполнено #define OBSERVE_TX 0x08

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*