nRF24L01+ : побеждаем модуль.

Тема в разделе "Проводная и беспроводная связь", создана пользователем ИгорьК, 19 июн 2014.

  1. MakD

    MakD Нерд

    Всего есть 5 режимов работы модуля: Power down, Standby-I, Standby-II, RX, TX. Модуль слушает эфир только в режиме RX.
    По даташиту прерывание бывает трех видов: RX_DR, TX_OK и TX_FAIL. Меня пока интересует только прием. Чтобы прерывание RX_DR возникло, модуль должен получить данные, чтобы получить данные, модуль должен быть в режиме RX. В режиме RX модуль жрёт 13 мА (или около того). То есть от батарейки 2300 мАч он проработает 176 часов - дорогое удовольствие. Получить прерывание по приему и разбудить МК мы можем только в режиме RX. Грустно.
    Иными словами, если архитектура системы подразумевает центральное устройство (уж его-то мы подключим к сети) и выносные датчики, то последние сами должны инициировать процедуру передачи данных. В противном случае мы принимаем непосредственное участие в глобальном потеплении...
    Я все правильно понимаю?
     
  2. parovoZZ

    parovoZZ Гуру

    Конечно. Если надо что-то передать на устройство с батарейкой, то оно должно запрашивать у центрального устройства - есть что для меня?
     
  3. Miller_VA

    Miller_VA Нерд

    Не скажу, что очень хотел помочь, но у самого подобная задача будет в недалёком будущем.
    Поэтому, на пару, может и легче будет разобраться. Не всё опробовал, но кое-что есть.
    Итак по существу. У меня сейчас на столе такая конфигурация: два передатчика (Arduino+nRf24) и
    один приёмник(ATMega16+nRF24). На основе твоего скетча, сделал передачу для 2-х Arduino,
    естественно с разными адресами, не синхронной передачей и, каждый со своими данными. В приёмнике мне легче разбираться, у меня отладчик полный(IAR). Не спеша делаю следующее:
    1. Отключаю AutoAsk -> radio.setAutoAck(0); и убираю radio.enableAckPayload();
    Пробую принять сначала от каждого передатчика, потом одновременно от двоих. Как-то работает.
    2. Возвращаю radio.enableAckPayload(); От первого приём есть, от второго - нет. Какая-то херня(техн.термин).
    Оказалось, что от передатчика, который передаёт на Pipe2(адрес 0x3A3A3A3AD2LL), приёма нет.
    Изучаю. В итоге, смотрю в регистр DYNPD (0x1C), там 03. Читаю документацию на nRF24(регистры). Решаю,
    что нужна "1" и во 2-м разряде DYNPD. Пришлось в библиотеке RF24.cpp сделать коррекцию в функции enableAckPayload(), именно в ней прописывался DYNPD. Заработало. Ну вроде как заработало :)
    3. radio.setAutoAck(1) - пока оставлю в покое. Возвращать какие-то данные в передатчик я пока не планирую.
    Это уже более высокий уровень протокола. Может потом.
     
    ImrDuke нравится это.
  4. parovoZZ

    parovoZZ Гуру

    Это обязательно, хоть мы и знаем длину получаемого пакета.
     
  5. Miller_VA

    Miller_VA Нерд

    Эта "находка" пока до конца не понята, но без неё не работало. Из чисто интереса, напрашивается вопрос.
    Вы действительно хотите всё "жёстко" контролировать? Ну подготовили данные, отдали передатчику, разрешили передатчику несколько раз достучаться, приёмник должен ответить, что пакет принят - этого не достаточно? nRF24 позволяет больше. Например, можно вернуть передатчику всё, что Вы получили. Пусть смотрит, может ещё чего захочет. Как раз наличие всех Ask-ов у nRF и позволяет это всё реализовать. А зачем делать сложно, если мы следим за температурой и пр. лабудой. Хотя, где-то я встречал, человек поставил кнопку на унитазе и очень хотел знать, что там ЗАНЯТО! Ото серьёзно.
     
  6. parovoZZ

    parovoZZ Гуру

    Чтобы без нее работало, необходимо в соответствующий регистр из набора RX_PW_P0 - RX_PW_P5 записать длину принимаемых данных. Длину передаваемых данных указывать не надо - трансивер их сам считает.
     
  7. ImrDuke

    ImrDuke Гик

    Спасибо огромное за подсказку!
    С такими настройками заработало.

    Код (C++):
    void RadioSetup() {
      radio.begin();                           // активировать модуль
      radio.setCRCLength(RF24_CRC_8);          // Использовать CRC-8
      radio.setAutoAck(0);                     // режим подтверждения приёма, 1 вкл 0 выкл
      radio.setPayloadSize(32);                // размер пакета, в байтах
      radio.openReadingPipe(1, Pipe_Rx[0]);
      radio.openReadingPipe(2, Pipe_Rx[1]);
      radio.openReadingPipe(3, Pipe_Rx[2]);
      radio.openWritingPipe(Pipe_Tx);          // открываем канал для передачи данных
      radio.setChannel(CH_NUM);                // выбираем канал (в котором нет шумов!)
      radio.setPALevel(SIG_POWER);             // УРОВЕНЬ МОЩНОСТИ RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX
      radio.setDataRate(SIG_SPEED);            // СКОРОСТЬ ОБМЕНА RF24_2MBPS, RF24_1MBPS, RF24_250KBPS
      radio.powerUp();                         // начать работу
      radio.startListening();                  // начинаем слушать эфир, мы приёмный модуль
    }
     
  8. Miller_VA

    Miller_VA Нерд

    Ура! НО! Я бы как-то вернул enableAckPayload(). Пусть передатчик повторит ещё, если пакет не прошёл.
    Нужно поковырять(изучить) библиотеку.
     
  9. parovoZZ

    parovoZZ Гуру

    это разрешает полезную нагрузку в ACK пакете и к повтору пакетов отношения не имеет. Количество пакетов и интервал между ними задается в совсем другом месте.
     
  10. Miller_VA

    Miller_VA Нерд

    Сдаётся мне, мы о разных вещах говорим. Каждый о своём.
     
  11. parovoZZ

    parovoZZ Гуру

    Я библиотеку не изучал - не было необходимости. Гораздо проще открыть дашик и там всё узнать, чем искать несостыковки в библе. С регистрами напрямую общаться проще - всё, как на ладони.
     
  12. parovoZZ

    parovoZZ Гуру

    Автответ задается setAutoAck(); Передатчик ничего не знает про приемник. Может быть, что у приемника автоответ не настроен вообще. Это не мешает передатчику отправить пакет, а приемнику его принять. Но передатчик не дождавшись ответа, передаст его еще раз через заданный интервал времени, а потом еще и еще. И так, пока не исчерпает разрешенный лимит. Затем взведет бит о достигнутом максимуме попыток передачи пакета и завершит работу в буквальном смысле. Чтобы продолжить работу, надо этот бит снять. Далее вручную очистить буфер.
    Если у нас пакет ACK без полезной нагрузки, то надо обязательно включить динамическую длину пакета. Всё из-за того, что мы не можем указать трансиверу принимать данные нулевой длины в пакете. Записанный ноль в регистрах RX_PW_P0 - RX_PW_P5 просто напросто отключат pipe на прием.
     
  13. Miller_VA

    Miller_VA Нерд

    Ну библиотеку можно иногда и посмотреть. Основная масса пользователей общаются с регистрами как раз через функции библиотеки. Даташит изучить, регистры распечатать - это уже для серьёзного проекта. Не всем это и надо. Легче на форуме спросить. Кстати, а есть здесь интересующиеся, которым хотелось бы разобраться с Ask -ами и пр. лабудой? Тогда мы с paravoZZ-ом продолжим. А так на двоих - не интересно.
    Хотя бы третий нужен.
     
  14. parovoZZ

    parovoZZ Гуру

    - это профессиональные программисты, которые получив первые сэмплы микросхем от производителя со всей документацией, уже через неделю ставят своё изделие на конвейер.
     
  15. Miller_VA

    Miller_VA Нерд

    Очередные пол-дня коту под хвост. Мне нужно сделать не просто обмен м-ду модулями, а сделать 100% достоверность. Передатчик передал, приёмник принял, ответил с Asck -ом и полученными данными, передатчик всё проверил и если всё хорошо, передаёт следующие данные. Чтобы с чего-то начать беру скетч pingpair из примеров. Там, правда только начало для меня, но и оно не работает. На первую передачу приёмник отвечает и всё ОК, но дальше приёмник всё игнорирует. Больше не отвечает. Я то это победил и даже где-то на форумах(может и здесь) встречал подобную ситуацию. Но мне попалось это: https://arduino.stackovernet.com/ru/q/4100. Прочитал, сделал как там, работает. Может кому-нибудь пригодится.
     
    ImrDuke нравится это.
  16. ImrDuke

    ImrDuke Гик

    Подскажите как правильно включать питание nRF только для отправки сообщений.
    Если добавляю выделенную строку, модуль отказывается работать.
    Пробовал ставить delay и после включения и перед выключением.

    Код (C++):
    void Transmit() {
      noInterrupts();
      digitalWrite(nrfPIN, HIGH);
      radio.powerUp();                                       // Включить передатчик
      radio.stopListening();                                 // не слушаем радиоэфир, мы передатчик
      delayMicroseconds(128);
      memset(Transmit_Data, 0, sizeof(Transmit_Data));       // Обнуляем массив
      Transmit_Data[0] = DeviceID;                           // Номер устройства
      radio.flush_tx();
      radio.write(&Transmit_Data, sizeof(Transmit_Data));    // Отправить по радио
      radio.startListening();                                // начинаем слушать эфир, мы приёмный модуль
      delayMicroseconds(128);
      radio.powerDown();                                     // Выключить передатчик
      digitalWrite(nrfPIN, LOW);
      interrupts();
    }
     
  17. parovoZZ

    parovoZZ Гуру

    Перед включением обязательно отпустить ногу CE!. Затем отправляешь в регистр config бит включения и нулевой бит приемника. Далее засекаешь время на 1.5 мс - в этот промежуток времени с трансивером можешь делать всё, что заблагорассудиться - грузить данные, вычитывать, конфигурить что-то (но не регистры, которые влияют на радиочасть!), но только не дергать ногу СЕ! По прошествии полутора миллисекунд можешь дернуть ногу СЕ и далее необходимо ждать ещё 130мкс - в это время трансивер настраивает PLL и вроде как игнорит команды по SPI - но это не точно. Я не проверял.
     
  18. ImrDuke

    ImrDuke Гик

    Блин! Точно! Надо же после отключения инициализировать модуль заново!!!
    Попробую вечером так.

    Код (C++):
    void Transmit() {
      noInterrupts();
      digitalWrite(nrfPIN, HIGH);
      [B]RadioSetup();                         // Настройки радиомодуля[/B]
      radio.powerUp();                                       // Включить передатчик
      radio.stopListening();                                 // не слушаем радиоэфир, мы передатчик
      delayMicroseconds(128);
      memset(Transmit_Data, 0, sizeof(Transmit_Data));       // Обнуляем массив
      Transmit_Data[0] = DeviceID;                           // Номер устройства
      radio.flush_tx();
      radio.write(&Transmit_Data, sizeof(Transmit_Data));    // Отправить по радио
      radio.startListening();                                // начинаем слушать эфир, мы приёмный модуль
      delayMicroseconds(128);
      radio.powerDown();                                     // Выключить передатчик
      digitalWrite(nrfPIN, LOW);
      interrupts();
    }
     
  19. parovoZZ

    parovoZZ Гуру

    Если скинуто питание физически, то надо. Если нет - то не надо.
     
  20. ImrDuke

    ImrDuke Гик

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