Бюджетная радиосвязь для микроконтроллеров AVR / AVR / Сообщество

Бюджетная радиосвязь для микроконтроллеров AVR / AVR / Сообщество Мультикоптеры

Бюджетная радиосвязь для микроконтроллеров avr / avr / сообщество

Бюджетная радиосвязь для микроконтроллеров AVR / AVR / Сообщество

Здравствуйте. Задался я недавно целью организовать радиосвязь между контроллерами AVR, бюджет был очень ограничен и поэтому в качестве контроллеров для теста были выбраны два Atmega8 давно валявшиеся без дела. Когда же начался поиск подопытного трансивера я был неприятно удивлен ценами на готовые радио модули, короче на те деньги что я рассчитывал в Украине можно было купить только пару «плата приемника — плата передатчика» на 433 мгц (такая китайская там еще катушки проволочные на плате). Но хотелось то полноценный двусторонний линк и поэтому я начал искать в инете подходящий вариант. Им оказался радиомодуль на 2,4 Ггц. NRF24L01 привлек конечно ценой.

В базовой версии без усилителя и с антенной выполненной на печатной плате. На момент написания статьи пару таких модулей на ebay можно было купить по $3.23
Бюджетная радиосвязь для микроконтроллеров AVR / AVR / Сообщество
Есть еще версия подороже с усилителем и мощностью в 10dBm и заявленной дальностью в 260 метров.
Бюджетная радиосвязь для микроконтроллеров AVR / AVR / Сообщество
Но сие чудо дороже и одна шт. уже стоит $13.89
Я остановился на первом варианте. И так что же у него внутри пройдемся бегло по характеристикам.

Тарансивер выполнен в виде единого чипа.
Режим передачи ShockBurst™ (ефективный быстрый режим передачи данных).
Aвтоматический ACK (подтверждение получения пакета) и настройки повторной передачи пакета если до кого то в сети не дошло.
Автоматический контроль CRC.
RF передача по 125 возможным каналам.
Интерфейс (SPI) со скоростью 0-8 Mbps.
Быстрое переключение возможен режим frequency hopping
5V tolerant входы.
Режимы Standby Modes и Power Down Mode.
Вывод прерывания по событиям (можно настраивать в спец. регистре)

Смотрите про коптеры:  Аппаратура радиоуправления на ардуино своими руками | Пикабу

Ну и табличка из даташита на случай если что пропустил
Бюджетная радиосвязь для микроконтроллеров AVR / AVR / Сообщество
Еще в процессе копания в инфе на чип была мной была обнаружена интересная фишка
Приемник чипа NRF24L01 одновременно может принимать данные по шести каналам. То есть принимать данные от шести разных передатчиков.
Бюджетная радиосвязь для микроконтроллеров AVR / AVR / Сообщество
Весь фокус в шести каналах которые несут свой уникальный 40 битный адрес, тоесть находясь в одном частотном канале приемник принимает пакеты автоматически распознает адрес и складывает данные в буфер адресуемого канала. У каждого канал есть свои настройки размера буфера, наличия подтверждения ACK итд… В общем я еще сам не все постиг.

Ладно вернемся к Atmega8. Разработку Устройства начал с обзора существующих решений. И нашел проект заграничного колеги Davide Gironi
вобщем его проект соответствовал моим задумкам. Поэтому я взял за основу его схему и код за что ему спасибо и уважуха. Слегка модифицировав схему вышло следующее.

Схема.
Бюджетная радиосвязь для микроконтроллеров AVR / AVR / Сообщество
Плата.
Бюджетная радиосвязь для микроконтроллеров AVR / AVR / Сообщество

Ну и готовый девайс.
Бюджетная радиосвязь для микроконтроллеров AVR / AVR / Сообщество
Бюджетная радиосвязь для микроконтроллеров AVR / AVR / Сообщество

Ну и принцип работы на видео. Когда есть связь мигают желтые светодиоды при нажати кнопки на передатчике зажигаются зеленые.

Проверял на дальность получились следующие результаты. В помещении офиса связь держится через три стены пеноблоков, в жилом доме из красного кирпича пробивается одна несущая кирпичная стена и связь есть на 2 метра от нее. На открытом воздухе когда передатчик размещен вплотную к окну на четвертом этаже, связь есть в прямой видимости на 85 метрах через перекресток с троллейбусными проводами и фонарями уличного освещения.

Вообщем радиомодуль мне понравился, заказал себе еще два буду сеть из датчиков температуры собирать.

Полезные ссылки:
Davide Gironi nRF24L01 atmega library and development board
Everything You Need to Know about the nRF24L01
AVRLib/nRF24L01

Архив с кодом, проект в Eagle можно скачать на официальной странице проекта

Радиомодуль nrf24l01 быстрый старт. / радио / сообщество

Бюджетная радиосвязь для микроконтроллеров AVR / AVR / Сообщество

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

отсюда

.

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

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

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

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

Бюджетная радиосвязь для микроконтроллеров AVR / AVR / Сообщество
Как видим все просто. Кварц выбран 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. Он сильно черновой и с магическими числами, но рабочий. Прошивку можно сразу загрузить в платку и она будет работать. Если что, то код можно напильником допилить. Надо бы будет как нибудь, замутить статью про более продвинутые возможности данного радио модуля.

Ссылки по теме

  1. Обзор радио модуля NRF24L01
  2. nRF24L01 : побеждаем модуль.
  3. nRF24L01 и Ардуино: побеждаем модуль (видео)
  4. SE8R01. Подделка под NRF24L01 (видео)
  5. Обзор радио модуля NRF24L01 PA LNA

Шаг 1

Загрузить в контроллер платы Ардуино скетч сканера эфира, который можно найти среди примеров Arduino IDE: Файл -> Примеры -> RF24 -> scanner. Ниже под спойлером есть этот скетч с несущественным изменением. В нем изменено время между стартом и остановкой сканирования одного канала с 128 мксек на 512 мксек.

Увеличение времени позволило за один цикл сканирования всего диапазона выявлять больше источников помех и сигналов. Это равнозначно замене результата измерений в канале на сумму четырех соседних результатов в этом канале до изменения времени сканирования. При этом, время прохода всего прослушиваемого диапазона сканером увеличилось несущественно: примерно с 8 до 10 сек.

В разных скетчах адрес канала в командах приводится в разных форматах: в одних — …(0x6f), в других — …(112). Перевод с одного формата в другой станет понятным с примера перевода. Например, для (0x1а) — это: (1 1)*16 а = (1 1)*16 10 = 42. Отсчет каналов начинается с частоты 2,4 ГГц, далее идет увеличение частоты на 1 МГц с увеличением номера канала на 1.

скетч сканера эфира
/*
Победа над nRF24L01: на три шага ближе, сканер эфира
https://habr.com/ru/post/476716/
*/

/*
  Copyright (C) 2023 J. Coliz <[email protected]>

  This program is free software; you can redistribute it and/or
  modify it under the terms of the GNU General Public License
  version 2 as published by the Free Software Foundation.
*/

/**
   Channel scanner

   Example to detect interference on the various channels available.
   This is a good diagnostic tool to check whether you're picking a
   good channel for your application.

   Inspired by cpixip.
   See http://arduino.cc/forum/index.php/topic,54795.0.html
*/

#include <SPI.h>
#include "nRF24L01.h"
#include "RF24.h"
#include "printf.h"

//
// Hardware configuration
//

// Set up nRF24L01 radio on SPI bus plus pins 9 & 10

RF24 radio(9, 10); //Arduino UNO

//
// Channel info
//

const uint8_t num_channels = 128;
uint8_t values[num_channels];

//
// Setup
//

void setup(void)
{
  //
  // Print preamble
  //

  Serial.begin(9600);
  Serial.println("Scanner Air On");
  printf_begin();

  //
  // Setup and configure rf radio
  //

  radio.begin();
  radio.setAutoAck(false);

  // Get into standby mode
  radio.startListening();
  radio.printDetails();  
  delay(5000);              

  // Print out header, high then low digit
  int i = 0;
  while ( i < num_channels )
  {
    printf("%x", i >> 4);
      i;
  }
  printf("nr");
  i = 0;
  while ( i < num_channels )
  {
    printf("%x", i & 0xf);
      i;
  }
  printf("nr");
}

//
// Loop
//

const int num_reps = 100;

void loop(void)
{
  // Clear measurement values
  memset(values, 0, sizeof(values));

  // Scan all channels num_reps times
  int rep_counter = num_reps;
  while (rep_counter--)
  {
    int i = num_channels;
    while (i--)
    {
      // Select this channel
      radio.setChannel(i);

      // Listen for a little
      radio.startListening();
      delayMicroseconds(512);
      radio.stopListening();

      // Did we get a carrier?
      if ( radio.testCarrier() )
          values[i];
    }
  }

  // Print out channel measurements, clamped to a single hex digit
  int i = 0;
  while ( i < num_channels )
  {
    printf("%x", min(0xf, values[i] & 0xf));
      i;
  }
  printf("nr");
}

Далее подключаем модуль nRF24L01 к плате Ардуино или любому прототипу, собранному, допустим, на контроллере ATMEGA328P. Я собрал два образца на платах для прототипирования на контроллере ATMEGA328P по схеме контроллер резонатор. Один образец подключаю к компу через плату Arduino UNO, а второй — через конвертор USB/TTL.

Мощность стабилизатора платы Arduino UNO вполне приемлема для подключения дополнительной импульсной нагрузки такой, как nRF24L01 c адаптером 5В/3,3В для этого модуля или без адаптера.

На мониторе последовательного порта Arduino IDE увидите нечто похожее:

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

Обратите внимание на чистый диапазон, начиная с канала 4а. У меня он остается чистым даже, если на расстоянии нескольких метров работает старая СВЧ-печь — мощный источник помех в этом диапазоне. А в общем-то, в Интернете рекомендуют выбирать каналы для своих проектов выше «60».

Если на каналах — шум, но радиомодуль определяется (смотрим преамбулу на мониторе Arduino IDE, подробно тут) — это однозначно копия. Не отчаивайтесь — ее тоже можно запустить.

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

Шаг 2

По схеме, аналогичной первой, собираем второй радиоузел. Это будет передатчик. В его контроллер загружаем скетч передатчика (под спойлером).

Передатчик без пауз в работе передает сигнал на канале 6f (112).

Подаем питание на сканер эфира и передатчик. Присмотритесь что творится на канале 6f и соседних с ним каналах. Сканер эфира при включенном передатчике рано или поздно прорисует единички или другие одноразрядные числа в шестнадцатиричном исчислении в области 6f, на который запрограммирован передатчик. Наберитесь терпения на 1 — 2 минуты, особенно при работе со сканером из примеров.

Увидев сигнал от передатчика делаем следующий шаг.

Шаг 3

Загружаем вместо сканера скетч приемника (под спойлером).

Логика работы приемника такая же, как и у сканера эфира, но он в отличие от сканера принимает сигналы только на частоте передатчика 6f и, как и сканер, не посылает автоответ. Скорость обмена информацией и размер контрольной суммы у приемника такие же, как у передатчика.

Включаем передатчик и приемник. Если приемник принимает хотя бы каждый третий пакет — это уже успех. У меня не получилось. Приемник по непонятным причинам принимал максимум 50 пакетов.

Подумал о увеличении мощности передаваемого сигнала с помощью дополнительной антенны. Для начала, подключил зажимом монтажный провод «папа-мама» к «корню» штатной антенны передатчика. И счастье привалило: сразу 999 принятых пакетов — максимально возможное число из 1 000!

Юзерам, которые захотят сделать все грамотно, придется поработать. Дополнительная антенна в данном случае — это отрезок коаксиального кабеля с волновым сопротивлением 50 Ом и длиной 115 мм. Антенна подключается к выводу 13 (АNT2) микросхемы nRF24L01 .

Схему подключения и номиналы нескольких недостающих smd компонентов, которые надо поставить на плату радиомодуля, можно найти на принципиальной электрической схеме nRF24L01 тут. Впрочем, есть альтернатива — в магазин за NRF24L01 PA LNA

Теперь обязательно припаиваем между пинами GND и VCC обеих радиомодулей по два конденсатора. Керамический конденсатор, выполняющий роль ВЧ-фильтра, емкостью не менее 0,15 мкФ (чем больше, тем лучше) и электролит емкостью около 10 мкФ (можно и больше, но бесполезно)

Тут не могу не упомянуть о решении, предложенном GennPen в комментариях. Это установка на платах nRF24L01 отсутствующего конденсатора С6 (1…2pF). Конденсатор будет выполнять роль пассивной нагрузки. Без пассивной нагрузки модули nRF24L01 со встроенной антенной «захлебываются» и часто нормально работают только на пониженных мощностях передатчика.

После того, как удалось установить наилучшую связь в паре передатчик — приемник, можно провести тестирование на определение дальности связи радиомодулей, задав мощность передатчика и свои критерии качества связи, допустим, 300 принятых пакетов из 1000. У меня пара в режиме усилителя PA_MAX обеспечивает связь «999:1000» в пределах квартиры через 3 кирпичных простенка.

И наконец, несколько слов о своей скромной статистике работы с модулем. В свое время купил 8 шт. радиомодулей nRF24L01 . Приобрел в разное время с интервалом больше года, в разных интернет-магазинах и, судя по стилю маркировки, от разных производителей.

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

Кроме того, nRF24L01 совместим с малопотребляющим nRF52832 и другими. Это особенно важно для автономных систем с ограниченным ресурсом источников. В итоге удалось соединить все 8 радиомодулей по эфиру. Вывод простой — не надо верить мифам, что рынок переполнен неработающими копиями (клонами, репликами, подделками).

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

Конечно, эти простые шаги не могут гарантировать решение всех проблем с nRF24L01 — мне их и не перечесть, но после того, как их сделаете, будете уверены, что:

Все! Надеюсь, как и у меня, у вас в дальнейшем поубавится проблем с nRF24L01 в своих проектах. Успехов!

Оцените статью
Радиокоптер.ру
Добавить комментарий