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