/* Arduino SdSpi Library
* Copyright (C) 2013 by William Greiman
*
* This file is part of the Arduino SdSpi Library
*
* This Library is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This Library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the Arduino SdSpi Library. If not, see
* .
*/
/**
* \file
* \brief SdSpi class for V2 SD/SDHC cards
*/
#ifndef SdSpi_h
#define SdSpi_h
#include
#include "SdFatConfig.h"
#include "utility/SoftSPI.h"
//------------------------------------------------------------------------------
/**
* \class SdSpiBase
* \brief Virtual SPI class for access to SD and SDHC flash memory cards.
*/
class SdSpiBase {
public:
/** Initialize the SPI bus */
virtual void begin() = 0;
/** Set SPI options for access to SD/SDHC cards.
*
* \param[in] divisor SCK clock divider relative to the system clock.
*/
virtual void init(uint8_t divisor);
/** Receive a byte.
*
* \return The byte.
*/
virtual uint8_t receive() = 0;
/** Receive multiple bytes.
*
* \param[out] buf Buffer to receive the data.
* \param[in] n Number of bytes to receive.
*
* \return Zero for no error or nonzero error code.
*/
virtual uint8_t receive(uint8_t* buf, size_t n) = 0;
/** Send a byte.
*
* \param[in] data Byte to send
*/
virtual void send(uint8_t data) = 0;
/** Send multiple bytes.
*
* \param[in] buf Buffer for data to be sent.
* \param[in] n Number of bytes to send.
*/
virtual void send(const uint8_t* buf, size_t n) = 0;
/** \return true if hardware SPI else false */
virtual bool useSpiTransactions() = 0;
};
//------------------------------------------------------------------------------
/**
* \class SdSpi
* \brief SPI class for access to SD and SDHC flash memory cards.
*/
#if SD_SPI_CONFIGURATION >= 3
class SdSpi : public SdSpiBase {
#else // SD_SPI_CONFIGURATION >= 3
class SdSpi {
#endif // SD_SPI_CONFIGURATION >= 3
public:
/** Initialize the SPI bus */
void begin();
/** Set SPI options for access to SD/SDHC cards.
*
* \param[in] divisor SCK clock divider relative to the system clock.
*/
void init(uint8_t divisor);
/** Receive a byte.
*
* \return The byte.
*/
uint8_t receive();
/** Receive multiple bytes.
*
* \param[out] buf Buffer to receive the data.
* \param[in] n Number of bytes to receive.
*
* \return Zero for no error or nonzero error code.
*/
uint8_t receive(uint8_t* buf, size_t n);
/** Send a byte.
*
* \param[in] data Byte to send
*/
void send(uint8_t data);
/** Send multiple bytes.
*
* \param[in] buf Buffer for data to be sent.
* \param[in] n Number of bytes to send.
*/
void send(const uint8_t* buf, size_t n);
/** \return true - uses SPI transactions */
bool useSpiTransactions() {
return true;
}
};
//------------------------------------------------------------------------------
/**
* \class SdSpiLib
* \brief Arduino SPI library class for access to SD and SDHC flash
* memory cards.
*/
#if SD_SPI_CONFIGURATION >= 3 || SD_SPI_CONFIGURATION == 1 || defined(DOXYGEN)
#include
#if SD_SPI_CONFIGURATION >= 3
class SdSpiLib : public SdSpiBase {
#else // SD_SPI_CONFIGURATION >= 3
class SdSpiLib {
#endif // SD_SPI_CONFIGURATION >= 3
public:
/**
* Initialize SPI pins.
*/
void begin() {
SPI.begin();
}
/** Set SPI options for access to SD/SDHC cards.
*
* \param[in] divisor SCK clock divider relative to the system clock.
*/
void init(uint8_t divisor) {
SPI.setBitOrder(MSBFIRST);
SPI.setDataMode(SPI_MODE0);
#ifndef SPI_CLOCK_DIV128
SPI.setClockDivider(divisor);
#else // SPI_CLOCK_DIV128
int v;
if (divisor <= 2) {
v = SPI_CLOCK_DIV2;
} else if (divisor <= 4) {
v = SPI_CLOCK_DIV4;
} else if (divisor <= 8) {
v = SPI_CLOCK_DIV8;
} else if (divisor <= 16) {
v = SPI_CLOCK_DIV16;
} else if (divisor <= 32) {
v = SPI_CLOCK_DIV32;
} else if (divisor <= 64) {
v = SPI_CLOCK_DIV64;
} else {
v = SPI_CLOCK_DIV128;
}
SPI.setClockDivider(v);
#endif // SPI_CLOCK_DIV128
}
/** Receive a byte.
*
* \return The byte.
*/
uint8_t receive() {
return SPI.transfer(0XFF);
}
/** Receive multiple bytes.
*
* \param[out] buf Buffer to receive the data.
* \param[in] n Number of bytes to receive.
*
* \return Zero for no error or nonzero error code.
*/
uint8_t receive(uint8_t* buf, size_t n) {
for (size_t i = 0; i < n; i++) {
buf[i] = SPI.transfer(0XFF);
}
return 0;
}
/** Send a byte.
*
* \param[in] b Byte to send
*/
void send(uint8_t b) {
SPI.transfer(b);
}
/** Send multiple bytes.
*
* \param[in] buf Buffer for data to be sent.
* \param[in] n Number of bytes to send.
*/
void send(const uint8_t* buf , size_t n) {
for (size_t i = 0; i < n; i++) {
SPI.transfer(buf[i]);
}
}
/** \return true - uses SPI transactions */
bool useSpiTransactions() {
return true;
}
};
#endif // SD_SPI_CONFIGURATION >= 3 || SD_SPI_CONFIGURATION == 1
//------------------------------------------------------------------------------
/**
* \class SdSpiSoft
* \brief Software SPI class for access to SD and SDHC flash memory cards.
*/
template
class SdSpiSoft : public SdSpiBase {
public:
/**
* initialize SPI pins
*/
void begin() {
m_spi.begin();
}
/**
* Initialize hardware SPI - dummy for soft SPI
* \param[in] divisor SCK divisor - ignored.
*/
void init(uint8_t divisor) {}
/** Receive a byte.
*
* \return The byte.
*/
uint8_t receive() {
return m_spi.receive();
}
/** Receive multiple bytes.
*
* \param[out] buf Buffer to receive the data.
* \param[in] n Number of bytes to receive.
*
* \return Zero for no error or nonzero error code.
*/
uint8_t receive(uint8_t* buf, size_t n) {
for (size_t i = 0; i < n; i++) {
buf[i] = receive();
}
return 0;
}
/** Send a byte.
*
* \param[in] data Byte to send
*/
void send(uint8_t data) {
m_spi.send(data);
}
/** Send multiple bytes.
*
* \param[in] buf Buffer for data to be sent.
* \param[in] n Number of bytes to send.
*/
void send(const uint8_t* buf , size_t n) {
for (size_t i = 0; i < n; i++) {
send(buf[i]);
}
}
/** \return false - no SPI transactions */
bool useSpiTransactions() {
return false;
}
private:
SoftSPI m_spi;
};
//------------------------------------------------------------------------------
#if SD_SPI_CONFIGURATION == 0 || SD_SPI_CONFIGURATION >= 3
/** Default is custom fast SPI. */
typedef SdSpi SpiDefault_t;
#elif SD_SPI_CONFIGURATION == 1
/** Default is Arduino library SPI. */
typedef SdSpiLib SpiDefault_t;
#elif SD_SPI_CONFIGURATION == 2
/** Default is software SPI. */
typedef SdSpiSoft
SpiDefault_t;
#else // SD_SPI_CONFIGURATION == 0 || SD_SPI_CONFIGURATION >= 3
#error bad SD_SPI_CONFIGURATION
#endif // SD_SPI_CONFIGURATION == 0 || SD_SPI_CONFIGURATION >= 3
//------------------------------------------------------------------------------
// Use of in-line for AVR to save flash.
#ifdef __AVR__
//------------------------------------------------------------------------------
inline void SdSpi::begin() {
#ifdef __AVR_ATmega328P__
// Save a few bytes for 328 CPU - gcc optimizes single bit '|' to sbi.
PORTB |= 1 << 2; // SS high
DDRB |= 1 << 2; // SS output mode
DDRB |= 1 << 3; // MOSI output mode
DDRB |= 1 << 5; // SCK output mode
#else // __AVR_ATmega328P__
// set SS high - may be chip select for another SPI device
digitalWrite(SS, HIGH);
// SS must be in output mode even it is not chip select
pinMode(SS, OUTPUT);
pinMode(MOSI, OUTPUT);
pinMode(SCK, OUTPUT);
#endif // __AVR_ATmega328P__
}
//------------------------------------------------------------------------------
inline void SdSpi::init(uint8_t divisor) {
uint8_t b = 2;
uint8_t r = 0;
// See AVR processor documentation.
for (; divisor > b && r < 7; b <<= 1, r += r < 5 ? 1 : 2) {}
SPCR = (1 << SPE) | (1 << MSTR) | (r >> 1);
SPSR = r & 1 ? 0 : 1 << SPI2X;
}
//------------------------------------------------------------------------------
inline uint8_t SdSpi::receive() {
SPDR = 0XFF;
while (!(SPSR & (1 << SPIF))) {}
return SPDR;
}
//------------------------------------------------------------------------------
inline uint8_t SdSpi::receive(uint8_t* buf, size_t n) {
if (n-- == 0) {
return 0;
}
SPDR = 0XFF;
for (size_t i = 0; i < n; i++) {
while (!(SPSR & (1 << SPIF))) {}
uint8_t b = SPDR;
SPDR = 0XFF;
buf[i] = b;
}
while (!(SPSR & (1 << SPIF))) {}
buf[n] = SPDR;
return 0;
}
//------------------------------------------------------------------------------
inline void SdSpi::send(uint8_t data) {
SPDR = data;
while (!(SPSR & (1 << SPIF))) {}
}
//------------------------------------------------------------------------------
inline void SdSpi::send(const uint8_t* buf , size_t n) {
if (n == 0) {
return;
}
SPDR = buf[0];
if (n > 1) {
uint8_t b = buf[1];
size_t i = 2;
while (1) {
while (!(SPSR & (1 << SPIF))) {}
SPDR = b;
if (i == n) {
break;
}
b = buf[i++];
}
}
while (!(SPSR & (1 << SPIF))) {}
}
#endif // __AVR__
#endif // SdSpi_h