|
|
|
|
|
|
|
|
/* |
|
|
/* |
|
|
* Copyright (c) 2010 by Cristian Maglie <c.maglie@bug.st> |
|
|
* Copyright (c) 2010 by Cristian Maglie <c.maglie@bug.st> |
|
|
* Copyright (c) 2014 by Paul Stoffregen <paul@pjrc.com> (Transaction API) |
|
|
* Copyright (c) 2014 by Paul Stoffregen <paul@pjrc.com> (Transaction API) |
|
|
|
|
|
* Copyright (c) 2014 by Matthijs Kooijman <matthijs@stdin.nl> (SPISettings) |
|
|
* SPI Master library for arduino. |
|
|
* SPI Master library for arduino. |
|
|
* |
|
|
* |
|
|
* This file is free software; you can redistribute it and/or modify |
|
|
* This file is free software; you can redistribute it and/or modify |
|
|
|
|
|
|
|
|
#define SPI_CLOCK_MASK 0x03 // SPR1 = bit 1, SPR0 = bit 0 on SPCR |
|
|
#define SPI_CLOCK_MASK 0x03 // SPR1 = bit 1, SPR0 = bit 0 on SPCR |
|
|
#define SPI_2XCLOCK_MASK 0x01 // SPI2X = bit 0 on SPSR |
|
|
#define SPI_2XCLOCK_MASK 0x01 // SPI2X = bit 0 on SPSR |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SPISettings { |
|
|
|
|
|
public: |
|
|
|
|
|
SPISettings(uint32_t clock, uint8_t bitOrder, uint8_t dataMode) { |
|
|
|
|
|
if (__builtin_constant_p(clock)) { |
|
|
|
|
|
init_AlwaysInline(clock, bitOrder, dataMode); |
|
|
|
|
|
} else { |
|
|
|
|
|
init_MightInline(clock, bitOrder, dataMode); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
private: |
|
|
|
|
|
void init_MightInline(uint32_t clock, uint8_t bitOrder, uint8_t dataMode) { |
|
|
|
|
|
init_AlwaysInline(clock, bitOrder, dataMode); |
|
|
|
|
|
} |
|
|
|
|
|
void init_AlwaysInline(uint32_t clock, uint8_t bitOrder, uint8_t dataMode) |
|
|
|
|
|
__attribute__((__always_inline__)) { |
|
|
|
|
|
// Clock settings are defined as follows. Note that this shows SPI2X |
|
|
|
|
|
// inverted, so the bits form increasing numbers. Also note that |
|
|
|
|
|
// fosc/64 appears twice |
|
|
|
|
|
// SPR1 SPR0 ~SPI2X Freq |
|
|
|
|
|
// 0 0 0 fosc/2 |
|
|
|
|
|
// 0 0 1 fosc/4 |
|
|
|
|
|
// 0 1 0 fosc/8 |
|
|
|
|
|
// 0 1 1 fosc/16 |
|
|
|
|
|
// 1 0 0 fosc/32 |
|
|
|
|
|
// 1 0 1 fosc/64 |
|
|
|
|
|
// 1 1 0 fosc/64 |
|
|
|
|
|
// 1 1 1 fosc/128 |
|
|
|
|
|
|
|
|
|
|
|
// We find the fastest clock that is less than or equal to the |
|
|
|
|
|
// given clock rate. The clock divider that results in clock_setting |
|
|
|
|
|
// is 2 ^^ (clock_div + 1). If nothing is slow enough, we'll use the |
|
|
|
|
|
// slowest (128 == 2 ^^ 7, so clock_div = 6). |
|
|
|
|
|
uint8_t clockDiv; |
|
|
|
|
|
|
|
|
|
|
|
// When the clock is known at compiletime, use this if-then-else |
|
|
|
|
|
// cascade, which the compiler knows how to completely optimize |
|
|
|
|
|
// away. When clock is not know, use a loop instead, which generates |
|
|
|
|
|
// shorter code. |
|
|
|
|
|
if (__builtin_constant_p(clock)) { |
|
|
|
|
|
if (clock >= F_CPU / 2) { |
|
|
|
|
|
clockDiv = 0; |
|
|
|
|
|
} else if (clock >= F_CPU / 4) { |
|
|
|
|
|
clockDiv = 1; |
|
|
|
|
|
} else if (clock >= F_CPU / 8) { |
|
|
|
|
|
clockDiv = 2; |
|
|
|
|
|
} else if (clock >= F_CPU / 16) { |
|
|
|
|
|
clockDiv = 3; |
|
|
|
|
|
} else if (clock >= F_CPU / 32) { |
|
|
|
|
|
clockDiv = 4; |
|
|
|
|
|
} else if (clock >= F_CPU / 64) { |
|
|
|
|
|
clockDiv = 5; |
|
|
|
|
|
} else { |
|
|
|
|
|
clockDiv = 6; |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
uint32_t clockSetting = F_CPU / 2; |
|
|
|
|
|
clockDiv = 0; |
|
|
|
|
|
while (clockDiv < 6 && clock < clockSetting) { |
|
|
|
|
|
clockSetting /= 2; |
|
|
|
|
|
clockDiv++; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Compensate for the duplicate fosc/64 |
|
|
|
|
|
if (clockDiv == 6) |
|
|
|
|
|
clockDiv = 7; |
|
|
|
|
|
|
|
|
|
|
|
// Invert the SPI2X bit |
|
|
|
|
|
clockDiv ^= 0x1; |
|
|
|
|
|
|
|
|
|
|
|
// Pack into the SPISettings class |
|
|
|
|
|
spcr = _BV(SPE) | _BV(MSTR) | ((bitOrder == LSBFIRST) ? _BV(DORD) : 0) | |
|
|
|
|
|
(dataMode & SPI_MODE_MASK) | ((clockDiv >> 1) & SPI_CLOCK_MASK); |
|
|
|
|
|
spsr = clockDiv & SPI_2XCLOCK_MASK; |
|
|
|
|
|
} |
|
|
|
|
|
uint8_t spcr; |
|
|
|
|
|
uint8_t spsr; |
|
|
|
|
|
friend class SPIClass; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SPIClass { |
|
|
class SPIClass { |
|
|
public: |
|
|
public: |
|
|
// Initialize the SPI library |
|
|
// Initialize the SPI library |
|
|
|
|
|
|
|
|
// Before using SPI.transfer() or asserting chip select pins, |
|
|
// Before using SPI.transfer() or asserting chip select pins, |
|
|
// this function is used to gain exclusive access to the SPI bus |
|
|
// this function is used to gain exclusive access to the SPI bus |
|
|
// and configure the correct settings. |
|
|
// and configure the correct settings. |
|
|
inline static void beginTransaction(uint8_t clockDiv, uint8_t bitOrder, uint8_t dataMode) { |
|
|
|
|
|
|
|
|
inline static void beginTransaction(SPISettings settings) { |
|
|
if (interruptMode > 0) { |
|
|
if (interruptMode > 0) { |
|
|
#ifdef SPI_AVR_EIMSK |
|
|
#ifdef SPI_AVR_EIMSK |
|
|
if (interruptMode == 1) { |
|
|
if (interruptMode == 1) { |
|
|
|
|
|
|
|
|
cli(); |
|
|
cli(); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
// these long expressions each compile to only 2 instructions |
|
|
|
|
|
SPCR = _BV(SPE) | _BV(MSTR) | ((bitOrder == LSBFIRST) ? _BV(DORD) : 0) | |
|
|
|
|
|
(dataMode & SPI_MODE_MASK) | (clockDiv & SPI_CLOCK_MASK); |
|
|
|
|
|
SPSR = (clockDiv >> 2) & SPI_2XCLOCK_MASK; |
|
|
|
|
|
|
|
|
SPCR = settings.spcr; |
|
|
|
|
|
SPSR = settings.spsr; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Write to the SPI bus (MOSI pin) and also receive (MISO pin) |
|
|
// Write to the SPI bus (MOSI pin) and also receive (MISO pin) |