Sending "float" data - SPI
Objective:
We will send a float type of data (ex: 27.88) from Master to Slave Arduino
Theory:
Before we start coding we need to know the basics of how SPI communication works. I won't go with details here, you will better understand from joony786's article on SPI serial communication. It will be very helpful to understand the codes.
Hardware Setup:
Full Code:
Master Arduino
#include<SPI.h> //main library
void intToBytes(int x); //converts integer to bytes
byte sendData[2]; //stores data that to be sent
void setup()
{
Serial.begin(9600); //MCU & Serial Monitor communication starts
SPI.begin(); //SPI communication starts
SPI.setClockDivider(SPI_CLOCK_DIV16); //data transfering speed
digitalWrite(SS, LOW); //the clock pulse started from Master
}
void loop()
{
float f= 27.88;
int x= f*100;
intToBytes(x); //converting integer to bytes
SPI.transfer(sendData[0]); //sending 2 bytes
SPI.transfer(sendData[1]);
delay(10);
}
void intToBytes(int x)
{
sendData[0]= (x >>8); //bitwise operation
sendData[1]= x & 0xFF;
}
Slave Arduino
#include<SPI.h> //main library
void bytesToInt(byte b0, byte b1); //converts bytes to integer
byte receiveData[2]; //stores incoming bytes
int a; //stores converted integer
int i=0;
volatile bool flag=false; //used for confirmation
void setup()
{
Serial.begin(9600);
SPI.setClockDivider(SPI_CLOCK_DIV16);
pinMode(SS, INPUT_PULLUP); //make this device slave
pinMode(MISO, OUTPUT); //transfer data to Master
SPCR |= _BV(SPE);
SPCR |= !(_BV(MSTR));
SPI.attachInterrupt(); //data handler
}
void loop()
{
if(flag == true) //data received confirmation
{
bytesToInt(receiveData[0], receiveData[1]);
float f= (float) a/100; //converting integer to float
Serial.println(f);
}
flag = false; //reset for next loop
delay(10);
}
ISR(SPI_STC_vect)
{
receiveData[i]= SPDR; //data received one by one
i++;
if(i==2) //we only expecting 2 data bytes
{
i=0; //reset for next loop
flag=true; //data received confirmation
}
}
void bytesToInt(byte b0, byte b1)
{
a = (b0 << 8) | (b1); //bitwise operation
}
Code Explanation:
At Master
void intToBytes(int x);
We can not directly send/receive the "integer/float" type data from Slave. All Serial Communication is only possible with "byte" type data(ex: 0x12). So we first convert the float to integer then integer to bytes. Then only the bytes data is sent to Slave Arduino. We created this user-defined function which will take an integer input then will split it into 2 data bytes. We use the Bitwise (>>, &) operators for this.
SPI.begin();
SPI.begin() only used for Master, in slave we need to write multiple lines to establish the communication.
SPI.setClockDivider(SPI_CLOCK_DIV16);
Setting the data transferring speed at 1Mbit/s. Others option are "SPI.setClockDivider(SPI_CLOCK_DIVa)" where a=2,4,6,8,16,........
digitalWrite(SS, LOW);
The clock pulse started from Mastera, Slave cant start clock pulses. Without clock pulses, data can not be transferred.
At Slave:
void bytesToInt(byte b0, byte b1);
We created this user-defined function for receiving purpose which will take the incoming 2-byte type of data as input and convert those to one integer. So at the Slave's monitor, we will see the same float data that was sent from Master Arduino.
volatile bool flag=false;
By using "volatile" we specified/fixed a memory location for "flag" which is a "bool" type variable, so next time when we need to access this variable compiler will know the exact location, the compiler won't need to reload and recheck variable's current/new memory location, unlike regular "int, float, char, bool" type variable. Learn more about it.
pinMode(MISO, OUTPUT);
This line helps to transfer data from Slave to Master. Though we dont need to transfer data from Slave to Master here but I think it's a good practice to use a common pattern everywhere.
SPCR |= _BV(SPE);
SPCR |= !(_BV(MSTR));
These 2 lines help to establish SPI communication from the Slave side, where at the master we only used "SPI.begin()"
SPI.attachInterrupt();
When there will be any interference/data transfer, a default "ISR(SPI_STC_vect)" function will run automatically to send & receive data. Here "SPI_STC_vect" is known as SPI status vector. This function only used in Slave Arduino, Master Arduino doesn't have this function.
receiveData[i]= SPDR;
"SPDR" is the register where data is saved byte by byte, but old data is replaced immediately after new data comes, that's why this function has to be as smallest as possible. No delay/print function can be used here. Because these functions may increase the run time and we can lose data. Here first byte will be received then "i" increments, then the next byte will be received, it will keep receiving bytes until our expected data is stored completely.
Result:
Slave's Serial Monitor
See More Examples:
Comments