AtTiny45 и nrf24l01

Тема в разделе "Микроконтроллеры AVR", создана пользователем geher, 22 мар 2014.

  1. geher

    geher Гуру

    Есть планы начать работать с ATTiny45. Причем хочется "подружить" это дело с nrf24l01, чтобы сделать беспроводной датчик температуры и влажности.
    Вроде нашел в сети всю нужную информацию, но есть вопросы, на которые хочется получить информацию до начала экспериментов. Тем более, что это будет мой первый опыт с AtTiny45.

    Пишут, что для управления nrf24l01 нужно как минимум 4 пина (всего 6, но на IRQ и EN можно забить, поскольку предполагается работа только на передачу), питание и земля не в счет, им пины не нужны. Остается еще два пина. Один из них занят, ибо он RESET. Пишут, что его можно отключить, получив еще один свободный пин.
    Помимо nrf24l01 нужно подключить датчик, занимающий два пина. Конечно, можно докупить второй DHT22 (первый используется в основном модуле метеостанции), которому достаточно одного провода, или использовать в метеостанции двухпроводный, но тут есть одна загвоздка. Двухпроводный датчик существенно более медленный, что нехорошо сказывается на отображении секунд часов (мелочь, а неприятно). И притом хочется его все-таки использовать, чтобы зря не валялся. Да и характеристики у него получше для внешнего применения.

    После столь длинного предисловия перехожу собственно к вопросам.
    1. Можно ли использовать один из пинов, задействованных в обмене по SPI, для других целей (у датчика есть CLK и DATA)?
    2. Чем теоретически может грозить отключение RESET?

    PS. Читал статью, в которой описывается подключение nrf24l01 по трем пинам, но меня этот вариант не устраивает, поскольку требует дополнительно к 3В питания еще и 5В.
     
    spasum нравится это.
  2. geher

    geher Гуру

    PPS. DHT22 тоже 5 вольт требует.
     
  3. geher

    geher Гуру

    Однако, нехорошо получается. От EN (CE, Chip Enable) и CS при передаче не отказаться. Т.е. свободен только тот самый пин RESET, отключение которого чревато проблемами с перепрошивкой ATTiny.
    Т.е. все-таки придется совместно использовать какой-то (какие-то) пины для работы с датчиком.
    EN и CS отпадают. Появление на них сигнала чревато ненужным дерганьем передатчика.
    Остаются SCK, MOSI и MISO. Что будет, если на эти пины что-то выдавать (что-то с них принимать) в то время, как CS = HIGH (т.е. вроде как в теории nrf24l01 не обменивается ничем по SPI)?
     
  4. Airbus

    Airbus Радиохулиган Модератор

    Тини45 это принципиально?Может сразу взять Тини 2313 или 4313?Там всё на борту и ножек много да и по цене не дороже.
     
  5. geher

    geher Гуру

    Просто ATTiny45 уже есть. Покупать что-то еще пока нет желания.
    Да и задача сама по себе представляется интересной (из нездорового любопытства).
    Это не светодиодом на ардуино помигать.
     
  6. geher

    geher Гуру

    Все прекрасно заработало.
    Пришлось немного попотеть с втискиванием кода в доступные 4 киб. Библиотека радиомодуля хотя и адаптирована к AtTiny, но заняла сама по себе больше половины из них (не зря авторы позиционировали ее, судя по названию, все-таки под Attiny85). Заодно быстро выяснил, что sprintf - это зло в виде примерно полутора кб бездарно занятой памяти. Понятно, что если переписать по человечески, да еще на ассемблере, то будет лучше, но это совсем другие трудозатраты. Да и мне пока хватает достигнутого за несколько часов трудов.
    Повесил в итоге датчик на пины PB3 (Data датчика и CE радиомодуля) и PB4 (CLK датчика и CSN радиомодуля). Исходил из того, что в найденном мной примере именно эти пины задействовались под другие нужды параллельно с радиомодулем.
    Т.е. пока SCK молчит, пины радиомодуля CE и CSN оказались безопасны для использования в других целях.
     
    Последнее редактирование: 29 мар 2014
  7. geher

    geher Гуру

    Итак, сделал рабочее устройство, состоящее из регулятора напряжения LM317, AtTiny45, радиомодуля NRF24L01 и температурно-влажностного датчика SHT10.
    Но столкнулся с небольшой трудностью по обеспечению этого устройства электропитанием.
    Я решил поначалу запитать это от двух CR-2035, но они как-то подозрительно быстро просели до 2.5В каждая (буквально за несколько минут работы).
    Запитал от аккумулятора GP 9V ("Крона"). Вроде пока работает нормально.
    В результате задался следующим вопросом.
    Кто что может посоветовать из элементов питания (желательно перезаряжаемое) для наиболее долгой работы получившегося метеодатчика, и чтобы габариты при этом были поменьше?
    Или это можно как-то запитать без регулятора от чего-то на 3-3.3 В ?
    Режим работы - опрос и передача данных примерно раз в 20 секунд. Потребление во время передачи 120 мА, в остальное вреия укладывается в 1-2 мА.
     
    Последнее редактирование: 12 апр 2014
    gonzales и Tomasina нравится это.
  8. geher

    geher Гуру

    Поскольку к проекту возник интерес, то выкладываю архивы со скетчем для ATTiny и модифицированными библиотеками (вариант с DHT22 пока не проверял, руки не дошли).
    На принимающей же стороне все выглядит так:
    Объявление (Mega2560, потому такие номера пинов):
    Код (Text):
    #include "nRF24L01.h"
    #include "RF24.h"

    RF24 radio(48,49);
    Инициализация:
    Код (Text):
    //Start radio
      pinMode(49,OUTPUT);
      digitalWrite(49,HIGH);

      radio.begin();

      radio.setDataRate(RF24_1MBPS);
      radio.setPALevel(RF24_PA_MAX);
      radio.setChannel(76);
      radio.enableDynamicPayloads();
      radio.setRetries(15,15);

      radio.setCRCLength(RF24_CRC_16);

      //radio.openWritingPipe(0xF0F0F0F0E2LL);
      radio.openReadingPipe(1,0xF0F0F0F0E3LL);

      radio.startListening();
    Функция приема:
    Код (Text):
    void CheckRadio()
    {
        char receivePayload[31];
        struct rb{
          int temp;
          int humidity;
          int crc;    
        } *radiobuffer;
        uint8_t len = 0;
        uint8_t pipe = 0;
        radiobuffer=(struct rb*)receivePayload;
        // Loop thru the pipes 0 to 5 and check for payloads  
    #if SERDEBMODE
          //Serial.print("Radio:");
          //Serial.println(millis());
          //Serial.println("Radio:Check");
    #endif
        if ( radio.available( &pipe ) ) {
    #if SERDEBMODE
          printserialtimestamp();
          Serial.println("Radio:Available");
    #endif
          bool done = false;
          while (!done)
          {
            len = radio.getDynamicPayloadSize();
            done = radio.read( &receivePayload,len );
          }
    #if SERDEBMODE
          printserialtimestamp();
          Serial.print("Radio:packlen=");
          Serial.println(len);
    #endif
          if ((len==(sizeof(word)*3))&&
              (radiobuffer->crc==(radiobuffer->temp+radiobuffer->humidity)))
          {
            outerActual=true;
            outerTime=millis();
            outerTemperature=radiobuffer->temp/10.0;
            outerHumidity=radiobuffer->humidity;
    #if SERDEBMODE
          } else {
            printserialtimestamp();
            Serial.println("Radio:Bad CRC");
    #endif
          }
        } else {
    #if SERDEBMODE
          //Serial.println("Radio:No data");
    #endif
        }
    }
     
     

    Вложения:

    • attiny45_mirf_temp_hum.zip
      Размер файла:
      1,7 КБ
      Просмотров:
      480
    • DHT85.zip
      Размер файла:
      3,5 КБ
      Просмотров:
      516
    • SHT85.zip
      Размер файла:
      4 КБ
      Просмотров:
      449
  9. gonzales

    gonzales Гик

    Не компилируется для ATtiny45. Ошибки
    Код (Text):
    C:\Program Files (x86)\Arduino\libraries\SPI85\SPI85.cpp: In static member function 'static void SPI85Class::begin()':
    C:\Program Files (x86)\Arduino\libraries\SPI85\SPI85.cpp:51:11: error: 'SCK' was not declared in this scope
      pinMode(SCK, OUTPUT);
              ^
    C:\Program Files (x86)\Arduino\libraries\SPI85\SPI85.cpp:52:11: error: 'MOSI' was not declared in this scope
      pinMode(MOSI, OUTPUT);
              ^
    C:\Program Files (x86)\Arduino\libraries\SPI85\SPI85.cpp:53:11: error: 'MISO' was not declared in this scope
      pinMode(MISO,INPUT);
              ^
    C:\Program Files (x86)\Arduino\libraries\SPI85\SPI85.cpp: In static member function 'static void SPI85Class::end()':
    C:\Program Files (x86)\Arduino\libraries\SPI85\SPI85.cpp:62:11: error: 'SCK' was not declared in this scope
      pinMode(SCK, INPUT);
              ^
    C:\Program Files (x86)\Arduino\libraries\SPI85\SPI85.cpp:63:11: error: 'MOSI' was not declared in this scope
      pinMode(MOSI, INPUT);
              ^
    C:\Program Files (x86)\Arduino\libraries\SPI85\SPI85.cpp:64:11: error: 'MISO' was not declared in this scope
      pinMode(MISO,INPUT);
    Зато, если выбрать как устройство ATTiny85 - то компилируется. Что то нужно поменять в библиотеке для корректной работы?
     
  10. geher

    geher Гуру

    Похоже, что я про это просто забыл, что наступал на те же грабли. Моя версия библиотеки модифицирована и отличия следующие:
    В оригинальной
    https://github.com/stanleyseow/attiny-nRF24L01
    присутствуют в файле SPI85.cpp в подкаталоге SPI85 строки.
    Код (Text):
    if defined( __AVR_ATtiny85__ )
    const static uint8_t SS  = PB4;
    const static uint8_t MOSI = PB1;
    const static uint8_t MISO = PB0;
    const static uint8_t SCK  = PB2;
    #endif
    // attiny84 tested working using Arduino Digital Pins below
    #if defined( __AVR_ATtiny84__ )
    const static uint8_t SS  = 3;
    const static uint8_t MOSI = 5;
    const static uint8_t MISO = 4;
    const static uint8_t SCK  = 6;
    #endif
    Их надо заменить на такие (в этом случае компилироваться будет под 45 и не будет под 85 и 84) или к ним надо добавить такие (получится универсальный вариант):
    Код (Text):
    #if defined( __AVR_ATtiny45__ )
    const static uint8_t SS  = PB4;
    const static uint8_t MOSI = PB1;
    const static uint8_t MISO = PB0;
    const static uint8_t SCK  = PB2;
    #endif
    Т.е. нужно добавить варианты значений данных констант для ATtiny45 (совпадают с теми, что для ATtiny85)

    Еще прилагаю исправленную версию файлов библиотеки Mirf85, что в подкаталоге Mirf85
    (это когда принимал меры по сокращению кода, сколько-то байт этим выиграл)
     

    Вложения:

    • mirf85.zip
      Размер файла:
      10 КБ
      Просмотров:
      516
    Последнее редактирование: 25 авг 2014
  11. gonzales

    gonzales Гик

    это я тоже делал, только не помогло. А помогло вот так
    Код (Text):
    //#if defined( __AVR_ATtiny45__ )
    const static uint8_t SS  = PB4;
    const static uint8_t MOSI = PB1;
    const static uint8_t MISO = PB0;
    const static uint8_t SCK  = PB2;
    //#endif
     
  12. gonzales

    gonzales Гик

    Ошибка при компилировании скетча для Меги в строке
    done = radio.read( &receivePayload,len );
    error: void value not ignored as it ought to be
     
  13. geher

    geher Гуру

    У меня такого не было.
    Сейчас посмотрел на библиотеку, что у себя, и на ту, что лежит на том месте, где я брал.
    https://github.com/stanleyseow/RF24
    А она там изменилась. Пример пропал, который я использовал, а функция
    radio.read( &receivePayload,len );
    не возвращает больше никакого значения.

    Вот оно как бывает.
    Решений проблемы может быть несколько.
    Например, убрать цикл.
    Т.е.
    Код (Text):
    while (!done)
          {
            len = radio.getDynamicPayloadSize();
            done = radio.read( &receivePayload,len );
          }
     
    заменить на
    Код (Text):
    len = radio.getDynamicPayloadSize();
    radio.read( &receivePayload,len );
     
    Так оно делается в некоторых примерах обновленной библиотеки.
    При этом некоторые другие примеры из обновленной библиотеки используют старый формат вызова данной функции, что вроде как должно привести к тому, что они не будут компилироваться.
     
    Последнее редактирование: 25 авг 2014
  14. gonzales

    gonzales Гик

    спасибо, я уже тоже к этому пришел. пока возвращает len = 0, сейчас буду смотреть код для Tiny
     
  15. gonzales

    gonzales Гик

    походу я убил AtTiny45. При выгрузке пишет
    avrdude: Yikes! Invalid device signature.
    Double check connections and try again, or use -F to override
    this check.
     
  16. gonzales

    gonzales Гик

    В общем слетела сигнатура контроллера, поэтому avrdude без параметра -F просто игнорирует его. Как восстановить сигнатуру не нашел, завтра поеду за новым МК.
    Возможно я сам его испортил, когда пытался прошить с подключенным радиомодулем.
     
  17. gonzales

    gonzales Гик

    С ATtiny я разобрался, все шьется, но никак не удается наладить обмен. Для упрощения, я взял Ваш скетч для Тино и выкинул оттуда все про датчик температуры. Получилось вот
    Код (Text):
    //#define DHTMODE 0
    #define SENSORMODE 1

    #define nop() __asm volatile ("nop")
    #if 1
    nop();
    #endif

    #include <avr/io.h>
    #include <inttypes.h>
    #include <avr/interrupt.h>
    #if SENSORMODE
    //#if DHTMODE
    //  #include <DHT85.h>
    //#else
    //  #include <SHT85.h>
    //#endif
    #endif
    #include <util/delay.h>
    #include <Mirf85.h>
    #include <nRF24L0185.h>
    //#include <MirfHardwareSpiDriver85.h>
    #include <SPI85.h>

    // ATtiny25/45/85 Pin map
    //                                +-\/-+
    //                Reset/Ain0 (D 5) PB5  1|o  |8  Vcc
    //  nRF24L01 CE, Pin3 - Ain3 (D 3) PB3  2|    |7  PB2 (D 2) Ain1 - nRF24L01 SCK, pin5
    // nRF24L01 CSN, Pin4 - Ain2 (D 4) PB4  3|    |6  PB1 (D 1) pwm1 - nRF24L01 MOSI, pin7
    //                                GND  4|    |5  PB0 (D 0) pwm0 - nRF24L01 MISO, pin6
    // Pinout
    // CE  - Pin 2 / D3 / nRF3
    // CSN  - Pin 3 / D4 / nRF4
    // GND  - Pin 4
    // MISO - Pin 5 / D0 / nRF7
    // MOSI - Pin 6 / D1 / nRF6
    // SCK  - Pin 7 / D2 / nRF5
    // Vcc  - Pin 8 ( 3.3V )

    int bufferSize = 0;
    struct buf {
      int temp;
      int humidity;
      int crc;
    } buffer;
    uint16_t counter = 0;
    uint8_t nodeID = 0;

    #define CE  PB3 //ATTiny
    #define CSN PB4 //ATTiny
    #if SENSORMODE
    //#if DHTMODE
    //  DHT dht(PB4);
    //#else
    //  SHT1x sht1x(PB3, PB4);
    //#endif
    #endif

    void setup(){

      #if SENSORMODE
    //  #if DHTMODE
    //    dht.begin();
    // #endif
      #endif
      pinMode(4, OUTPUT);

      Mirf.cePin = CE;
      Mirf.csnPin = CSN;
    //  Mirf.spi = &MirfHardwareSpi;
      Mirf.init();

      byte RADDR[] = {
        0xe2, 0xf0, 0xf0, 0xf0, 0xf0  };
      byte TADDR[] = {
        0xe3, 0xf0, 0xf0, 0xf0, 0xf0  };

      // Get nodeID from TXADDR and mask the first byte (0xff)
      nodeID = *TADDR & 0xff;

      Mirf.channel = 0x4c; // Same as rpi-hub and sendto_hub
      Mirf.rfsetup = 0x06; // 1Mbps, Max power
      Mirf.setRADDR(RADDR);
      Mirf.setTADDR(TADDR);
      Mirf.config();

      // Enable dynamic payload
      Mirf.configRegister( FEATURE, 1<<EN_DPL );
      Mirf.configRegister( DYNPD, 1<<DPL_P0 | 1<<DPL_P1 | 1<<DPL_P2 | 1<<DPL_P3 | 1<<DPL_P4 | 1<<DPL_P5 );


    //  delay(100);

      // Print out register readinds for important settings
      //uint8_t rf_ch, rf_setup = 0;
      //byte tx_addr[5];
      //byte rx_addr[5];

      //Mirf.readRegister(RF_CH, &rf_ch,sizeof(rf_ch));
      //Mirf.readRegister(RF_SETUP, &rf_setup, sizeof(rf_setup));
      //Mirf.readRegister(TX_ADDR, tx_addr, sizeof(tx_addr));
      //Mirf.readRegister(RX_ADDR_P1, rx_addr, sizeof(rx_addr));

      delay(2000);      // For serial debug to read the init config output

    }

    void loop(){

      uint8_t sensor1;

      // Read from sensor at pin 4 (CSN)
      #if SENSORMODE
    //  #if DHTMODE
          buffer.temp = 100;
        buffer.humidity = 25;
    //  buffer.temp = dht.readTemperature()*10;
    //  buffer.humidity = dht.readHumidity();
    ///  #else
    //    buffer.temp = sht1x.readTemperatureC()*10;
    //    buffer.humidity = sht1x.readHumidity();
    //  #endif
      buffer.crc=buffer.temp+buffer.humidity;
      delay(10);
      pinMode(4, OUTPUT);
      #endif
      if ((buffer.temp>-100.0)&&(buffer.humidity>-100.0))
      {
        //sprintf(buffer,"T%03.0f,%02.0fZ",temp_c,humidity);
        Mirf.payload = sizeof(buffer);

        Mirf.send((byte *) &buffer);
        while( Mirf.isSending() )
        {
          delay(1);
        }

        delay(20000);

        //delay(500);
      }

    } // End loop()
     
    то есть я передаю просто константы.

    Далее скетч для меги
    Код (Text):
    #include "nRF24L01.h"
    #include "RF24.h"
    #include <SPI.h>

    RF24 radio(48,49);

    #define SERDEBMODE 1

    void setup() {
      // put your setup code here, to run once:
      Serial.begin(9600);
    pinMode(49,OUTPUT);
      digitalWrite(49,HIGH);
    //Start radio
      radio.begin();

      radio.setDataRate(RF24_1MBPS);
      radio.setPALevel(RF24_PA_MAX);
      radio.setChannel(76);
      radio.enableDynamicPayloads();
      radio.setRetries(15,15);

      radio.setCRCLength(RF24_CRC_16);

      radio.openReadingPipe(1,0xF0F0F0F0E3LL);

      radio.startListening();
    }

    void CheckRadio()
    {
        char receivePayload[31];
        struct rb{
          int temp;
          int humidity;
          int crc;  
        } *radiobuffer;
        uint8_t len = 0;
        uint8_t pipe = 0;
        radiobuffer=(struct rb*)receivePayload;
        // Loop thru the pipes 0 to 5 and check for payloads
    #if SERDEBMODE
          //Serial.print("Radio:");
          //Serial.println(millis());
          //Serial.println("Radio:Check");
    #endif
        if ( radio.available( &pipe ) ) {
    #if SERDEBMODE
          //printserialtimestamp();
          Serial.println("Radio:Available");
    #endif
          //bool done = false;
          //while (!done)
          //{
            len = radio.getDynamicPayloadSize();
            radio.read( &receivePayload,len );
         
            // Format string for printing ending with 0
          //}
    #if SERDEBMODE
          //printserialtimestamp();
        // Serial.print("Radio:packlen=");
          Serial.println(len);
    #endif
          if ((len==(sizeof(word)*3))&&
              (radiobuffer->crc==(radiobuffer->temp+radiobuffer->humidity)))
          {
          Serial.println("Radio: data");
        //  outerActual=true;
        //  outerTime=millis();
        //  outerTemperature=radiobuffer->temp/10.0;
        //  outerHumidity=radiobuffer->humidity;
    #if SERDEBMODE
          } else {
          //  printserialtimestamp();
          //  Serial.println("Radio:Bad CRC");
    #endif
          }
        } else {
    #if SERDEBMODE
          Serial.println("Radio:No data");
    #endif
        }
    }


    void loop() {
     
      CheckRadio();
      //delay(200);
      // put your main code here, to run repeatedly:

    }
    но я все время получаю длину пакета len=0

    подскажите пожалуйста, где ошибка
     
  18. geher

    geher Гуру

    Навскидку могу предположить проблемы с контактами или с правильным подключением радиомодулей. Я сам периодически путал CE и CSN, MISO и MOSI.
    И питать их надо от 3.3. От пяти можно спалить с некоторой вероятностью. У меня один радиомодуль накрылся, когда на +5 по ошибке питание подключил.

    Возможно, имеет смысл сменить зашумленный радиоканал.
    Для тини это в строке
    Mirf.channel = 0x4c;
    Для меги
    radio.setChannel(76);
    Они, естественно, должны иметь одинаковое значение.
     
  19. gonzales

    gonzales Гик

    на меге должен гореть светодиод Tx при работе модуля? Я пробывал поменять на меге MISO и MOSI местами, но тогда вообще не инициализируется модуль - if ( radio.available( &pipe ) )
    Хочу провести эксперимент. Повесить на мегу два радиомодуля, с разными пинами CE и CSN. Один соответственно на передачу, второй на прием и посмотреть работоспособность.
     
  20. gonzales

    gonzales Гик

    Вообще с подключением какая-то хрень)))) Сплошные несостыковки)))


    вот тут Джереми наглядно показывает, что мастер и слейв по SPI шине подключаются MOSI-MOSI, MISO-MISO.
    http://amperka.ru/product/avr-attiny45 вот тут есть распиновки ATTiny45, где написано,
    PB1-MISO, PB0-MOSI
    А по схеме подключения из примера
    Код (Text):
    // ATtiny25/45/85 Pin map
    //                                +-\/-+
    //                Reset/Ain0 (D 5) PB5  1|o  |8  Vcc
    //  nRF24L01 CE, Pin3 - Ain3 (D 3) PB3  2|    |7  PB2 (D 2) Ain1 - nRF24L01 SCK, pin5
    // nRF24L01 CSN, Pin4 - Ain2 (D 4) PB4  3|    |6  PB1 (D 1) pwm1 - nRF24L01 MOSI, pin7
    //                                GND  4|    |5  PB0 (D 0) pwm0 - nRF24L01 MISO, pin6
    PB1 подключается к MOSI радиомодуля, а PB0 к MISO.

    Так как же правильно нужно подключить?