В предыдущей части урока мы начали писать функцию инициализации модуля, написали функции чтения и записи регистров, а также познакомились с некоторыми командами и регистрами.
Следующий регистр — 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+ без антенны можно купить здесь NRF24L01+
Адаптер для NRF24L01 можно купить здесь (5 штук) Адаптер для NRF24L01
Отладочную плату STM32F103C8T6 можно приобрести здесь STM32F103C8T6
Программатор недорогой можно купить здесь ST-Link V2
Техническая документация:
Смотреть ВИДЕОУРОК в RuTube (нажмите на картинку)
Смотреть ВИДЕОУРОК в YouTube (нажмите на картинку)
Спасибо за уроки. Очень доходчиво. Связь наладил, работает. Хотелось бы знать ( скорее я пропустил или не понял) как наладить связь с передатчиками с разными адресами. Т.е. приемник сканирует и при нахождении передатчика выдает его адрес и предлагает внести его в адрес приемника для дальнейшей совместной работы
И Вам спасибо за интерес к ресурсу!
Работа с несколькими передатчиками в более поздних уроках. В оглавлении есть.
Спасибо, поищу. Меня интересует возможность работать с передатчиком, адрес которого не известен приемнику.
Насчёт сканирования адресов, честно говоря, не понял. Перебирать все возможные комбинации байтов адреса мы не сможем, времени не хватит, можно за это время пропустить пакет.
Спасибо Вам ОГРОМНОЕ за такие хорошие уроки! Все подробно и понятно.
На последнем рисунке (скриншот из терминала) EN_AA и EN_RXADDR должны быть равны 0x02,так как в уроке 103 мы именно 0x02 записали в регистры.
Огромная просьба сделать урок на AVR 8 бит по этой теме.
Добрый день!
Огромное спасибо за подробные и понятные уроки. Присоединяюсь к просьбе Антона, об уроке на AVR 8 бит по этой теме.
Спасибо.
С уважением, Владимир.