-
-
Notifications
You must be signed in to change notification settings - Fork 62
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
397 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,236 @@ | ||
/* Copyright (c) Kuba Szczodrzyński 2023-09-23. */ | ||
|
||
#if LT_ARD_HAS_SPI || DOXYGEN | ||
|
||
#include "SPI.h" | ||
|
||
#include <ArduinoPrivate.h> | ||
|
||
SPIClass::~SPIClass() {} | ||
|
||
int SPIClass::getPortByPins(pin_size_t sck, pin_size_t miso, pin_size_t mosi) { | ||
if (sck == PIN_INVALID || miso == PIN_INVALID || mosi == PIN_INVALID) | ||
return -1; | ||
#if defined(PINS_SPI0_SCK) && defined(PINS_SPI0_MISO) && defined(PINS_SPI0_MOSI) | ||
if (ltArrayContains(PINS_SPI0_SCK, sck) && ltArrayContains(PINS_SPI0_MISO, miso) && | ||
ltArrayContains(PINS_SPI0_MOSI, mosi)) | ||
return 0; | ||
#endif | ||
#if defined(PINS_SPI1_SCK) && defined(PINS_SPI1_MISO) && defined(PINS_SPI1_MOSI) | ||
if (ltArrayContains(PINS_SPI1_SCK, sck) && ltArrayContains(PINS_SPI1_MISO, miso) && | ||
ltArrayContains(PINS_SPI1_MOSI, mosi)) | ||
return 1; | ||
#endif | ||
#if defined(PINS_SPI2_SCK) && defined(PINS_SPI2_MISO) && defined(PINS_SPI2_MOSI) | ||
if (ltArrayContains(PINS_SPI2_SCK, sck) && ltArrayContains(PINS_SPI2_MISO, miso) && | ||
ltArrayContains(PINS_SPI2_MOSI, mosi)) | ||
return 2; | ||
#endif | ||
return -1; | ||
} | ||
|
||
bool SPIClass::setPinsPrivate(pin_size_t sck, pin_size_t miso, pin_size_t mosi, pin_size_t cs) { | ||
// default to already set pins | ||
if (sck == PIN_INVALID) | ||
sck = this->sck; | ||
if (miso == PIN_INVALID) | ||
miso = this->miso; | ||
if (mosi == PIN_INVALID) | ||
mosi = this->mosi; | ||
|
||
if (cs != PIN_INVALID) | ||
this->cs = cs; | ||
|
||
if (sck == PIN_INVALID && miso == PIN_INVALID && mosi == PIN_INVALID) { | ||
// set pins by port number | ||
#if defined(PINS_SPI0_SCK) && defined(PINS_SPI0_MISO) && defined(PINS_SPI0_MOSI) | ||
if (this->port == 0) { | ||
this->sck = PINS_SPI0_SCK[0]; | ||
this->miso = PINS_SPI0_MISO[0]; | ||
this->mosi = PINS_SPI0_MOSI[0]; | ||
return true; | ||
} | ||
#endif | ||
#if defined(PINS_SPI1_SCK) && defined(PINS_SPI1_MISO) && defined(PINS_SPI1_MOSI) | ||
if (this->port == 1) { | ||
this->sck = PINS_SPI1_SCK[0]; | ||
this->miso = PINS_SPI1_MISO[0]; | ||
this->mosi = PINS_SPI1_MOSI[0]; | ||
return true; | ||
} | ||
#endif | ||
#if defined(PINS_SPI2_SCK) && defined(PINS_SPI2_MISO) && defined(PINS_SPI2_MOSI) | ||
if (this->port == 2) { | ||
this->sck = PINS_SPI2_SCK[0]; | ||
this->miso = PINS_SPI2_MISO[0]; | ||
this->mosi = PINS_SPI2_MOSI[0]; | ||
return true; | ||
} | ||
#endif | ||
} | ||
|
||
// set port number by specified pins | ||
int port = this->getPortByPins(sck, miso, mosi); | ||
if (port == -1) | ||
// no such port | ||
return false; | ||
if (this->fixedPort && port != this->port) | ||
// allow only same port for fixed port instances | ||
return false; | ||
this->port = port; | ||
this->sck = sck; | ||
this->miso = miso; | ||
this->mosi = mosi; | ||
return true; | ||
} | ||
|
||
bool SPIClass::setPins(pin_size_t sck, pin_size_t miso, pin_size_t mosi, pin_size_t cs) { | ||
if (!this->data) | ||
return this->setPinsPrivate(sck, miso, mosi, cs); | ||
return this->begin(sck, miso, mosi, cs); | ||
} | ||
|
||
bool SPIClass::begin(pin_size_t sck, pin_size_t miso, pin_size_t mosi, pin_size_t cs) { | ||
if (!this->setPinsPrivate(sck, miso, mosi, cs)) | ||
return false; | ||
return this->begin(); | ||
} | ||
|
||
__attribute__((weak)) void SPIClass::beginTransaction(SPISettings settings) { | ||
auto oldSettings = this->settings; | ||
|
||
if (settings._clock != oldSettings._clock) | ||
this->setFrequency(settings._clock); | ||
if (settings._bitOrder != oldSettings._bitOrder) | ||
this->setBitOrder(settings._bitOrder); | ||
if (settings._dataMode != oldSettings._dataMode) | ||
this->setDataMode(settings._dataMode); | ||
|
||
// this->inTransaction = true; | ||
if (useCs && this->cs != PIN_INVALID) { | ||
digitalWrite(useCs, LOW); | ||
} | ||
} | ||
|
||
__attribute__((weak)) void SPIClass::endTransaction() { | ||
// this->inTransaction = false; | ||
if (useCs && this->cs != PIN_INVALID) { | ||
digitalWrite(useCs, HIGH); | ||
} | ||
} | ||
|
||
__attribute__((weak)) void SPIClass::setHwCs(bool useCs) { | ||
this->useCs = useCs; | ||
if (this->cs != PIN_INVALID) { | ||
if (useCs) | ||
pinMode(this->cs, OUTPUT); | ||
else | ||
pinModeRemove(this->cs, PIN_GPIO); | ||
} | ||
} | ||
|
||
__attribute__((weak)) void SPIClass::setClockDivider(uint32_t clockDiv) { | ||
this->setFrequency(LT.getCpuFreq() / clockDiv); | ||
} | ||
|
||
__attribute__((weak)) uint32_t SPIClass::getClockDivider() { | ||
return LT.getCpuFreq() / this->settings._clock; | ||
} | ||
|
||
__attribute__((weak)) void SPIClass::setBitOrder(uint8_t bitOrder) { | ||
this->settings._bitOrder = bitOrder; | ||
} | ||
|
||
__attribute__((weak)) uint16_t SPIClass::transfer16(uint16_t data) { | ||
ByteUint16 data16; | ||
data16.val = data; | ||
if (this->settings._bitOrder == LSBFIRST) { | ||
data16.lsb = this->transfer(data16.lsb); | ||
data16.msb = this->transfer(data16.msb); | ||
} else { | ||
data16.msb = this->transfer(data16.msb); | ||
data16.lsb = this->transfer(data16.lsb); | ||
} | ||
return data16.val; | ||
} | ||
|
||
__attribute__((weak)) uint32_t SPIClass::transfer32(uint32_t data) { | ||
ByteUint32 data32; | ||
data32.val = data; | ||
if (this->settings._bitOrder == LSBFIRST) { | ||
data32.lsb = this->transfer(data32.lsb); | ||
data32.byte1 = this->transfer(data32.byte1); | ||
data32.byte2 = this->transfer(data32.byte2); | ||
data32.msb = this->transfer(data32.msb); | ||
} else { | ||
data32.msb = this->transfer(data32.msb); | ||
data32.byte2 = this->transfer(data32.byte2); | ||
data32.byte1 = this->transfer(data32.byte1); | ||
data32.lsb = this->transfer(data32.lsb); | ||
} | ||
return data32.val; | ||
} | ||
|
||
__attribute__((weak)) void SPIClass::write16(uint16_t data) { | ||
ByteUint16 data16; | ||
data16.val = data; | ||
if (this->settings._bitOrder == LSBFIRST) { | ||
this->write(data16.lsb); | ||
this->write(data16.msb); | ||
} else { | ||
this->write(data16.msb); | ||
this->write(data16.lsb); | ||
} | ||
} | ||
|
||
__attribute__((weak)) void SPIClass::write32(uint32_t data) { | ||
ByteUint32 data32; | ||
data32.val = data; | ||
if (this->settings._bitOrder == LSBFIRST) { | ||
this->write(data32.lsb); | ||
this->write(data32.byte1); | ||
this->write(data32.byte2); | ||
this->write(data32.msb); | ||
} else { | ||
this->write(data32.msb); | ||
this->write(data32.byte2); | ||
this->write(data32.byte1); | ||
this->write(data32.lsb); | ||
} | ||
} | ||
|
||
__attribute__((weak)) void SPIClass::transferBytes(const uint8_t *data, uint8_t *out, uint32_t len) { | ||
while (len--) { | ||
*(out++) = this->transfer(*(data++)); | ||
} | ||
} | ||
|
||
__attribute__((weak)) void SPIClass::writeBytes(const uint8_t *data, uint32_t len) { | ||
while (len--) { | ||
this->write(*(data++)); | ||
} | ||
} | ||
|
||
// __attribute__((weak)) void SPIClass::transferBits(uint32_t data, uint32_t *out, uint8_t bits) {} | ||
// __attribute__((weak)) void SPIClass::writePixels(const void *data, uint32_t len) {} | ||
// __attribute__((weak)) void SPIClass::writePattern(const uint8_t *data, uint8_t len, uint32_t repeat) {} | ||
|
||
#if LT_HW_SPI0 | ||
SPIClass SPI(0); | ||
#elif LT_HW_SPI1 | ||
SPIClass SPI(1); | ||
#elif LT_HW_SPI2 | ||
SPIClass SPI(2); | ||
#endif | ||
|
||
#if LT_HW_SPI0 | ||
SPIClass SPI0(PORT_FIXED_BIT | 0); | ||
#endif | ||
#if LT_HW_SPI1 | ||
SPIClass SPI1(PORT_FIXED_BIT | 1); | ||
#endif | ||
#if LT_HW_SPI2 | ||
SPIClass SPI2(PORT_FIXED_BIT | 2); | ||
#endif | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
/* Copyright (c) Kuba Szczodrzyński 2023-09-23. */ | ||
|
||
#pragma once | ||
|
||
#if !LT_ARD_HAS_SPI | ||
#error "SPI library not implemented for this chip family" | ||
#endif | ||
|
||
#include <Arduino.h> | ||
|
||
typedef struct SPIData SPIData; | ||
|
||
#define SPI_MODE0 0 //!< CPOL=0, CPHA=0 | ||
#define SPI_MODE1 1 //!< CPOL=0, CPHA=1 | ||
#define SPI_MODE2 2 //!< CPOL=1, CPHA=0 | ||
#define SPI_MODE3 3 //!< CPOL=1, CPHA=1 | ||
|
||
#define SPI_LSBFIRST 0 | ||
#define SPI_MSBFIRST 1 | ||
|
||
#define SPI_HAS_TRANSACTION | ||
|
||
class SPISettings { | ||
public: | ||
SPISettings() {} | ||
|
||
SPISettings(uint32_t clock, uint8_t bitOrder, uint8_t dataMode) | ||
: _clock(clock), _bitOrder(bitOrder), _dataMode(dataMode) {} | ||
|
||
uint32_t _clock{1000000}; | ||
uint8_t _bitOrder{SPI_MSBFIRST}; | ||
uint8_t _dataMode{SPI_MODE0}; | ||
}; | ||
|
||
class SPIClass { | ||
private: | ||
uint8_t port{0}; //!< port number, family-specific | ||
bool fixedPort{false}; //!< whether port is not changeable | ||
pin_size_t sck{PIN_INVALID}; //!< SCK pin number of this instance | ||
pin_size_t miso{PIN_INVALID}; //!< MISO pin number of this instance | ||
pin_size_t mosi{PIN_INVALID}; //!< MOSI pin number of this instance | ||
pin_size_t cs{PIN_INVALID}; //!< CS pin number of this instance | ||
|
||
private: | ||
SPIData *data{nullptr}; //!< family-specific, created in begin(), destroyed in end() | ||
SPISettings settings{}; //!< current SPI settings | ||
bool inTransaction{false}; //!< whether transaction is in progress | ||
bool useCs{false}; //!< whether to toggle CS automatically | ||
|
||
int getPortByPins(pin_size_t sck, pin_size_t miso, pin_size_t mosi); | ||
|
||
public: | ||
SPIClass(uint32_t port) : port(port) { | ||
this->fixedPort = bool(port & PORT_FIXED_BIT); | ||
} | ||
|
||
SPIClass(pin_size_t sck, pin_size_t miso, pin_size_t mosi) : sck(sck), miso(miso), mosi(mosi) {} | ||
|
||
~SPIClass(); | ||
|
||
bool begin(); | ||
bool end(); | ||
void setFrequency(uint32_t frequency); | ||
void setDataMode(uint8_t dataMode); | ||
|
||
uint8_t transfer(uint8_t data); | ||
void write(uint8_t data); | ||
|
||
private: | ||
bool setPinsPrivate(pin_size_t sck, pin_size_t miso, pin_size_t mosi, pin_size_t cs = PIN_INVALID); | ||
|
||
public: | ||
bool setPins(pin_size_t sck, pin_size_t miso, pin_size_t mosi, pin_size_t cs = PIN_INVALID); | ||
bool begin(pin_size_t sck, pin_size_t miso, pin_size_t mosi, pin_size_t cs = PIN_INVALID); | ||
|
||
void beginTransaction(SPISettings settings); | ||
void endTransaction(); | ||
void setHwCs(bool useCs); | ||
|
||
void setClockDivider(uint32_t clockDiv); | ||
uint32_t getClockDivider(); | ||
void setBitOrder(uint8_t bitOrder); | ||
|
||
inline void transfer(void *data, uint32_t len) { | ||
this->transferBytes((const uint8_t *)data, (uint8_t *)data, len); | ||
} | ||
|
||
uint16_t transfer16(uint16_t data); | ||
uint32_t transfer32(uint32_t data); | ||
void write16(uint16_t data); | ||
void write32(uint32_t data); | ||
|
||
void transferBytes(const uint8_t *data, uint8_t *out, uint32_t len); | ||
void writeBytes(const uint8_t *data, uint32_t len); | ||
|
||
void transferBits(uint32_t data, uint32_t *out, uint8_t bits); | ||
void writePixels(const void *data, uint32_t len); | ||
void writePattern(const uint8_t *data, uint8_t len, uint32_t repeat); | ||
|
||
int8_t pinSS() { | ||
return (int8_t)this->cs; | ||
} | ||
}; | ||
|
||
#if LT_HW_SPI0 || LT_HW_SPI1 || LT_HW_SPI2 | ||
extern SPIClass SPI; | ||
#endif | ||
|
||
#if LT_HW_SPI0 | ||
extern SPIClass SPI0; | ||
#endif | ||
#if LT_HW_SPI1 | ||
extern SPIClass SPI1; | ||
#endif | ||
#if LT_HW_SPI2 | ||
extern SPIClass SPI2; | ||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
/* Copyright (c) Kuba Szczodrzyński 2023-09-23. */ | ||
|
||
#pragma once | ||
|
||
#include LT_VARIANT_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.