// Copyright 2009 Olivier Gillet.
//
// Author: Olivier Gillet (ol.gillet@gmail.com)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
//
// -----------------------------------------------------------------------------
//
// Fast SPI communication (using the hardware implementation). This will take
// ownership of the pins 11 (data output), 12 (data input) and 13 (clock), +
// a user-definable pin for slave selection. Pin 10 should be kept as an output
// pin, since the SPI master/slave mode is based upon the value of this pin.
#ifndef AVRLIB_SPI_H_
#define AVRLIB_SPI_H_
#include "avrlib/avrlib.h"
#include "avrlib/gpio.h"
#include "avrlib/serial.h"
namespace avrlib {
IORegister(SPSR);
typedef BitInRegister DoubleSpeed;
typedef BitInRegister TransferComplete;
template
class SpiMaster {
public:
enum {
buffer_size = 0,
data_size = 8
};
static void Init() {
SpiSCK::set_mode(DIGITAL_OUTPUT);
SpiMOSI::set_mode(DIGITAL_OUTPUT);
SpiMISO::set_mode(DIGITAL_INPUT);
SpiSS::set_mode(DIGITAL_OUTPUT); // I'm a master!
SpiSS::High();
SlaveSelect::set_mode(DIGITAL_OUTPUT);
SlaveSelect::High();
// SPI enabled, configured as master.
uint8_t configuration = _BV(SPE) | _BV(MSTR);
if (order == LSB_FIRST) {
configuration |= _BV(DORD);
}
DoubleSpeed::clear();
switch (speed) {
case 2:
DoubleSpeed::set();
case 4:
break;
case 8:
DoubleSpeed::set();
case 16:
configuration |= _BV(SPR0);
break;
case 32:
DoubleSpeed::set();
case 64:
configuration |= _BV(SPR1);
break;
case 128:
configuration |= _BV(SPR0);
configuration |= _BV(SPR1);
break;
}
SPCR = configuration;
}
static inline void PullUpMISO() {
SpiMISO::High();
}
static inline void Begin() {
SlaveSelect::Low();
}
static inline void End() {
SlaveSelect::High();
}
static inline void Strobe() {
SlaveSelect::High();
SlaveSelect::Low();
}
static inline void Write(uint8_t v) {
Begin();
Send(v);
End();
}
static inline uint8_t Read() {
Begin();
uint8_t result = Receive();
End();
return result;
}
static inline void Send(uint8_t v) {
Overwrite(v);
Wait();
}
static inline uint8_t Receive() {
Send(0xff);
return ImmediateRead();
}
static inline uint8_t ImmediateRead() {
return SPDR;
}
static inline void Wait() {
while (!TransferComplete::value());
}
static inline void OptimisticWait() {
Wait();
}
static inline void Overwrite(uint8_t v) {
SPDR = v;
}
static inline void WriteWord(uint8_t a, uint8_t b) {
Begin();
Send(a);
Send(b);
End();
}
};
template
class SpiSlave {
public:
enum {
buffer_size = 128,
data_size = 8
};
static void Init() {
SpiSCK::set_mode(DIGITAL_INPUT);
SpiMOSI::set_mode(DIGITAL_INPUT);
SpiMISO::set_mode(DIGITAL_OUTPUT);
SpiSS::set_mode(DIGITAL_INPUT); // Ohhh mistress, ohhhh!
// SPI enabled, configured as master.
uint8_t configuration = _BV(SPE);
if (order == LSB_FIRST) {
configuration |= _BV(DORD);
}
if (enable_interrupt) {
configuration |= _BV(SPIE);
}
SPCR = configuration;
}
static inline void Reply(uint8_t value) {
SPDR = value;
}
static inline uint8_t readable() {
return TransferComplete::value();
}
static inline uint8_t ImmediateRead() {
return SPDR;
}
static inline uint8_t Read() {
while (!readable());
return ImmediateRead();
}
};
template
struct UartSpiPort {
static inline uint8_t tx_ready() { return TxReadyBit::value(); }
static inline uint8_t data() { return *DataRegister::ptr(); }
static inline void set_data(uint8_t value) { *DataRegister::ptr() = value; }
static inline void Setup(uint16_t rate) {
*PrescalerRegister::ptr() = 0;
XckPort::set_mode(DIGITAL_OUTPUT);
TxPort::set_mode(DIGITAL_OUTPUT);
RxPort::set_mode(DIGITAL_INPUT);
*ControlRegisterC::ptr() = CFlags;
*ControlRegisterB::ptr() = BFlags;
*PrescalerRegister::ptr() = rate;
}
};
#ifdef HAS_USART0
typedef UartSpiPort<
UartSpi0XCK,
UartSpi0TX,
UartSpi0RX,
UBRR0Register,
UCSR0BRegister,
_BV(RXEN0) | _BV(TXEN0),
UCSR0CRegister,
_BV(UMSEL01) | _BV(UMSEL00),
BitInRegister,
UDR0Register> UartSpiPort0;
#endif // HAS_USART0
#ifdef HAS_USART1
typedef UartSpiPort<
UartSpi1XCK,
UartSpi1TX,
UartSpi1RX,
UBRR1Register,
UCSR1BRegister,
_BV(RXEN1) | _BV(TXEN1),
UCSR1CRegister,
_BV(UMSEL11) | _BV(UMSEL10),
BitInRegister,
UDR1Register> UartSpiPort1;
#endif // HAS_USART1
template
class UartSpiMaster {
public:
enum {
buffer_size = 0,
data_size = 8
};
static void Init() {
SlaveSelect::set_mode(DIGITAL_OUTPUT);
SlaveSelect::High();
Port::Setup((speed / 2) - 1);
}
static inline void Begin() {
SlaveSelect::Low();
}
static inline void End() {
SlaveSelect::High();
}
static inline void Strobe() {
SlaveSelect::High();
SlaveSelect::Low();
}
static inline void Write(uint8_t v) {
Begin();
Send(v);
End();
}
static inline void Send(uint8_t v) {
Overwrite(v);
Wait();
}
static inline void Wait() {
while (!Port::tx_ready());
}
static inline void OptimisticWait() { }
static inline void Overwrite(uint8_t v) {
Port::set_data(v);
}
static inline void WriteWord(uint8_t a, uint8_t b) {
Begin();
Send(a);
Send(b);
End();
}
};
#define SPI_RECEIVE ISR(SPI_STC_vect)
} // namespace avrlib
#endif AVRLIB_SPI_H_