Skip to content

Commit

Permalink
[wiring] Implement SPI API library
Browse files Browse the repository at this point in the history
  • Loading branch information
kuba2k2 committed Sep 23, 2023
1 parent 4f4d3a9 commit 8576eba
Show file tree
Hide file tree
Showing 6 changed files with 397 additions and 0 deletions.
236 changes: 236 additions & 0 deletions cores/common/arduino/libraries/api/SPI/SPI.cpp
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
117 changes: 117 additions & 0 deletions cores/common/arduino/libraries/api/SPI/SPI.h
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
5 changes: 5 additions & 0 deletions cores/common/arduino/src/compat/pins_arduino.h
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
34 changes: 34 additions & 0 deletions cores/common/arduino/src/wiring/wiring_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,37 @@ static bool ltArrayContains(C &&c, T e) {
};

#endif // __cplusplus

// Additional custom structures

typedef union {
uint16_t val;

struct {
#if BYTE_ORDER == LITTLE_ENDIAN
uint8_t lsb;
uint8_t msb;
#else
uint8_t msb;
uint8_t lsb;
#endif
};
} ByteUint16;

typedef union {
uint32_t val;

struct {
#if BYTE_ORDER == LITTLE_ENDIAN
uint8_t lsb;
uint8_t byte1;
uint8_t byte2;
uint8_t msb;
#else
uint8_t msb;
uint8_t byte2;
uint8_t byte1;
uint8_t lsb;
#endif
};
} ByteUint32;
Loading

0 comments on commit 8576eba

Please sign in to comment.