Hi All
I have currently moved from Mega 2560 to Giga R1, surprisingly i have not run into many issues with the Giga R1 which is a positive.
Last year @Delta_G kindly invested a lot of time in helping me with an example for SPI Full Duplex which i have used on many small projects with great success on Mega 2560 and currently a larger project that is in progress.
Unfortunately my custom SPI library for full duplex will not operate on mbed_giga architecture and I'm not advance or experienced enough to convert this. could anyone help me with this conversion, see below.
SPI_Structs.h
#ifndef SPI_STRUCTS_H
#define SPI_STRUCTS_H
typedef struct {
uint8_t switches[2];
char message[10];
bool reverse;
} master_info_t;
typedef struct {
uint16_t analog[4];
uint8_t ledStates[2];
uint32_t time;
} slave_info_t;
#endif //SPI_STRUCTS_H
SPI_Master_Config.h
#ifndef SPI_MASTER_CONFIG_H
#define SPI_MASTER_CONFIG_H
typedef enum {
LSB_FIRST = 0,
MSB_FIRST
} option_DORD_t;
typedef enum {
SPI_MODE_0 = 0, // CPOL = 0 CPHA = 0
SPI_MODE_1, // CPOL = 0 CPHA = 1
SPI_MODE_2, // CPOL = 1 CPHA = 0
SPI_MODE_3 // CPOL = 1 CPHA = 1
} option_SPI_MODE_t;
typedef enum {
FOSC_4 = 0, // 4MHz
FOSC_16, // 1MHz
FOSC_64, // 250KHz
FOSC_128 // 125KHz
} option_SPI_SPEED_t;
const option_DORD_t SPI_DATA_ORDER = MSB_FIRST;
const option_SPI_MODE_t SPI_MODE = SPI_MODE_0;
const option_SPI_SPEED_t SPI_SPEED = FOSC_4;
#define MAX_RESPONSE_LENGTH 40
// microseconds of delay to let the slave fetch data for response
// My testing shows that you need at least 10 here.
#define TRANSMISSION_DELAY 20
#endif //SPI_MASTER_CONFIG_H
SPI_Master.cpp
#include "SPI_Master.h"
void SPI_Slave::sendCommand(uint8_t command) {
digitalWrite(sspin, LOW);
// send first byte of command
sendByte(command);
digitalWrite(sspin, HIGH);
}
uint8_t SPI_Slave::sendByte(uint8_t b) {
// load data register to start transfer
SPDR = b;
// wait for transfer
while (!(SPSR & (1 << SPIF)))
;
//read result
uint8_t rv = SPDR;
return rv;
}
void SPI_Slave::init() {
digitalWrite(sspin, HIGH);
pinMode(sspin, OUTPUT);
}
void SPI_Slave::setupSPI() {
// set state before we drive the pin.
digitalWrite(SS, HIGH);
pinMode(SS, OUTPUT);
pinMode(MOSI, OUTPUT);
pinMode(MISO, INPUT);
pinMode(SCK, OUTPUT);
// Kill SPI to setup
SPCR = 0;
SPCR = 0
/*Full speed*/
// | (0 << SPR0) | (0 << SPR1)
| SPI_SPEED
// /*sample on leading*/ | (0<<CPHA)
// /*leading edge is rising*/ | (0<<CPOL)
| (SPI_MODE << CPHA)
/*Master Mode*/
| (1 << MSTR)
/*MSB First*/
| (SPI_DATA_ORDER << DORD)
/*SPI Enable*/
| (1 << SPE)
// /*No interrupt8*/ | (0<<SPIE)
;
// set SPSR for double speed
// DOUBLE SPEED DOES NOT WORK IN SLAVE MODE!
// SPSR = (1 << SPI2X);
}
SPI_Master.h
#ifndef SPI_MASTER_H
#define SPI_MASTER_H
#include "Arduino.h"
#include "SPI_Master_Config.h"
class SPI_Slave {
private:
uint8_t sendByte(uint8_t b);
uint8_t sspin;
public:
SPI_Slave(uint8_t p) :
sspin(p) {
}
void init();
void sendCommand(uint8_t command);
template<typename T>
void sendCommand(uint8_t command, T *response, size_t resLength);
template<typename T, typename U>
void bulkTransfer(uint8_t command, T *source, size_t ssize, U *response, size_t rsize);
static void setupSPI();
};
template<typename T, typename U>
void SPI_Slave::bulkTransfer(uint8_t command, T *source, size_t ssize, U *response, size_t rsize) {
digitalWrite(sspin, LOW);
// send command byte
sendByte(command);
// start bulk transfer
for(int s=0, r=0; ((s<ssize) || (r<rsize)); s++, r++){
delayMicroseconds(TRANSMISSION_DELAY); // allow some time for the slave to load registers
// if source index is still less than source size then get a byte, else send 0's
uint8_t out = (s<ssize)? ((uint8_t*)source)[s] : 0;
uint8_t in = sendByte(out);
if((response != NULL) && (r<rsize)){
// if we still expect more response then add it.
((uint8_t*)response)[r] = in;
}
}
digitalWrite(sspin, HIGH);
}
template<typename T>
void SPI_Slave::sendCommand(uint8_t command, T *response, size_t resLength) {
digitalWrite(sspin, LOW);
// send first byte of command
sendByte(command);
if (response != NULL) {
// loop through the rest of the bytes and get responses
if (resLength > MAX_RESPONSE_LENGTH) {
resLength = MAX_RESPONSE_LENGTH;
}
for (uint8_t i = 0; i < resLength; i++) {
delayMicroseconds(TRANSMISSION_DELAY); // allow some time for the slave to load registers
((uint8_t*)response)[i] = sendByte(0); // send 0's to get response
}
}
digitalWrite(sspin, HIGH);
}
#endif // SPI_MASTER_H
SPI_Master Example
#include "SPI_Master.h"
#include "SPI_Master_Config.h"
#include "SPI_Structs.h"
// Create a slave with pin 4 as SS.
SPI_Slave slave1(4);
// Let's use a couple of buttons this time
const uint8_t NUMBER_OF_BUTTONS = 2;
const uint8_t buttonPins[NUMBER_OF_BUTTONS] = { 7, 8 };
// Create the structs for exchange
master_info_t masterStruct;
slave_info_t slaveStruct;
// time between transmissions in ms
uint32_t xmInterval = 100;
void setup() {
SPI_Slave::setupSPI(); // make sure this is first line of setup.
slave1.init(); // initialize the slave (setup SS pin)
// setup some buttons
for (int i = 0; i < NUMBER_OF_BUTTONS; i++) {
pinMode(buttonPins[i], INPUT_PULLUP);
}
// add some data to the master struct
strcpy(masterStruct.message, "Hi There!");
masterStruct.reverse = false;
Serial.begin(115200);
Serial.println("\n*\n*\n*\nStarting SPI_Bulk_Master.ino\n*\n*\n*");
}
void loop() {
// Read buttons and update our master struct
for (int i = 0; i < NUMBER_OF_BUTTONS; i++) {
masterStruct.switches[i] = digitalRead(buttonPins[i]);
}
static uint32_t pm = millis();
uint32_t cm = millis();
// periodically send the struct
if (cm - pm >= xmInterval) {
pm = cm;
Serial.println("Sending:");
// Send the master struct and receive the slave struct
// The data frame going master to slave always comes first
slave1.bulkTransfer(0x47, &masterStruct, sizeof(masterStruct), &slaveStruct, sizeof(slaveStruct));
Serial.println("Received");
// print the received data
for (int i = 0; i < 4; i++) {
Serial.println(slaveStruct.analog[i]);
}
for (int i = 0; i < 2; i++) {
Serial.println(slaveStruct.ledStates[i]);
}
Serial.println(slaveStruct.time);
}
// take a few commands over serial to manipulate the master struct we are sending
if (Serial.available()) {
char c = Serial.read();
switch (c) {
case 'r':
masterStruct.reverse = !masterStruct.reverse;
break;
case 'h':
strcpy(masterStruct.message, "Hi There!");
break;
case 'g':
strcpy(masterStruct.message, "Goodbye");
break;
default:
break;
}
}
}
SPI_Slave_Config.h
#ifndef SPI_SLAVE_CONFIG_H
#define SPI_SLAVE_CONFIG_H
typedef enum {
LSB_FIRST = 0,
MSB_FIRST
} option_DORD_t;
typedef enum {
SPI_MODE_0 = 0, // CPOL = 0 CPHA = 0
SPI_MODE_1, // CPOL = 0 CPHA = 1
SPI_MODE_2, // CPOL = 1 CPHA = 0
SPI_MODE_3 // CPOL = 1 CPHA = 1
} option_SPI_MODE_t;
typedef enum {
FOSC_4 = 0, // 4MHz
FOSC_16, // 1MHz
FOSC_64, // 250KHz
FOSC_128 // 125KHz
} option_SPI_SPEED_t;
const option_DORD_t SPI_DATA_ORDER = MSB_FIRST;
const option_SPI_MODE_t SPI_MODE = SPI_MODE_0;
const option_SPI_SPEED_t SPI_SPEED = FOSC_4;
#define MAX_RESPONSE_LENGTH 40
#endif //SPI_SLAVE_CONFIG_H
SPI_Slave.cpp
#include "SPI_Slave.h"
volatile uint8_t commandBuffer;
volatile uint8_t responseBuffer[MAX_RESPONSE_LENGTH];
volatile uint8_t bytesToRespond = 0;
volatile uint8_t bytesToReceive = 0;
volatile uint8_t *bulkSourcePtr;
void (*checkFunction)(uint8_t);
void checkCommand(uint8_t com) {
checkFunction(com);
}
void setCheckFunction(void (*cf)(uint8_t)) {
checkFunction = cf;
}
boolean bulkTransferInProgress() {
return !!bytesToReceive;
}
ISR(SPI_STC_vect) {
// SPIF flag is cleared by hardware
static uint8_t comIndex = 0;// index for commandBuffer
static uint8_t resIndex = 0; // index for responseBuffer
static uint8_t sourceIndex = 0;
// If we are responding or receiving then don't check for commands
if (bytesToReceive) {
// If we are receiving more bytes then save them and load any response
if(bulkSourcePtr) {
bulkSourcePtr[sourceIndex++] = SPDR;
}
bytesToReceive--;
// if there's more to send load it
if(bytesToRespond >= 1) {
bytesToRespond--;
SPDR = responseBuffer[resIndex++];
} else {
SPDR = 0;
}
} else if (bytesToRespond) {
// Else if we aren't receiving but still have more to respond
// if there's more to send load it
// The double test looks funny. It's to catch that last
// zero the master sends to get the last byte of the response
// We don't want to take a command there, but we don't need to load anything.
if (--bytesToRespond) {
SPDR = responseBuffer[resIndex++];
}
} else {
commandBuffer = SPDR;
// We have a complete command. Reset all the indices and start a cycle
comIndex = 0;
resIndex = 0;
sourceIndex = 0;
// checkCommand needs to be implemented by user. It loads bytes in responseBuffer and returns the number to be sent.
// it is up to user code on the master side to expect the right number and collect them all.
checkCommand(commandBuffer);
// if there's anything to respond
if (bytesToRespond) {
// load the first byte for the next transfer
SPDR = responseBuffer[resIndex++];
}
}
}
void setupSPI() {
pinMode(SS, INPUT);
pinMode(MOSI, INPUT);
pinMode(MISO, OUTPUT);
pinMode(SCK, INPUT);
SPCR = 0
/*Full speed*/
// | (0 << SPR0) | (0 << SPR1)
| SPI_SPEED
// /*sample on leading*/ | (0<<CPHA)
// /*leading edge is rising*/ | (0<<CPOL)
| (SPI_MODE << CPHA)
/*Master Mode*/
| (1 << MSTR)
/*MSB First*/
| (SPI_DATA_ORDER << DORD)
/*SPI Enable*/
| (1 << SPE)
/*Enable Interrupt*/| (1 << SPIE);
// set SPSR for double speed
// DOUBLE SPEED DOES NOT WORK IN SLAVE MODE!
// SPSR = (1 << SPI2X);
}
SPI_Slave.h
#ifndef SPI_SLAVE_H
#define SPI_SLAVE_H
#include "Arduino.h"
#include "SPI_Slave_Config.h"
void setupSPI();
void checkCommand(uint8_t);
void setCheckFunction(void (*cf)(uint8_t));
bool bulkTransferInProgress();
extern volatile uint8_t bytesToReceive;
extern volatile uint8_t responseBuffer[MAX_RESPONSE_LENGTH];
extern volatile uint8_t bytesToRespond;
extern volatile uint8_t* bulkSourcePtr;
// Source refers to the information going master to slave. So Slave is receiving source and sending response.
template <typename T, typename U>
void setBulkTransferResponse(T *source, size_t ssize, U *response, size_t rsize){
if(rsize > MAX_RESPONSE_LENGTH) {
rsize = MAX_RESPONSE_LENGTH;
}
// Put the response in the response buffer so the interrupt will send it.
memcpy(responseBuffer, (uint8_t*)response, rsize);
bytesToRespond = rsize;
bulkSourcePtr = (uint8_t*)source;
bytesToReceive = ssize;
}
// Normally called from within interrupt context
template <typename T>
void setResponse(T *res, size_t size) {
if (size > MAX_RESPONSE_LENGTH) {
size = MAX_RESPONSE_LENGTH;
}
memcpy(responseBuffer, (uint8_t*)res, size);
bytesToRespond = size;
}
#endif //SPI_SLAVE_H
SPI_Slave Example
#include "SPI_Slave.h"
#include "SPI_Slave_Config.h"
#include "SPI_Structs.h"
// Create the structs for exchange
master_info_t masterStruct;
slave_info_t slaveStruct;
// for loop to track when to print
volatile bool newData = false;
void setup() {
setupSPI(); //make sure first line of setup
setCheckFunction(commandChecker); // Set the command check function
// put some dummy data in the slave struct
slaveStruct.analog[0] = 123;
slaveStruct.analog[1] = 234;
slaveStruct.analog[2] = 456;
slaveStruct.analog[3] = 789;
Serial.begin(115200);
Serial.println("\n*\n*\n*\nStarting SPI_Bulk_Slave.ino\n*\n*\n*");
}
void loop() {
// If there's new data and it's finished coming in.
if (newData && !bulkTransferInProgress()) {
newData = false;
Serial.println("Received");
// Print the data that was received
for (int i = 0; i < 2; i++) {
Serial.println(masterStruct.switches[i]);
}
Serial.println(masterStruct.message);
Serial.println(masterStruct.reverse);
Serial.println("*****");
// Set some led states based on the received data
slaveStruct.ledStates[0] = masterStruct.switches[0];
slaveStruct.ledStates[1] = masterStruct.reverse ? !masterStruct.switches[1] : masterStruct.switches[1];
}
}
// This function is called from an interrupt context
// Variables modified here should be marked volatile.
void commandChecker(uint8_t command) {
switch (command) {
case 0x00:
// do nothing this
break;
case 0x47:
// Set for bulk receive
slaveStruct.time = millis(); // get one last quick value for slave struct.
// set the transfer to receive the master struct and send the slave struct
// The data frame going master to slave always comes first
setBulkTransferResponse(&masterStruct, sizeof(masterStruct), &slaveStruct, sizeof(slaveStruct));
// tell loop that a transmission was made so it will print.
newData = true;
break;
default:
// do nothing on other commands
break;
}
}
Also anyone that has spent many hours and many topics on Arduino Forum trying to find SPI Full Duplex Example that works, the code above will answer your preys.
Again the source code above is the credit of @Delta_G .
Thanks