Tuesday, August 13, 2013

Getting nRF24L01 working with attiny84

attiny84


After getting the attiny85 (8 pins) working with nRF24L01, I find that while this chip is nice and small, the lack of extra pins is a real hassle when I need to add any sensors/actuators to the MCU.

When I received my attiny84 ICs from Element14, now the fun begins...

attiny84 pinout

As usual, I hook up the attiny84 and test a blink sketch to ensure the attiny84 is working correctly. The first thing I encounter is that the blink is very slow, about 8 to 10 secs slower and I was wondering why. By the way, I'm using this Arduino tiny-core https://code.google.com/p/arduino-tiny/ as there are other tiny-cores available on the net with different settings.

After posting my problems on Arduino forum, I found out I need to do a Tools->Burn Bootloader to set the correct speed and fuses on the attiny84. After the "Burn Bootloader", the attiny84 was operating correctly at 8Mhz without an external crystal.

attiny84 
The above pictures are my test setup of the attiny84 on a breadboard with breadboard power supply running on 3.3V, nRF24L01 on an UNO adapter, USBtinyISP and logic analyzer and mini hooks.

The "fun" starts when trying to get the SPI pinout for attiny84 for running the nRF24L01. Since it uses the same tiny core was the attiny85 and I got the attiny85 working, it should not be too difficult, I guess.

After hooking up the attiny84 according to the above image and flashed the hex into the attiny84, I did not get correct settings on the four register I was monitoring on the serial/debug monitor. For troubleshooting, I have enabled "TinyDebugSerial" to read the four register, namely RF_CH, RF_SETUP, TX_ADDR and RX_ADDR.

I always needed to verify these settings and proper communications to the nRF24L01 as I used many different data rate, channel/frequencies and TX/RX address on all these nRF24L01 radios all the time.

  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));

I also hook up a Logic Analyzer to the SPI pins to "see" what is going on during those SPI transfers. A proper SPI transfer would look something like below, with Enable/Slave Select (SS) pin held low during active SPI with Clock (SCK) supplying the clockrate for the MOSI/MISO.

SPI transfers

The issue seems to be SPI related as I do not get SPI patterns similar to the above, I immediately looked at SPI85 library that was downloaded from Arduino forum and it was working fine on the attiny85 on my earlier blog entry.

A bit about the SPI for the attiny, as the attiny does not have dedicated pins for SPI, it uses Universal Serial Interface (USI) for both SPI or I2C. See http://playground.arduino.cc/Code/USI-SPI for details on the USI-SPI on Arduino playground.

By viewing this link on Arduino Playground, it gives *hint* NOT to use the MOSI/MISO pins for attiny as they are ONLY used for In-Circuit Serial Programming (ICSP) only.

By looking at the SPI85.cpp codes, there are two lines that seems to differentiate between attiny85 and attiny84.

#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

#if defined( __AVR_ATtiny84__ )
const static uint8_t SS   = PA7;
const static uint8_t MOSI = PA6;
const static uint8_t MISO = PA5;
const static uint8_t SCK  = PA4;
#endif


Here are some snapshot from the datasheets as like most of you, I only 
will read through the datasheet as a last resort as they are not written for 
normal people like myself. I always wonder if the author even understand what 
he himself wrote if he reads it a few years later.



attiny x4 pinout


attiny x4 pin alternate functions
From the above info/snapshots from the datasheet, I will have to assume that whoever 
wrote the SPI85 library was just following putting support for attiny84 without actually 
tested an attiny84 before as clearly it shows the pins was incorrect.

The pin PA5, while listed as MISO (for ICSP), was also USI-DO (data out) and PA6 while 
listed as MOSI (for ICSP), was also USI-DI (data in). As stated above in Arduino 
playground, for attiny, the MISO/MOSI is used only for ICSP and not for USI-SPI.

That was easy to solve, just swapped PA5 and PA6 and everything will work fine, 
right.. but it turns out that the PA5 and PA6 is logically mapped to value of 5 and 6 
and when the codes does an Arduino code of pinMode and digitalWrite, it execute 
the wrong pins for the attiny. A Digital pin5 mapped to PA5 and Digital pin6 mapped 
to PA4 (the pin used for the SCK/USCK).. no wonder the SPI display on the 
logic analyzer goes haywire..

Once, I traced the issue and figure out the problems, I just had to hard code the Arduino digital pins
to the AVR_ATtiny84 defines as below and changed the MOSI/MISO source of confusing to 
USI_DO and USI_DI :-

#if defined( __AVR_ATtiny84__ )
const static uint8_t SS   = 3;
const static uint8_t USI_DO = 5; 
const static uint8_t USI_DI = 4;
const static uint8_t SCK  = 6;
#endif

After that, I was able to see the correct settings on the Serial Debug or the SPI patterns on the 
logic analyzer. I do not know enough about how the tiny cores was written to put the 
proper attiny84 pin names but it was finally working.

If you figure out on how to fix the attiny84 pins, please let me know. The nRF24L01 library
was located at the summary links below.

There is an example of attiny84 codes in the Mirf library at github repo.

Summary Links :-

Mirf (nRF24L01) libs for UNO/attiny84/attiny85 :- https://github.com/stanleyseow/arduino-nrf24l01
Arduino tiny-cores :- https://code.google.com/p/arduino-tiny/