Продолжаем изучение модулей беспроводной передачи данных — 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 (нажмите на картинку)
Смотреть ВИДЕОУРОК в YouTube (нажмите на картинку)
долго мучелся пока в функцию NRF24L01_RX_Mode(SPI_HandleTypeDef* SPIx) не добавил regval |= (1<<PRIM_RX); и поскокали битики . спс за уроки!
Я рад, что у Вас всё получилось!
regval = NRF24_ReadReg(OBSERVE_TX);
на данном этапе кейл пишет, что «OBSERVE_TX» не определена, хотя действие выполнено #define OBSERVE_TX 0x08