Compare commits

...

17 Commits

Author SHA1 Message Date
8bf3d1a874 Make use of C++ standard library 2022-05-29 16:14:42 +02:00
85346f258f Fix volatile compound assignments being deprecated in C++20 2022-05-29 14:45:57 +02:00
852ad5a318 Expose spi word size using both software and hardware backends 2022-05-28 17:03:40 +02:00
f359f46ffc Disable interrupts while transmitting data 2022-05-28 16:03:59 +02:00
999cd0e0c9 Implement software spi 2022-05-28 15:43:42 +02:00
bb78d2291d Make chip select pin configurable 2022-05-26 14:58:47 +02:00
70aabc07f6 Added constexpr and checking of supported chip 2020-02-21 17:35:23 +01:00
4b644b22bf Completely separted driver from interface 2020-02-21 17:29:45 +01:00
c73d83dff4 Removed deinit 2020-02-21 17:26:30 +01:00
3fae89285f Moved init to hardware driver class 2020-02-21 17:26:14 +01:00
e0fd37dbb0 Added config and hardware templates 2020-02-21 17:10:49 +01:00
8e6c3738f8 Moved function implementation inside class 2020-02-21 17:01:56 +01:00
d28cfc2929 Replaced legacy io library with new templated io library 2020-02-21 16:56:30 +01:00
aa78ced242 Added namespace and renamed class 2020-02-21 16:49:22 +01:00
9735d4e86a Changed include guard to pragma once 2020-02-21 16:48:31 +01:00
df6470c846 Moved all code to header 2020-02-21 16:47:47 +01:00
390c105da8 Added license file and updated clangformat and gitattributes 2020-02-21 16:46:06 +01:00
9 changed files with 390 additions and 239 deletions

13
.clang-format Normal file
View File

@ -0,0 +1,13 @@
---
BasedOnStyle: LLVM
ColumnLimit: 120
IndentWidth: 4
TabWidth: 4
UseTab: ForIndentation
AlignEscapedNewlines: DontAlign
AllowShortFunctionsOnASingleLine: Empty
AlwaysBreakTemplateDeclarations: true
BreakBeforeBraces: Custom
BraceWrapping:
AfterFunction: true
...

12
.gitignore vendored
View File

@ -1,5 +1,11 @@
.vs
*/Release
*/Debug
Release
Debug
*.componentinfo.xml
*/avrdude.bat
*.elf
*.o
*.hex
*.srec
*.eeprom
*.lss
*.map

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 BlackMark
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

63
config.hpp Normal file
View File

@ -0,0 +1,63 @@
#pragma once
#include <cstdint>
#include "../io/io.hpp"
namespace spi {
enum class ClockDiv {
DIV_4 = 0,
DIV_16 = 1,
DIV_64 = 2,
DIV_128 = 3,
DIV_2X_2 = 4,
DIV_2X_8 = 5,
DIV_2X_32 = 6,
DIV_2X_64 = 7,
};
enum class Mode {
MODE_0 = 0,
MODE_1 = 1,
MODE_2 = 2,
MODE_3 = 3,
};
enum class Side {
MASTER,
SLAVE,
};
enum class BitOrder {
LSB_FIRST,
MSB_FIRST,
};
template <ClockDiv freq = ClockDiv::DIV_128, Mode mode = Mode::MODE_0, Side side = Side::MASTER,
BitOrder bitOrder = BitOrder::MSB_FIRST, io::P ssPin = io::P::B2, bool pullup = false>
struct HardwareConfig {
static constexpr auto FREQ = freq;
static constexpr auto MODE = mode;
static constexpr auto SIDE = side;
static constexpr auto BIT_ORDER = bitOrder;
static constexpr auto SS_PIN = ssPin;
static constexpr auto PULLUP = pullup;
};
template <io::P sckPin, io::P misoPin, io::P mosiPin, io::P ssPin, std::uint32_t freq = 100'000,
Mode mode = Mode::MODE_0, BitOrder bitOrder = BitOrder::MSB_FIRST, std::uint8_t bits = 8, bool pullup = false>
struct SoftwareConfig {
static constexpr auto SCK_PIN = sckPin;
static constexpr auto MISO_PIN = misoPin;
static constexpr auto MOSI_PIN = mosiPin;
static constexpr auto SS_PIN = ssPin;
static constexpr auto FREQ = freq;
static constexpr auto MODE = mode;
static constexpr auto BIT_ORDER = bitOrder;
static constexpr auto BITS = bits;
static constexpr auto PULLUP = pullup;
};
} // namespace spi

139
hardware.hpp Normal file
View File

@ -0,0 +1,139 @@
#pragma once
#include <cstdint>
#include "../io/io.hpp"
namespace spi {
#if defined(__AVR_ATmega328P__)
template <class Cfg>
class Hardware {
public:
using word_t = std::uint8_t;
static void init()
{
if constexpr (Cfg::SIDE == Side::MASTER) {
sm_ss = true;
sm_sck.dir(io::Dir::OUT);
sm_miso.dir(io::Dir::IN);
sm_miso.pullup(Cfg::PULLUP);
sm_mosi.dir(io::Dir::OUT);
sm_ss.dir(io::Dir::OUT);
} else {
sm_sck.dir(io::Dir::IN);
sm_miso.dir(io::Dir::OUT);
sm_mosi.dir(io::Dir::IN);
sm_ss.dir(io::Dir::IN);
sm_ss.pullup(true);
}
setClockDiv();
setMode();
setMaster();
setBitOrder();
SPCR = SPCR | (1 << SPE);
}
static word_t transfer(word_t data)
{
SPDR = data;
while (!(SPSR & (1 << SPIF)))
;
return SPDR;
}
static void select(bool selectState)
{
sm_ss = !selectState;
}
private:
static io::Pin<io::P::B5> sm_sck;
static io::Pin<io::P::B4> sm_miso;
static io::Pin<io::P::B3> sm_mosi;
static io::Pin<Cfg::SS_PIN> sm_ss;
static void setClockDiv()
{
std::uint8_t ui8ClockDiv = static_cast<std::uint8_t>(Cfg::FREQ);
if (ui8ClockDiv & 1) {
SPCR = SPCR | (1 << SPR0);
} else {
SPCR = SPCR & ~(1 << SPR0);
}
if (ui8ClockDiv & (1 << 1)) {
SPCR = SPCR | (1 << SPR1);
} else {
SPCR = SPCR & ~(1 << SPR1);
}
if (ui8ClockDiv & (1 << 2)) {
SPSR = SPSR | (1 << SPI2X);
} else {
SPSR = SPSR & ~(1 << SPI2X);
}
}
static void setMode()
{
if (Cfg::MODE == Mode::MODE_0 || Cfg::MODE == Mode::MODE_1) {
setCPOL(false);
} else {
setCPOL(true);
}
if (Cfg::MODE == Mode::MODE_0 || Cfg::MODE == Mode::MODE_2) {
setCPHA(false);
} else {
setCPHA(true);
}
}
static void setMaster()
{
if constexpr (Cfg::SIDE == Side::MASTER) {
SPCR = SPCR | (1 << MSTR);
} else {
SPCR = SPCR & ~(1 << MSTR);
}
}
static void setBitOrder()
{
if constexpr (Cfg::BIT_ORDER == BitOrder::LSB_FIRST) {
SPCR = SPCR | (1 << DORD);
} else {
SPCR = SPCR & ~(1 << DORD);
}
}
static void setCPOL(bool bCPOL)
{
if (bCPOL) {
SPCR = SPCR | (1 << CPOL);
} else {
SPCR = SPCR & ~(1 << CPOL);
}
}
static void setCPHA(bool bCPHA)
{
if (bCPHA) {
SPCR = SPCR | (1 << CPHA);
} else {
SPCR = SPCR & ~(1 << CPHA);
}
}
};
#else
#error "This chip is not supported"
#endif
} // namespace spi

114
software.hpp Normal file
View File

@ -0,0 +1,114 @@
#pragma once
#include "../clock.hpp"
#include <type_traits>
#include <utility>
#include <cstdint>
#include <avr/interrupt.h>
#include "../io/io.hpp"
#include "../util/util.hpp"
namespace spi {
template <typename Cfg>
class Software {
static constexpr double calcClockDelay()
{
constexpr auto staticClockCycles = 10;
constexpr auto maxFrequency = F_CPU / staticClockCycles;
static_assert(Cfg::FREQ <= maxFrequency, "SPI frequency not achievable using selected clock speed");
constexpr auto staticDelay = (1.0 * 1000 * 1000 / maxFrequency);
const auto delayUs = ((1.0 * 1000 * 1000 / Cfg::FREQ) - staticDelay) / 2;
return (delayUs > 0 ? delayUs : 0);
}
public:
static_assert(Cfg::BITS >= 1 && Cfg::BITS <= 64, "Word size must be in range [1-64]");
// clang-format off
using word_t = std::conditional_t<Cfg::BITS <= 8, std::uint8_t,
std::conditional_t<Cfg::BITS <= 16, std::uint16_t,
std::conditional_t<Cfg::BITS <= 32, std::uint32_t,
std::uint64_t>>>;
// clang-format on
static void init()
{
sm_sck = ((Cfg::MODE == Mode::MODE_0 || Cfg::MODE == Mode::MODE_1) ? false : true);
sm_ss = true;
sm_sck.dir(io::Dir::OUT);
sm_miso.dir(io::Dir::IN);
sm_miso.pullup(Cfg::PULLUP);
sm_mosi.dir(io::Dir::OUT);
sm_ss.dir(io::Dir::OUT);
}
static word_t transfer(const word_t data)
{
const auto oldInterruptState = disableInterrupts();
auto dataIn = word_t{};
util::for_constexpr(
[&](const auto idx) {
constexpr auto i = idx.value;
constexpr auto bitPos = (Cfg::BIT_ORDER == BitOrder::MSB_FIRST ? Cfg::BITS - i - 1 : i);
if constexpr (Cfg::MODE == Mode::MODE_0 || Cfg::MODE == Mode::MODE_2) {
sm_mosi = data >> bitPos & 1;
_delay_us(DELAY_US);
sm_sck.toggle();
const auto receivedBit = sm_miso.read();
dataIn |= word_t{receivedBit} << bitPos;
_delay_us(DELAY_US);
sm_sck.toggle();
} else {
sm_sck.toggle();
sm_mosi = data >> bitPos & 1;
_delay_us(DELAY_US);
sm_sck.toggle();
const auto receivedBit = sm_miso.read();
dataIn |= word_t{receivedBit} << bitPos;
_delay_us(DELAY_US);
}
},
std::make_index_sequence<Cfg::BITS>{});
enableInterrupts(oldInterruptState);
return dataIn;
}
static void select(const bool selectState)
{
sm_ss = !selectState;
}
private:
static io::Pin<Cfg::SCK_PIN> sm_sck;
static io::Pin<Cfg::MISO_PIN> sm_miso;
static io::Pin<Cfg::MOSI_PIN> sm_mosi;
static io::Pin<Cfg::SS_PIN> sm_ss;
static constexpr auto DELAY_US = calcClockDelay();
static inline std::uint8_t disableInterrupts()
{
const auto oldInterruptState = SREG;
cli();
return oldInterruptState;
}
static inline void enableInterrupts(const std::uint8_t oldInterruptState)
{
SREG = oldInterruptState;
}
};
} // namespace spi

172
spi.cpp
View File

@ -1,172 +0,0 @@
#include "spi.h"
//////////////////////////////////////////////////////////////////////////
InOutPin SPI::sm_cSCK;
InOutPin SPI::sm_cMISO;
InOutPin SPI::sm_cMOSI;
InOutPin SPI::sm_cSS;
//////////////////////////////////////////////////////////////////////////
void SPI::setCPOL( bool bCPOL )
{
if( bCPOL )
{
SPCR |= ( 1 << CPOL );
}
else
{
SPCR &= ~( 1 << CPOL );
}
}
//////////////////////////////////////////////////////////////////////////
void SPI::setCPHA( bool bCPHA )
{
if( bCPHA )
{
SPCR |= ( 1 << CPHA );
}
else
{
SPCR &= ~( 1 << CPHA );
}
}
//////////////////////////////////////////////////////////////////////////
void SPI::init( ClockDiv enmClockDiv /* = ClockDiv::CLKDIV_128 */, Mode enmMode /* = Mode::MODE_0 */, bool bMaster /* = true */, bool bLSBFirst /* = false */, bool bMISOPullup /* = false */ )
{
sm_cSCK.setPin( sm_enmSCK );
sm_cMISO.setPin( sm_enmMISO );
sm_cMOSI.setPin( sm_enmMOSI );
sm_cSS.setPin( sm_enmSS );
if( bMaster )
{
sm_cSS.write( true );
sm_cSCK.setDirection( InOut::Dir::D_OUT, false );
sm_cMISO.setDirection( InOut::Dir::D_IN, bMISOPullup );
sm_cMOSI.setDirection( InOut::Dir::D_OUT, false );
sm_cSS.setDirection( InOut::Dir::D_OUT, false );
}
else
{
sm_cSCK.setDirection( InOut::Dir::D_IN, false );
sm_cMISO.setDirection( InOut::Dir::D_OUT, false );
sm_cMOSI.setDirection( InOut::Dir::D_IN, false );
sm_cSS.setDirection( InOut::Dir::D_IN, true );
}
setClockDiv( enmClockDiv );
setMode( enmMode );
setMaster( bMaster );
setBitOrder( bLSBFirst );
SPCR |= ( 1 << SPE );
}
//////////////////////////////////////////////////////////////////////////
void SPI::deinit()
{
SPCR = 0;
sm_cSCK.setDirection( InOut::Dir::D_IN, false );
sm_cMISO.setDirection( InOut::Dir::D_IN, false );
sm_cMOSI.setDirection( InOut::Dir::D_IN, false );
sm_cSS.setDirection( InOut::Dir::D_IN, false );
}
//////////////////////////////////////////////////////////////////////////
void SPI::setClockDiv( ClockDiv enmClockDiv )
{
uint8_t ui8ClockDiv = static_cast<uint8_t>( enmClockDiv );
if( ui8ClockDiv & 1 )
{
SPCR |= ( 1 << SPR0 );
}
else
{
SPCR &= ~( 1 << SPR0 );
}
if( ui8ClockDiv & ( 1 << 1 ) )
{
SPCR |= ( 1 << SPR1 );
}
else
{
SPCR &= ~( 1 << SPR1 );
}
if( ui8ClockDiv & ( 1 << 2 ) )
{
SPSR |= ( 1 << SPI2X );
}
else
{
SPSR &= ~( 1 << SPI2X );
}
}
//////////////////////////////////////////////////////////////////////////
void SPI::setMode( Mode enmMode )
{
if( enmMode == Mode::MODE_0 || enmMode == Mode::MODE_1 )
{
setCPOL( false );
}
else
{
setCPOL( true );
}
if( enmMode == Mode::MODE_0 || enmMode == Mode::MODE_2 )
{
setCPHA( false );
}
else
{
setCPHA( true );
}
}
//////////////////////////////////////////////////////////////////////////
void SPI::setMaster( bool bMaster )
{
if( bMaster )
{
SPCR |= ( 1 << MSTR );
}
else
{
SPCR &= ~( 1 << MSTR );
}
}
//////////////////////////////////////////////////////////////////////////
void SPI::setBitOrder( bool bLSBFirst )
{
if( bLSBFirst )
{
SPCR |= ( 1 << DORD );
}
else
{
SPCR &= ~( 1 << DORD );
}
}
//////////////////////////////////////////////////////////////////////////
uint8_t SPI::transfer( uint8_t ui8Data )
{
SPDR = ui8Data;
while( !( SPSR & ( 1 << SPIF ) ) );
return SPDR;
}
//////////////////////////////////////////////////////////////////////////
void SPI::select( bool bSelect )
{
sm_cSS.write( !bSelect );
}

64
spi.h
View File

@ -1,64 +0,0 @@
/*
* Copyright (c) by BlackMark 2016-2017
* Date 17/09/2017
* Version 1.4
*/
#ifndef SPI_H
#define SPI_H
#include <avr/io.h>
#include "../inout/inout.h"
class SPI
{
public:
enum class ClockDiv
{
CLKDIV_4 = 0,
CLKDIV_16 = 1,
CLKDIV_64 = 2,
CLKDIV_128 = 3,
CLKDIV_2X_2 = 4,
CLKDIV_2X_8 = 5,
CLKDIV_2X_32 = 6,
CLKDIV_2X_64 = 7
};
enum class Mode
{
MODE_0 = 0,
MODE_1 = 1,
MODE_2 = 2,
MODE_3 = 3
};
private:
static void setCPOL( bool bCPOL );
static void setCPHA( bool bCPHA );
static constexpr InOut::Pin sm_enmSCK = InOut::Pin::P_B5;
static constexpr InOut::Pin sm_enmMISO = InOut::Pin::P_B4;
static constexpr InOut::Pin sm_enmMOSI = InOut::Pin::P_B3;
static constexpr InOut::Pin sm_enmSS = InOut::Pin::P_B2;
static InOutPin sm_cSCK;
static InOutPin sm_cMISO;
static InOutPin sm_cMOSI;
static InOutPin sm_cSS;
public:
static void init( ClockDiv enmClockDiv = ClockDiv::CLKDIV_128, Mode enmMode = Mode::MODE_0, bool bMaster = true, bool bLSBFirst = false, bool bMISOPullup = false );
static void deinit();
static void setClockDiv( ClockDiv enmClockDiv );
static void setMode( Mode enmMode );
static void setMaster( bool bMaster );
static void setBitOrder( bool bLSBFirst );
static uint8_t transfer( uint8_t ui8Data );
static void select( bool bSelect );
};
#endif

31
spi.hpp Normal file
View File

@ -0,0 +1,31 @@
#pragma once
#include "config.hpp"
#include "hardware.hpp"
#include "software.hpp"
#include "../io/io.hpp"
namespace spi {
template <class Driver>
struct Spi {
using word_t = typename Driver::word_t;
static inline void init()
{
Driver::init();
}
static inline word_t transfer(const word_t data)
{
return Driver::transfer(data);
}
static inline void select(const bool selectState)
{
Driver::select(selectState);
}
};
} // namespace spi