Compare commits

...

16 Commits

9 changed files with 384 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.

61
config.hpp Normal file
View File

@@ -0,0 +1,61 @@
#pragma once
#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, uint32_t freq = 100'000, Mode mode = Mode::MODE_0,
BitOrder bitOrder = BitOrder::MSB_FIRST, 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

137
hardware.hpp Normal file
View File

@@ -0,0 +1,137 @@
#pragma once
#include "../io/io.hpp"
namespace spi {
#if defined(__AVR_ATmega328P__)
template <class Cfg>
class Hardware {
public:
using word_t = 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()
{
uint8_t ui8ClockDiv = static_cast<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

112
software.hpp Normal file
View File

@@ -0,0 +1,112 @@
#pragma once
#include "../clock.hpp"
#include <stdint.h>
#include <avr/interrupt.h>
#include "../io/io.hpp"
#include "../util/type.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 = util::conditional_t<Cfg::BITS <= 8, uint8_t,
util::conditional_t<Cfg::BITS <= 16, uint16_t,
util::conditional_t<Cfg::BITS <= 32, uint32_t,
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);
}
},
util::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 uint8_t disableInterrupts()
{
const auto oldInterruptState = SREG;
cli();
return oldInterruptState;
}
static inline void enableInterrupts(const 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