The standard SPI protocol is designed for single-master and multiple-slave communication.
In a multi-master SPI setup, there can be more than one master, but special precautions must be taken for bus arbitration to ensure that only one master communicates at a time.
To handle bus arbitration and avoid data transfer conflicts in a multi-master SPI setup, here's how we can manage the SPI bus.
By properly managing when and how masters disable and enable the SPI bus, conflicts between masters can be avoided, allowing safe communication in a multi-master SPI setup.
Let's go through the hardware connections step by step,
By following these steps, we will have a setup where two Arduino boards (masters) can communicate with an SD card module (slave) using SPI, with proper handling of bus arbitration.
As we have Master 1 and Master 2, so we have to develop two different codes.And below points, we have to take care of the codes.
#include <SPI.h>
#include <SD.h>
#define CS_PIN 10
#define SWITCH_INC 3
#define SWITCH_RST 4
#define DEBOUNCE_DELAY 50
uint8_t counter = 0;
unsigned long lastDebounceTime[2] = {0, 0}; // Last debounce time for each button
uint8_t lastButtonState[2] = {HIGH, HIGH}; // Last recorded state of each button
uint8_t buttonState[2] = {HIGH, HIGH}; // Current state of each button
uint8_t switchPins[2] = {SWITCH_INC, SWITCH_RST}; // Array to store button pins
const char* FILE_NAME = "count.txt"; // File name for storing the counter on the SD card
void setup() {
Serial.begin(115200);
for (uint8_t i = 0; i < 2; i++) {
pinMode(switchPins[i], INPUT_PULLUP);
}
counter = readCounterFromSD(); // Read the initial counter value from the SD card
Serial.print("Initial counter value: ");
Serial.println(counter);
}
void loop() {
// Check if the increment button is pressed
if (isButtonPressed(0)) {
Serial.println("\nIncrement button pressed.");
counter++;
writeCounterToSD(counter); // Save updated counter to SD card
Serial.print("Counter Incremented to ");
Serial.println(counter);
}
// Check if the reset button is pressed
if (isButtonPressed(1)) {
Serial.println("\nReset button pressed.");
counter = 0;
writeCounterToSD(counter); // Save reset counter to SD card
Serial.println("Counter reset to 0.");
}
}
// Reads the counter value from the SD card
uint8_t readCounterFromSD() {
uint8_t storedCounter = 0;
initSDCard();
// Check if the file exists on the SD card
if (SD.exists(FILE_NAME)) {
File file = SD.open(FILE_NAME, FILE_READ);
if (file) {
storedCounter = file.parseInt(); // Parse the counter value from the file
file.close();
Serial.println("Counter read from SD card.");
} else {
Serial.println("Error: Failed to read file.");
}
} else {
Serial.println("No counter file found. Starting with counter = 0.");
}
disableSPI();
return storedCounter;
}
// Writes the counter value to the SD card
void writeCounterToSD(uint8_t count) {
initSDCard();
// Remove the existing file if it exists
if (SD.exists(FILE_NAME)) {
SD.remove(FILE_NAME);
Serial.println("Old counter file deleted.");
}
// Create and write the new counter value to the file
File file = SD.open(FILE_NAME, FILE_WRITE);
if (file) {
file.print(count);
file.close();
Serial.println("Counter updated on SD card.");
} else {
Serial.println("Error: Failed to write to SD card.");
}
disableSPI();
}
// Initializes the SD card
void initSDCard() {
enableSPI(); // Enable SPI communication
Serial.println("SD card initializing");
while(!SD.begin(CS_PIN)){
Serial.print(".");
delay(1000);
}
Serial.println("SD card initialized.");
}
// Enables SPI communication
void enableSPI() {
Serial.println("Waiting for free the SPI bus. ");
while (!digitalRead(CS_PIN)) // Wait until the chip select pin is LOW
{
Serial.print(".");
delay(1000);
}
SPI.begin();
pinMode(CS_PIN, OUTPUT);
pinMode(11, OUTPUT); // MOSI pin
pinMode(12, INPUT); // MISO pin
pinMode(13, OUTPUT); // SCK pin
digitalWrite(CS_PIN, HIGH);
Serial.println("SPI Enabled.");
}
// Disables SPI communication by setting SPI pins to input mode and driving them low.
// This makes the pins high impedance, effectively disconnecting the SPI interface.
void disableSPI() {
SPI.end();
pinMode(11, INPUT); // MOSI pin
pinMode(12, INPUT); // MISO pin
pinMode(13, INPUT); // SCK pin
pinMode(CS_PIN, INPUT);
digitalWrite(11, LOW);
digitalWrite(12, LOW);
digitalWrite(13, LOW);
digitalWrite(CS_PIN, LOW);
Serial.println("SPI Disabled.");
}
// Checks if a button is pressed with debouncing
bool isButtonPressed(uint8_t switchIndex) {
uint8_t pin = switchPins[switchIndex];
int reading = digitalRead(pin);
// Check if the button state has changed
if (reading != lastButtonState[switchIndex]) {
lastDebounceTime[switchIndex] = millis();
}
lastButtonState[switchIndex] = reading;
// Validate the button press after debounce delay
if ((millis() - lastDebounceTime[switchIndex]) > DEBOUNCE_DELAY) {
if (reading != buttonState[switchIndex]) {
buttonState[switchIndex] = reading;
if (buttonState[switchIndex] == LOW) {
return true;
}
}
}
return false;
}
Setup
Loop
isButtonPressed(0).
writeCounterToSD().
isButtonPressed(1).
SPI Management
enableSPI()
:disableSPI()
:SPI.end().
SD Card Operations
initSDCard():
SD.begin(CS_PIN).
readCounterFromSD()
:writeCounterToSD():
Button Debouncing
isButtonPressed():
DEBOUNCE_DELAY
(50ms).#include <SPI.h>
#include <SD.h>
#define CS_PIN 10
#define SWITCH_PIN 3
#define DEBOUNCE_DELAY 50
uint8_t counter = 0;
unsigned long lastDebounceTime = 0;
uint8_t lastButtonState = HIGH;
uint8_t buttonState = HIGH;
const char* FILE_NAME = "count.txt"; // File name used to store the counter on the SD card
void setup() {
Serial.begin(115200);
pinMode(SWITCH_PIN, INPUT_PULLUP);
counter = readCounterFromSD(); // Read the counter value from the SD card at startup
Serial.print("Counter value: ");
Serial.println(counter);
}
void loop() {
// Check if the button is pressed
if (isButtonPressed()) {
Serial.println("\nRead button pressed.");
uint8_t count = readCounterFromSD(); // Fetch the counter value from the SD card
Serial.print("Stored count is: ");
Serial.println(count);
}
}
// Reads the counter value from the SD card
uint8_t readCounterFromSD() {
uint8_t storedCounter = 0;
initSDCard();
// Check if the file exists on the SD card
if (SD.exists(FILE_NAME)) {
File file = SD.open(FILE_NAME, FILE_READ); // Open the file for reading
if (file) {
storedCounter = file.parseInt(); // Read the counter value as an integer
file.close();
Serial.println("Counter read from SD card.");
} else {
Serial.println("Error: Failed to read the file.");
}
} else {
Serial.println("No counter file found. Starting with counter = 0.");
}
disableSPI();
return storedCounter;
}
// Initializes the SD card and sets up the SPI interface
void initSDCard() {
enableSPI(); // Enable SPI communication
Serial.println("SD card initializing");
while(!SD.begin(CS_PIN)){
Serial.print(".");
delay(1000);
}
Serial.println("SD card initialized.");
}
// Enables SPI communication
void enableSPI() {
Serial.println("Waiting for free the SPI bus. ");
while (!digitalRead(CS_PIN)) // Wait until the chip select pin is LOW
{
Serial.print(".");
delay(1000);
}
SPI.begin();
pinMode(CS_PIN, OUTPUT);
pinMode(11, OUTPUT); // MOSI pin
pinMode(12, INPUT); // MISO pin
pinMode(13, OUTPUT); // SCK pin
digitalWrite(CS_PIN, HIGH);
Serial.println("SPI Enabled.");
}
// Disables SPI communication by setting SPI pins to input mode and driving them low.
// This makes the pins high impedance, effectively disconnecting the SPI interface.
void disableSPI() {
SPI.end();
pinMode(11, INPUT); // MOSI pin
pinMode(12, INPUT); // MISO pin
pinMode(13, INPUT); // SCK pin
pinMode(CS_PIN, INPUT);
digitalWrite(11, LOW);
digitalWrite(12, LOW);
digitalWrite(13, LOW);
digitalWrite(CS_PIN, LOW);
Serial.println("SPI Disabled.");
}
// Checks if the button is pressed with debounce logic
bool isButtonPressed() {
int reading = digitalRead(SWITCH_PIN); // Read the current button state
// Detect button state change
if (reading != lastButtonState) {
lastDebounceTime = millis(); // Update debounce timer
}
lastButtonState = reading;
// Validate button press after debounce delay
if ((millis() - lastDebounceTime) > DEBOUNCE_DELAY) {
if (reading != buttonState) {
buttonState = reading;
// Return true if the button is pressed
if (buttonState == LOW) {
return true;
}
}
}
return false; // Return false if no valid press is detected
}
Loop
isButtonPressed()
), it reads and prints the counter value from the SD card.SPI Management
enableSPI():
Waits for the SPI bus to be free, configures SPI pins, and starts SPI communication.disableSPI():
SPI.end().
SD Card Operations
initSDCard():
Initializes the SD card and retries until successful.readCounterFromSD()
: Reads the counter value from count.txt or initializes it to 0 if the file doesn't exist.Button Debouncing
isButtonPressed()
: Debounces the button with a delay of 50ms and detects valid presses.
Master 2 Serial Monitor Output