SPI, Master receiving data from the Slave, how to?

Hi, i might have made it hard on myself on this project.

Perhaps it's best to start at the beginning. My final thesis is about extracting high resolution data. This data (in this case) will be generated at 90 kHz, i know it's not very fast for Arduino's. The transportation "line" (fieldbus) has a maximum speed of around 30 kHz. This means i need to buffer my generated data, since i don't want to lose any of it. So far it's pretty straight forward.
Now i've decided to use SPI communication. The way i've setup my case is as follows:

Slave1 > Master > Slave2

Slave1 = Simulation of my data generation. (Arduino Uno)
Master = Arduino DUE, this because it has the possibility to switch between multiple slaves
Slave2 = The fieldbus slave, which gives an interrupt to the master which than has to send data to the slave (Arduino Uno).

So since i'm pretty new to SPI i thought let's start with basics. I used the example code from Gammon Forum : Electronics : Microprocessors : SPI - Serial Peripheral Interface - for Arduino which has been very helpful.
I've been able to send data from the master to the slaves, simple texts. Without the interrupt it always "talks" with Slave 1 and with an interrupt it "talks" with Slave 2. So far so good.

Now which apparently is quite hard to do, i want Slave 1 to send data to the Master, instead of the Master sending data to the Slave. Like i mentioned before, i'd like to be able to send out data which will be generating within that Arduino to the master. This data are kind of counters which can go up to a value of 17-bits.

All the codes i've been able to find are ones where the Master sends data only. While SPI is a full duplex communication. I've also found in the link i mentioned earlier an example of a code where the slave actually sends some kind of information back to the master. It takes out a byte send by the master from the register of the SPI library (if i'm correct). It does it's a simple adding/substracting, puts the new value back in the register (SPDR), after the master has given a microsecond delay it takes that new value out of the register. With an arduino Uno as a master that works. The Arduino DUE is not accepting that it'll just return '0'.

For some reason the DUE can't be setup as a slave like the UNO. Which would make it 10 times easier since i would then quickly switch Slave 1 to a Master and the Master to a Slave (since it's possible for me to use two DUE's).
I've also read that it's supposed to be possible by sending the exact same amount of bytes from the master to the slave as the slave has to send to the master. No idea how to realise that (keep the type of arduino's in mind).

I'd be able to remove the simulation Arduino and simulate the data generation within the master, however this means i'd leave out quite a big part of my "Proof of concept" situation.

So my question in simple form is like: How can i send a buffer of data from my slave to my master.

I hope somebody is able to help me with my problem, if i was still too cryptic about something my sincere apologies.

I did quite some extensive research on this, normally i always find a solution within the forum unfortunately not this time.

Kind regards and thanks in advance!

Master = Arduino DUE, this because it has the possibility to switch between multiple slaves

Every master has this possibility if the client really is SPI compatible.

Now which apparently is quite hard to do, i want Slave 1 to send data to the Master, instead of the Master sending data to the Slave. Like i mentioned before, i'd like to be able to send out data which will be generating within that Arduino to the master.

You can send data from a slave to the master but only if the master is requesting it. The master provides the clock signal and also selects the slave by pulling it's SS line low.

So my question in simple form is like: How can i send a buffer of data from my slave to my master.

Simply copy the data in the SPDR when the master sends a value. That data will be sent back to the master while the slave receives the data from the master.

Thank you for your replying.

Unfortunately this isn't the exact answer i'm looking for, it's in the right direction though.

pylon:
Every master has this possibility if the client really is SPI compatible.

Yes you're right, i kind of used the wrong words here. What i meant was that the DUE has multiple SS pins, so i can connect multiple slaves at the same time. The UNO for example only has pin 10 for SS.

pylon:
You can send data from a slave to the master but only if the master is requesting it. The master provides the clock signal and also selects the slave by pulling it's SS line low.

That i understood, but normally when i send data from my master to a slave i use the command: Spi.transfer(SSpin, char 'x') (i write it like this since the DUE like i mentioned before can use several pins for SS).

So if i want to receive data from my slave, how am i supposed to write the code in my master in order to readout de SPDR from the register.
Like: Spi.transfer(SSpin, byte 'x') and after that digitalWrite (SS,LOW)? So that with the byte i send over i know what kind of data i want and the digitalWrite to let that data come in?

pylon:
Simply copy the data in the SPDR when the master sends a value. That data will be sent back to the master while the slave receives the data from the master.

I do know that i have to write the data i want to send over into SPDR. My concern is that the DUE doesn't read the SPDR from the register.

As an example, the code from Gammon about adding and substracting in the slave sending the new value back to the master works with UNO's. I tried to simply put the same code for the master in my DUE and i only readout '0' values. If i can get that to work, i think i'll be able to put in data on the slave's end. (This code can also be found in the same link i mentioned in my main post!)

Kind regards, really appreciated the help so far already!

Yes you're right, i kind of used the wrong words here. What i meant was that the DUE has multiple SS pins, so i can connect multiple slaves at the same time. The UNO for example only has pin 10 for SS.

That's wrong. The Due has multiple SPI buses (2 to be exact) but on the UNO you can use any pin as the SS pin to select a slave. Pin D10 is only fixed if the UNO is the slave.

So if i want to receive data from my slave, how am i supposed to write the code in my master in order to readout de SPDR from the register.

 uint8_t value = SPI.transfer(byte_to_send);

I do know that i have to write the data i want to send over into SPDR. My concern is that the DUE doesn't read the SPDR from the register.

The Due has no SPDR (as far as I know), that was for the UNO (which is a slave in your setup you told us).

As an example, the code from Gammon about adding and substracting in the slave sending the new value back to the master works with UNO's. I tried to simply put the same code for the master in my DUE and i only readout '0' values. If i can get that to work, i think i'll be able to put in data on the slave's end. (This code can also be found in the same link i mentioned in my main post!)

Post your code, we might find the error in it.

Sorry for my late reply, but after discussing it i decided to leave out that data generation slave. My master can generate data as well. I do have a problem with sending data though, the SPI terminates the communication after sending one variable. It's probably because of a '0' which comes after each variable.

The master is an Arduino DUE!

If i need to make a new thread for it i will, but i can't find the lock thread anywhere hehe.

If any thinks he/she can help solve this new problem i have thanks!

i'm not putting the whole code here since generating the data works fine i already checked and debugged all of that.
The code for transferring my buffer through SPI is as follows:

void Spi_Transfer()
{
  if(pos<sizeof buf)
  {
    buf[pos++]='\n';
  }
 for(const char *p = buf;(z=*p); p++)  
  SPI.transfer (4,z);
pos=0;
}

So simply for now, i'm trying to send three variables from that buffer with SPI. I already made the integers into bytes and that kinda works (atleast something is working).

If you really want to see that code (it works like i said too many times already) here it is:

void Int_2_Bytes (int integer)
{
  //Serial.println("In Int_2_Bytes Function");

  if ((integer==Data_Array[0])&&(Randomizing_processed==false))
   {
    itoa (Data_Array[0],a1,10);  
   }
   if ((integer==Data_Array[1])&&(Randomizing_processed==false))
    {
     itoa (Data_Array[1],b1,10);
    }
   if (integer==Data_Array[2]&&(Randomizing_processed==false))
    {
     itoa (Data_Array[2],c1,10);
     randomize_complete=true;
    }
   
   if(randomize_complete==true)
    {
    for (int counter=0;counter<3; counter++)
     {
      for (int bytecounter_a=0; bytecounter_a<7; bytecounter_a ++)
       {
        if ((pos < sizeof buf)&&counter==0)
         {
          buf [pos++]=a1[bytecounter_a];  
         }
       }
       
      for (int bytecounter_b=0; bytecounter_b<7; bytecounter_b ++)
       {
        if ((pos < sizeof buf)&&counter==1)
         {
          buf [pos++]=b1[bytecounter_b];  
         }
       }
      for (int bytecounter_c=0; bytecounter_c<7; bytecounter_c ++)
       { 
       // Serial.print("bytecounter_c=");Serial.println(bytecounter_c);
         if (bytecounter_c==6)
        {
         randomize_complete=false; 
        }   
        if ((pos < sizeof buf)&&counter==2)
        {
         // Serial.print("Pointer position in buffer=");Serial.println(pos);
          buf [pos++]=c1[bytecounter_c];  
        }
       }
      }
     }
  return;
}

And yes i know that code could probably be written more effeciently, but that's not the point at the moment :).

So back to the part where i transfer my buffer. I do receive the first variable at the slave if i replace

buf[pos++]='\n';

with:

SPI.transfer (4,'\n');

Which means most likely that in my buffer the character '\0' passes by after each variable which prevents the other variables being transmitted. So my question is more like how do you prevent the communication being terminated when it sees that character '\0'

Sorry if this question is insanely long and perhaps a bit understandable :slight_smile: I haven't been able to find anything really helpful with google or on this forum so far.

If you really want to see that code (it works like i said too many times already) here it is:

Why are your converting all data to ASCII (strings)? This is completely unnecessary, you don't have a serial (UART) line where you don't know where the data starts/ends. Sending strings is inefficient and error prone.

The code for that scary conversion isn't readable because it's missing a lot of initialization and definition parts.

So my question is more like how do you prevent the communication being terminated when it sees that character '\0'

The communication isn't terminated in this case but it might be that your code does something wrong if it finds a zero character. As I don't understand what that code does or should do I cannot help you any further.

The master is an Arduino DUE!

I already did understand that but how does that matter here?

Dear Pylon, i understand it's hard to understand what the code is supposed to do i guess i haven't been really clear.

The communication isn't terminated in this case but it might be that your code does something wrong if it finds a zero character. As I don't understand what that code does or should do I cannot help you any further.

Basically what my program has to do is, sending variables through SPI.
The first phase of my program has to be able to send three different values which add up to a total of 89000 (Since a pulses occur at a frequency of 89kHz and there are three different types of pulse that can occur).

Why are your converting all data to ASCII (strings)? This is completely unnecessary, you don't have a serial (UART) line where you don't know where the data starts/ends. Sending strings is inefficient and error prone.

The code for that scary conversion isn't readable because it's missing a lot of initialization and definition parts.

The reason why i convert it to ASCII strings is because i need to buffer those pulses, until the master receives green light for communicaiton. Which happends after every 3 cycles (aproximately) of the master.
Since the variables are generated as an integer i convert it to a string so i can put it in a buffer, since i don't want to lose any data.
Perhaps i should generate the variables in a specified char('number of bytes') if that's possible?
I don't know if that's possible or if it even resolves the 'challenge', since it still be ASCII characters.

Like i said i'm pretty new to SPI communication so if it's not necessary to do it like i do i'll gladly do it different!

The communication isn't terminated in this case but it might be that your code does something wrong if it finds a zero character. As I don't understand what that code does or should do I cannot help you any further.

I'm able to send the first generated variable with my program. I tested it with a fixed value instead of a random value. Unfortunately i haven't been able to find yet what my program does when it finds a zero character. The thing is when i tried to send over the first variable 1000 or another value with zeros that worked, or i'm just being stupid and those zeros are totaly different than the zero character i mentioned.

The reason why i mentioned the master again is in case people are wondering about my SPI setup since the setup is slightly different for a DUE. You can never be too sure, it's quite irrelevant here i know.

Maybe it might help if i comment my full program and then post it? So it's more readable?
If i forgot to answer anything let me know.

Once again i really appreciate you trying to help :slight_smile:

The reason why i convert it to ASCII strings is because i need to buffer those pulses, until the master receives green light for communicaiton. Which happends after every 3 cycles (aproximately) of the master.
Since the variables are generated as an integer i convert it to a string so i can put it in a buffer, since i don't want to lose any data.

I still don't see the need of an ASCII string. Why don't you just copy the values to some buffering integers and then send those (byte for byte) to the master?

Like i said i'm pretty new to SPI communication so if it's not necessary to do it like i do i'll gladly do it different!

I realized that already :slight_smile: . You're transferring the data as you probably would using an asynchronous serial interface (UART). One of the great advantages of a synchronous serial interface (as SPI is) is that you know when a message starts and when it ends without having to use some special marker characters or the like. So in your case if you have three long integer to transmit, you just have to send 12 bytes to the opposite side. This is fast and very reliable.

I'm able to send the first generated variable with my program. I tested it with a fixed value instead of a random value. Unfortunately i haven't been able to find yet what my program does when it finds a zero character. The thing is when i tried to send over the first variable 1000 or another value with zeros that worked, or i'm just being stupid and those zeros are totaly different than the zero character i mentioned.

That's just a guess but you probably made the mistake on the other (master) side. If you receive a character stream that contains bytes with value zero a C string is terminated and the rest is ignored. If you don't take strict care you quickly loose control over such stuff. One reason more not to use strings in this case.

Maybe it might help if i comment my full program and then post it? So it's more readable?

I guess this is a good idea. We might give you hints how to optimize it.

I still don't see the need of an ASCII string. Why don't you just copy the values to some buffering integers and then send those (byte for byte) to the master?

If you mean what i think you mean and that is i can transfer an integer (in this case 4 bytes total) into a buffer by bitshifting it as follows:

 for(int i=0; i<4; i++)
      buf[i] = (Data_Array[0]>>8*i); //Data_Array is the integer of 32 bits for an uno it has to be a long int

I tried that and it worked! I feel pretty stupid now if this is what you meant hehe. I did learn a lot of doing it the way i did it at first.

I also readout the value on the slave side by bitshifting the otherway and readout the perfect value. To clarify that i won't have trouble with bigger values i tried to send over 4294967295 (max value for unsigned 32-bits). And it worked!

This makes my program much more effecient and shorter!

I do have one question left, since you mentioned the following

I realized that already :slight_smile: . You're transferring the data as you probably would using an asynchronous serial interface (UART). One of the great advantages of a synchronous serial interface (as SPI is) is that you know when a message starts and when it ends without having to use some special marker characters or the like. So in your case if you have three long integer to transmit, you just have to send 12 bytes to the opposite side. This is fast and very reliable.

So this means that i don't have to send the ASCII character to end the communication? So after sending all the bytes through SPI that i want to transfer it automaticly recognizes and ends the communication?(probably thanks to protocol i suppose).

If this wasn't what you had in mind, i probably misinterpeted it. Since you mentioned writing data to integer buffers, i used a buffer of 100 bytes.

This transformation of code is also the reason why i haven't posted it in this reply. It has changed quite a lot now so if you still feel like seeing the code i'll haply post it for ya!

PS: I only tested it with one variable so far, not multiple like i want to do after this!

THANKS!

So this means that i don't have to send the ASCII character to end the communication? So after sending all the bytes through SPI that i want to transfer it automaticly recognizes and ends the communication?(probably thanks to protocol i suppose).

You don't have to send an end character because with SPI you have a CS line for every slave. At the start of a message the line is pulled low and pulled high again after the message ended. That way the master and the slave know how where a message starts and where it ends. It's the master that defines it (because it's him to control the CS line).

If this wasn't what you had in mind, i probably misinterpeted it. Since you mentioned writing data to integer buffers, i used a buffer of 100 bytes.

That's a waste of memory in my opinion. You have 3 integer values that you want to store until you read them next time, so an array of three integers should be enough.

You don't have to send an end character because with SPI you have a CS line for every slave. At the start of a message the line is pulled low and pulled high again after the message ended. That way the master and the slave know how where a message starts and where it ends. It's the master that defines it (because it's him to control the CS line).

Makes sense, should've known that. Thanks though! :slight_smile:

That's a waste of memory in my opinion. You have 3 integer values that you want to store until you read them next time, so an array of three integers should be enough.

Quite close, but i do have 3 integers every cycle of my master. The slave wants the data once per three master cycles. So i would have 9 integers. Which would result in 36 bytes. Still a buffer of 100 is overkill i agree, but i want to expand the amount of data i'll be sending over!

But the main question is, am i doing the process correctly now with that bit shifting code i posted earlier, or did you still mean a different way of doing it?

Kind regards :slight_smile:

But the main question is, am i doing the process correctly now with that bit shifting code i posted earlier, or did you still mean a different way of doing it?

It's one possibility to do it. I would simply using pointer casting or a union to make the translation. As long as the endianess of both processors is the same, this doesn't need any translation.

An example to show you the concept:

uint32_t int_value = 3444;
uint8_t *byte_ptr = (uint6_t *) &int_value;
for (uint8_t i = 0; i < 4; i++) {
  example.transfer(byte_ptr[i]);
}

Dear Pylon, my sincere apologies for my way too late reaction. Had a vacation, should've replied before!

Now i understand your code, i'll try to implement that later on. Thanks.

Something else though, i've been using a logic analyzer since my SPI communication cycle costs more time than expected. I think it's because of the SPI library. The extra time it costs is also not constant example, sometimes the extra time is 1,25 microsecs the other time with the same data it's 1,33 microsecs. A colleague of mine suggests to try hardware SPI instead of software SPI, trying to figure that out as we speak.

Something else that's bugging me is that the SS line randomly toggles during a communication cycle, which makes the byte invalid. The communcation frequency isn't the problem as far as i can tell. I've added an image file of the logic analyzer which shows the unwanted toggling of the SS. The byte i'm sending over is constantly a value of 10 (just for debugging).

Last thing, when i try to go higher than 6MHz for the clock it becomes unstable. The master has a max clock speed of 84MHz and the slave of 16MHz. So in my opinion it should alteast be capable of supporting 15MHZ (to be on the safe side), theoretically it should support 16MHz in this case.

Thanks a lot!

A colleague of mine suggests to try hardware SPI instead of software SPI, trying to figure that out as we speak.

If you use SPI.transfer() as in the code above you're already using the hardware SPI.

The extra time it costs is also not constant example, sometimes the extra time is 1,25 microsecs the other time with the same data it's 1,33 microsecs.

This could be an interrupt. Standard Arduino code use timer0 interrupt to provide the millis() functionality (also used for delay() and similar stuff). We're talking about 0.08µs this about one CPU clock on the UNO.

Something else that's bugging me is that the SS line randomly toggles during a communication cycle, which makes the byte invalid. The communcation frequency isn't the problem as far as i can tell. I've added an image file of the logic analyzer which shows the unwanted toggling of the SS. The byte i'm sending over is constantly a value of 10 (just for debugging).

That looks like line noise to me. It seems that your bus is to long. How long is the complete length of your SPI bus? What type of cabling are you using for it?

Last thing, when i try to go higher than 6MHz for the clock it becomes unstable. The master has a max clock speed of 84MHz and the slave of 16MHz. So in my opinion it should alteast be capable of supporting 15MHZ (to be on the safe side), theoretically it should support 16MHz in this case.

As far as I know the maximum frequency the UNO supports as an SPI slave is about 4MHz. If you go higher the time used for the receiving interrupt is becoming to long for a reliable function. With 4MHz you have only a few instructions to execute between two calls of the ISR. The time is enough for 32 clock cycles. The entrance into the ISR and the return needs at least 11 clock cycles (without one instruction executed). So you have about 20 clock cycles to execute the ISR it no other task ever run on the CPU. I'd say 4MHz is quite a high value already.

Hey, thanks for the answers!

That looks like line noise to me. It seems that your bus is to long. How long is the complete length of your SPI bus? What type of cabling are you using for it?

Hmm yea, this could be noise indeed, i'm using simple jumper wires, so a crappy connection could cause it. For the length it's MAX 10 cm. I don't think the distance is the problem in this case. I'm also using a breadboard so i can connect the Analyzer, but before i used the analyzer i had it directly connected to the other Arduino at that time i also lost a byte randomly. So the breadboard could enhance the noise effect perhaps, but is not the main cause.

As far as I know the maximum frequency the UNO supports as an SPI slave is about 4MHz. If you go higher the time used for the receiving interrupt is becoming to long for a reliable function. With 4MHz you have only a few instructions to execute between two calls of the ISR. The time is enough for 32 clock cycles. The entrance into the ISR and the return needs at least 11 clock cycles (without one instruction executed). So you have about 20 clock cycles to execute the ISR it no other task ever run on the CPU. I'd say 4MHz is quite a high value already.

And for the max 4MHz UNO, i can live with that while experimenting. The hardware i'm using later on is capable of supporting SPI on a frequency of 25MHz (and still be stable). But even so, if i put my clock on the master at like 16 or 20 MHz (even while the UNO is not capable of communicating on that speed) the clock of the Master still should show a clock pulse which is constant instead of different lengths? Or is it that the hardware of the UNO somehow distorts this signal then?

Like my first sentence 4MHz is okay for experimenting, but the process i'm trying to extract later on has a process frequency of 90KHz, which gives me multiple parameters i want to "stream". Which gives me about 11,25us of time to send the necessary parameters. For experimenting i don't have to be able to send them all, just show it's possible :slight_smile:

I hope this question i'm gonna ask will be my last, you've been very helpful all the way and i hope you're able and willing to answer this question:

As far as i've learned so far, when using hardware SPI your program should be able to do other tasks parallel while sending a byte through SPI. Instead of sending a buffer i want to send data per byte. While sending the byte (as far as i know) i should be able to prepare my next byte for transfer (parallel). If so, how?

For example:

Byte->SPI.transfer(pin,byte)->SPI.transfer(pin,new_byte)->SPI.transfer(pin,new_byte_2)
Prepare new_byte -> Prepare new_byte_2

Thanks Mr Pylon :slight_smile:

But even so, if i put my clock on the master at like 16 or 20 MHz (even while the UNO is not capable of communicating on that speed) the clock of the Master still should show a clock pulse which is constant instead of different lengths?

No, the clock signal doesn't need to be evenly distributed over time, that what synchronous serial communication means. In an asynchronous serial setup you have to take care the the bits are equal in length but a synchronous signal may have a lot of jitter, but because of the shared clock signal both parties now when the actual data is available for read.

As far as i've learned so far, when using hardware SPI your program should be able to do other tasks parallel while sending a byte through SPI. Instead of sending a buffer i want to send data per byte. While sending the byte (as far as i know) i should be able to prepare my next byte for transfer (parallel). If so, how?

Although this is true for the hardware it's not true for the SPI library. That one waits for the byte be sent (and received) if you call SPI.write().
If you want to prepare some stuff while the hardware does the transfer you have to go very close to the hardware and handle the registers yourself.
The Due is capable of sending/receiving data via SPI using DMA but I'm not aware of a library supporting this.