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+ без антенны можно купить здесь (целых 4 штуки) NRF24L01+

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

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

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

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

 

 

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

 

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 не будет опубликован. Обязательные поля помечены *

*