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

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

  1. parovoZZ

    parovoZZ Гуру

    под автоответ нет специального буфера. Используется буфер TX FIFO.
    А где в статусе 4-ый бит? MAX_RT - он то и останавливает работу модуля.
    Это отчет на момент остановки? Судя по всему - нет. Интересно же момент остановки глянуть.
     
  2. Faig

    Faig Нерд

    Но ни один модуль не зависает, если приемнику прислать сообщение он опять его примет. Я сейчас подумал, а ни проверить ли мне метод stopListening() попробовать принять сообщение после выполнения этой команды, может он вообще не принимает команды.
     
  3. Miller_VA

    Miller_VA Нерд

    Тут знакомая фраза проскочила! Бывает и такое. Значит Вы на "мусоре" работали. В ходе переделок остаётся что-нибудь с прошлой инициализации nRF24 вот она и работает, потом выключаешь... и больше не работает, никогда!
    Теперь по существу. Скопировал Ваши скетчи(которые чуть выше), стабильность полная - не работают.
    Мелкие замечания:
    1. При компиляции была Warning на
    char message[32] = "qwertyuiopasdfghjklzxc1234567890"; //some test message
    говорит, что данных больше чем объявленный массив. Когда идёт инициализация строки (то что в кавычках) в конце подставляется НОЛЬ, т.е. тогда массив действительно равен 33. Решение:
    объвляйте так: char message[] = ...... ну и дальше по тексту.
    2. Если бы Вы применяли radio.printDetails(); есть такая функция, хотя я её очень не люблю(переписал на другую), можно было-бы видеть, что там в регистрах, адресах и т.д. Меня она подтолкнула.
    3. Главное. В Sendere, в строке:
    radio.openReadingPipe(2, pipe02_); //At least one of reading pipes must be full adress
    поменяйте трубу на 1. Так:
    radio.openReadingPipe(1, pipe02_); //At least one of reading pipes must be full adress
    Мне кажется, я больше ничего не трогал, НО получил в Receiver какие-то строчки, типа:
    newMessage: qwerty......890
    sending response...
    ack received: 123
    Мы это ищем?
     
  4. parovoZZ

    parovoZZ Гуру

    Там какие-то параметры берутся не из регистров, а из переменных. Впрочем, я библиотекой не пользуюсь. Передачу параметров контролирую исключительно ЛА. При острой необходимости все записанные регистры перечитываю заново. Если надо, то могу выложить 100% рабочий код инициализации передатчика и приемника с пакетами автоподтверждения. Но на регистрах!
     
  5. Miller_VA

    Miller_VA Нерд

    Так человек-же ничем вообще не пользуется. А так бы увидел, какие у него адреса по трубам и куда-чего он посылает.
     
  6. Faig

    Faig Нерд

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

    Ой, прошу вас пожалуйста немного по побдробнее обьясните почему надо именно так? Неужели я не правильно понял логику труб?


    Я все стеснялся об этом вас попросить...)))
    Вы поймите меня, сам я не программист и не спец по электронике, я в прошлом сис.админ и подход у меня к таким делам совершенно иной, я привык с чужим готовым кодом работать (с глючным естественно, как обычно). Поэтому чаще других доверяю библиотекарям )))
     
  7. Miller_VA

    Miller_VA Нерд

    "И тут Остапа понесло"(из 13 стульев). Меня тоже сейчас понесёт! Я ТОЖЕ НЕ ПРОГРАММИСТ!!! Я разрабатываю, изготавливаю, продаю, сопровождаю приборы с программным управлением. Т.е. в приборах обязательно присутствует микроконтроллер. А для него нужна всё таки программа. Поэтому, знание языка программирования, его синтаксис и пр. лабуду нужно знать. Ассемблер я давно забросил, пишу на С. Оболочка для разработки IAR. Там можно многое: написать программу, скомпилировать, загрузить в МП. Ну и отладить можно по шагам, с остановками, с чтением ресурсов и т.д. Провести, так сказать, комплексную программно-аппаратную отладку прибора. Итак, а теперь, конкретно о Ваших потугах. Ну есть такое мнение, что если питание не совсем в порядке, согласен, что-то можно и попортить. Например, в регистрах nRf24. Может быть.
    Я как-то наплевательски к этому отношусь. Какие-бы сопли у меня на макете не болтались, они обычно не были главными. Всё-таки, если понимаешь мат-часть(цитирую paravoZZ, даташит читать прежде всего) программа начинает работать, и очень даже стабильно, как у волка на морозе, сами знаете что...
    Сейчас продолжу...
     
  8. Miller_VA

    Miller_VA Нерд

    Всё правильно понимаешь. Трубы они есть. Ну кто-то их так назвал, ну пусть будут трубы. Их несколько. На любую можно принимать данные. Главное, указать правильный адрес этой трубы. В твоём случае:
    radio.openReadingPipe(2, pipe02_); //At least one of reading pipes must be full adress
    Всё чудесно, для приёма будем использовать трубу 2 и адрес её из pipe02.
    Теперь открываем даташит, смотрим register map, там такое:
    Receive address data pipe 2. Only LSB. MSBytes are equal to RX_ADDR_P1[39:8]
    Т.е. ты задал только один байт для адреса 2 трубы. А ещё 4 байта он возьмёт из адреса трубы 1.
    Но трубу 1 никто-же не инициализировал, кому она нужна..., её адрес так и остался после подачи питания
    "0xC2C2C2C2C2". Ну а дальше... проснулся утром... оно не работает. Это и был "мусор".
    И ещё продолжу...
     
    Faig нравится это.
  9. Miller_VA

    Miller_VA Нерд

    На будущее. Просто предлагаю. Уберите из текста программы radio.begin(); Почему-то она всех путает. После подачи питания в регистрах nRF24 вполне конкретная информация. В Register Map она озвучена. Когда ковыряешь программу, нужно не забывать, что в регистрах nRF24 может остаться что-нибудь с "прошлого раза". Т.е. микропроцессор выполнить новый код, а nRF24 она же сама по себе. Что нибудь да и останется. Есть только два пути. Каждый раз после переделки программы записывать её в Arduino, потом выключать питание на всё, потом включать и только тогда пробовать. Ересь конечно полная. Поэтому, инициализация всех(практически) регистров в начале программы и только потом полезная работа. Эта инициализация и будет Вашей radio.begin();. Это уже второй путь. Эту часть по инициализации можно будет таскать от проекта к проекту. И всё будет работать. И ещё. Просто маленькое замечание. То что называют библиотекой, это вовсе и не библиотека. Обыкновенный текст программы, который пристёгивается к проекту. Его и изменить можно. Что я успешно и делаю. Ну мне так иногда надо.
     
    Faig нравится это.
  10. Miller_VA

    Miller_VA Нерд

    Вернусь, чуть подробней. Я просто поменял трубу 2 на трубу1. Можно было и со 2 трубой работать. Но тогда для первой задать адрес из pipe02. Ну простаивала бы она, к ней же никто не обращается с таким адресом. Как там...стесняюсь спросить...так работает или нет? Что ещё будем ковырять?
     
    Faig нравится это.
  11. parovoZZ

    parovoZZ Гуру

    Код (C++):
        //.. Инициализация трансивера
    void nRF_Init(void)
    {
         uint8_t Buf[5];

        nRF_CE_DDR |= (1<<nRF_CE);
        nRF_CSN_DDR |= (1<<nRF_CSN);

        nRF_CE_PORT |= (0<<nRF_CE);
        nRF_CSN_PORT |= (1<<nRF_CSN);

        //.. CONFIG
        Buf[0] =    (0<<nRF_MASK_RX_DR) | (0<<nRF_MASK_TX_DS) | (0<<nRF_MASK_MAX_RT) |    // маски прерываний от событий
    #if (PTX != no)
            (0<<nRF_PRIM_RX) |                        // Режим передатчика
    #else
            (1<<nRF_PRIM_RX) |                        // Режим приемника для MIDI приемника
    #endif
            (1<<nRF_EN_CRC) | (0<<nRF_CRCO) |                // Проверка CRC разрешена, 1 байт CRC
            (1<<nRF_PWR_UP);                        // Запускаем трансивер
        SPI_WriteArray(nRF_WR_REG(nRF_CONFIG), 1, Buf);                // Отправляем команду. Пин CSN удерживается внутри функции

        //.. RF_CH  Настройка канала
        Buf[0] = nRF_channel;                    // Установка частоты канала передачи
        SPI_WriteArray(nRF_WR_REG(nRF_RF_CH), 1, Buf);            // см. Global_Settings.h

        //.. RF_SETUP  Настройки радиоканала
        Buf[0] = (0<<nRF_RF_DR) | ((0x03)<<nRF_RF_PWR0);            // Скорость передачи 1 Mbps, мощность: 0dbm
        SPI_WriteArray(nRF_WR_REG(nRF_RF_SETUP), 1, Buf);      
       
        Buf[0] = RX_ADDR_msb;                  
        Buf[1] = RX_ADDR_msb;
        Buf[2] = RX_ADDR_msb;
        Buf[3] = RX_ADDR_msb;

    #if (PTX == no)                // Если MIDI приемник
        //.. RX_ADDR_P0  Адрес канала 0 приемника
        Buf[4] = RX_ADDR_P0;
        SPI_WriteArray(nRF_WR_REG(nRF_RX_ADDR_P0), 5, Buf);

        //.. RX_ADDR_P1  Адрес канала 1 приемника
        Buf[4] = RX_ADDR_P1;
        SPI_WriteArray(nRF_WR_REG(nRF_RX_ADDR_P1), 5, Buf);

        //.. RX_ADDR_P2  Адрес канала 2 приемника
        Buf[0] = RX_ADDR_P2;
        SPI_WriteArray(nRF_WR_REG(nRF_RX_ADDR_P2), 1, Buf);

        //.. RX_ADDR_P3  Адрес канала 3 приемника
        Buf[0] = RX_ADDR_P3;
        SPI_WriteArray(nRF_WR_REG(nRF_RX_ADDR_P3), 1, Buf);

        //.. RX_ADDR_P4  Адрес канала 4 приемника
        Buf[0] = RX_ADDR_P4;
        SPI_WriteArray(nRF_WR_REG(nRF_RX_ADDR_P4), 1, Buf);

        //.. RX_ADDR_P5  Адрес канала 5 приемника
        Buf[0] = RX_ADDR_P5;
        SPI_WriteArray(nRF_WR_REG(nRF_RX_ADDR_P5), 1, Buf);

        //.. FEATURE  Опции
        Buf[0] = (1<<nRF_EN_DPL) | (1<<nRF_EN_ACK_PAY);            // Разрешаем данные
        SPI_WriteArray(nRF_WR_REG(nRF_FEATURE), 1, Buf);            // переменной длины

        //.. EN_AA  Автоматическая отправка ACK о приеме данных по каналу
        Buf[0] = (1<<nRF_ENAA_P0)|(1<<nRF_ENAA_P1)|(1<<nRF_ENAA_P2)|(1<<nRF_ENAA_P3)|(1<<nRF_ENAA_P4)|(1<<nRF_ENAA_P5);
        SPI_WriteArray(nRF_WR_REG(nRF_EN_AA), 1, Buf);

        //.. DYNPD  Прием данных переменной длины
        Buf[0] = (1<<nRF_DPL_P0)|(1<<nRF_DPL_P1)|(1<<nRF_DPL_P2)|(1<<nRF_DPL_P3)|(1<<nRF_DPL_P4)|(1<<nRF_DPL_P5);
        SPI_WriteArray(nRF_WR_REG(nRF_DYNPD), 1, Buf);

        //.. EN_RXADDR  Используемые каналы приемника
        Buf[0] = (1<<nRF_ERX_P0)|(1<<nRF_ERX_P1)|(1<<nRF_ERX_P2)|(1<<nRF_ERX_P3)|(1<<nRF_ERX_P4)|(1<<nRF_ERX_P5);
        SPI_WriteArray(nRF_WR_REG(nRF_EN_RXADDR), 1, Buf);

    /*    //.. Длина принимаемых данных по каналам
        Buf[0] = nRF_SEND_LEN;
        SPI_WriteArray(nRF_WR_REG(nRF_RX_PW_P0), 1, Buf);
        SPI_WriteArray(nRF_WR_REG(nRF_RX_PW_P1), 1, Buf);
        SPI_WriteArray(nRF_WR_REG(nRF_RX_PW_P2), 1, Buf);
        SPI_WriteArray(nRF_WR_REG(nRF_RX_PW_P3), 1, Buf);
        SPI_WriteArray(nRF_WR_REG(nRF_RX_PW_P4), 1, Buf);
        SPI_WriteArray(nRF_WR_REG(nRF_RX_PW_P5), 1, Buf);*/

    #endif

    #if (PTX != no)                // Если передатчик
        //.. RX_ADDR_P0  Адрес канала 0 приемника
        Buf[4] = RX_ADDR;                // На этот адрес будут приходить ACK пакеты
        SPI_WriteArray(nRF_WR_REG(nRF_RX_ADDR_P0), 5, Buf);

        //.. TX_ADDR  Адрес канала удаленного приемника
        SPI_WriteArray(nRF_WR_REG(nRF_TX_ADDR), 5, Buf);    // Адрес канала MIDI приемника, на который будут уходить пакеты

        //.. EN_RXADDR  Используемые каналы приемника. На канале 0 принимается пакет автоподтверждения
        Buf[0] = (1<<nRF_ERX_P0)|(0<<nRF_ERX_P1)|(0<<nRF_ERX_P2)|(0<<nRF_ERX_P3)|(0<<nRF_ERX_P4)|(0<<nRF_ERX_P5);
        SPI_WriteArray(nRF_WR_REG(nRF_EN_RXADDR), 1, Buf);
    /*
        //.. Длина принимаемых данных по каналам
        Buf[0] = nRF_ACK_LEN;
        SPI_WriteArray(nRF_WR_REG(nRF_RX_PW_P0), 1, Buf);
    */
         
        //.. SETUP_RETR  Настройка автоподтверждения
        Buf[0] = nRF_REPEAT_INTERVAL | nRF_REPEAT_MAX;            // Автоподтверждение и интервал повтора
        SPI_WriteArray(nRF_WR_REG(nRF_SETUP_RETR), 1, Buf);        // смотреть Settings.h

        //.. FEATURE  Опции
        Buf[0] = (1<<nRF_EN_DPL) | (1<<nRF_EN_ACK_PAY);            // Разрешаем прием данных
        SPI_WriteArray(nRF_WR_REG(nRF_FEATURE), 1, Buf);            // переменной длины

        //.. DYNPD  Прием данных переменной длины
        Buf[0] = (1<<nRF_DPL_P0)|(0<<nRF_DPL_P1)|(0<<nRF_DPL_P2)|(0<<nRF_DPL_P3)|(0<<nRF_DPL_P4)|(0<<nRF_DPL_P5);
        SPI_WriteArray(nRF_WR_REG(nRF_DYNPD), 1, Buf);

        //.. EN_AA  Автоматическая отправка ACK о приеме данных по каналу
        Buf[0] = (1<<nRF_ENAA_P0)|(0<<nRF_ENAA_P1)|(0<<nRF_ENAA_P2)|(0<<nRF_ENAA_P3)|(0<<nRF_ENAA_P4)|(0<<nRF_ENAA_P5);
        SPI_WriteArray(nRF_WR_REG(nRF_EN_AA), 1, Buf);

    #endif
    }
    Код (C++):
    define no   0
    Файл с этой функцией используется одновременно во всех проектах для того, чтобы обеспечить одинаковые значения в необходимых регистрах. Передатчик или приемник задает глобальный параметр PTX. Если PTX = no, то это приемник. Если PTX не равен нулю (в настроечном файле этому параметру присваивается номер трубы, с которой будет работать передатчик) - то это передатчик.
     
    Faig нравится это.
  12. Faig

    Faig Нерд

    Насчет 32 байт, это зависит от IDE или компилятора ( даже незнаю). У меня не ругается и работает без него. По моему главное на приеме 33 байта сделать если надо (ведь все равно передаются только 32 байт).

    Вот где, как я понимаю, проблема. Я почему-то посчитал что полный адрес надо указать только в первый раз на любой трубе ( не важно на первой или пятой). А оказалось что, полностью надо писать именно первую трубу, не по порядку в котором инициализируешь, а по номеру трубы, тобишь pipe01 надо прописывать полностью обязательно даже если ее не используешь. Вот это и был мусор, вы абсолютно правы. Но я все еще раз перепроверю соблюдая чистоту эксперимента. Спасибо вам большое, вы спасли меня от "технического безумия"!

    С первого взгляда работает, но я еще перепроверю все, по возможности, тщательно.

    parovoZZ думаю вы устали уже принимать благодарности от форумчан, но еще раз, большое вам спасибо!


    P.S.: Мысли в слух, чисто для процесса разработки и ознакомления с модулями, можно было бы при старте ардуины кодом выключать питание на радио-модуле потом включать его, и только потом уже инициализировать. Тогда бы голова меньше болела и проблем было бы меньше. А так, полная инициализация (если ничего не упустить в ней, как в моем случае первую трубу).
     
    Последнее редактирование: 14 фев 2019
  13. Faig

    Faig Нерд

    А насчет совета читать даташит, ведь не для всех он написан понятным языком ( я не про английский). например для меня это почти как "китайская грамота", хотя я имею опыт написания на C собственного Serial.print() метода (дефолтный оочень медленный и глючный). Иной раз откроешь следующую страницу в даташите и тебе нужен переводчик с английского на английский, или же просто не можешь уловить суть (за деревьями леса невидишь)...
     
  14. Faig

    Faig Нерд

  15. parovoZZ

    parovoZZ Гуру

    Смотри картинки. Вот пример:
    Вот картинка из даташита:
    Screenshot_2019-02-14 .png
     
    Faig нравится это.
  16. ImrDuke

    ImrDuke Гик

    Есть успехи?
     
  17. michaelukolov

    michaelukolov Нуб

    Доброго времени суток!
    Имеется два беспроводных модуля NRF24L01+PA+LNA с внешними SMA антеннами, прошу помощи в решении проблемы.
    Задача: передача двух байтов с одной Arduino на другую. Один модуль подключен к Mega 2560, питание подключено через AMS1117-3.3, которая подключена к 5В линии Arduino. На обоих модулях стоят конденсаторы рядом с пинами, 100мкФ электролит и 100нФ керамика. Второй модуль подключён к Nano V3, питание такое же - AMS1117-3.3.
    Суть в том, что работает на очень малом расстоянии. Скорость стоит минимальная, чувствительность MAX. В доме достаточно выйти в другую комнату и уже доходят не все пакеты. На улице 12м стабильно, больше 15 - пропадают многие пакеты. Каналы перебирал, использую наименее засорённый. Сами платы модулей, по советам бывалых, обвёрнуты фольгой (кстати на стабильность значительно повлияло - лучше стало).
    Вообще, эксплуатироваться должно на открытой территории (площадка 500х60м), поэтому больше важна дистанция на открытой местности. Когда покупал, рассчитывал, что хотя бы на 200-300м будет хватать, а на деле..
    В чём ещё может быть причина?
     
  18. parovoZZ

    parovoZZ Гуру

    на самом деле, столько не надо. Вполне достаточно 1 микрофарада керамики.

    есть уже в экране.

    брак.
     
  19. ImrDuke

    ImrDuke Гик

    Покажите код
     
  20. michaelukolov

    michaelukolov Нуб

    Было подозрение, т.к. в комментариях много подобного наблюдалось. А какой именно - не проверить?

    Шлю без перерыва, дабы по мере изменения расстояния сразу видеть результат.
    А еще, если в loop поставить задержку более 10мс после каждой отправки пакета, то вообще ничего не отправляется на любом расстоянии.
    Передатчик:
    Код (C++):
    #include <SPI.h>                                
    #include <nRF24L01.h>
    #include <RF24.h>
       
    RF24 radio(36, 38);

    void setup()
    {
        radio.begin();    
        radio.setChannel(5);                              
        radio.setDataRate(RF24_250KBPS);              
        radio.setPALevel(RF24_PA_MAX);
        radio.setPayloadSize(2);
        radio.setRetries(15,15);
        radio.openWritingPipe (0x1234567890LL);
    }

    void loop()
    {
      const char text[] = "hi";
      radio.write(&text, sizeof(text));
    }
    Приёмник:
    Код (C++):
    #include <SPI.h>                
    #include <nRF24L01.h>                                
    #include <RF24.h>    
                             
    RF24 radio(9, 10);                            

    void setup()
    {  
        Serial.begin(115200);    
        radio.begin();                                
        radio.setChannel(5);                              
        radio.setDataRate(RF24_250KBPS);            
        radio.setPALevel(RF24_PA_MAX);
        radio.setPayloadSize(2);
        radio.setRetries(15,15);
        radio.openReadingPipe (1, 0x1234567890LL);
        radio.startListening  ();
    }

    void loop()
    {
        if(radio.available())
        {
          char text[32] = {0};
          radio.read(&text, sizeof(text));
          Serial.println(text);
        }
    }