Setting SPI bit order

Hello,

For a long time I had no success getting my 320x240 Sharp Memory Display (which I have connected to my Redboard Artemis ATP) to work well. With the Adafruit GFX library, it worked just fine (albeit very slowly) with bit-banged spi, but it did not work with HW spi, using the MbedSPI class as in the included Example06_SPI.

What is perhaps unusual about this display is that it expects LSB first bit order. I was eventually able to make it work by hacking the gfx lib to reverse the bits of each byte before transmitting (0b10100011 → 0b11000101). What I find curious is that once I had it working, changing the definition of SPI_ORDER has no effect, it keeps working. So I believe this is the cause of my problems - the SPI transfer does not seem to care about the SPI_ORDER setting, and always transmits each byte in order of most significant bit to least significant bit.

Below is a minimal-ish program (based on the SPI example, using no external libraries) that successfully drives my display (showing “static”) over hw SPI whether SPI_ORDER is set to LSBFIRST or MSBFIRST - I would expect this setting to make a difference? Or do I need to set it in some other way?

Thank you,

Andreas

#include <SPI.h>

#define SHARP_SCK  SPI_CLK
#define SHARP_MOSI SPI_SDO
#define SHARP_MISO NC
#define SHARP_CS   D13
#define SHARP_VDD  D12

#define SPI_FREQ 2000000
#define SPI_MODE SPI_MODE0
#define SPI_ORDER LSBFIRST

MbedSPI mySPI(NC, SHARP_MOSI, SHARP_SCK); // declare the custom MbedSPI object mySPI
extern "C" SPIName spi_get_peripheral_name(PinName mosi, PinName miso, PinName sclk);

void setup() {
  Serial.begin(115200);
  while (!Serial) {}
  Serial.printf("Using SPI %d\n", spi_get_peripheral_name(SHARP_MOSI, SHARP_MISO, SHARP_SCK));

  Serial.printf("SPI_ORDER is %d\n", SPI_ORDER);

  pinMode(SHARP_CS, OUTPUT);
  digitalWrite(SHARP_CS, LOW);

  pinMode(SHARP_VDD, OUTPUT);
  digitalWrite(SHARP_VDD, HIGH);

  mySPI.begin();
}

const size_t len = 44;
uint8_t tx_buff[len];

uint8_t swap(uint8_t b) {
  b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
  b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
  b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
  return b;
}

int i = 0;
void loop() {
  int vcom = i++ % 2;

  //   fill with random data
  for (int j = 0; j < len; j++) {
    tx_buff[j] = rand();
  }
  
  // except first byte for command
  tx_buff[0] = 0x80 | (vcom << 6);

  // and last two bytes for trailer
  tx_buff[len - 2] = 0x00;
  tx_buff[len - 1] = 0x00;
  
  tx_buff[1] = swap(i);
  mySPI.beginTransaction(SPISettings(SPI_FREQ, SPI_ORDER, SPI_MODE));
  digitalWrite(SHARP_CS, HIGH);
  mySPI.transfer(&tx_buff, len);
  mySPI.endTransaction();
  digitalWrite(SHARP_CS, LOW);
}

SPI_ORDER is NOT used with SPI.transfer(), it is only used with SPI.transfer16(). It takes an uint16_t value.

Hi Paul, thank you for your reply!

paulvha:
SPI_ORDER is NOT used with SPI.transfer(), it is only used with SPI.transfer16(). It takes an uint16_t value.

This indeed seems to be the case, but I propose that it is a bug.

Likely a misunderstanding of what MSBFIRST / LSBFIRST means in this context - it should be read as bit order, not byte order. This is also what SparkFun’s own tutorial on SPI says, in the section titled “Programming for SPI”: https://learn.sparkfun.com/tutorials/se … ce-spi/all

I uploaded a small SPI test program to my Arduino UNO, which alternates between transmitting with LSBFIRST and MSBFIRST. I then used a “logic analyzer” to capture the resulting waveform, and it seems to support my statement as the bits within each byte are reversed. Results using transfer16 were the same.

My UNO code:

#import <SPI.h>
#define SPIFREQ 250000

const int len = 2;
uint8_t buffer[len];

void setup()
{  
  pinMode(10, OUTPUT);
  digitalWrite(10, HIGH);
  SPI.begin();
}

void loop() {
  buffer[0] = 0xa0; buffer[1] = 0xcc;
  // 1010 0000 1100 1100

  digitalWrite(10, LOW);
  SPI.beginTransaction(SPISettings(SPIFREQ, MSBFIRST, SPI_MODE0));
  SPI.transfer(&buffer, len);
  SPI.endTransaction();  
  digitalWrite(10, HIGH);
  digitalWrite(10, HIGH);
  digitalWrite(10, HIGH);

  buffer[0] = 0xa0; buffer[1] = 0xcc;
  // 1010 0000 1100 1100
  digitalWrite(10, LOW);
  SPI.beginTransaction(SPISettings(SPIFREQ, LSBFIRST, SPI_MODE0));
  SPI.transfer(&buffer, len);
  SPI.endTransaction();
  digitalWrite(10, HIGH);
  digitalWrite(10, HIGH);
  digitalWrite(10, HIGH);
}

you got a point. I have checked the source code. While the Apollo3 processor can be instructed to do either LSB or MSB (BIT) first, the driver does not have code to set it. Sparkfun should fix this and this should be raised on https://github.com/sparkfun/Arduino_Apollo3 as an issue. If you need a workaround earlier, let me know, it is does not look like a difficult fix to implement.

Thank you for looking into it! My temporary workaround of reversing the bits before transmitting is good enough for me, at least for now.

I submitted a github issue: https://github.com/sparkfun/Arduino_Apollo3/issues/478