Основы работы с nRF24l01

Алексей Н
Offline
Зарегистрирован: 02.01.2016

Добрый день, участники форума! Пытаюсь разобраться с радиомодулем nRF24l01. Запустил сканер. Запустил  пример Getting Started. Пытаюсь понемногу менять уже работающий код, чтобы понять принципы работы. Хочу изменить скетч Getting Started так, чтобы передавать не число, а символ. Для этого изменил в передатчике код так, чтобы он всегда отсылал в ответ символ "W", а в приемнике изменил код так, чтобы он этот символ выводил:
 

  unsigned long got_time;  //Было
  char send_char = 0;  //Стало

 

Все заработало, но обнаружился неожиданный эффект. Если при передаче числа в оригинальном скетче
выключить передатчик, то приемник соответствено отображает ответ "ноль" и показывает задержку ответа.

Now sending
Sent 6055876, Got response 6055876, Round-trip delay 1128 microseconds
Now sending
failed
Failed, response timed out.
Sent 7058768, Got response 0, Round-trip delay 268520 microseconds

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

Sent 8126712, Got response W, Round-trip delay 1080 microseconds
Now sending
failed
Failed, response timed out.
Sent 9129208, Got response W, Round-trip delay 268512 microseconds

Из-за чего это может быть? И как с этим бороться? На Амперке не ответили :(.

Joiner
Offline
Зарегистрирован: 04.09.2014

Очевидно какой-то переменной присваивается значение "W" и так и торчит в приемнике и не изменяется. Скорее всего после первого вывода на экран это значение сразу надо заменить на  "", а уж после этого вновь считывать.

a5021
Offline
Зарегистрирован: 07.07.2013

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

Алексей Н
Offline
Зарегистрирован: 02.01.2016

Прошу прощения, конечно, надо было сразу приложить скетчи. Но к вечеру в голове образовалась каша. Выкладывю код приемника и передатчика. За основу взят пример Getting Started из библиотеки TMRh20. Обработки ситуации, когда приходит ноль в этом коде нет, но и вопрос то в том, что ноль не приходит.

Передатчик получает сигнал от приемника и отправляет ответ. Если этот ответ unsigned long, то все работает как надо. Передатчик отключен, передатчик выводит в сериал ноль. С char приемник выводит в сериал постоянное значение, хотя при его перезагрузке выводит снова ноль. Я так понимаю дело в каких-то буферах, где остется полученное значение, но я не могу понять где. И опять же, с unsigned long все работает как надо.

Передатчик


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

/* Hardware configuration: Set up nRF24L01 radio on SPI bus plus pins 7 & 8 */
RF24 radio(7, 8);
/**********************************************************/

byte addresses[][6] = {"1Node", "2Node"};

void setup() {
  Serial.begin(115200);
  Serial.println(F("Transpoder"));


  radio.begin();
  delay(2000);
  radio.enableDynamicPayloads();
  radio.enableAckPayload();
  radio.setDataRate(RF24_1MBPS);
  radio.setChannel(0);
  radio.setRetries(15, 15);
  radio.setAutoAck(true);
  radio.setCRCLength(RF24_CRC_16);
  radio.setPALevel(RF24_PA_MIN);
  radio.powerUp();


  radio.openWritingPipe(addresses[1]); //Передача "1Node"
  radio.openReadingPipe(1, addresses[0]); //Прием "2Node"

  // Start the radio listening for data
  radio.startListening();
}  // setup

void loop() {


  /****************** Pong Back Role ***************************/
  unsigned long got_time; // Было
  char send_char = 'W'; // Стало


  if ( radio.available()) {
    // Variable for the received timestamp
    while (radio.available()) {                             
      radio.read( &got_time, sizeof(got_time) );          
    }

    radio.stopListening();                         
    //      radio.write( &got_time, sizeof(got_time) ); // Было   
    radio.write( &send_char, sizeof(send_char) ); // Стало
    radio.startListening();                  
    Serial.print(F("Sent response "));
    //      Serial.println(got_time);  // Было
    Serial.println(send_char);  // Стало
  }

} // Loop

Приемник

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

/* Hardware configuration: Set up nRF24L01 radio on SPI bus plus pins 7 & 8 */
RF24 radio(7, 8);
/**********************************************************/

byte addresses[][6] = {"1Node", "2Node"};

// Used to control whether this node is sending or receiving
//bool role = 1;

void setup() {
  Serial.begin(115200);
  //  Serial.println(F("RF24/examples/GettingStarted"));
  //  Serial.println(F("*** PRESS 'T' to begin transmitting to the other node"));
  //

  Serial.println(F("Reciver"));
  radio.begin();
  delay(2000);
  radio.enableDynamicPayloads();
  radio.enableAckPayload();
  radio.setDataRate(RF24_1MBPS);
  radio.setChannel(0);
  radio.setRetries(15, 15);
  radio.setAutoAck(true);
  radio.setCRCLength(RF24_CRC_16);
  radio.setPALevel(RF24_PA_MIN);
  radio.powerUp();

  radio.openWritingPipe(addresses[0]);
  radio.openReadingPipe(1, addresses[1]);


  // Start the radio listening for data
  radio.startListening();
}

void loop() {


  /****************** Ping Out Role ***************************/

  radio.stopListening();                                 


  Serial.println(F("Now sending"));

  unsigned long start_time = micros();                           
  if (!radio.write( &start_time, sizeof(start_time) )) {
    Serial.println(F("failed"));
  }

  radio.startListening();                                  

  unsigned long started_waiting_at = micros();               
  boolean timeout = false;                                 

  while ( ! radio.available() ) {                       
    if (micros() - started_waiting_at > 200000 ) {      
      timeout = true;
      break;
    }
  }

  if ( timeout ) {            
    Serial.println(F("Failed, response timed out."));
  }
  unsigned long got_time;   // Было   
  char send_char;           // Стало
//  radio.read( &got_time, sizeof(got_time) );  // Было 
  radio.read( &send_char, sizeof(send_char) );    // Стало
  unsigned long end_time = micros();

  // Spew it
  Serial.print(F("Sent "));
  Serial.print(start_time);
  Serial.print(F(", Got response "));
//  Serial.print(got_time);  // Было
  Serial.print(send_char);    // Стало
  Serial.print(F(", Round-trip delay "));
  Serial.print(end_time - start_time);
  Serial.println(F(" microseconds"));


  // Try again 2s later
  delay(2000);

} // Loop

Ну и сравнение с битьем топором я совершенно не понял. По моему вполне разумное желание понять, что происходит с приемником, если передатчик по какой-то причине выходит из строя.

a5021
Offline
Зарегистрирован: 07.07.2013

Вы продолжаете упорствовать в бессмысленных действиях. Приемник у вас не сообщил о поступлении новых данных, из цикла ожидания вы вываливаетесь по таймауту, но при этом зачем-то лезете читать приемный буфер. Что вы там ожидаете обнаружить? Внеземное послание?  Если не выполнялась команда FLUSH_RX (а у вас она не выполнялась), то в ответ на команду чтения приемного буфера, приемник будет всегда возвращать последние корректно принятые данные. Если компанда FLUSH_RX выполнялась, то возвращаемым значением будут нули.

Цитата:
Ну и сравнение с битьем топором я совершенно не понял.

Смысл в сравнимой пользе от такоего эксперимента. То есть полном отсутствии таковой.

Цитата:
По моему вполне разумное желание понять, что происходит с приемником, если передатчик по какой-то причине выходит из строя.

В даташите на NRF24L01 подробно описано, как приемник производит декодирование эфирного сигнала. Уверяю, что своим могучим экспериментом вы ни на миллиметр не приблизились к пониманию логики работы приемника.

Если описать вкратце, то приемник работает так: слушает эфир на выбранном канале и при обнаружении данных на несущей чавтоте начинает побитово их запихивать в сдвиговый регистр входного буфера. После каждого бита приемник анализирует, находится ли в буфере корректный заголовок пакета, соответствует ли адрес получателя его собственному адресу, правильно ли указана длина принятых данных и совпадает ли контрольная сумма. Если хотя бы что-то в принятых данных не соответствует ожиданиями приемника (ваш случай с прерыванием передачи), то приемник не предпринимает никаких действий, а продолжает заниматься тем, чем и занимался -- слушать эфир, запихивать биты в регистр и анализировать принятое. Если приемник обнаруживает, что во входном буфере все данные корректны, он переносит их в приемный буфер, который доступен для чтения извне (в данном случае ардуиной) и сигнализирует в статусе, что принят пакет данных, попутно отправив подтверждение передатчику, если последний его запрашивал.

Используемая вами библиотека написана таким образом, что функция radio.available() запрашивает статус модуля и если там утстановлен бит наличия принятых данных, то она возвращает TRUE, иначе FALSE.

Внеся правку в исходный код примера, вы нарушили логику его работы. В примере чтение данных выполнялось так:

    if ( timeout ){                                             // Describe the results
        Serial.println(F("Failed, response timed out."));
    }else{
        unsigned long got_time;                                 // Grab the response, compare, and send to debugging spew
        radio.read( &got_time, sizeof(unsigned long) );
        unsigned long end_time = micros();
        
        // Spew it
        Serial.print(F("Sent "));
        Serial.print(start_time);
        Serial.print(F(", Got response "));
        Serial.print(got_time);
        Serial.print(F(", Round-trip delay "));
        Serial.print(end_time-start_time);
        Serial.println(F(" microseconds"));
    }

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

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

Алексей Н
Offline
Зарегистрирован: 02.01.2016

Логика программы была изменена специально, чтобы посмотреть, как реагирует приемник на, например такую ситуацию, когда передатчик доступен, но ничего не шлет. Про оставание в буфере последнего значения я знал, но я также и был абсолютно уверен, что функция очистки буфера FLUSH_RX вызывается внутри функций StartListening и StopListening. После Вашего ответа я в сотый раз открыл rf24.cpp, чтобы найти этот кусок и доказать Вам, что Вы неправы и проблема гораздо глубже и сложнее... и обнаружил, что FLUSH_RX закомменитрована. После того, как я ее раскомментил, все очищается как положено.

Большое спасибо за уделенное мне внимание. Надеюсь Вас это не сильно затруднило. Еще раз благодарю Вас за помощь,

a5021
Offline
Зарегистрирован: 07.07.2013

Алексей Н пишет:
Логика программы была изменена специально, чтобы посмотреть, как реагирует приемник на, например такую ситуацию, когда передатчик доступен, но ничего не шлет.

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