Posts

Running multiple slave devices on Arduino SPI bus – data formats

Bit reversal code

Bit reversal code

This is Part 3 of 3-part series of articles. Part 1 talks about ways of tweaking SPI code while Part 2 talks about hardware modifications.

After finishing hardware modifications for my three-SPI-device setup I started coding and hit a snag. Any device would happily work by its own, WiFi and SD were also happy together, however, adding NFC Shield to the mix would disable other two. If I moved NFC initialization to the beginnig, other two devices would work but NFC would not. At the same time, if a device was just present on a bus and not initialized, other two devices were not affected. It became clear that initialization itself was the source of error.

Seeedstudio NFC Shield uses NXP PN532 transmission module. This module supports several communication interfaces, namely SPI, I2C, and HSU. In SPI mode the data format is ‘LSB first’, i.e., transmission starts from bit 0. Such format is uncommon; all other SPI shields I’m aware of – Ethernet, WiFi, USB Host, etc., are using ‘MSB first’ format – the transmission starts with bit 7.

A quick look into NFC library source code revealed the following line in PN532::begin() function:

pn532_SPI.setBitOrder(LSBFIRST);

This line sets the data format. In Atmega microcontrollers the default SPI data format is ‘MSB first’ – all other SPI devices don’t have to set it during initalization. Initial revision of PN532 code ( written by Adafruit ) was using software SPI and awkward bit order was dealt with in write() and read() functions. When Seeedstudio modified the code to work with hardware SPI, they just switched SPI to ‘LSB first’ format without much thinking, breaking compatibility with the rest of the world. Surely, when I commented out this line, NFC initialization stopped breaking WiFi. Predictably, NFC also stopped working.

Luckily for me, the SPI is pretty basic protocol and bit order setting in SPI controller doesn’t mean much. It really doesn’t matter how the bit order is set; if we need ‘MSB first’ for the majority of the devices we can initialize SPI in a normal way and then modify write() and read() functions for ‘LSB first’ device to serve it reversed bytes. This is exactly what I did. Below is modified version of Seeedstudio PN532 library (also presented on a title screenshot) – lines 6 and 16 perform bit reversing:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/************** low level SPI ********/
/*Function:Transmit a byte to PN532 through the SPI interface. */
inline void PN532::write(uint8_t _data) 
{
  /* bit reversing code copied vetbatim from http://graphics.stanford.edu/~seander/bithacks.html#BitReverseObvious */
  _data  = ((_data * 0x0802LU & 0x22110LU) | (_data * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16;
 
  pn532_SPI.transfer(_data);
}
 
/*Function:Receive a byte from PN532 through the SPI interface */
inline uint8_t PN532::read(void) 
{
  uint8_t data_ = pn532_SPI.transfer(0);
  /* bit reversing code copied vetbatim from http://graphics.stanford.edu/~seander/bithacks.html#BitReverseObvious */
  data_  = ((data_ * 0x0802LU & 0x22110LU) | (data_ * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16;
 
  return data_;
}

After making these modifications, commenting out the ‘LSB first’ setting in the PN532:begin() and also modifying WiFi code to stay away from pin 9 (see this article for discussion) all three devices are happily working together without conflicts. The library mod for PN532 can be left there permanently – the chip will never know that it is communicating with “misconfigured” SPI host. I’m hoping Seeedstudio will fix their code eventually; in the mean time, if you have SPI compatibility issues, simply make code modifications presented in this article.

Enjoy,
Oleg.

4 comments to Running multiple slave devices on Arduino SPI bus – data formats

  • I happen to have “Hacker’s Delight” by Henry S. Warren lying around, and your post encouraged me to browse through it for a more efficient way to reverse bits. And indeed I found one:

    x = (x & 0x55) <> 1;
    x = (x & 0x33) <> 2;
    x = (x & 0x0F) <> 4;

    This code is about 25% faster (optimization -Os). I think it’s easier to read, too.

  • Evan Davey

    Hi Oleg,

    Thanks for the article. I’m using the NFC v2 shield and I still can’t get it to work with your changes above.

    I’m trying to get it to work with the Seeedstudio SD card shield (which I have modified to use a chipSelect of pin 2).

    I can talk to the SD Card shield with the NFC, and the NFC by itself, but as soon as I connect both, the NFC stops working.

    Any ideas?

    Cheers
    Evan

  • Ray

    I’m currently trying the same as is done here and I’m kinda unexperienced with arduino. I have a arduino mega 2560 and added both a wifi shield(official) and a nfc shield(V2.0) on top of it.

    I removed the wifiled in the WIFI library, and swapped the path from SS->10 to SS->9. I then edited the PN532.cpp file according to http://www.circuitsathome.com/mcu/running-multiple-slave-devices-on-arduino-spi-bus-data-formats and I did the hardware modifications as it is done in part 2.

    Now when I use either shield on it’s own on top of the arduino, they both work flawlessly. Only when I stack the 2 shields together on the arduino, only the Wifi shield seems to function.

    In order to test if the shields work I run the provided scripts. For wifi this is the script that scans the available networks, for NFC it is a script that reads the targetID.

    Does anyone know how I can fix this, as at the end of the guide I still get
    ‘Hello!
    Didn’t find PN53x board’

    Thanks in advance