Вы находитесь на странице: 1из 39

Попался ко мне в руки вот такой радио модуль nRF24L01+, вот 

отсюда. 

Довольно таки известный радио модуль. Вот решил попробовать сие изобретение человечества. Цель статьи это быстрый старт.
Скажем передача байта от одного радио модуля к другому и обратно. Пихаем байт в USART, из USART он попадает уже в SPI радио
модуля и далее в пространство. Это все. Никаких ненужных лишних подробностей, которые не касаются быстрого старта. 

nRF24L01+ работает по SPI интерфейсу. Это значит что нужна какая нибудь платка с микроконтроллером, на борту которого имеется
SPI интерфейс. Такую платку можно сделать самому или нарыть готовую. Я пошел по второму пути. И нарыл вот такую…

Готовая такая платка для работы с nRF24L01+. Ничего лишнего. Описание и схема платы есть на сайте. Плата разработана на базе
любимыми многими МК Atmega8. На плате выведены ноги МК RESET, SCK, MISO, MOSI. Это значит, что в плату можно залить свою
прошивку, удалив существующую. Что я и решил сделать. Та прошивка которая есть в плате, позволяет по USART отправлять байты
другому радио модулю. Удобно однако. Это значит можно вообще не думать про сам радио модуль. Просто отправляй нужные тебе
байты, а с другой стороны принимай. Вот и все, быстро и удобно. Кому не охота разбираться с самим радио модулем само то. Но в
таком случае разобраться как работает сам радио модуль нам не светит. Поэтому будем грызть железку сами.

Вот схема платки.


Как видим все просто. Кварц выбран 7.3728, это чтоб минимизировать при передаче байтов через USART количество ошибок.
Селектор скорости, для выбора необходимой скорости USART. Три светодиода для отладки. Есть еще четыре свободных пина РС2,
РС3, РС4, РС5. На них можно повесить кнопки какие или еще чего. Ну это уж, что пользователь решит.

Для начала инициализируем ноги Atmega8 в соответствии со схемой.

/*НАСТРОЙКА ПОРТОВ*/
#if 1
      DDRB =  0b11101111;       //если 0 значит вход
      PORTB = 0b00010100;       //если 1 подтяжка включена

      DDRC =  0b00001110;      
      PORTC = 0b11110001;      

      DDRD =  0b10000000;
      PORTD = 0b01111111;      
#endif

Начнем пожалуй с интерфейса SPI. В Atmega8 для его настройки существует всего два регистра SPCR и SPSR. В регистре SPCR битом
SPE — включим интерфейс SPI. А битом MSTR — укажем что наш МК будет мастером, то есть обмен данными с радио модулем будем
всегда начинать мы (других вариантов и нету). В регистре SPSR битом SPI2X — ускорим скорость SPI до максимума. Ну это я так
сделал чисто для теста. Этого можно не делать. На этом настройка SPI закончена.

/*Настройка SPI*/                                
#if 1
       
SPCR = (0<<SPIE)|(1<<SPE)|(0<<DORD)|(1<<MSTR)|(0<<CPOL)|(0<<CPHA)|(0<<SPR1)|(0<<SPR0);
       
  /*    SPIE - разрешение прерывания от SPI, 1-ON,0-OFF
        SPE  - вкл/выкл SPI,                 1-ON,0-OFF
        DORD - порядок передачи данных.      1-байт передается с младшего бита, 0->7
                                             0-байт передается со старшего бита.7->0                      
        MSTR - Выбор режима (Master/Slave)   1-Master, 0-Slave
        CPOL - полярность тактового сигнала  0-импульсы положительной полярности
                                             1-импульсы отрицательной полярности
        CPHA - Фаза тактового сигнала        0-биты считываются по переднему фронту ноги SCK
                                             1-биты считываются по заднему фронту ноги SCK
        SPR1 - Скорость передачи
        SPR0 - Скорость передачи
       
        SPI2X  SPR1  SPR0   Частота SCK
          0     0     0           F_CPU/4
          0     0     1           F_CPU/16
          0     1     0           F_CPU/64
          0     1     1           F_CPU/128
          1     0     0           F_CPU/2
          1     0     1           F_CPU/8
          1     1     0           F_CPU/32
          1     1     1           F_CPU/64
        */
                                 
SPSR = (0<<SPIF)|(0<<WCOL)|(1<<SPI2X);

  /*    SPIF  - Флаг прерывания от SPI. уст в 1 по окончании передачи байта


        WCOL  - Флаг конфликта записи. Уст в 1 если байт не передан, а уже попытка записать новый.
        SPI2X - Удвоение скорости обмена.*/
#endif
Теперь надо бы настроить USART. Там малек посложнее будет. Но тоже ничего, справиться можно.

/*Настройка USART*/
                                         
#if     1          
       
uint8_t a=(PIND>>2) & 0b00000111;//это мы считали состояние нашего селектора скорости и в следующем блоке кода
                                 //установим скорость USART.
switch (a)
{
        case 0:UBRRL=3;//115200  000
        break;
        case 1:UBRRL=5;//76800   001
        break;
        case 2:UBRRL=7;//57600   010
        break;
        case 3:UBRRL=11;//38400  011
        break;
        case 4:UBRRL=15;//28800  100
        break;
        case 5:UBRRL=23;//19200  101
        break;
        case 6:UBRRL=31;//14400  110
        break;
        case 7:UBRRL=47;//9600   111
        break;
       
        default:UBRRL=47;
                        break;
}      
                         
UCSRA = (0<<RXC)|(0<<TXC)|(0<<UDRE)|(0<<FE)|(0<<DOR)|(0<<PE)|(0<<U2X)|(0<<MPCM);
  /*       RXC -        Флаг завершения приема. Флаг устанавливается в 1 при наличии  
                        непрочитанных данных в буфере приемника (регистр данных UDR). Сбрасывается
                        флаг аппаратно после опустошения буфера. Если бит RXCIE0  
                        регистра UCSR0B установлен, то при установке флага генерируется
                        запрос на прерывание «прием завершен»
           TXC -        Флаг завершения передачи. Флаг устанавливается в 1 после передачи всех  
                        битов посылки из сдвигового регистра передатчика при условии, что в регистр
                        данных UDR не было загружено новое значение. Если бит TXCIE0
                        регистра UCSR0B установлен, то при установке флага  
                        генерируется прерывание «передача завершена». Флаг сбрасывается аппаратно при  
                        выполнении подпрограммы обработки прерывания или программно, записью
                        в него лог. 1
           UDRE -       Флаг опустошения регистра данных. Данный флаг устанавливается в 1
                        при пустом буфере передатчика (после пересылки байта из регистра данных
                        UDR в сдвиговый регистр передатчика). Установленный флаг означает,
                        что в регистр данных можно загружать новое значение. Если бит UDRIE
                        регистра UCSR0B установлен, генерируется запрос на прерывание «регистр
                        данных пуст». Флаг сбрасывается аппаратно, при записи в регистр данных
            FE -        Флаг ошибки кадрирования. Флаг устанавливается в 1 при обнаружении
                        ошибки кадрирования, т. е. если первый стоп-бит принятой посылки равен 0.
                        Флаг сбрасывается при приеме стоп-бита, равного 1
            DOR -       Флаг переполнения. Флаг устанавливается в 1, если в момент обнаружения
                        нового старт-бита в сдвиговом регистре приемника находится последнее
                        принятое слово, а буфер приемника полон (содержит два байта). Флаг  
                        сбрасывается при пересылке принятых данных из сдвигового регистра  
                        приемника в буфер.
            UPE -       Флаг ошибки контроля четности. Флаг устанавливается в 1, если в данных,
                        находящихся в буфере приемника, выявлена ошибка контроля четности.
                        При отключенном контроле четности этот бит постоянно сброшен в 0
            U2X -       Удвоение скорости обмена. Если этот бит установлен в 1, то коэффициент
                        деления предделителя контроллера скорости передачи уменьшается с 16 до
                        8, удваивая тем самым скорость асинхронного обмена по  последовательному
                        каналу. Этот бит используется только при асинхронном режиме работы
                        и в синхронном режиме должен быть сброшен
            MPCM -      Режим мультипроцессорного обмена. Если этот бит установлен в 1, ведомый
                        микроконтроллер ожидает приема кадра, содержащего адрес. Кадры,
                        не содержащие адреса устройства, игнорируются */

    UCSRB = (1<<RXCIE)|(0<<TXCIE)|(0<<UDRIE)|(1<<RXEN)|(1<<TXEN)|(0<<UCSZ2)|(0<<RXB8)|(0<<TXB8);
  /*     RXCIE - Разрешение прерывания по завершении приема. Если данный бит установлен
                         в 1, то при установке флага RXC0 регистра UCSR0A  генерируется прерывание
                         «прием завершен» (если флаг I регистра SREG установлен в1)

         TXCIE - Разрешение прерывания по завершении передачи. Если данный бит установлен


                         в 1, то при установке флага TXC регистра UCSRA генерируется прерывание
                         «передача завершена» (если флаг 1 регистра SREG  установлен в 1)

          UDRIE - Разрешение прерывания при очистке регистра данных UART. Если данный бит
                         установлен в 1, то при установке флага UDRE  регистра UCSRA
                         генерируется прерывание «регистр данных пуст» (если флаг I  
                         регистра SREG установлен в 1)

           RXEN  - Разрешение приема. При установке этого бита в 1 разрешается работа  


                         приемника USART и переопределяется функционирование вывода RXD
                         При сбросе бита RXEN0 работа приемника запрещается, а его буфер
                         сбрасывается. Значения флагов ТХС0, DOR0 и FE0 при
                         этом становятся недействительными
           TXEN  - Разрешение передачи. При установке этого бита в 1 разрешается работа  
                         передатчика UART и переопределяется функционирование вывода TXD.
                         Если бит сбрасывается в 0 во время передачи, то выключение передатчика
                         произойдет только после завершения передачи данных, находящихся в  
                         сдвиговом регистре и буфере передатчика

           UCSZ2 - Формат посылок. Этот бит совместно с битами UCSZ01:0 регистра UCSR0C
                         используется для задания размера слов данных, передаваемых по
                         последовательному каналу.

           RXB8  - 8-й бит принимаемых данных. При использовании 9-битных слов данных этот
             бит содержит значение старшего бита принятого слова. Содержимое этого
                         бита должно быть считано до прочтения регистра данных UDR0.

           TXB8  - 8-й бит передаваемых данных. При использовании 9-битных слов данных  
                         содержимое этого бита является старшим битом передаваемого слова.  
                         Требуемое значение должно быть занесено в этот бит до загрузки байта данных в  
                         регистр UDR0 */

   UCSRC = (1<<URSEL)|(0<<UMSEL)|(0<<UPM1)|(0<<UPM0)|(0<<USBS)|(1<<UCSZ1)|(1<<UCSZ0)|(0<<UCPOL);

/*
Режим работы USART
-----------------------------------  
 UMSEL  Режим работы USART
-----------------------------------
   0     Асинхронный USART
   1     Синхронный USART
-----------------------------------

Режим работы схемы контроля и формирования бита четности.


-----------------------------------
UPM1    UPM0    Режим работы схемы
-----------------------------------
   0       0    Disabled
   0       1    Reserved
   1       0    Включена, проверка на четность  Even Parity
   1       1    Включена, проверка на нечетность  Odd Parity
-----------------------------------

Количество стоповых битов


-----------------------------------
USBS  Stop Bit(s)
 0      1-bit
 1      2-bit
-----------------------------------

Определение размера слова данных


-----------------------------------
UCSZ2 UCSZ1 UCSZ0  Размер слова данных
-----------------------------------
   0      0      0    5-bit
   0      0      1    6-bit
   0      1      0    7-bit
   0      1      1    8-bit
   1      0      0    Reserved
   1      0      1    Reserved
   1      1      0    Reserved
   1      1      1    9-bit
-----------------------------------

UCPOL - Полярность тактового сигнала. Значение этого бита определяет момент  


                 выдачи и считывания данных на выводах модуля. Бит используется только при
                 работе в синхронном режиме. При работе в асинхронном режиме он должен
                 быть сброшен в 0.
-------------------------------------------------------------------
UCPOL  Выдача данных на вывод TXD  Считывание данных с вывода RXD
-------------------------------------------------------------------
        0    Спадающий фронт ХСК         Нарастающий фронт ХСК
        1    Нарастающий фронт ХСК       Спадающий фронт ХСК
-------------------------------------------------------------------    
Обратите внимание на то, что в моделях ATmega8515x/8535x,
ATmega8x/16x/32x и ATmegal62x регистр UBRRH размещается по тому же
адресу, что и регистр управления UCSRC. Поэтому при обращении по
этим адресам необходимо выполнить ряд дополнительных действий для
выбора конкретного регистра.
При записи регистр определяется состоянием старшего бита  
записываемого значения URSEL. Если этот бит сброшен в 0, изменяется  
содержимое регистра UBRRH. Если же старший бит значения установлен в 1,  
изменяется содержимое регистра управления UCSRC. */
#endif

Как работает USART разбираться не будем, (уже разобрано не один раз) ибо тема сейчас другая. Этот кусок кода можно
скопировать, и при желании разобраться потом.

Вот как бы с настройкой МК все. Теперь надо бы слегка вникнуть в сам радио модуль. В чипе nRF24L01+ есть память ОЗУ. То есть
набор байтов. В эти байты надо в нужные места воткнуть нули или единицы. Ну точно так же как мы это сделали с Atmega8 выше.
Воткнув их радио модуль будет готов к работе. Для начала ознакомимся с некоторыми регистрами радио модуля.

Регистр STATUS — самый популярный и часто используемый регистр. В нем находится информация что происходит с нашим радио
модулем. Ну значит определим его и его биты для использования в нашей программе. То что не прокомментировано, значит это для
быстрого старта пока не надо.

/*регистр STATUS*/
        #define STATUS          0x07
        #define RX_DR           6 /*прерывание: данные получены. Для сброса записать 1.*/
        #define TX_DS           5 /*прерывание: данные переданы. Для сброса записать 1.*/
        #define MAX_RT          4 /*прерывание: данные не переданы. Для сброса записать 1.*/
        #define RX_P_NO2        3
        #define RX_P_NO1        2
        #define RX_P_NO0        1
        #define TX_FULL0        0 /*флаг переполнения TX FIFO буфера передачи. 1-переполнен, 0-есть еще место.*/

Как видим адрес регистра STATUS 0х07. Всего у нас регистров управления nRf24L01 аж 29 штук. Следующий по нужности регистр
это регистр CONFIG. Определим и его.

/*регистр CONFIG*/    //Конфигурационный регистр


        #define CONFIG          0x00
        #define MASK_RX_DR  6 //вкл/откл прерывание от бита RX_DR в рег. STATUS. 0-вкл, 1-выкл.
        #define MASK_TX_DS  5 //вкл/откл прерывание от бита TX_DS в рег. STATUS. 0-вкл, 1-выкл.
        #define MASK_MAX_RT 4 //вкл/откл прерывание от бита MAX_RT в рег. STATUS. 0-вкл, 1-выкл.
        #define EN_CRC      3 //включение CRC. По умолчанию вкл. если один из битов регистра EN_AA включен.
        #define CRCO        2 //режим CRC. 0-1 байт, 1-2 байта.
        #define PWR_UP      1 //1-POWER UP, 0-POWER DOWN, по умолчанию 0.
        #define PRIM_RX     0 //0-режим передачи, 1-режим приема.
Он идет самым первым в адресной карте регистров. 

Ну и еще один регистр это регистр RX_PW_P0.

#define RX_PW_P0        0x11//указываем в нем из скольких байтов будет состоять наше поле данных для отправки.

Ну как бы для быстрого старта достаточно этих трех регистров. 

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

#if 1 //Описание команд


        #define R_REGISTER                      0x00 //читаем регистр
        #define W_REGISTER                      0x20 //пишем в регистр
        #define R_RX_PAYLOAD                    0x61 //считывание из буфера принятых данных из космоса
        #define W_TX_PAYLOAD                    0xA0 //запись данных в буфер для отправки в космос
        #define FLUSH_TX                        0xE1 //очистка буфера отправки
        #define FLUSH_RX                        0xE2 //очистка буфера приема
        #define REUSE_TX_PL                     0xE3
        #define ACTIVATE                        0x50
        #define R_RX_PL_WID                     0x60
        #define W_ACK_PAYLOAD                   0xA8
        #define W_TX_PAYLOAD_NOACK              0x58
        #define NOP                             0xFF //команда заглушка, ничего не делает.
#endif
Мне пригодились только первые четыре команды и последняя NOP. Это все. Теперь надо бы написать пару функций для работы с
радио модулем.

Но перед этим еще определим пару макросов для удобной и наглядной работы с битами.

#define Bit(bit)  (1<<(bit))

#define ClearBit(reg, bit)       reg &= (~(1<<(bit)))


//пример: ClearBit(PORTB, 1); //сбросить 1-й бит PORTB

#define SetBit(reg, bit)          reg |= (1<<(bit))

#define BitIsClear(reg, bit)    ((reg & (1<<(bit))) == 0)


//пример: if (BitIsClear(PORTB,1)) {...} //если бит очищен

#define BitIsSet(reg, bit)       ((reg & (1<<(bit))) != 0)


//пример: if(BitIsSet(PORTB,2)) {...} //если бит установлен

#define InvBit(reg, bit)          reg ^= (1<<(bit))


//пример: InvBit(PORTB, 1); //инвертировать 1-й бит PORTB

Да кстати надо еще пояснить, что означают ноги радио модуля.

IRQ — выход прерывания. Когда все пучком, то нога находится прижатой к питанию (высокий уровень). Но как только что нибудь
произошло, например их космоса/в космос поймали/отправили байт, или байт не отправился, то этот пин сразу падает на землю. Как
тока он упал, надо все бросить и считать регистр STATUS. И в этом регистре уже конкретно видно что произошло. Это видно из трех
битов-флагов — RX_DR, TX_DS, MAX_RT. Что они означают выше прокомментировано. Как только стало ясно какие биты равны
значению 1, их тут же надо сбросить, записью 1. Иначе нога IRQ всегда будет лежать на земле. Причем сбросить надо только те
биты в которых значение 1. Те в которых 0 писать ничего ненадо, ну 0 можно записать, но не 1. Иначе IRQ опять упадет на землю. 
CE — вход. Если модуль в режиме приема, то СЕ прижимаем к питанию. Если режим передачи, то СЕ лежит на земле. И когда надо
передать байт, СЕ поднимаем к питанию не менее чем на 10 мксек, потом снова прижимаем к земле. Байт улетел в пространство.

CSN — вход. Всегда находится прижатым к питанию. Если надо что то записать/считать в радио модуль, то перед началом любых
телодвижений прижимаем к земле. Записали/считали — прижимаем снова к питанию.

CSK, MOSI, MISO — пины SPI интерфейса. Подключаются к одноименным выводам МК. MISO определяем на вход, MOSI, CSK на
выход. Все это делаем ручками, так как при включения интерфейса SPI эти выводы сами по себе не переопределяются по
назначению.

Ну и как же считать значение регистра STATUS?


1.Прижимаем вывод CSN(SS) МК к земле, тем самым сообщаем о начале обмена данных.
ClearBit(PORTB,CSN);
2.Пишем в регистр отправки данных SPI что нибудь, пусть это будет NOP.
SPDR=NOP;
3.Ждем когда байт улетит. Когда он улетит то бит SPIF станет 1.
while(BitIsClear(SPSR,SPIF));
Бит SPIF сам сбросится аппаратно если разрешено прерывание «байт отправлен». Но прерывание у нас запрещено. Поэтому SPIF
можно сбросить вторым способом. Надо записать/считать байт отправки/передачи данных SPDR. Байт отправки/передачи данных
SPDR это на самом деле два разных байта находящихся под разными адресами, но имя одинаковое. Тоже самое, что и байт UDR в
USART.
4.Если байт NOP улетел то прижимаем SCN к питанию. Обмен завершен.
SetBit(PORTB,CSN);
5.Считываем принятый байт из SPDR.
uint8_t a=SPDR;
Обмен данными завершен.

Вывод: всегда 1й вывалившийся байт из SPI радиомодуля это будет значение байта STATUS. Что бы мы туда не писали это будет
железно значение STATUS. Не всегда это надо, если не надо то этот байт можно игнорировать. Итак функция чтения регистра статус
может иметь такой вид.

uint8_t r_register(uint8_t a)//чтение байта из озу. a-адрес считываемого байта.


{
        ClearBit(PORTB,CSN);//Прижимаем вывод CSN(SS) МК к земле, тем самым сообщаем о начале обмена данных.
        SPDR=a;//пишем в SPDR что нибудь.
        while(BitIsClear(SPSR,SPIF));//ожидаем когда освободится SPI.
        SetBit(PORTB,CSN);//Вывод CSN(SS) МК к питанию, обмен данных завершен.
        return SPDR;
}//uint8_t a=r_register(STATUS);
Как же считать значение регистра CONFIG или любого другого? 
Отправляем в радиомодуль сперва адрес регистра который хотим считать, а потом отправляем например NOP. Считываем из SPDR
наше значение считываемого регистра.
Приведенную функцию для чтения STATUS можно причесать и сделать универсальной для чтения всех регистров.

uint8_t r_register(uint8_t a)//чтение байта из озу. a-адрес байта


{
        ClearBit(PORTB,CSN);//Прижимаем вывод CSN(SS) МК к земле, тем самым сообщаем о начале обмена данных.
        SPDR=a;
        while(BitIsClear(SPSR,SPIF));//ожидаем когда освободится SPI для последующей записи байта
        if (a==STATUS)
        {
                SetBit(PORTB,CSN);//Вывод CSN(SS) МК к питанию, обмен данных завершен.
                return SPDR;
        }
        SPDR=NOP;
        while(BitIsClear(SPSR,SPIF));
        SetBit(PORTB,CSN);//Вывод CSN(SS) МК к питанию, обмен данных завершен.
        return SPDR;
}//uint8_t a=r_register(CONFIG);
Как записать что нибудь в регистр CONFIG? 
Для записи данных в регистры, есть команда W_REGISTER 0x20. Берем значение 0х20 и накладываем маску с адресом регистра в
который хотим записать что нибудь и отправляем в SPI. Потом отправляем в SPI уже то значение которое хотим записать в регистр.
Все. Функция записи может иметь такой вид.

void w_register(uint8_t a,uint8_t b)//а-адрес регистра, b-что пишем в регистр.


{
        a=a | W_REGISTER;//накладываем маску
        ClearBit(PORTB,CSN);
        SPDR=a;
        while(BitIsClear(SPSR,SPIF));
        SPDR=b;
        while(BitIsClear(SPSR,SPIF));
        a=SPDR;//это для сброса флага SPIF
        SetBit(PORTB,CSN);
}//W_REGISTER (CONFIG,0b00000110);
Как сбросить флаги в регистре STATUS?
Считываем сначала значение регистра STATUS, а потом это же значение туда записываем.

uint8_t b= r_register(STATUS);//прочитали статус регистр


w_register(STATUS, b);//сброс флагов прерываний - обязательно
Как перевести модуль на прием данных?
1.Нужно в регистре CONFIG выставить бит PWR_UP в 1.
2.Ждем не менее 1.5 мсек
3.Выставляем в CONFIG бит PRIM_RX в 1.
4.Ногу CE поднимаем к питанию.
5.Ждем не менее 135 мксек.

Как раз таки на этих 135 мксек я и погорел. После смены значение бита PRIM_RX радио модуль находится в неопределенном
состоянии в течении 135 мксек. В этот момент в него нельзя ничего писать. Все равно не поймет. Так что эта выдержка обязательна.
Кстати шаг 1 и 2 делается один раз в начале инициализации модуля, а потом только меняем значение бита PRIM_RX и ноги СЕ.

w_register(CONFIG,(1<<PWR_UP)|(1<<EN_CRC)|(0<<PRIM_RX));
_delay_ms(2);
w_register(CONFIG,(1<<PWR_UP)|(1<<EN_CRC)|(1<<PRIM_RX));
SetBit(PORTC,CE);
_delay_us(135);
//режим према RX
А так переводим на передачу данных

ClearBit(PORTC,CE);
w_register(CONFIG,(1<<PWR_UP)|(1<<EN_CRC)|(0<<PRIM_RX));
_delay_us(135);
Перед всеми манипуляциями с модулем один раз в начале в момент инициализации надо в регистре RX_PW_P0 указать чему будет
равен размер поля данных. В нашем случае будем стрелять по одному байту. Поэтому поле будет равно 1 байту. Максимум можно до
32 байт.

w_register(RX_PW_P0,1);//размер поля данных 1 байт.


Конкретно как отправить 1 байт в пространство?
Замутим для начала две функции, настройка модуля на прием и передачу.

void prx(void)//Настроим nRF на прием.


{
        w_register(CONFIG,(1<<PWR_UP)|(1<<EN_CRC)|(1<<PRIM_RX));
        SetBit(PORTC,CE);
        _delay_us(135);
        //режим према RX
}
void ptx(void)//Настроим nRF на передачу и сразу же отправим байт в пространство.
{
        ClearBit(PORTC,CE);
        w_register(CONFIG,(1<<PWR_UP)|(1<<EN_CRC)|(0<<PRIM_RX));
        SetBit(PORTC,CE);
        _delay_us(15);
        ClearBit(PORTC,CE);
        _delay_us(135);
}
А теперь функцию отправки байта.

void send_byte(uint8_t a)//отправка байта.


{
        w_register(W_TX_PAYLOAD,a);//запись байта в буфер TX для отправки
        ptx();//передача байта
        while (BitIsSet(PINC,IRQ));//Ждем пока байт не передан
        uint8_t b= r_register(STATUS);//прочитали статус регистр
        w_register(STATUS, b);//сброс флагов прерываний - обязательно
        prx();//на прием
}
Получается что сперва надо: 
1. Положить отправляемый байт в буфер отправки данных w_register(W_TX_PAYLOAD,a);
2. Функцией ptx(); отправили байт в космос.
3. Подождали когда байт улетел.
4. Сбросили флаги прерываний.
5. Настроили модуль на прием.
Ну вот и все.

В обычном режиме модуль все время находится на приеме данных. А что делать если вдруг модуль поймал байт от другого модуля? В
главном цикле можно замутить опрос флагов прерываний. И анализируя эти флаги можно выяснить, принят ли байт.

if (BitIsClear(PINC,IRQ))//если нога IRQ упала, значит надо выяснить, может принят байт?
        {
                uint8_t a= r_register(STATUS);//прочитали статус регистр
                w_register(STATUS, a);//сброс флагов прерываний - обязательно
                if (BitIsSet(a,RX_DR))//если этот бит равен 1 то байт принят.
                {
                        buff_RX[IN_RX++]=r_register(R_RX_PAYLOAD);//чтение 1 байта из буфера приема RX_FIFO в озу buff
и отправка в USART.
                }
        }
Байты ведь могут сыпаться один за одним. И если пытаться принятые байты пихать сразу в USART, то особо ничего не выйдет. Ибо
скорость работы SPI и USART очень сильно отличаются. Поэтому кажется неплохо бы замутить кольцевые буфера. В один буфер
складывать данные которые получены, а в другой складывать то, что надо отправить в эфир. Ну и потом пусть по прерываниям сам
МК выгребает данные и распихивает их по интерфейсам, отправляемые в SPI, принятые в USART. 

#define BUF_SIZE 256


static uint8_t buff_RX[BUF_SIZE], IN_RX, OUT_RX;//тут у нас буфер в который складываем принимаемые данные до 256 байт
static uint8_t buff_TX[BUF_SIZE], IN_TX, OUT_TX;;//А сюда кладем данные для отправки до 256 байт
IN_RX, OUT_RX, IN_TX, OUT_TX это маркеры начала и конца кольцевого буфера.

В главном цикле

if (IN_TX != OUT_TX)//если конец и начало не совпадают значит в буфер отправки что то попало из USART. И это что то
надо выдать в эфир
        {
                send_byte(buff_TX[OUT_TX++]);//отправим из буфера 1 байт в пространство.
        }

Весь проект как есть выкладываю в топик. Писал в AtmelStudio6.2. Он сильно черновой и с магическими числами, но рабочий.
Прошивку можно сразу загрузить в платку и она будет работать. Если что, то код можно напильником допилить. Надо бы будет как
нибудь, замутить статью про более продвинутые возможности данного радио модуля.
симальная мощность передатчика самого чипа (без внешних усилителей) – 0dBm (1мВт); чувствительность
приёмника -82dBm при скорости 2Мбит/с, и -85dBm при скорости 1Мбит/с.

Чип работает на частотах в диапазоне 2400-2525 МГц. Конкретная частота из этого диапазона с шагом в 1МГц
задаётся настройками.

Энергопотребление чипа составляет около 0,9мкА в режиме power-down, до 11,3мА при передаче, и до 13,5мА при
приёме, и 26 и 320мкА в режимах готовности (standby-I и standby-II). Использование автоматизации в передаче
пакета позволяет минимизировать время нахождения в энергоёмких режимах, тем самым значительно снижая
энергопотребление устройств работающих в режиме передатчика.

Чип может работать как в режиме приёмника, так и в режиме передатчика, но в каждый момент времени может
находиться только в одном из этих двух состояний. Формирование пакетов и контроль передачи происходит
автоматически. Максимальная длина полезного содержимого пакета – 32 байта.

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

Приёмник поддерживает получение данных по шести каналам, в зависимости от переданного адреса. Каналы 1-5
различаются только младшим байтом адреса. Адрес канала 0 может быть настроен независимо, но при передаче
этот канал используется для получения подтверждений приёма.

Назначение выводов
GND

Общий, "земля".

VCC

Питание, от 1.9 до 3.6 Вольт. Однако, если напряжение на линиях MOSI, SCK, CE, SN более 3.6 Вольт, то Н
напряжение питания должно быть в пределах 2.7 - 3.3 Вольт. а

CE

Входной, активный - высокий уровень. Наличие высокого уровня на этом входе активирует режим приёма. В
режиме передачи импульс не менее 10мкс начинает передачу.

CSN

Входной, активный - низкий уровень. Низкий уровень устанавливает границы сеанса обмена по шине SPI.

SCK

Входной. Тактовый импульс для обмена по шине SPI. Данные на линиях MOSI и MISO считываются Р
противоположной стороной по нарастающему фронту на линии SCK, и выставляются по спаду. а

MOSI
Входной. Линия SPI, передающая данные от микроконтроллера к радио-модулю.

MISO

Выходной. Линия SPI, передающая данные от радио-модуля к микроконтроллеру.

IRQ
С
Выходной, активный - низкий уровень. При наличии одного из прерываний устанавливается низкий уровень на о
этой линии.

Команды
Сеанс обмена начинается с установки низкого уровня на линии CSN. Первый переданный после этого от
микроконтроллера байт является командой. Одновременно с байтом команды от микроконтроллера, к
микроконтроллеру по линии MISO передаётся байт статуса.

Перечень команд:

16-
Дво
чн
Наименован ичн Размер Описан
ый
ие ый данных ие
ко
код
д

Прочита
0x0
000n 1-5 ть
R_REGISTER 0+
nnnn (приём) регистр
n
n

Записат
0x2 1-5
001n ь
W_REGISTER 0+ (переда
nnnn регистр
n ча)
n

R_RX_PAYLOA 0110 0x6 1-32 Принять


D 0001 1 (приём) данные
из
верхнег
о слота
очереди
приёмн
ика.
После
чтения
данные
из
очереди
удаляют
ся

Записат
ьв
очередь
1-32 передат
W_TX_PAYLO 1010 0xA
(переда чика
AD 0000 0
ча) данные
для
отправк
и

Сбросит
ь
1110 0xE
FLUSH_TX 0 очередь
0001 1
передат
чика

Сбросит
ь
1110 0xE
FLUSH_RX 0 очередь
0010 2
приёмн
ика
Использ
овать
повторн
о
1110 0xE
REUSE_TX_PL 0 последн
0011 3
ий
передан
ный
пакет

Прочита
ть
размер
данных
принято
го
пакета
в
начале
очереди
приёмн
ика.
Значени
е
0110 0x6 1 больше
R_RX_PL_WID
0000 0 (приём) 32,
означае
т
ошибку
приёма,
в таком
случае
пакет
должен
быть
удалён
командо
й
FLUSH_
RX

W_ACK_PAYL 1010 0xA 1-32 Записат


OAD 1ppp 8+ (переда ь
p ча) данные
для
отправк
ис
пакетом
подтвер
ждения
по
каналу
p. При
этом
бит
EN_ACK
_PAY в
регистр
е
FEATUR
E
должен
быть
установ
лен

Записат
ьв
очередь
передат
чика
данные
для
1-32
W_TX_PAYLO 1011 0xB отправк
(переда
AD_NOACK 0000 0 и, для
ча)
которых
не
требует
ся
подтвер
ждение
приёма.

NOP 1111 0xF 0 Нет


1111 F операци
и.
Может
быть
использ
овано
для
чтения
регистр
а
статуса

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

Регистры
0x00 CONFIG

Регистр настроек.

7 6 5 4 3 2 1 0

P
E P
R
MA MA MAS N C W
I
SK_ SK_ K_M _ R R
- M
RX_ TX_ AX_ C C _
_
DR DS RT R O U
R
C P
X

MASK_RX_DR, MASK_TX_DS, MASK_MAX_RT - маскировка источников прерываний. При установке одного из


этих бит в единицу, соответствующее событие и установка соответствующего бита в регистре STATUS не будет
генерировать сигнал прерывания на линии IRQ.
EN_CRC - включает расчёт контрольной суммы (CRC). Если включено автоподтверждение приёма путём установки
хотя бы одного бита в регистре EN_AA, то значение этого бита устанавливается в единицу автоматически.
CRCO - Определяет размер поля контрольной суммы (CRC): 0 - 1 байт; 1 - 2 байта
PWR_UP - Включает питание. Если этот бит равен нулю, то чип находится в режиме отключения, и потребляет
около 0,9мкА, в таком режиме радиообмен невозможен.
После включения питания до начала работы в режиме приёмника или передатчика (т.е. до выставления высокого
уровня на линии CE) необходимо выдержать паузу, достаточную для выхода осциллятора на режим, типично 1.5мс
(подробнее см. в разделе "Необходимые временные задержки").
PRIM_RX - Выбор режима: 0 - PTX (передатчик) 1 - PRX (приёмник).
Если переход в режим передатчика осуществляется сразу после завершения приёма пакета (появления
прерывания RX_DR), и автоматическое подтверждение приёма включено, то необходимо обеспечить паузу с момента
появления прерывания до перевода в режим PTX, во время которой чип отправит пакет подтверждения, типично не
более 203 мкс (подробнее см. в разделе "Необходимые временные задержки").

0x01 EN_AA

Включает автоподтверждение приёма.

7 6 5 4 3 2 1 0
EN EN EN EN EN EN
AA AA AA AA AA AA
- -
_P _P _P _P _P _P
5 4 3 2 1 0

ENAA_Px - установка бита включает автоматическую отправку подтверждения приёма данных по


соответствующему каналу.

0x02 EN_RXADDR

Выбирает активные каналы приёмника.

7 6 5 4 3 2 1 0

ER ER ER ER ER ER
- - X_ X_ X_ X_ X_ X_
P5 P4 P3 P2 P1 P0

ERX_Px - включает приём данных по соответствующему каналу.


При использовании устройства в качестве передатчика с включенной функцией автоподтверждения, ответ от
удалённого устройства принимается на канале 0. Поэтому бит ERX_P0 должен быть установлен в 1, для
использования передачи с автоподтвреждением.

0x03 SETUP_AW

Задаёт длину поля адреса.

7 6 5 4 3 2 1 0

- - - - - - AW

AW - два бита, задающие длину поля адреса: 01 - 3 байта; 10 - 4 байта; 11 - 5 байт.
Эта настройка влияет на передачу и приём пакетов по всем каналам. Данная настройка у передающего и
принимающего устройств должна быть идентичной.

0x04 SETUP_RETR

Настройка параметров автоматического повтора отправки.

7 6 5 4 3 2 1 0

ARD ARC

ARD - четыре бита, задающие время ожидания подтверждения приёма перед повторной отправкой:  0000 -
250мкс; 0001 - 500мкс; 0010 - 750мкс; ... ; 1111 - 4000мкс;
Значение поля ARD необходимо выбирать в зависимости от скорости обмена и количества данных, передаваемых с
пакетом подтверждения. Если данные с пакетом подтверждения не передаются, то для скоростей обмена 1 и 2
Мбит/с достаточное время ожидания 250мкс. Если вместе с пакетом подтверждения на скорости 1Мбит/с передаётся
более 5 байт данных, или более 15 на скорости 2Мбит/с, или скорость обмена выбрана 250кбит/с, то необходимо
выбрать паузу 500мкс.
ARC - четыре бита, задающие количество автоматических повторов отправки. 0000 - автоповтор отключен; 0001 -
возможен однократный повтор; ... ; 1111 - возможно до 15 повторов.

0x05 RF_CH

Регистр задаёт номер радиоканала - частоту несущей с шагом 1Мгц. Радиочастота несущей вычисляется по
формуле 2400 + RF_CH МГц.
Допустимые значения от 0 до 125. При обмене на скорости 2Мбит/с, частота должна отличатся от частоты
используемой другими устройствами минимум на 2 МГц.

0x06 RF_SETUP

Задаёт настройки радиоканала.

7 6 5 4 3 2 1 0

CONT RF_D PLL RF_D


RF_P
_WAV - R_LO _LO R_HI -
WR
E W CK GH

CONT_WAVE - Непрерывная передача несущей. Предназначено для тестирования


RF_DR_LOW - Включает низкую скорость передачи 250кбит/с. При этом бит RF_DR_HIGH должен быть 0
PLL_LOCK - предназначено для тестирования.
RF_DR_HIGH - Выбор скорости обмена (при значении бита RF_DR_LOW = 0): 0 - 1Мбит/с; 1 - 2Мбит/с
RF_PWR - 2 бита, задающих мощность передатчика: 00 - -18dBm; 01 - -12dBm; 10 - -6dBm; 11 - 0dBm
0x07 STATUS

Регистр статуса. Его значение также передаётся на линии MISO одновременно с передачей байта команды по
интерфейсу SPI.

7 6 5 4 3 2 1 0

RX TX
MAX TX_F
- _D _D RX_P_NO
_RT ULL
R S

RX_DR - Прерывание по получению пакета. Бит устанавливается в единицу, когда усройство в режиме приняло
адресованый ему пакет с совпавшей контрольной суммой. Бит сбрасывается путём записи в него значения 1.
Принятый пакет доступен в очереди приёмника, и может быть прочитан командой R_RX_PAYLOAD, либо удалён
командой FLUSH_RX.
TX_DS - Прерывание по успешной отправке пакета. Бит устанавливается в единицу, когда устройство в режиме
передатчика успешно отправило пакет и, если включено автоподтверждение, приняло подтверждение получения.
После успешной отправки пакет удаляется из очереди передатчика. Бит сбрасывается путём записи в него значения
1.
MAX_RT - Прерывание по превышению числа попыток повторной отправки. Бит устанавливается в единицу, когда
устройство в режиме передатчика предприняло заданное в регистре SETUP_RETR количество попыток отправки, но
так и не получило подтверждение от удалённого устройства. Передаваемый пакет остаётся в очереди передатчика.
Для удаления его можно воспользоваться командой FLUSH_TX. Дальнейшая коммуникация невозможна, пока этот бит
установлен. Бит сбрасывается путём записи в него значения 1.
Пока любой из бит RX_DR, TX_DS, MAX_RT установлен в единицу и соответствующее ему прерывание не
запрещено (т.е. соответствующие из бит MASK_RX_DR, MASK_TX_DS, MASK_MAX_RT в регистре CONFIG равны
нулю), то на выходе IRQ чипа устанавливается низкий уровень. Для сброса значений этих бит, необходимо записать
регистр STATUS с этими битами, установленными в 1.

RX_P_NO - три бита кодирующие номер канала, пакет по которому доступен в начале очереди приёмника для
чтения. Значения 000 - 101 кодируют каналы с 0 по 5, соответственно, значение 111 указывает, что очередь
приёмника пуста.
TX_FULL - значение 1 этого бита показывает, что в очереди передатчика нет свободных слотов.

0x08 OBSERVE_TX

Регистр контроля передатчика

7 6 5 4 3 2 1 0

PLOS_CNT ARC_CNT

PLOS_CNT - четыре бита, значение которых увеличивается, вплоть до достижения 15, при каждой отправке, на
которую не получено ожидаемое подтверждение. Значение сбрасывается при записи в регистр RF_CH.
ARC_CNT - четыре бита, возвращающие количество предпринятых повторов отправки при передаче последнего
пакета. Сбрасывается в 0, когда начинается отправка очередного пакета.
0x09 RPD

Оценка мощности принимаемого сигнала

7 6 5 4 3 2 1 0

- - - - - - - RPD

RPD - младший бит принимает значение 1, если чип находится в режиме приёмника, и уровень принимаемого
сигнала превышает -64dBm

0x0A RX_ADDR_P0

40-битный (5 байт) регистр, используемый для указания адреса канала 0 приёмника. Этот канал используется для
приёма автоподтверждений в режиме передатчика. Автоподтверждения высылаются принимающей стороной с
указанием собственного адреса. Поэтому значение этого регистра должно соответствовать значению
регистра TX_ADDR для корректной работы в режиме передатчика.
Реальная используемая ширина адреса задаётся в регистре SETUP_AW.
Значение регистра записывается и читается, начиная с младших байт. Если записано менее 5 байт, то старшие
байты остаются неизменными.
Значение регистра по умолчанию: 0xE7E7E7E7E7

0x0B RX_ADDR_P1

40-битный (5 байт) регистр, используемый для указания адреса канала 1 приёмника. Старшие 4 байта этого
регистра являются общими для адресов на каналах 1 - 5.
Реальная используемая ширина адреса задаётся в регистре SETUP_AW.
Значение регистра записывается и читается, начиная с младших байт. Если записано менее 5 байт, то старшие
байты остаются неизменными.
Значение регистра по умолчанию: 0xC2C2C2C2C2

0x0C-0x0F RX_ADDR_P2 - RX_ADDR_P5

8-битные регистры, задающие значения младшего байта адреса для каналов 2-5. Значения старших 32 бит берутся
из регистра RX_ADDR_P1.
Значение регистров по умолчанию: 0xC3, 0xC4, 0xC5, 0xC6, соответственно.

0x10 TX_ADDR

40-битный (5 байт) регистр, используемый в режиме передатчика в качестве адреса удалённого устройства. При
включенном режиме автоподтверждения, удалённое устройство ответит подтверждением с указанием своего же
адреса. Это подтверждение принимается на канале 0, поэтому для успешной передачи, значение
регистра RX_ADDR_P0 должно быть идентично этому.
Реальная используемая ширина адреса задаётся в регистре SETUP_AW.
Значение регистра записывается и читается, начиная с младших байт. Если записано менее 5 байт, то старшие
байты остаются неизменными.
Значение регистра по умолчанию: 0xE7E7E7E7E7

0x11-0x16 RX_PW_P0 - RX_PW_P5


8-битные регистры, задающие размер данных, принимаемых по каналам, соответственно 0-5, если не включена
поддержка произвольной длины пакетов в регистрах DYNPD и FEATURE. Значение 0 указывает что канал не
используется. Допустимы значения длины от 1 до 32.

0x17 FIFO_STATUS

Состояние очередей FIFO приёмника и передатчика

7 6 5 4 3 2 1 0

TX RX
TX_ TX_ RX_
_F _F
- REU EMP - - EMP
UL UL
SE TY TY
L L

TX_REUSE Признак готовности последнего пакета для повтрной отправки. Устанавливается командой


REUSE_TX_PL.
TX_FULL Флаг переполнения FIFO очереди передатчика: 0 - есть свободное место в очереди; 1 - очередь
переполнена.
TX_EMPTY Флаг освобождения FIFO очереди передатчика: 0 - в очереди есть данные; 1 - очередь пуста.
RX_FULL Флаг переполнения FIFO очереди приёмника: 0 - есть свободное место в очереди; 1 - очередь
переполнена.
RX_EMPTY - Флаг освобождения FIFO очереди приёмника: 0 - в очереди есть данные; 1 - очередь пуста.

0x1C DYNPD

Разрешение использования пакетов произвольной длины.

7 6 5 4 3 2 1 0

DP DP DP DP DP DP
- - L_ L_ L_ L_ L_ L_
P5 P4 P3 P2 P1 P0

DPL_Px разрешает приём пакетов произвольной длины по соответствующему каналу. При этом такая опция
должна быть включена установленным битом EN_DPL в регистре FEATURE, а также включено автоподтверждение
установкой соответствующего бита ENAA_Px в регистре EN_AA
В случае если бит принимает значение 0, то размер данных в принимаемых пакетах должен быть равен значению
соответствующего регистра RX_PW_Px

0x1D FEATURE

Регистр опций
7 6 5 4 3 2 1 0

EN
EN_AC EN_DYN
- - - - - _D
K_PAY _ACK
PL

EN_DPL включает поддержку приёма и передачи пакетов с размером поля данных произвольной длины.
В этом случае приём пакетов произвольной длины по каналам должен быть разрешён в регистре DYNPD и
включено автоподтверждение в регистре EN_AA.
Если опция отключена, то размер принимаемых данных задаётся значением регистров RX_PW_P0 - RX_PW_P5.
При передаче также передаётся поле, указывающее длительность пакета. Длина передаваемого пакета
определяется размером записанных командой W_TX_PAYLOAD данных и она должна соответствовать настройкам
принимающей стороны.

EN_ACK_PAY включает поддержку передачи данных с пакетами подтверждения.


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

EN_DYN_ACK разрешает передавать пакеты, не требующие подтверждения приёма.


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

Структура пакета
Чипы nRF24L01+ автоматически производят сборку пакета при передаче, контроль адреса, проверку контрольной
суммы и разбор пакета при приёме.
Поэтому передача и приёма данных осуществляется без оглядки на истинную структуру пакета, передаваемого в
эфир, чья структура выглядит следующим образом:

Преамбу Адр Управляющ Данн


CRC
ла ес ее поле ые

1-2
3-5 0-32
1 байт 9 бит байт
байт байта
а

Значения всех полей передаются вначале старшим битом.

Преамбула

Последовательность бит 01010101, или 10101010, служащая для синхронизации приёмника. Если старший бит в
адресе 1, то преамбула 10101010, иначе 01010101
Адрес

Длина поля адреса задаётся значением бит AW в регистре SETUP_AW. Чтобы исключить ложные обнаружения
адреса (что может привести к пропуску реального адреса), желательно чтобы адрес удовлетворял следующим
условиям:
- Старшие биты адреса не должны быть чередующимися значениями нулей и единиц, поскольку в таком случае
часть преамбулы может быть спутана с адресом.
- Адрес должен содержать несколько переходов из нуля в единицу и обратно, иначе за адрес могут быть приняты
помехи.

Пакеты подтверждения приёма высылаются приёмником с указанием собственного адреса в этом поле. Поэтому
передающая сторона должна быть настроена на получение пакетов по этому же адресу на канале 0, т.е. значение
регистра RX_ADDR_P0 должно быть равно значению регистра TX_ADDR.

Управляющее поле

имеет следующую структуру

Длина данных PID NO_ACK

6 бит 2 бита 1 бит

Длина данных - размер поля "Данные" в пакете. Если опция данных произвольной длины отключена, принимает
значение 33 (100001), в этом случае длина данных на принимающей стороне определяется значением
соответствующего регистра RX_PW_Px. Значения в диапазоне 1-32 кодируют размер данных в режиме произвольной
длины, значение 0 указывает на отсутствие данных и используется в пакетах подтверждения. Режим произвольной
длины должен быть включен у передающей стороны для канала 0, чтобы принимать пакеты подтверждения.
PID - двухбитное поле, значение которого циклически увеличивается на 1 при отправке нового пакета. В случае
если принимающая сторона приняла пакет, но отправленное подтверждение о приёме не дошло до отправляющей
стороны, может быть предпринята повторная отправка с таким же значением PID, как при первой попытке. Если
приёмник получает пакет, где поле PID равно полученному в предыдущий раз и значение поля CRC также идентично
последнему полученному, то автоматически отправляется подтверждение о получении, но полученные данные
считаются повтором и игнорируются, не помещаются в очередь FIFO приёмника, и прерывание TX_DS в этом случае
не появляется.
NO_ACK - флаг указывающий получателю на то, что подтверждение получения пакета высылать не требуется.
Сами пакеты подтверждения маркируются этим флагам. Также можно отправить пакет не требующий подтверждения
командой W_TX_PAYLOAD_NOACK, если в регистре FEATURE установлен бит EN_DYN_ACK.

Данные

Поле данных содержит, собственно, передаваемые данные. Их длина определяется количеством данных,
загруженных одной из команд W_TX_PAYLOAD, W_TX_PAYLOAD_NOACK, W_ACK_PAYLOAD.

CRC

Контрольная сумма, размер определяется значением бита CRCO в регистре CONFIG. Рассчитывается по полю


адреса, управляющему полю и полю данных. Если при приёме пакета контрольная сумма не совпала, то пакет
игнорируется, никаких действий не предпринимается.
Краткое описание алгоритма приёма
Переключение в режим приёмника осуществляется установкой бита PRIM_RX в регистре CONFIG.
Соответствующие каналы приёма должны быть разрешены в регистрах EN_AA и EN_RXADDR, и их адреса
настроены в регистрах RX_ADDR_Pх.
Прослушивание эфира начинается с появлением на линии CE высокого уровня. Приёмник анализирует эфир и
пытается выделить адресованные ему пакеты с совпадающей контрольной суммой. Когда очередной такой пакет
получен, выставляется бит RX_DR в регистре STATUS, и на линии прерывания появляется низкий уровень. Три бита
начиная с RX_P_NO в регистре STATUS показывают номер канала, по которому пришёл пакет. Прочитать
содержимое полученного пакета можно командой R_RX_PAYLOAD. Сбросить бит RX_DR в регистре STATUS можно
путём записи в него единицы.

Краткое описание алгоритма передачи


Переключение в режим передатчика осуществляется записью значения 0 в бит PRIM_RX в регистре CONFIG. В
регистры TX_ADDR и RX_ADDR_P0 должен быть загружен адрес удалённой стороны.
После этого, данные для отправки помещаются в очередь передатчика командой W_TX_PAYLOAD. Начало передачи
инициализируется кратким, но не менее 10мкс импульсом на линии CE.
Если пакет передан успешно и подтверждение получено, в регистре STATUS выставляется бит TX_DS, если
превышено допустимое количество повторов, а подтверждение передачи не получено, выставляется бит MAX_RT.
Обе ситуации приводят к выставлению на линии IRQ низкого уровня.
Если выставлен бит MAX_RT, то переданный пакет остаётся в очереди передатчика, удалить его можно
командой FLUSH_TX. Сбросить биты TX_DS и MAX_RT в регистре STATUS можно путём записи в них единиц. Пока
бит MAX_RT установлен, дальнейший радиообмен невозможен.

Необходимые временные задержки


После включения питания (установки бита PWR_UP в регистре CONFIG) происходит запуск осциллятора. До
начала работы в режиме приёмника или передатчика (т.е. до выставления высокого уровня на линии CE) необходимо
выдержать паузу, достаточную для выхода осциллятора на режим.
Необходимое для этого время пропорционально эквивалентной индуктивности используемого резонатора. Для
типичных резонаторов со значением этого параметра не превышающего 30 мГн, достаточно задержки 1.5мс. При
значениях эквивалентной индуктивности больше, необходимое время задержки пропорционально возрастает

Эквивалентная индуктивность Необходимое время


резонатора задержки

30мГн и меньше (типичный вариант) 1.5мс

60мГн 3мс

90мГн 4.5мс
Если включено автоматическое подтверждение приёма, то прерывание RX_DR появляется сразу по получению
пакета, после этого nRF24L01+ автоматически переходит в режим передатчика, после чего в течение 130мкс
происходит выравнивание частоты блока PLL, и затем осуществляется отправка пакета подтверждения.
В этом случае, если отправка ответа предполагается сразу после получения пакета (появления прерывания
RX_DR), то перед сбросом бита PRIM_RX и переходом в режим передатчика необходимо выдержать паузу
достаточную, чтобы чип успел завершить передачу пакета подтверждения. Рассчитать это время можно по формуле:
Время_подтверждения = 130мкс + ((длина_адреса + длина_CRC + размер_данных_в_подтверждении)
* 8 + 17) / скорость_обмена
Это время отсчитывается с момента появления прерывания RX_DR. Поскольку управляющий микроконтроллер на
обработку прерывания, чтение пакета данных, подготовку ответа и прочее также затратит некоторое время,
реальная необходимая задержка может быть значительно меньше.
Типичное время, необходимое для отправки подтверждений не включающих в себя передаваемых данных:

Настройки Время, мкс

1Мбит/с, адрес 3 байта, CRC 1 байт 179

1Мбит/с, адрес 5 байт, CRC 2 байта 203

2Мбит/с, адрес 5 байт, CRC 2 байта 166.5

Определения для языка C

/* Команды */

#define R_REGISTER 0x00 // + n Прочитать регистр n


#define W_REGISTER 0x20 // + n Записать регистр n
#define R_RX_PAYLOAD 0x61 // Принять данные данные из верхнего слота очереди
приёмника.
#define W_TX_PAYLOAD 0xA0 // Записать в очередь передатчика данные для отправки
#define FLUSH_TX 0xE1 // Сбросить очередь передатчика
#define FLUSH_RX 0xE2 // Сбросить очередь приёмника
#define REUSE_TX_PL 0xE3 // Использовать повторно последний переданный пакет
#define R_RX_PL_WID 0x60 // Прочитать размер данных принятого пакета в начале
очереди приёмника.
#define W_ACK_PAYLOAD 0xA8 // + p Записать данные для отправки с пакетом
подтверждения по каналу p.
#define W_TX_PAYLOAD_NOACK 0xB0 // Записать в очередь передатчика данные, для отправки
без подтверждения
#define NOP 0xFF // Нет операции. Может быть использовано для чтения
регистра статуса

/* Регистры */

#define CONFIG 0x00 // Регистр настроек


#define EN_AA 0x01 // Выбор автоподтверждения
#define EN_RXADDR 0x02 // Выбор каналов приёмника
#define SETUP_AW 0x03 // Настройка размера адреса
#define SETUP_RETR 0x04 // Настройка повторной отправки
#define RF_CH 0x05 // Номер радиоканала, на котором осуществляется работа. От 0 до
125.
#define RF_SETUP 0x06 // Настройка радиоканала
#define STATUS 0x07 // Регистр статуса.
#define OBSERVE_TX 0x08 // Количество повторов передачи и потерянных пакетов
#define RPD 0x09 // Мощность принимаемого сигнала. Если младший бит = 1, то
уровень более -64dBm
#define RX_ADDR_P0 0x0A // 3-5 байт (начиная с младшего байта). Адрес канала 0
приёмника.
#define RX_ADDR_P1 0x0B // 3-5 байт (начиная с младшего байта). Адрес канала 1
приёмника.
#define RX_ADDR_P2 0x0C // Младший байт адреса канала 2 приёмника. Старшие байты из
RX_ADDR_P1
#define RX_ADDR_P3 0x0D // Младший байт адреса канала 3 приёмника. Старшие байты из
RX_ADDR_P1
#define RX_ADDR_P4 0x0E // Младший байт адреса канала 4 приёмника. Старшие байты из
RX_ADDR_P1
#define RX_ADDR_P5 0x0F // Младший байт адреса канала 5 приёмника. Старшие байты из
RX_ADDR_P1
#define TX_ADDR 0x10 // 3-5 байт (начиная с младшего байта). Адрес удалённого
устройства для передачи
#define RX_PW_P0 0x11 // Размер данных при приёме по каналу 0: от 1 до 32. 0 - канал
не используется.
#define RX_PW_P1 0x12 // Размер данных при приёме по каналу 1: от 1 до 32. 0 - канал
не используется.
#define RX_PW_P2 0x13 // Размер данных при приёме по каналу 2: от 1 до 32. 0 - канал
не используется.
#define RX_PW_P3 0x14 // Размер данных при приёме по каналу 3: от 1 до 32. 0 - канал
не используется.
#define RX_PW_P4 0x15 // Размер данных при приёме по каналу 4: от 1 до 32. 0 - канал
не используется.
#define RX_PW_P5 0x16 // Размер данных при приёме по каналу 5: от 1 до 32. 0 - канал
не используется.
#define FIFO_STATUS 0x17 // Состояние очередей FIFO приёмника и передатчика
#define DYNPD 0x1C // Выбор каналов приёмника для которых используется
произвольная длина пакетов.
#define FEATURE 0x1D // Регистр опций

/* Биты регистров */

// CONFIG
#define MASK_RX_DR 6 // Запрещает прерывание по RX_DR (получение пакета)
#define MASK_TX_DS 5 // Запрещает прерывание по TX_DS (завершение отправки пакета)
#define MASK_MAX_RT 4 // Запрещает прерывание по MAX_RT (превышение числа повторных
попыток отправки)
#define EN_CRC 3 // Включает CRC
#define CRCO 2 // Размер поля CRC: 0 - 1 байт; 1 - 2 байта
#define PWR_UP 1 // Включение питания
#define PRIM_RX 0 // Выбор режима: 0 - PTX (передатчик) 1 - PRX (приёмник)

// EN_AA
#define ENAA_P5 5 // Включает автоподтверждение данных, полученных по каналу 5
#define ENAA_P4 4 // Включает автоподтверждение данных, полученных по каналу 4
#define ENAA_P3 3 // Включает автоподтверждение данных, полученных по каналу 3
#define ENAA_P2 2 // Включает автоподтверждение данных, полученных по каналу 2
#define ENAA_P1 1 // Включает автоподтверждение данных, полученных по каналу 1
#define ENAA_P0 0 // Включает автоподтверждение данных, полученных по каналу 0

// EN_RXADDR
#define ERX_P5 5 // Включает канал 5 приёмника
#define ERX_P4 4 // Включает канал 4 приёмника
#define ERX_P3 3 // Включает канал 3 приёмника
#define ERX_P2 2 // Включает канал 2 приёмника
#define ERX_P1 1 // Включает канал 1 приёмника
#define ERX_P0 0 // Включает канал 0 приёмника

// SETUP_AW
#define AW 0 // Два бита, Выбирает ширину поля адреса: 1 - 3 байта; 2 - 4 байта; 3 - 5
байт.

#define SETUP_AW_3BYTES_ADDRESS (1 << AW)


#define SETUP_AW_4BYTES_ADDRESS (2 << AW)
#define SETUP_AW_5BYTES_ADDRESS (3 << AW)

// SETUP_RETR
#define ARD 4 // 4 бита. Задаёт значение задержки перед повторной отправкой пакета: 250
x (n + 1) мкс
#define ARC 0 // 4 битай. Количество повторных попыток отправки, 0 - повторная отправка
отключена.

#define SETUP_RETR_DELAY_250MKS (0 << ARD)


#define SETUP_RETR_DELAY_500MKS (1 << ARD)
#define SETUP_RETR_DELAY_750MKS (2 << ARD)
#define SETUP_RETR_DELAY_1000MKS (3 << ARD)
#define SETUP_RETR_DELAY_1250MKS (4 << ARD)
#define SETUP_RETR_DELAY_1500MKS (5 << ARD)
#define SETUP_RETR_DELAY_1750MKS (6 << ARD)
#define SETUP_RETR_DELAY_2000MKS (7 << ARD)
#define SETUP_RETR_DELAY_2250MKS (8 << ARD)
#define SETUP_RETR_DELAY_2500MKS (9 << ARD)
#define SETUP_RETR_DELAY_2750MKS (10 << ARD)
#define SETUP_RETR_DELAY_3000MKS (11 << ARD)
#define SETUP_RETR_DELAY_3250MKS (12 << ARD)
#define SETUP_RETR_DELAY_3500MKS (13 << ARD)
#define SETUP_RETR_DELAY_3750MKS (14 << ARD)
#define SETUP_RETR_DELAY_4000MKS (15 << ARD)

#define SETUP_RETR_NO_RETRANSMIT (0 << ARC)


#define SETUP_RETR_UP_TO_1_RETRANSMIT (1 << ARC)
#define SETUP_RETR_UP_TO_2_RETRANSMIT (2 << ARC)
#define SETUP_RETR_UP_TO_3_RETRANSMIT (3 << ARC)
#define SETUP_RETR_UP_TO_4_RETRANSMIT (4 << ARC)
#define SETUP_RETR_UP_TO_5_RETRANSMIT (5 << ARC)
#define SETUP_RETR_UP_TO_6_RETRANSMIT (6 << ARC)
#define SETUP_RETR_UP_TO_7_RETRANSMIT (7 << ARC)
#define SETUP_RETR_UP_TO_8_RETRANSMIT (8 << ARC)
#define SETUP_RETR_UP_TO_9_RETRANSMIT (9 << ARC)
#define SETUP_RETR_UP_TO_10_RETRANSMIT (10 << ARC)
#define SETUP_RETR_UP_TO_11_RETRANSMIT (11 << ARC)
#define SETUP_RETR_UP_TO_12_RETRANSMIT (12 << ARC)
#define SETUP_RETR_UP_TO_13_RETRANSMIT (13 << ARC)
#define SETUP_RETR_UP_TO_14_RETRANSMIT (14 << ARC)
#define SETUP_RETR_UP_TO_15_RETRANSMIT (15 << ARC)

// RF_SETUP
#define CONT_WAVE 7 // (Только для nRF24L01+) Непрерывная передача несущей (для
тестов)
#define RF_DR_LOW 5 // (Только для nRF24L01+) Включает скорость 250кбит/с. RF_DR_HIGH
должен быть 0
#define PLL_LOCK 4 // Для тестов
#define RF_DR_HIGH 3 // Выбор скорости обмена (при значении бита RF_DR_LOW = 0): 0 -
1Мбит/с; 1 - 2Мбит/с
#define RF_PWR 1 // 2бита. Выбирает мощность передатчика: 0 - -18dBm; 1 - -16dBm; 2
- -6dBm; 3 - 0dBm

#define RF_SETUP_MINUS18DBM (0 << RF_PWR)


#define RF_SETUP_MINUS12DBM (1 << RF_PWR)
#define RF_SETUP_MINUS6DBM (2 << RF_PWR)
#define RF_SETUP_0DBM (3 << RF_PWR)

#define RF_SETUP_1MBPS (0 << RF_DR_HIGH)


#define RF_SETUP_2MBPS (1 << RF_DR_HIGH)
#define RF_SETUP_250KBPS (1 << RF_DR_LOW) // этот режим не должен использоваться с
контролем доставки

// STATUS
#define RX_DR 6 // Флаг получения новых данных в FIFO приёмника. Для сброса флага
нужно записать 1
#define TX_DS 5 // Флаг завершения передачи. Для сброса флага нужно записать 1
#define MAX_RT 4 // Флаг превышения установленного числа повторов. Без сброса (записать
1) обмен невозможен
#define RX_P_NO 1 // 3 бита. Номер канала, данные для которого доступны в FIFO
приёмника. 7 - FIFO пусто.
#define TX_FULL_STATUS 0 // Признак заполнения FIFO передатчика: 1 - заполнено; 0 - есть
доступные слоты
// (переименовано из TX_FULL во избежание путаницы с одноимённым битом из
регистра FIFO_STATUS)

// OBSERVE_TX
#define PLOS_CNT 4 // 4 бита. Общее количество пакетов без подтверждения. Сбрасывается
записью RF_CH
#define ARC_CNT 0 // 4 бита. Количество предпринятых повторов при последней отправке.

// FIFO_STATUS
#define TX_REUSE 6 // Признак готовности последнего пакета для повторной отправки.
#define TX_FULL_FIFO 5 // Флаг переполнения FIFO очереди передатчика.
// (переименовано из TX_FULL во избежание путаницы с одноимённым битом из
регистра STATUS)
#define TX_EMPTY 4 // Флаг освобождения FIFO очереди передатчика.
#define RX_FULL 1 // Флаг переполнения FIFO очереди приёмника.
#define RX_EMPTY 0 // Флаг освобождения FIFO очереди приёмника.

// DYNDP
#define DPL_P5 5 // Включает приём пакетов произвольной длины по каналу 5
#define DPL_P4 4 // Включает приём пакетов произвольной длины по каналу 4
#define DPL_P3 3 // Включает приём пакетов произвольной длины по каналу 3
#define DPL_P2 2 // Включает приём пакетов произвольной длины по каналу 2
#define DPL_P1 1 // Включает приём пакетов произвольной длины по каналу 1
#define DPL_P0 0 // Включает приём пакетов произвольной длины по каналу 0

// FEATURE
#define EN_DPL 2 // Включает поддержку приёма и передачи пакетов произвольной длины
#define EN_ACK_PAY 1 // Разрешает передачу данных с пакетами подтверждения приёма
#define EN_DYN_ACK 0 // Разрешает использование W_TX_PAYLOAD_NOACK

Подключение SPI
Чип nRF24L01+ обменивается данными с управляющим микроконтроллером по шине SPI в режиме 0 (CPHA=0,
CPOL=0). Это значит, что на линии SCK до начала обмена выдерживается низкий уровень. Начинается обмен с
установки низкого уровня на линии CSN, при этом и ведущая и ведомая сторона выставляют на линиях MOSI и MISO
соответственно, значения старших бит передаваемых байт. При нарастающем фронте на линии SCK, происходит
чтение выставленных значений противоположными сторонами. При спадающем фронте SCK, выставляются значения
следующих бит. Передача идёт от старших бит в байте к младшим. Сеанс обмена завершается установкой высокого
уровня на линии CSN.
nRF24L01+ поддерживает обмен по SPI на частоте до 10МГц. Однако, в случае большой паразитной емкости линий
данных и при наличии помех, может потребоваться уменьшить частоту.

Примеры ниже показывают различные варианты подключения микроконтроллера к шине SPI и определяют две
функции:
void spi_init() - производит первичную настройку интерфейса SPI;
uint8_t spi_send_recv(uint8_t data) - отправляет 1 байт по SPI, по линии MOSI, дожидается окончания обмена и
возвращает байт принятый по линии MISO.
В этих примерах не определяется использование линии CSN.

Использование аппаратного интерфейса SPI


Аппаратный интерфейс SPI микроконтроллеров AVR позволяет организовать обмен на частоте до половины
частоты процессора.
При инициализации интерфейса в режиме «мастера», необходимо вручную установить соответствующие биты в
регистрах направлений (DDRx). Выводы MOSI, SCK и SS должны быть настроены на выход (соответствующие биты в
регистре DDR – единицы), направление вывода MISO переопределяется на «вход» самим интерфейсом.
Когда интерфейс SPI в микроконтроллерах AVR работает в режиме «мастера», вывод SS управляется
программистом самостоятельно и не влияет на работу интерфейса, поэтому нет нужды подключать именно этот
выход к входу CSN управляемого чипа. Однако, этот вывод микроконтроллера ДОЛЖЕН быть настроен на «выход»,
поскольку, в случае если он настроен на «вход», и на нём появится низкий уровень, то интерфейс SPI прервёт обмен
и перейдёт в режим «ведомого». Во избежание путаницы, рекомендую использовать этот вывод для подключения к
входу CSN.

Пример кода инициализации интерфейса SPI для микроконтроллеров AVR


#define SPI_DDR DDRB

#define SPI_SS 2
#define SPI_MOSI 3
#define SPI_SCK 5

// Инициализация интерфейса
void spi_init() {
SPI_DDR |= (1 << SPI_MOSI) | (1 << SPI_SCK) | (1 << SPI_SS);
SPCR = (1 << SPE) | (1 << MSTR); // режим 0, мастер, частота 1/4 от частоты ЦП
}

// Передаёт и принимает 1 байт по SPI, возвращает полученное значение


uint8_t spi_send_recv(uint8_t data) {
SPDR = data;
while (!(SPSR & (1 << SPIF)));
return SPDR;
}
Значения SPI_DDR, SPI_SS, SPI_MOSI, SPI_SCK в примере даны для МК ATmega8(A),
ATmega48/88/168/328(P/A/PA). Для других МК подставьте нужные значения

Использование аппаратного интерфейса USART в режиме SPI-


master
Некоторые микроконтроллеры из серии AVR (напр. ATmega48/88/168/328(P/A/PA)) позволяют переключить
интерфейс USART в режим SPI-master. Вывод TX микроконтроллера подключается к линии MOSI, RX – к MISO, XCK
– к SCK.
Скорость обмена задаётся значением пары регистров UBRRnL, UBBRnH. На время инициализации рекомендуется
установить их значение в 0, дабы на линии XCK появился нужный уровень.
Направление выводов TX и RX переопределяется интерфейсом, однако направление вывода XCK нужно задать
на «выход» явно.
Интерфейс использует буферизацию принимаемых и отправляемых данных, однако для работы в режиме SPI
удобнее использовать не буферизированный обмен, дожидаясь окончания приёма.

Пример кода инициализации интерфейса SPI для микроконтроллеров AVR


#define USART_DDR DDRD

#define USART_XCK 4

// Инициализация интерфейса USART0 в режиме SPI-master


void spi_init() {
UBRR0 = 0;
USART_DDR |= (1 << USART_XCK);
UCSR0C = (1 << UMSEL01) | (1 << UMSEL00); // выбор режима SPI-master
UCSR0B = (1 << RXEN0) | (1 << TXEN0); // Включение приёмника и передатчика
UBRR0 = 1; // Выбор частоты интерфейса 1/4 от частоты ЦП
}

// Передаёт и принимает 1 байт по SPI, возвращает полученное значение


uint8_t spi_send_recv(uint8_t data) {
UDR0 = data;
while (!(UCSR0A & (1 << RXC0)));
return UDR0;
}
Значения USART_DDR и USART_XCK в примере даны для МК ATmega48/88/168/328(P/A/PA). Для других МК
подставьте нужные значения

Использование программной реализации интерфейса SPI


Программная реализация интерфейса, хоть и накладывает ограничения на максимальную скорость обмена, но
позволяет подключать ведомое устройство к любым доступным портам ввода/вывода.

Пример кода инициализации интерфейса SPI для микроконтроллеров AVR


#define SWSPI_DDR DDRB
#define SWSPI_PORT PORTB
#define SWSPI_PIN PINB

#define SWSPI_MOSI 6
#define SWSPI_MISO 7
#define SWSPI_SCK 8

// Инициализация программного интерфейса SPI


void spi_init() {
SWSPI_PORT &= ~((1 << SWSPI_MOSI) | (1 << SWSPI_SCK));
SWSPI_DDR |= (1 << SWSPI_MOSI) | (1 << SWSPI_SCK);
SWSPI_DDR &= ~ (1 << SWSPI_MISO);
SWSPI_PORT |= (1 << SWSPI_MISO); // подтяжка на линии MISO
}

// Передаёт и принимает 1 байт по SPI, возвращает полученное значение


uint8_t spi_send_recv(uint8_t data) {
for (uint8_t i = 8; i > 0; i--) {
if (data & 0x80)
SWSPI_PORT |= (1 << SWSPI_MOSI); // передача единички
else
SWSPI_PORT &= ~(1 << SWSPI_MOSI); // передача нулика
SWSPI_PORT |= (1 << SWSPI_SCK);
data <<= 1;
if (SWSPI_PIN & (1 << SWSPI_MISO)) // Чтение бита на линии MISO
data |= 1;
SWSPI_PORT &= ~(1 << SWSPI_SCK);
}
return data;
}

Взаимодействие с nRF24L01+
Функции настройки портов и базового взаимодействия
Линия CSN, определяющая сеанс обмена по интерфейсу SPI, имеет активным низкий уровень. Линия CE,
наоборот, активна по высокому уровню. Чтобы избежать путанницы определим
функции radio_assert_ce, radio_deassert_ce, csn_assert, csn_deassert, которые будут переводить линии
ce/csn к активному (assert) и неактивному (deassert) уровню.
Также ниже будут определены функции читающие/записывающие регистры и вызывающие выполнение команд
чипа nRF24L01+.
Но для начала нужно определить порты, на которых будет происходить обмен.
#define RADIO_PORT PORTD
#define RADIO_DDR DDRD
#define RADIO_PIN PIND

#define RADIO_CSN 1
#define RADIO_CE 2
#define RADIO_IRQ 3

// Выбирает активное состояние (высокий уровень) на линии CE


inline void radio_assert_ce() {
RADIO_PORT |= (1 << RADIO_CE); // Установка высокого уровня на линии CE
}

// Выбирает неактивное состояние (низкий уровень) на линии CE


inline void radio_deassert_ce() {
RADIO_PORT &= ~(1 << RADIO_CE); // Установка низкого уровня на линии CE
}

// Поскольку функции для работы с csn не предполагается использовать в иных файлах, их


можно объявить static

// Выбирает активное состояние (низкий уровень) на линии CSN


inline static void csn_assert() {
RADIO_PORT &= ~(1 << RADIO_CSN); // Установка низкого уровня на линии CSN
}

// Выбирает неактивное состояние (высокий уровень) на линии CSN


inline static void csn_deassert() {
RADIO_PORT |= (1 << RADIO_CSN); // Установка высокого уровня на линии CSN
}

// Инициализирует порты
void radio_init() {
RADIO_DDR |= (1 << RADIO_CSN) | (1 << RADIO_CE); // Ножки CSN и CE на выход
RADIO_DDR &= ~(1 < RADIO_IRQ); // IRQ - на вход
csn_deassert();
radio_deassert_ce();
spi_init();
}

// Выполняет команду cmd, и читает count байт ответа, помещая их в буфер buf, возвращает
регистр статуса
uint8_t radio_read_buf(uint8_t cmd, uint8_t * buf, uint8_t count) {
csn_assert();
uint8_t status = spi_send_recv(cmd);
while (count--) {
*(buf++) = spi_send_recv(0xFF);
}
csn_deassert();
return status;
}

// Выполняет команду cmd, и передаёт count байт параметров из буфера buf, возвращает
регистр статуса
uint8_t radio_write_buf(uint8_t cmd, uint8_t * buf, uint8_t count) {
csn_assert();
uint8_t status = spi_send_recv(cmd);
while (count--) {
spi_send_recv(*(buf++));
}
csn_deassert();
return status;
}

// Читает значение однобайтового регистра reg (от 0 до 31) и возвращает его


uint8_t radio_readreg(uint8_t reg) {
csn_assert();
spi_send_recv((reg & 31) | R_REGISTER);
uint8_t answ = spi_send_recv(0xFF);
csn_deassert();
return answ;
}

// Записывает значение однобайтового регистра reg (от 0 до 31), возвращает регистр


статуса
uint8_t radio_writereg(uint8_t reg, uint8_t val) {
csn_assert();
uint8_t status = spi_send_recv((reg & 31) | W_REGISTER);
spi_send_recv(val);
csn_deassert();
return status;
}

// Читает count байт многобайтового регистра reg (от 0 до 31) и сохраняет его в буфер
buf,
// возвращает регистр статуса
uint8_t radio_readreg_buf(uint8_t reg, uint8_t * buf, uint8_t count) {
return radio_read_buf((reg & 31) | R_REGISTER, buf, count);
}

// Записывает count байт из буфера buf в многобайтовый регистр reg (от 0 до 31),
возвращает регистр статуса
uint8_t radio_writereg_buf(uint8_t reg, uint8_t * buf, uint8_t count) {
return radio_write_buf((reg & 31) | W_REGISTER, buf, count);
}

// Возвращает размер данных в начале FIFO очереди приёмника


uint8_t radio_read_rx_payload_width() {
csn_assert();
spi_send_recv(R_RX_PL_WID);
uint8_t answ = spi_send_recv(0xFF);
csn_deassert();
return answ;
}

// Выполняет команду. Возвращает регистр статуса


uint8_t radio_cmd(uint8_t cmd) {
csn_assert();
uint8_t status = spi_send_recv(cmd);
csn_deassert();
return status;
}

// Возвращает 1, если на линии IRQ активный (низкий) уровень.


uint8_t radio_is_interrupt() {
return (RADIO_PIN & RADIO_IRQ) ? 0 : 1;
}

В крайних случаях, если нужно сэкономить линию, пин прерывания можно не подключать, а проверять наличие
прерывания анализируя регистр статуса:

uint8_t radio_is_interrupt() {
// использовать этот вариант только в крайних случаях!!!
return (radio_cmd(NOP) & ((1 << RX_DR) | (1 << TX_DS) | (1 << MAX_RT))) ? 1 : 0;
}

Первоначальная настройка регистров

Хотя все регистры имеют определённое значение по-умолчанию, которое загружается в них после подачи питания,
рекомендую явно задать значения всех необходимых регистров. Особенно это актуально, если линия CSN не
притянута внешним резистором к питанию: тогда в момент сброса микроконтроллера, радио-модуль может
улавливать помехи и хаотичным образом перенастроить регистры.
Пример ниже показывает инициализацию с готовностью работы в режиме приёма по каналу 1 и передачи.
Выбирается длина адреса 5-байт, радио канал 3 (2403 МГц). Собственный адрес 0xE7E7E7E7E7, адрес удалённой
стороны - 0xC2C2C2C2C2

// Функция производит первоначальную настройку устройства. Возвращает 1, в случае


успеха, 0 в случае ошибки
uint8_t radio_start() {
uint8_t self_addr[] = {0xE7, 0xE7, 0xE7, 0xE7, 0xE7}; // Собственный адрес
uint8_t remote_addr[] = {0xC2, 0xC2, 0xC2, 0xC2, 0xC2}; // Адрес удалённой стороны
uint8_t chan = 3; // Номер радио-канала (в диапазоне 0 - 125)

radio_deassert_ce();
for(uint8_t cnt = 100;;) {
radio_writereg(CONFIG, (1 << EN_CRC) | (1 << CRCO) | (1 << PRIM_RX)); // Выключение
питания
if (radio_readreg(CONFIG) == ((1 << EN_CRC) | (1 << CRCO) | (1 << PRIM_RX)))
break;
// Если прочитано не то что записано, то значит либо радио-чип ещё инициализируется,
либо не работает.
if (!cnt--)
return 0; // Если после 100 попыток не удалось записать что нужно, то выходим с
ошибкой
_delay_ms(1);
}

radio_writereg(EN_AA, (1 << ENAA_P1)); // включение автоподтверждения только по каналу


1
radio_writereg(EN_RXADDR, (1 << ERX_P0) | (1 << ERX_P1)); // включение каналов 0 и 1
radio_writereg(SETUP_AW, SETUP_AW_5BYTES_ADDRESS); // выбор длины адреса 5 байт
radio_writereg(SETUP_RETR, SETUP_RETR_DELAY_250MKS | SETUP_RETR_UP_TO_2_RETRANSMIT);
radio_writereg(RF_CH, chan); // Выбор частотного канала
radio_writereg(RF_SETUP, RF_SETUP_1MBPS | RF_SETUP_0DBM); // выбор скорости 1 Мбит/с и
мощности 0dBm

radio_writereg_buf(RX_ADDR_P0, &remote_addr[0], 5); // Подтверждения приходят на канал


0
radio_writereg_buf(TX_ADDR, &remote_addr[0], 5);

radio_writereg_buf(RX_ADDR_P1, &self_addr[0], 5);

radio_writereg(RX_PW_P0, 0);
radio_writereg(RX_PW_P1, 32);
radio_writereg(DYNPD, (1 << DPL_P0) | (1 << DPL_P1)); // включение произвольной длины
для каналов 0 и 1
radio_writereg(FEATURE, 0x04); // разрешение произвольной длины пакета данных

radio_writereg(CONFIG, (1 << EN_CRC) | (1 << CRCO) | (1 << PWR_UP) | (1 << PRIM_RX));


// Включение питания
return (radio_readreg(CONFIG) == ((1 << EN_CRC) | (1 << CRCO) | (1 << PWR_UP) | (1 <<
PRIM_RX))) ? 1 : 0;
}

Отправка и приём сообщений


Двусторонний обмен

В общем случае, алгоритм работы программы таков: всё время радио-модуль находится в режиме приёма (PRX),
ожидая, когда к нему обратятся по радио-эфиру.
Когда возникает необходимость передать пакет, nRF24L01+ переводится в режим передатчика, осуществляет
отправку пакета, а по завершению - удачному, или нет - возвращается в режим приёмника.

Примере ниже определены функции для обмена по такому сценарию


Вызов функции send_data осуществляет отправку буфера указанной длины.
Основной цикл программы периодически вызывает функцию check_radio, которая, в свою очередь, анализирует
прерывания от радио-чипа и, в зависимости от них, вызывает:
- функцию on_packet когда принят новый пакет по каналу 1.
- функцию on_send_error когда при передаче пакета было превышено количество попыток.

// Вызывается, когда превышено число попыток отправки, а подтверждение так и не было


получено.
void on_send_error() {
// TODO здесь можно описать обработчик неудачной отправки
}

// Вызывается при получении нового пакета по каналу 1 от удалённой стороны.


// buf - буфер с данными, size - длина данных (от 1 до 32)
void on_packet(uint8_t * buf, uint8_t size) {
// TODO здесь нужно написать обработчик принятого пакета

// Если предполагается немедленная отправка ответа, то необходимо обеспечить задержку ,


// во время которой чип отправит подтверждение о приёме
// чтобы с момента приёма пакета до перевода в режим PTX прошло:
// 130мкс + ((длина_адреса + длина_CRC + длина_данных_подтверждения) * 8 + 17) /
скорость_обмена
// При типичных условиях и частоте МК 8 мГц достаточно дополнительной задержки 100мкс
}

// Помещает пакет в очередь отправки.


// buf - буфер с данными, size - длина данных (от 1 до 32)
uint8_t send_data(uint8_t * buf, uint8_t size) {
radio_deassert_ce(); // Если в режиме приёма, то выключаем его
uint8_t conf = radio_readreg(CONFIG);
if (!(conf & (1 << PWR_UP))) // Если питание по какой-то причине отключено,
возвращаемся с ошибкой
return 0;
uint8_t status = radio_writereg(CONFIG, conf & ~(1 << PRIM_RX)); // Сбрасываем бит
PRIM_RX
if (status & (1 << TX_FULL_STATUS)) // Если очередь передатчика заполнена,
возвращаемся с ошибкой
return 0;
radio_write_buf(W_TX_PAYLOAD, buf, size); // Запись данных на отправку
radio_assert_ce(); // Импульс на линии CE приведёт к началу передачи
_delay_us(15); // Нужно минимум 10мкс, возьмём с запасом
radio_deassert_ce();
return 1;
}

void check_radio() {
if (!radio_is_interrupt()) // Если прерывания нет, то не задерживаемся
return;
uint8_t status = radio_cmd(NOP);
radio_writereg(STATUS, status); // Просто запишем регистр обратно, тем самым сбросив
биты прерываний

if (status & ((1 << TX_DS) | (1 << MAX_RT))) { // Завершена передача успехом, или нет,
if (status & (1 << MAX_RT)) { // Если достигнуто максимальное число попыток
radio_cmd(FLUSH_TX); // Удалим последний пакет из очереди
on_send_error(); // Вызовем обработчик
}
if (!(radio_readreg(FIFO_STATUS) & (1 << TX_EMPTY))) { // Если в очереди передатчика
есть что передавать
radio_assert_ce(); // Импульс на линии CE приведёт к началу передачи
_delay_us(15); // Нужно минимум 10мкс, возьмём с запасом
radio_deassert_ce();
} else {
uint8_t conf = radio_readreg(CONFIG);
radio_writereg(CONFIG, conf | (1 << PRIM_RX)); // Устанавливаем бит PRIM_RX: приём
radio_assert_ce(); // Высокий уровень на линии CE переводит радио-чип в режим
приёма
}
}
uint8_t protect = 4; // В очереди FIFO не должно быть более 3 пакетов. Если больше,
значит что-то не так
while (((status & (7 << RX_P_NO)) != (7 << RX_P_NO)) && protect--) { // Пока в очереди
есть принятый пакет
uint8_t l = radio_read_rx_payload_width(); // Узнаём длину пакета
if (l > 32) { // Ошибка. Такой пакет нужно сбросить
radio_cmd(FLUSH_RX);
} else {
uint8_t buf[32]; // буфер для принятого пакета
radio_read_buf(R_RX_PAYLOAD, &buf[0], l); // начитывается пакет
if ((status & (7 << RX_P_NO)) == (1 << RX_P_NO)) { // если datapipe 1
on_packet(&buf[0], l); // вызываем обработчик полученного пакета
}
}
status = radio_cmd(NOP);
}
}

// Основной цикл
int main(void) {
radio_init();
while (!radio_start()) {
_delay_ms(1000);
}
// Перед включением питания чипа и сигналом CE должно пройти время достаточное для
начала работы осциллятора
// Для типичных резонаторов с эквивалентной индуктивностью не более 30мГн достаточно
1.5 мс
_delay_ms(2);

radio_assert_ce();

for(;;) {
check_radio();

// TODO здесь основной код программы


}
}

Только приём

В случае, если устройство работает только на приём, т.е. принимает пакеты, но не отправляет их, канал 0 может
быть запрещён в регистрах EN_RXADDR и DYNPD в radio_start().
Из функции check_radio() можно исключить часть, ответственную за прерывания передатчика.

Только передача

Если вы реализуете пульт дистанционного управления, то он должен обеспечивать только передачу. А в целях
экономии энергии сразу после передачи одиночной команды переводить радио-чип в спящий режим.
В этом случае в radio_start() канал 1 может быть запрещён в регистрах EN_RXADDR и DYNPD.

Функции передачи сообщения и обработчики прерываний могут выглядеть так:


// Помещает пакет в очередь отправки.
// buf - буфер с данными, size - длина данных (от 1 до 32)
uint8_t send_data(uint8_t * buf, uint8_t size) {
radio_deassert_ce(); // Если в режиме приёма, то выключаем его
uint8_t conf = radio_readreg(CONFIG);
// Сбрасываем бит PRIM_RX, и включаем питание установкой PWR_UP
uint8_t status = radio_writereg(CONFIG, (conf & ~(1 << PRIM_RX)) | (1 << PWR_UP));
if (status & (1 << TX_FULL_STATUS)) // Если очередь передатчика заполнена,
возвращаемся с ошибкой
return 0;
if (!(conf & (1 << PWR_UP))) // Если питание не было включено, то ждём, пока
запустится осциллятор
_delay_ms(2);
radio_write_buf(W_TX_PAYLOAD, buf, size); // Запись данных на отправку
radio_assert_ce(); // Импульс на линии CE приведёт к началу передачи
_delay_us(15); // Нужно минимум 10мкс, возьмём с запасом
radio_deassert_ce();
return 1;
}

void check_radio() {
if (!radio_is_interrupt()) // Если прерывания нет, то не задерживаемся
return;
uint8_t status = radio_cmd(NOP);
radio_writereg(STATUS, status); // Просто запишем регистр обратно, тем самым сбросив
биты прерываний

if (status & ((1 << TX_DS) | (1 << MAX_RT))) { // Завершена передача успехом, или нет,
if (status & (1 << MAX_RT)) { // Если достигнуто максимальное число попыток
radio_cmd(FLUSH_TX); // Удалим последний пакет из очереди
on_send_error(); // Вызовем обработчик
}
if (!(radio_readreg(FIFO_STATUS) & (1 << TX_EMPTY))) { // Если в очереди передатчика
есть что передавать
radio_assert_ce(); // Импульс на линии CE приведёт к началу передачи
_delay_us(15); // Нужно минимум 10мкс, возьмём с запасом
radio_deassert_ce();
} else {
uint8_t conf = radio_readreg(CONFIG);
radio_writereg(CONFIG, conf & ~(1 << PWR_UP)); // Если пусто, отключаем питание
}
}
uint8_t protect = 4; // В очереди FIFO не должно быть более 3 пакетов. Если больше,
значит что-то не так
while (((status & (7 << RX_P_NO)) != (7 << RX_P_NO)) && protect--) { // Пока в очереди
есть принятый пакет
radio_cmd(FLUSH_RX); // во всех случаях выкидываем пришедший пакет.
status = radio_cmd(NOP);
}
}
Код:00384
Артикул:NRF24L01
В наличии:есть

150 руб
 1 

В корзину

Описание Характеристики Комплектация


   

Модуль приемопередатчика 2,4 ГГц NRF24L01

Модуль NRF24L01 позволяет связать приборы радиоканалом передачи данных. С помощью NRF24L01 до семи приборов объединяются в общую радиосеть
топологии звезда на частоте 2,4 ГГц. Один прибор в радиосети ведущий, остальные ведомые. При упрощенном рассмотрении модуль приемопередатчика 2,4
ГГц NRF24L01 является конвертером интерфейса SPI в радиосигнал. Берет на себя все функции преобразования проводного интерфейса SPI в радиосигнал,
содержит приемник, передатчик и миниатюрную антенну. Специалисту не обязательно знать особенности кодирования модулем данных по радио, достаточно
правильно организовать работу SPI и установить настройки каждого модуля работающего в радиомосте.
Основа модуля микросхема nRF24L01+  фирмы Nordic Semiconductor. На плате размещены компоненты необходимые для работы МС и вилка разъема.
Установка выходной мощности модуля, каналов радиообмена и настройка протокола производятся через интерфейс SPI. Совместим с модулями nRF2401A,
nRF2402, nRF24E1 и nRF24E2.
Применение устройства наиболее актуально для мобильных приборов. Например, можно создать беспроводную связь с пультом управления видеоигрой,
джойстиком, компьютерными мышью и клавиатурой. Интересная область применения – управление движущимися системами малой робототехники: колесными
и гусеничными платформами, квадрокоптерами. Благодаря NRF24L01 становится возможным решить технические проблемы простой телемеханики и сбора
данных с датчиков. Это находит применение в охранно-пожарной сигнализации, в системах «умный дом», устройствах централизованного сбора информации и
других.

Характеристики

Питание
    Напряжение 1,9–3,6 В
    Ток
    13,5 мА когда скорость обмена 2 Мбод
    11,3 мA если мощность 0 dBм
    22 мА пиковое потребление при приеме

Частоты каналов 126


Скорости обмена: 256 Кбод, 1 Мбод, 2 Мбод
Модуляция GFSK
Чувствительность приемника –85 dBм при скорости 1 Мбод
Предельная температура воздуха
работа –40…85 °C
хранение –40…125 °C
Размеры.

Микросхема nRF24L01+

В микросхему входят: синтезатор частоты, усилитель мощности, генератор, демодулятор, модулятор и другие части, образующие многофункциональный
трансивер. Связь происходит в диапазоне частот 2,4–2,4835 ГГц. Частота, на которой будут работать модули, определяется номером канала. Они имеют шаг 1
МГц. Каналу 0 соответствует частота 2,4 ГГц, каналу 76 частота 2,476 ГГц. При скорости 250 Кбод связь возможна на большей дистанции. В режиме приема
данных RX потребление тока выше, чем в режиме передачи TX. Модуль работает в четырех режимах: Power Down – выключен, Standby – спящий режим, RX
Mode – приемник, TX Mode – передатчик. Микросхема nRF24L01+ имеет функции энергосбережения.
Надежный обмен данными гарантирует собственный протокол обмена Enhanced ShockBurst™. Прием данных подтверждает обратная связь в виде ответа.
Принимающий данные модуль приемопередатчика 2.4 ГГц NRF24L01 отвечает подтверждением приема. Если подтверждение приема не получено, то передача
повторяется.
Приемопередатчик – трансивер, имеет трехуровневый FIFO буфер приема, разделенный на шесть каналов, и трехуровневый FIFO буфер передачи. Одна
микросхема nRF24L01+ конфигурируется как центральный принимающий узел и 6 как сообщающие данные. Такие обозначения функций до некоторой степени
условны. На самом деле при любой роли МС в обмене данными каждая из них работает поочередно как приемник и передатчик. Обмен данными в такой сети
происходит на одном частотном канале. Благодаря большому количеству каналов рядом могут работать еще 7 микросхем и еще и еще…
В пакете передаваемых данных есть 9 бит идентификации после битов адреса. Первые 2 бита используются для индикации данных счетчика приема пакетов
для контроля очередности приема. Остальные семь бит не используются и зарезервированы под будущие продукты. Для совместимости с микросхемами
nRF2401, nRF24E1 и nRF905, nRF9E5 поле идентификации пакета может не использоваться. Количество повторных попыток передачи пакета задается
программно. Если отправить пакет не удалось, то генерируется прерывание для контроллера, а в регистре статуса трансивера устанавливается бит MAX_RT.
Для успешной передачи пакета вырабатывается сигнал прерывания (вывод TX_DS IRQ) и передающий FIFO буфер очищается.
Для настройки различных параметров и функций используются регистры микросхемы. Каждый регистр (кроме трех регистров полезной нагрузки) имеет 5-
битный адрес, который маскируется в R_REGISTER и W_REGISTER инструкциями, соответственно чтение и запись.

Доступны следующие регистры.

CONFIG – настройка прерываний, контрольной суммы, питания и статуса Tx/Rx.


EN_AA – включение и отключение Enhanced ShockBurst ™ на отдельных каналах Rx.
EN_RXADDR – включение и отключение канала Rx.
SETUP_AW – длина адреса.
SETUP_RETR – настройка задержки повтора и количества попыток связаться, если не получено подтверждение приема.
RF_CH – установка радиочастотного канала.
RF_SETUP – настройка скорости передачи по эфиру, выходной мощности и коэффициента усиления.
STATUS – статус битов состояния прерывания, буфер Tx FIFO полный и количество каналов получивших пакеты.
OBSERVE_TX – количество потерянных и повторно переданных пакетов.
CD – обнаружение несущей частоты.
RX_ADDR_Pn – адрес для Rx канала n.
TX_ADDR – адрес назначения передаваемых пакетов.
RX_PW_Pn – величина постоянной нагрузки на Rx канал n.
FIFO_STATUS – статус автоповтора, буфер Tx FIFO полный / пустой, Rx FIFO полный / пустой.
ACK_PLD – полезная нагрузка отправки пакетов ответа, если ответы пакетов включены (записывается с указанием W_ACK_PAYLOAD).
TX_PLD – Тх FIFO (записывается с инструкциями W_TX_PAYLOAD и W_TX_PAYLOAD_NO_ACK).
RX_PLD – Rx FIFO (читается с инструкцией R_RX_PAYLOAD).
DYNPD – включить или отключить функцию динамического расчета полезной нагрузки на каналы Rx.
FEATURE – включение или отключение динамической полезной нагрузки, ACK полезной нагрузки, и селективные функции ACK.

Подключение

Кроме выводов питания контакты линий сигналов могут подключаться к контактам прибора питающегося напряжением 5 В. Такая совместимость обеспечена
внутренними цепями микросхемы. При подключении к порту Р0 МК класса 51 нужен подтягивающий резистор 10 кОм, для других портов он не нужен. Входы
устройства подключаемого к модулю должны потреблять ток не более 10 мА. Модуль соединяется с микроконтроллером класса AVR без цепей согласования
уровней сигналов.

Расположение контактов соединителя.

Модуль имеет следующие контакты:

GND – общий провод,


VCC – питание 3,3 В,
CE – включение радиотракта микросхемы высоким уровнем,
CSN – Chip Select Not, активный низкий уровень. Если установлен низкий уровень, то модуль отвечает на SPI команды. Это более важный сигнал выбора МС
чем сигнал CE,
SCK – тактирование шины SPI, до 10 МГц,
MOSI – используется для передачи данных от микроконтроллера к устройству,
MISO – для передачи данных из устройства в микроконтроллер,
IRQ – выход сигнала для запроса прерывания при отправке и получении пакета.

Вилка соединителя модуля устанавливается в разъем, изображенный на фото:


 
Радиомодуль легко подключить к Arduino UNO. Соедините проводами одноименные контакты.

Подключение является универсальным и подходит для всех плат Arduino UNO, DUE, MEGA, Леонардо, Yun и подобных. Сигналы SPI выведены на соединитель
ICSP микроконтроллерного модуля Arduino. Контакт питания VCC соединяется с контактом стабилизатора Arduino напряжения 3,3 В. Общий провод
подключается к контакту GND. Сигналы выбора CE и CSN подключаются к контактам, определенным в библиотеке RF24, например 7 и 8.

Особенности программирования

Для программ Arduino используется библиотека RF24 https://github.com/maniacbug/RF24/ Эта библиотека снабжена большим количеством примеров. При записи
программы в Arduino модуль приемопередатчика 2,4 ГГц NRF24L01 нужно отключить от Arduino. Перед первой командой инициализации нужна пауза две
секунды после подачи питания. Необходимо сделать публичной функцию RF24::flush_tx в библиотеке RF24 и очищать буфер передачи перед отправкой нового
сообщения. По умолчанию модуль работает на передающем канале 76h.

Работа модуля в сети топологии звезда


По умолчанию модуль приемопередатчика 2,4 ГГц NRF24L01 сконфигурирован как ведущий и может получать данные по шести каналам. Каждый из шести
ведомых модулей должен быть сконфигурирован соответствующим образом, при этом ведомым модулям присваиваются уникальные адреса.

Примечание

Перед первым включением следует смонтировать на модуле 2 конденсатора. Между выводами VCC и GND припать конденсатор в SMD корпусе (планарный)
емкостью 0,1 мкФ со стороны пайки к монтажным площадкам на плате, затем к ним припаять электролитический конденсатор емкостью 100 мкФ на напряжение
10 В. Питать лучше не от Arduino, а от отдельного стабилизатора напряжения 3,3 В, способного обеспечить ток нагрузки 200 мА.