Hi
Background: I have a working SPI communication between an RPi (master) and a Arduino uno. In between these two there is a bidirectional level translator (LSF0108PWJ), which (from the datasheet) supports up to 100 MHz up translation and ≥100 MHz down translation at ≤30 pF capacitive load. With this information I want to point out that this component (probably) isn't the limiting factor regarding the transfer speed of the SPI
At first I used this SPI communication to send float numbers (from sensors) to the RPi. This is going quite succesfull at 128kHz (1 error in 50 float numbers). With error I mean that the SPDR just returnes the original buffer value, which was received from the RPi, back to the RPi. So it didn't succesfully write the new value to the SPDR. But I was OK with this.
As many people, i used the serial port for debugging, but due to a a stupid mistake I fysically destroyed something on my Uno. The serial port isn't working anymore on the arduino board. After removing the atmega328p from the board and hooking it up on a breadboard, it showed that the the SPI communication was still functioning properly. So it came to my mind to also do the debugging through the SPI-communication by makeing a 'Verbose-mode'. This verbose-mode sends the bytes of char's which are utf-8 decoded on the RPi. Which I got working (I'm using the RPi to programm the arduino uno) but only when using a speed of 32kHz. Otherwise there are too many faulty bytes in the transfer
Now is my question: why, oh why, can't I keep the speed at 128kHz (which isn't that fast...).
So to help understanding the code, here a bit of context:
- every variable which contains the word 'verbose' is a variable related to the 'verbose-mode'
- the verbose-mode is storing all "prints" in a char buffer (spiVerboseArray) of 256 bytes. The RPi then requests to send this buffer every 0.05s. This request interval of 0.05s is not a factor in the succesrate of the transfer. When I try it manually or automate it makes no difference
- the float values are converted to bytes by using a union. the pointer buffer (ftb_outArrayByte) stores the location of the floats. This is a major difference with the verbose buffer (spiVerboseArray), which actually stores the char's. I can't use a pointer buffer for the verbose buffer, because I also print the "__ LINE__" variable (for debugging) and this needs to be stored somewhere eventually. Storing this at one place and making a pointer buffer to look for that one place, just sounds like taking more memory without improving it (but I could be wrong?)
overview of the functions:
- ISR: I tried to reduce as much code here as I can. I also tried to changing the order of the "switch case" by first handling the verbose. This didn't change anything. So I guess this coding is not the main issue of the speed lag
- spiSetup: this is run at the Setup. The only 'special' thing here is that I make the pointer array where all locations of the float values are stored
- print: this method is only used for the verbose-mode. The print method assues a char array and here it just fills up the buffer, it separates the different char arrays with a scheidingsteken "|", only when the last char is a ":", it leaves the "|" out. The additional if-statements are there for when the buffer is getting overflown.
- printint: converts integers to char arrays
- printfloat: converts floats to char arrays. This is done by multiplying them by 1000, casting it to a long, and than separating the last 3 digits from the rest by a "."
to make the verbose code work: put #include "spi_comm.h" on top of your file, the spiSetup(); in the setup(), and for instance this code:
printint(__LINE__), print("Vpri:"), printfloat(Vpri_ftb.f);
Will return something like: "101|Vpri:12.100".
@128kHz I get this result from my Verbose-mode:
|900|���|788|boo���440|
|90���10|788|����t:440|
����|910|78����ost:440���900|910|���|boost:4���|
|900|���|788|bo����40|
|9����10|788|����t:440|
����|910|78���oost:440���900|910|��
There is a build in error detection in the RPi code which returns the standard '�' in the output, in case when the received byte is the same as the send byte (and the send byte is not a valid utf-8 character, so easy to detect)
@128kHz I get this result from the send floats:
-0.003 0.027 0.636 0.463 0.019 0.0 440.0 0.0 0.0 0.0
0.0 0.034 0.642 0.484 0.618 0.0 440.0 0.0 0.0 0.0
0.0 0.007 0.631 0.452 0.0 0.0 440.0 0.0 error 0.0
0.0 0.034 0.631 0.468 1.848 0.0 440.0 0.0 0.0 0.0
0.003 0.007 0.625 0.446 0.0 0.0 440.0 0.0 0.0 0.0
0.0 0.0 0.636 0.468 0.065 0.0 440.0 0.0 0.0 0.0
There is a build in error detection in the RPi code which returns 'error' in the results. Since all byte values are used the error detection is not as simple as the previous one, but still manageble: you just look if the values of the resulting float is within the range of expectation.
Below the complete code of my SPI communication:
#include <SPI.h> // SPI
bool VERBOSE = true; // if true : debugging mode => very slow !
// Structures for the SPI communication
union floatToByte { // union to convert floats to bytes for spi communication
float f;
byte b[4];
};
floatToByte returningFTB, dummy_ftb, Ipri_ftb, Iout_ftb, Vpri_ftb, Vout_ftb, GenFreq_ftb, pwm_switchDump_ftb, pwm_switchBoost_ftb, errorI2C_ftb, water_flag_ftb, emergency_ftb;
floatToByte* ftb_outArray[10] = {&Ipri_ftb, &Iout_ftb, &Vpri_ftb, &Vout_ftb, &GenFreq_ftb, &pwm_switchDump_ftb, &pwm_switchBoost_ftb, &errorI2C_ftb, &water_flag_ftb, &emergency_ftb}; // Make array of 10 floatToByte values
byte *ftb_outArrayByte[4*sizeof(ftb_outArray)/sizeof(ftb_outArray[0])]; // sizeof returns the number of bytes!!
//ftb_testy.f=123.4556; // THIS line will result in error; assignments are not allowed outside a function
volatile bool receivedThisCycle;
bool returningFTBbool, returningLengthBool, returningVerboseBool;
// make char array (1 byte)
//char spiVerboseArray[50]; //has max size of 50 = "0123456789abcdefghijklmnopqrstuvwxyz9876543210";
char spiVerboseArray[256]; // proberen: maak er een pointer van om het sneller te maken --> gaat niet want geprinte variabelen // = "0123456789abcdefghijklmnopqrstuvwxyz9876543210"; // vergeet de sizeSpiVerbose ook niet aan te passen!!
byte sizeSpiVerbose = 1; // deze gebruiken om de actuele grootte bij te houden // sizeof(spiVerboseArray); // een string eindigt altijd op "\00", dus je krijgt altijd de lengte van de char-array + 1! Hier niet "-1" doen, anders loop je naar de verkeerde geheugens
volatile byte spiReceived, spiVerbose_counter, spi_counter;
char printintGetal[17]; // getal dat nodig is om de conversie van int naar char te doen. We werken met uint16_t, dit heeft 17bytes nodig (1 extra voor null terminator)
const char scheidingsTeken[2] = "|"; // 1 extra voor null terminator
const char verwijzendTeken[2] = ":"; // 1 extra voor null terminator
//###################################################################
//
// SPI INTERRUPT ROUTINE: ensure communication with the RPi
/*
SPDR is a 8-bit wide register = 1 byte
The RPi will send the number of array it will want, and than send 0xff, to just recieve the values
Om dit te testen moeten heel veel Serial.prints uit staan in dit gedeelte!
*/
/**/
// V1
ISR (SPI_STC_vect) {
spiReceived = SPDR;
//receivedThisCycle = true;
switch (spiReceived) {
case 0xfc: // byte to get spiVerboseArray : 252
SPDR = spiVerboseArray[spiVerbose_counter];
spiVerbose_counter++;
break;
case 0xff: // number bytes: 255
SPDR = *ftb_outArrayByte[spi_counter]; //returningFTB.b[spi_counter];
spi_counter++;
break;
case 0xfd: // byte to get length : 253
SPDR = sizeSpiVerbose; //byte(sizeof(spiVerboseArray));
break;
case 0xfe: // closing byte: 254
SPDR = 0;
spi_counter = 0;
break;
case 0xfb: // closing byte for verbose : 251
SPDR = 0;
spiVerbose_counter = 0;
sizeSpiVerbose = 1;
break;
default: // if NO match, do the default.
//SPDR = (*ftb_outArray[spiReceived]).b[0]; //De inkomende transmissie duid aan waar te beginnen. --> op deze manier kan er een deel misgaan, maar niet alles
//returningFTB = *ftb_outArray[spiReceived];
//spi_counter = 1;
break;
}
} // end of interrupt service routine (ISR) for SPI
/**/
void spiSetup(){
pinMode(MISO, OUTPUT);
// maak van de ftb_outArrayByte, een eenvoudige array met locaties naar de bytes van de waardes die gevraagd worden. een float heeft 4 bytes
// turn on SPI in slave mode
SPCR |= _BV(SPE);
// turn on interrupts
//SPI.attachInterrupt();
SPCR |= _BV(SPIE);
for (byte i=0; i < sizeof(ftb_outArray)/sizeof(ftb_outArray[0]); i++){
//memcpy(&ftb_outArrayByte[4*i], (*ftb_outArray[i]).b, 4);
for (byte j =0; j<4; j++){
ftb_outArrayByte[j + i*4] = &(*ftb_outArray[i]).b[j];
}
}
}
//###################################################################
//
// print whatever to Serial or to RPi
void print(char theArray[]) {
if (VERBOSE){
int sizeArray = strlen(theArray);
char k[2] ={theArray[sizeArray-1]}; // char always size + 1 (for the terminating 0)
if ( sizeSpiVerbose == 1 && sizeArray +1 < sizeof(spiVerboseArray)){ // the array is gecleared, dus zien dat je hier vanaf 0 begint
strcpy (spiVerboseArray, theArray);
sizeSpiVerbose += sizeArray;
if (strcmp(k, verwijzendTeken) != 0){ // kijk of het laatste geen dubbele punt is
strcat(spiVerboseArray, scheidingsTeken);
sizeSpiVerbose += 1;
}
}
else if ( (sizeSpiVerbose + 1 + sizeArray) < (sizeof(spiVerboseArray) - 1)) //max size - 1, want char array eindigt altijd op een \x00. Deze checkt of de array er nog bij kan
{
strcat (spiVerboseArray, theArray);
sizeSpiVerbose += sizeArray;
if (strcmp(k, verwijzendTeken) != 0){
strcat(spiVerboseArray, scheidingsTeken);
sizeSpiVerbose += 1;
}
}
else if ( (sizeArray + 14) < (sizeof(spiVerboseArray) -1)) {
strcpy (spiVerboseArray, "OVERFLOW PART|"), strcat (spiVerboseArray, theArray);
sizeSpiVerbose = 14 + sizeArray;
}
else{
strcpy (spiVerboseArray, "OVERFLOW TOO LARGE TO FIT");
sizeSpiVerbose = 25;
}
/**/
}
}
void printint(int getal){
if (VERBOSE){
itoa(getal, printintGetal, 10);
print(printintGetal);
}
}
void printfloat(float getal){
/*
* test cases:
* printfloat(123.456);-25
printfloat(100.0);
printfloat((float) 440);
*/
if (VERBOSE){
//print("jup");
long getalint = getal*1000;
ltoa(getalint, printintGetal, 10);
//print(printintGetal);
int deelpunt = strlen(printintGetal) -3;
//printint(getalint);
//printint(deelpunt);
char firstint[17];
char secondint[4];
strncpy(firstint, printintGetal, deelpunt);
firstint[deelpunt] = '\0'; //terminator necessary
//print(firstint);
strcpy(secondint, printintGetal + deelpunt) ;
if (strcmp(secondint, "000") == 0){ // kijk of het laatste enkel nullen zijn
print(firstint);
}
else{
//samenvoegen
strcpy(printintGetal, firstint), strcat(printintGetal,"."), strcat(printintGetal, secondint);
print(printintGetal);
}
}
}



