STM Урок 103. Модули NRF24L01. Часть 4



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

 

Следующий регистр — DYNPD, который управляет использованием режима динамического количества байт в пакете для каждого канала обмена

 

 

Так как мы такой режим не используем, то тоже никие биты включать не будем

 

NRF24_WriteReg(FEATURE, 0);

NRF24_WriteReg(DYNPD, 0);

 

Следующий интересный регистр — STATUS, который обычно используется для того, чтобы узнать то или иное состояние, но порой нужно будет его использовать и для записи

 

 

Самый старщий бит не используется. Далее идут три бита, состояние которых зависит от прерывания, которое произошло. Тип прерываний полностью соответствует типам прерываний, рассмотренным нами выше и находящимся в конфигурационном регистре. При появлении низкого уровня на ножке IRQ мы исследуем данные биты, и чтобы сбросить флаг прерывания, мы должны занести единичку в соответствующий бит. То есть важен сам процесс занесения бита вручную, хотя вроде бы он там уже есть.

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

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

 

NRF24_WriteReg(DYNPD, 0);

NRF24_WriteReg(STATUS, 0x70); //Reset flags for IRQ

 

Следующий регистр — RF_CH, который устанавливает частоту работы передатчика. Мы передаём данные на одном из частотных каналов (не путать с каналами обмена данными), который мы здесь и устанавливаем

 

 

Мы заполняем биты 6:0 значением, которое и несёт в себе информацию, сколько мегагерц надо прибавить к частоте 2400, чтобы получить нужный канал. Всего может быть таких 126 каналов — от 2 и до 127. Например, если мы занесём в эти биты число 5, то получим частоту 2405 мегагерц.

Включим требуемый частотный канал

 

NRF24_WriteReg(STATUS, 0x70 ); //Reset flags for IRQ

NRF24_WriteReg(RF_CH, 76); // частота 2476 MHz

 

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

Следующий регистр — RF_SETUP, который используется для настройки скорости и мощности передачи

 

 

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

Следующий бит пропускаем, не используется вообще.

Биты RF_DR_LOW и RF_DR_HIGH используются для настройки скорости передачи. Скорость 250 kbps поддерживается только модулями со знаком «+«.

Бит PLL_LOCK включает блокировку приёма-передачи, используется также для тестирования.

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

Настроим данный регистр в функции инициализации модуля

 

NRF24_WriteReg(RF_CH, 76); // частота 2476 MHz

NRF24_WriteReg(RF_SETUP, 0x06); //TX_PWR:0dBm, Datarate:1Mbps

 

Теперь давайте научимся писать и читать буфер. Для этого добавим две функции выше функции инициализации

 

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

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

{

  CS_ON;

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

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

  CS_OFF;

}

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

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

{

  addr |= W_REGISTER;//включим бит записи в адрес

  CS_ON;

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

  DelayMicro(1);

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

  CS_OFF;

}

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

 

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

 

 

Следующий регистр — TX_ADDR, который несёт в себе информацию об адресе передатчика

 

 

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

 

extern SPI_HandleTypeDef hspi1;

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

#define TX_ADR_WIDTH 3

#define TX_PLOAD_WIDTH 2

uint8_t TX_ADDRESS[TX_ADR_WIDTH] = {0xb3,0xb4,0x01};

uint8_t RX_BUF[TX_PLOAD_WIDTH] = {0};

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

 

Мы продефайнили ширину адреса, также величину пакета (пока пусть будут 2 байта), также добавили массив с адресом и инициализировали буфер для пакетов.

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

 

NRF24_WriteReg(RF_SETUP, 0x06); //TX_PWR:0dBm, Datarate:1Mbps

NRF24_Write_Buf(TX_ADDR, TX_ADDRESS, TX_ADR_WIDTH);

 

Следующие регистры — RX_ADDR_P0:RX_ADDR_P5. Это регистры адресов каналов обена информации для приёмных устройств

 

 

Сюда мы заносим различные адреса передатчиков, чтобы отличить затем, в какой канал пришли данные. Адреса должны быть уникальные, причём для каналов Pipe2 — Pipe5 мы заносим только младший байт регистра, а старшие берутся из адреса канала pipe1.

Добавим адрес в канал pipe1 на случай, если нам придётся использовать модуль в качестве приёмника. Занесём туда тот же адрес, что и в регистр адреса передатчика

 

NRF24_Write_Buf(TX_ADDR, TX_ADDRESS, TX_ADR_WIDTH);

NRF24_Write_Buf(RX_ADDR_P1, TX_ADDRESS, TX_ADR_WIDTH);

 

Следующие регистры — RX_PW_P0:RX_PW_P5. В данные регистры заносится количество байт в пакете для каждого канала обмена информацией. Данные регистры занимают очень много места на скриншоте, поэтому постить я его не буду, там ничего нет особенного. В каждом из этих регистров только 6 младших битов, 2 старших не используются. Мы будем использовать пока величину пакета — 2 байта

 

NRF24_Write_Buf(RX_ADDR_P1, TX_ADDRESS, TX_ADR_WIDTH);

NRF24_WriteReg(RX_PW_P1, TX_PLOAD_WIDTH); //Number of bytes in RX payload in data pipe 1

 

Добавим также функцию очистки буферов FIFO приёма и передачи выше функции инициализации

 

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

void NRF24_FlushRX(void)

{

  uint8_t dt[1] = {FLUSH_RX};

  CS_ON;

  HAL_SPI_Transmit(&hspi1,dt,1,1000);

  DelayMicro(1);

  CS_OFF;

}

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

void NRF24_FlushTX(void)

{

  uint8_t dt[1] = {FLUSH_TX};

  CS_ON;

  HAL_SPI_Transmit(&hspi1,dt,1,1000);

  DelayMicro(1);

  CS_OFF;

}

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

 

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

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

Поэтому добавим функцию для включения режима приёма над функцией инициализации

 

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

void NRF24L01_RX_Mode(void)

{

  uint8_t regval=0x00;

  regval = NRF24_ReadReg(CONFIG);

  //разбудим модуль и переведём его в режим приёмника, включив биты PWR_UP и PRIM_RX

  regval |= (1<<PWR_UP)|(1<<PRIM_RX);

  NRF24_WriteReg(CONFIG,regval);

  CE_SET;

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

  // Flush buffers

  NRF24_FlushRX();

  NRF24_FlushTX();

}

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

 

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

 

 

Вызовем данную функцию в функции инициализации модуля и отключаем светодиод на плате, что будет свидетельствовать о том, что инициализация завершилась, то есть нигде ничего не повисло

 

  NRF24_WriteReg(RX_PW_P1, TX_PLOAD_WIDTH); //Number of bytes in RX payload in data pipe 1

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

  NRF24L01_RX_Mode();

  LED_OFF;

}

 

На функции чтения регистра и чтения буфера создадим прототипы в заголовочном файле NRF24.h

 

void NRF24_ini(void);

uint8_t NRF24_ReadReg(uint8_t addr);

void NRF24_Read_Buf(uint8_t addr,uint8_t *pBuf,uint8_t bytes);

 

Затем перейдём в файл main.c в функцию main() и добавим локальную переменную

 

/* USER CODE BEGIN 1 */

uint8_t dt_reg=0;

/* USER CODE END 1 */

 

В бесконечном цикле прочитаем несколько регистров и адрес из буфера, тем самым проверим, что обмен с модулем по шине SPI у нас нормально функционирует

 

/* USER CODE BEGIN 3 */

  HAL_Delay(1000);

  dt_reg = NRF24_ReadReg(CONFIG);

  sprintf(str1,"CONFIG: 0x%02Xrn",dt_reg);

  HAL_UART_Transmit(&huart1,(uint8_t*)str1,strlen(str1),0x1000);

  dt_reg = NRF24_ReadReg(EN_AA);

  sprintf(str1,"EN_AA: 0x%02Xrn",dt_reg);

  HAL_UART_Transmit(&huart1,(uint8_t*)str1,strlen(str1),0x1000);

  dt_reg = NRF24_ReadReg(EN_RXADDR);

  sprintf(str1,"EN_RXADDR: 0x%02Xrn",dt_reg);

  HAL_UART_Transmit(&huart1,(uint8_t*)str1,strlen(str1),0x1000);

  dt_reg = NRF24_ReadReg(STATUS);

  sprintf(str1,"STATUS: 0x%02Xrn",dt_reg);

  HAL_UART_Transmit(&huart1,(uint8_t*)str1,strlen(str1),0x1000);

  dt_reg = NRF24_ReadReg(RF_SETUP);

  sprintf(str1,"RF_SETUP: 0x%02Xrn",dt_reg);

  HAL_UART_Transmit(&huart1,(uint8_t*)str1,strlen(str1),0x1000);

  NRF24_Read_Buf(TX_ADDR,buf1,3);

  sprintf(str1,"TX_ADDR: 0x%02X, 0x%02X, 0x%02Xrn",buf1[0],buf1[1],buf1[2]);

  HAL_UART_Transmit(&huart1,(uint8_t*)str1,strlen(str1),0x1000);

  NRF24_Read_Buf(RX_ADDR_P1,buf1,3);

  sprintf(str1,"RX_ADDR: 0x%02X, 0x%02X, 0x%02Xrn",buf1[0],buf1[1],buf1[2]);

  HAL_UART_Transmit(&huart1,(uint8_t*)str1,strlen(str1),0x1000);

}

 

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

 

 

Как видим, у нас всё работает.

Спасибо всем за внимание!

Ждите следующих уроков по данному модулю, в которых мы уже будем пробовать осуществить обмен данными по радиоканалу между двумя такими рессиверами.

 

 

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

 

Исходный код

 

 

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

Модуль NRF24L01+ без антенны можно купить здесь (целых 4 штуки) NRF24L01+

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

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

 

 

Техническая документация:

nRF24L01

nRF24L01P

nrf24l01 tutorial

 

 

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

 

STM Модули NRF24L01

7 комментариев на “STM Урок 103. Модули NRF24L01. Часть 4
  1. Рашид:

    Спасибо за уроки. Очень доходчиво. Связь наладил, работает. Хотелось бы знать ( скорее я пропустил или не понял) как наладить связь с передатчиками с разными адресами. Т.е. приемник сканирует и при нахождении передатчика выдает его адрес и предлагает внести его в адрес приемника для дальнейшей совместной работы

  2. Рашид:

    Спасибо, поищу. Меня интересует возможность работать с передатчиком, адрес которого не известен приемнику.

    • Насчёт сканирования адресов, честно говоря, не понял. Перебирать все возможные комбинации байтов адреса мы не сможем, времени не хватит, можно за это время пропустить пакет.

  3. Mikhail_19:

    Спасибо Вам ОГРОМНОЕ за такие хорошие уроки! Все подробно и понятно.
    На последнем рисунке (скриншот из терминала) EN_AA и EN_RXADDR должны быть равны 0x02,так как в уроке 103 мы именно 0x02 записали в регистры.

  4. Антон:

    Огромная просьба сделать урок на AVR 8 бит по этой теме.

  5. Vladimir_Z:

    Добрый день!
    Огромное спасибо за подробные и понятные уроки. Присоединяюсь к просьбе Антона, об уроке на AVR 8 бит по этой теме.
    Спасибо.
    С уважением, Владимир.

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

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

*