6

I have an Arduino Due with a SAM3X8E chip and am trying to read SPI data from an external source that is communicating in SPI. I was able to get data by identifying specific bytes that are identifiers but realized that the clock for my board is not synced with the source coming in. I print out the bytes in hexadecimal format (08 00 FF AB etc) and each message is 128 bytes long.

To be clear, the source is an SPI master that is sending out data on its own MOSI line, but I am currently treating my board as a master as well since I do not know how to do Slave mode on Due. I connect the Slaves MOSI line to my boards MISO line, and just transfer data and read 128 bytes but it would appear that I am not getting the same data. Is there a way for my to take in the clock from the external clock and synchronize my board with that clock to make sure? My source code is below. The only methods I can think of are to either figure out how to implement the board in slave mode (i'm not familiar enough with the registers and how to change this, or read data in slave mode) or synchronize the clocks so my board is reading and sampling the source data correctly.

Could someone check if my thinking is correct, or do I not need to make any changes since the data is in a shift register?

Thanks


void setup(){
Serial.begin(9600);
SPI.begin();
SPISettings settings (50000000, LSBFIRST, SPI_MODE1); //Sampling rate    50Mhz, source's max speed
SPI.beginTransaction(settings);
}


void loop(){
  int i = 0;
  uint8_t value;
  value = SPI.transfer(0x00);
  while (value != 8){//searching for identifier 0x08
    value = SPI.transfer(0x00);
  }
  SPI.transfer(buffer, 127);//get the rest of the 127 bytes
  Serial.print(value, HEX);
  Serial.print(" ");
  for (i = 0; i < 127; i++){
    if (buffer[i] == 0){
      Serial.print(buffer[i],HEX);
      Serial.print(0, HEX);
    }
    else{
      Serial.print(buffer[i], HEX);
    }
    Serial.print(" ");
  }
  Serial.println("\n");
}

1 Answer 1

3

For your external source to be an SPI bus master, it has to be the only one generating the clock signal. Yet, calling SPI.transfer() also generates a clock signal from the Arduino; both devices are attempting to drive the clock line simultaneously, hence your problems.

You have to bit-bang this or manipulate the SAM registers yourself to configure it as a slave; the SPI library doesn't support slave mode. In order to bit-bang, you'll have to define your own MOSI, SCK, SS (and MISO, if you'll be writing to the master) pins and do all the sampling yourself with digitalRead(); there are no registers to be read from since you aren't using the SAM's SPI peripheral.

Since your SPI mode is MODE 1, it means the clock idle polarity is LOW, and that data is sampled on the falling edge. To bit-bang with digitalRead(), it is necessary that the SCK frequency be below 50 kHz (digitalRead() takes about 4us), taking overhead into account. You should use direct port manipulation, if you need a higher frequency. If you'll be blocking till you've read data, and assuming that the external source always sends data in byte units, then a basic algorithm:

  1. Create a byte array to hold all the expected bytes
  2. Define a byte variable (curr_byte, maybe) for holding each byte received
  3. Wait till the SS pin goes LOW
  4. Clear curr_byte to zero
  5. Wait till SCK goes HIGH
  6. Wait till SCK goes LOW
  7. Read the bit on the MOSI line and shift it into curr_byte
  8. If you've received up to 8 bits so far, save curr_byte to the byte array; if SS is still LOW, go to step 4, else exit this loop. But if you haven't received a complete byte yet, go to step 5.

After you leave the loop, you can then confirm the bytes you've received with Serial. If the external device can send data with any number of bits, then you should always check the SS line after you shift in a bit, to determine if the Due has been deselected or not i.e. if there are any more incoming bits.

5
  • Thanks, that seems to make sense. I think I understand bit-banging but am confused as to how I should sample accordingly since I can't call SPI.transfer(). Is it reading from SPDR or the data register? Because on Arduinho Due, I don't think it recognizes SPDR, but the objective should be to just wait until SCK is high and then read from the respective data register. I'm just not sure how to or which register the incoming external MOSI data will be found.
    – wallenut
    May 27, 2016 at 12:24
  • @wallenut I've edited my answer. May 27, 2016 at 14:07
  • That clears it up a lot. Thank you, I just have to deal with the locating where MOSI data is shifted into registers and read those and it should be fine.
    – wallenut
    May 27, 2016 at 15:13
  • Your help answers my question. I just do not think it will work for the high clock rate at which this sends data (50 Mhz). I'm going to mess around and try to change the registers and operate it in slave mode, but there is not much Arduino due documentation for slave mode. Any tips on how to use the Atmel Data sheet? This is my first real Arduino project.
    – wallenut
    May 31, 2016 at 12:50
  • @wallenut You won't be able to achieve 50 MHz; the Due has an 84 MHz clock and the lowest SPI prescaler is 2, giving you a maximum SCK frequency of 42 MHz. Is it possible for you to get the master to send data at lower speeds? For a sample Due slave sketch, check this out: github.com/manitou48/DUEZoo/blob/master/spislave.ino. You'll have to change the SPI mode to MODE1 in the sketch, set the clock speed with SPI.setClckDivider(SPI_CLOCK_DIV2) and adapt the loop() for your purposes. I haven't used a Due before though, so I may not be much help. May 31, 2016 at 13:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.