Skip to content

Instantly share code, notes, and snippets.

@YuuichiAkagawa

YuuichiAkagawa/SPI.h

Last active Apr 13, 2019
Embed
What would you like to do?
SPI.transfer16() support for GR-ROSE(v.0.91)
/*
* Copyright (c) 2010 by Cristian Maglie <c.maglie@arduino.cc>
* Copyright (c) 2014 by Paul Stoffregen <paul@pjrc.com> (Transaction API)
* Copyright (c) 2014 by Matthijs Kooijman <matthijs@stdin.nl> (SPISettings AVR)
* Copyright (c) 2014 by Andrew J. Kroll <xxxajk@gmail.com> (atomicity fixes)
* SPI Master library for arduino.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of either the GNU General Public License version 2
* or the GNU Lesser General Public License version 2.1, both as
* published by the Free Software Foundation.
*/
/* 25th Feb. 2019 : Modified for GR-ROSE by Yuuki Okamiya. */
#ifndef _SPI_H_INCLUDED
#define _SPI_H_INCLUDED
#include <Arduino.h>
// SPI_HAS_TRANSACTION means SPI has beginTransaction(), endTransaction(),
// usingInterrupt(), and SPISetting(clock, bitOrder, dataMode)
#define SPI_HAS_TRANSACTION 1
// SPI_HAS_NOTUSINGINTERRUPT means that SPI has notUsingInterrupt() method
#define SPI_HAS_NOTUSINGINTERRUPT 1
// SPI_ATOMIC_VERSION means that SPI has atomicity fixes and what version.
// This way when there is a bug fix you can check this define to alert users
// of your code if it uses better version of this library.
// This also implies everything that SPI_HAS_TRANSACTION as documented above is
// available too.
#define SPI_ATOMIC_VERSION 1
// Uncomment this line to add detection of mismatched begin/end transactions.
// A mismatch occurs if other libraries fail to use SPI.endTransaction() for
// each SPI.beginTransaction(). Connect an LED to this pin. The LED will turn
// on if any mismatch is ever detected.
//#define SPI_TRANSACTION_MISMATCH_LED 5
#ifndef LSBFIRST
#define LSBFIRST 0
#endif
#ifndef MSBFIRST
#define MSBFIRST 1
#endif
#ifndef __RX600__
#define SPI_CLOCK_DIV4 0x00
#define SPI_CLOCK_DIV16 0x01
#define SPI_CLOCK_DIV64 0x02
#define SPI_CLOCK_DIV128 0x03
#define SPI_CLOCK_DIV2 0x04
#define SPI_CLOCK_DIV8 0x05
#define SPI_CLOCK_DIV32 0x06
#define SPI_MODE0 0x00
#define SPI_MODE1 0x04
#define SPI_MODE2 0x08
#define SPI_MODE3 0x0C
#define SPI_MODE_MASK 0x0C // CPOL = bit 3, CPHA = bit 2 on SPCR
#define SPI_CLOCK_MASK 0x03 // SPR1 = bit 1, SPR0 = bit 0 on SPCR
#define SPI_2XCLOCK_MASK 0x01 // SPI2X = bit 0 on SPSR
#else
#define SPI_CLOCK_DIV2 0x00 // 30MHz
#define SPI_CLOCK_DIV4 0x01 // 15MHz
#define SPI_CLOCK_DIV6 0x02 // 10MHz
#define SPI_CLOCK_DIV8 0x03 // 7.5MHz
#define SPI_CLOCK_DIV10 0x04 // 6MHz
#define SPI_CLOCK_DIV12 0x05 // 5MHz
#define SPI_CLOCK_DIV14 0x06 // 4.3MHz
#define SPI_CLOCK_DIV16 0x07 // 3.75MHz
#define SPI_CLOCK_DIV20 0x09 // 3MHz
#define SPI_CLOCK_DIV30 0x0E // 2MHz
#define SPI_CLOCK_DIV60 0x1D // 1MHz
#define SPI_MODE0 0x0
#define SPI_MODE1 0x1
#define SPI_MODE2 0x2
#define SPI_MODE3 0x3
#define SPI_BIT_8 0x7
#define SPI_MODE_MASK 0x0003 // PHA = bit 0, POL = 1
#define SPI_BIT_MASK 0x0F00 //
#define SPI_DEFAULT_SPCMD (0x0704u)
#endif //__RX600__
// define SPI_AVR_EIMSK for AVR boards with external interrupt pins
#if defined(EIMSK)
#define SPI_AVR_EIMSK EIMSK
#elif defined(GICR)
#define SPI_AVR_EIMSK GICR
#elif defined(GIMSK)
#define SPI_AVR_EIMSK GIMSK
#endif
class SPISettings {
public:
SPISettings(uint32_t clock, uint8_t bitOrder, uint8_t dataMode) {
if (__builtin_constant_p(clock)) {
init_AlwaysInline(clock, bitOrder, dataMode);
} else {
init_MightInline(clock, bitOrder, dataMode);
}
}
SPISettings() {
init_AlwaysInline(4000000, MSBFIRST, SPI_MODE0);
}
private:
void init_MightInline(uint32_t clock, uint8_t bitOrder, uint8_t dataMode) {
init_AlwaysInline(clock, bitOrder, dataMode);
}
void init_AlwaysInline(uint32_t clock, uint8_t bitOrder, uint8_t dataMode)
__attribute__((__always_inline__)) {
#ifndef __RX600__
// Clock settings are defined as follows. Note that this shows SPI2X
// inverted, so the bits form increasing numbers. Also note that
// fosc/64 appears twice
// SPR1 SPR0 ~SPI2X Freq
// 0 0 0 fosc/2
// 0 0 1 fosc/4
// 0 1 0 fosc/8
// 0 1 1 fosc/16
// 1 0 0 fosc/32
// 1 0 1 fosc/64
// 1 1 0 fosc/64
// 1 1 1 fosc/128
// We find the fastest clock that is less than or equal to the
// given clock rate. The clock divider that results in clock_setting
// is 2 ^^ (clock_div + 1). If nothing is slow enough, we'll use the
// slowest (128 == 2 ^^ 7, so clock_div = 6).
uint8_t clockDiv;
// When the clock is known at compiletime, use this if-then-else
// cascade, which the compiler knows how to completely optimize
// away. When clock is not known, use a loop instead, which generates
// shorter code.
if (__builtin_constant_p(clock)) {
if (clock >= F_CPU / 2) {
clockDiv = 0;
} else if (clock >= F_CPU / 4) {
clockDiv = 1;
} else if (clock >= F_CPU / 8) {
clockDiv = 2;
} else if (clock >= F_CPU / 16) {
clockDiv = 3;
} else if (clock >= F_CPU / 32) {
clockDiv = 4;
} else if (clock >= F_CPU / 64) {
clockDiv = 5;
} else {
clockDiv = 6;
}
} else {
uint32_t clockSetting = F_CPU / 2;
clockDiv = 0;
while (clockDiv < 6 && clock < clockSetting) {
clockSetting /= 2;
clockDiv++;
}
}
// Compensate for the duplicate fosc/64
if (clockDiv == 6)
clockDiv = 7;
// Invert the SPI2X bit
clockDiv ^= 0x1;
// Pack into the SPISettings class
spcr = _BV(SPE) | _BV(MSTR) | ((bitOrder == LSBFIRST) ? _BV(DORD) : 0) |
(dataMode & SPI_MODE_MASK) | ((clockDiv >> 1) & SPI_CLOCK_MASK);
spsr = clockDiv & SPI_2XCLOCK_MASK;
#else
if (clock >= PCLK / 2) {
spbr = SPI_CLOCK_DIV2;
} else if (clock >= PCLK / 4) {
spbr = SPI_CLOCK_DIV4;
} else if (clock >= PCLK / 6) {
spbr = SPI_CLOCK_DIV6;
} else if (clock >= PCLK / 8) {
spbr = SPI_CLOCK_DIV8;
} else if (clock >= PCLK / 10) {
spbr = SPI_CLOCK_DIV10;
} else if (clock >= PCLK / 12) {
spbr = SPI_CLOCK_DIV12;
} else if (clock >= PCLK / 16) {
spbr = SPI_CLOCK_DIV16;
} else if (clock >= PCLK / 20) {
spbr = SPI_CLOCK_DIV20;
} else if (clock >= PCLK / 30) {
spbr = SPI_CLOCK_DIV30;
} else if (clock >= PCLK / 60) {
spbr = SPI_CLOCK_DIV60;
} else {
spbr = SPI_CLOCK_DIV60;
}
spcmd = SPI_DEFAULT_SPCMD; // default value
if(bitOrder == LSBFIRST) {
spcmd |= (1 << 12);
} else {
spcmd &= ~(1 << 12);
}
spcmd = (spcmd & ~SPI_MODE_MASK) | ((uint16_t)dataMode);
spcmd = (spcmd & ~SPI_BIT_MASK) | (SPI_BIT_8 << 8);
#endif
}
#ifndef __RX600__
uint8_t spcr;
uint8_t spsr;
#else
uint16_t spcmd;
uint8_t spbr;
#endif
friend class SPIClass;
};
class SPIClass {
public:
// Initialize the SPI library
static void begin();
// If SPI is used from within an interrupt, this function registers
// that interrupt with the SPI library, so beginTransaction() can
// prevent conflicts. The input interruptNumber is the number used
// with attachInterrupt. If SPI is used from a different interrupt
// (eg, a timer), interruptNumber should be 255.
static void usingInterrupt(uint8_t interruptNumber);
// And this does the opposite.
static void notUsingInterrupt(uint8_t interruptNumber);
// Note: the usingInterrupt and notUsingInterrupt functions should
// not to be called from ISR context or inside a transaction.
// For details see:
// https://github.com/arduino/Arduino/pull/2381
// https://github.com/arduino/Arduino/pull/2449
// Before using SPI.transfer() or asserting chip select pins,
// this function is used to gain exclusive access to the SPI bus
// and configure the correct settings.
inline static void beginTransaction(SPISettings settings) {
if (interruptMode > 0) {
#ifndef __RX600__
uint8_t sreg = SREG;
#else
uint8_t sreg = isNoInterrupts();
#endif
noInterrupts();
#ifdef SPI_AVR_EIMSK
if (interruptMode == 1) {
interruptSave = SPI_AVR_EIMSK;
SPI_AVR_EIMSK &= ~interruptMask;
SREG = sreg;
} else
#endif
{
interruptSave = sreg;
}
}
#ifdef SPI_TRANSACTION_MISMATCH_LED
if (inTransactionFlag) {
pinMode(SPI_TRANSACTION_MISMATCH_LED, OUTPUT);
digitalWrite(SPI_TRANSACTION_MISMATCH_LED, HIGH);
}
inTransactionFlag = 1;
#endif
#ifndef __RX600__
SPCR = settings.spcr;
SPSR = settings.spsr;
#else
RSPI1.SPCR.BIT.SPE = 0; //Stop SPI
RSPI1.SPCMD0.WORD = settings.spcmd;
RSPI1.SPBR = settings.spbr;
RSPI1.SPCR.BIT.SPE = 1; //Start SPI
#endif
}
// Write to the SPI bus (MOSI pin) and also receive (MISO pin)
inline static uint8_t transfer(uint8_t data) {
#ifndef __RX600__
SPDR = data;
/*
* The following NOP introduces a small delay that can prevent the wait
* loop form iterating when running at the maximum speed. This gives
* about 10% more speed, even if it seems counter-intuitive. At lower
* speeds it is unnoticed.
*/
asm volatile("nop");
while (!(SPSR & _BV(SPIF))) ; // wait
return SPDR;
#else
if(RSPI1.SPSR.BIT.OVRF == 1)
{
RSPI1.SPSR.BIT.OVRF = 0;
RSPI1.SPSR.BIT.SPRF = 1;
RSPI1.SPSR.BIT.SPTEF = 1;
}
RSPI1.SPDR.LONG = (unsigned long)data;
while(ICU.IR[40].BIT.IR == 0);
ICU.IR[40].BIT.IR = 0;
return (byte)RSPI1.SPDR.LONG;
#endif //__RX600__
}
inline static uint16_t transfer16(uint16_t data) {
union { uint16_t val; struct { uint8_t lsb; uint8_t msb; }; } in, out;
in.val = data;
#ifndef __RX600__
if (!(SPCR & _BV(DORD))) {
SPDR = in.msb;
asm volatile("nop"); // See transfer(uint8_t) function
while (!(SPSR & _BV(SPIF))) ;
out.msb = SPDR;
SPDR = in.lsb;
asm volatile("nop");
while (!(SPSR & _BV(SPIF))) ;
out.lsb = SPDR;
} else {
SPDR = in.lsb;
asm volatile("nop");
while (!(SPSR & _BV(SPIF))) ;
out.lsb = SPDR;
SPDR = in.msb;
asm volatile("nop");
while (!(SPSR & _BV(SPIF))) ;
out.msb = SPDR;
}
#else
//Add 2019.4.13 Yuuichi Akagawa
//MSB first?
if(RSPI1.SPCMD0.BIT.LSBF == 0) {
//MSB first transfer
out.msb = transfer(in.msb);
out.lsb = transfer(in.lsb);
}else{
//LSB first transfer
out.lsb = transfer(in.lsb);
out.msb = transfer(in.msb);
}
#endif //__RX600__
return out.val;
}
#ifndef __RX600__
inline static void transfer(void *buf, size_t count) {
if (count == 0) return;
uint8_t *p = (uint8_t *)buf;
SPDR = *p;
while (--count > 0) {
uint8_t out = *(p + 1);
while (!(SPSR & _BV(SPIF))) ;
uint8_t in = SPDR;
SPDR = out;
*p++ = in;
}
while (!(SPSR & _BV(SPIF))) ;
*p = SPDR;
}
#endif //__RX600__
// After performing a group of transfers and releasing the chip select
// signal, this function allows others to access the SPI bus
inline static void endTransaction(void) {
#ifdef SPI_TRANSACTION_MISMATCH_LED
if (!inTransactionFlag) {
pinMode(SPI_TRANSACTION_MISMATCH_LED, OUTPUT);
digitalWrite(SPI_TRANSACTION_MISMATCH_LED, HIGH);
}
inTransactionFlag = 0;
#endif
if (interruptMode > 0) {
#ifdef SPI_AVR_EIMSK
uint8_t sreg = SREG;
#endif
noInterrupts();
#ifdef SPI_AVR_EIMSK
if (interruptMode == 1) {
SPI_AVR_EIMSK = interruptSave;
SREG = sreg;
} else
#endif
{
#ifndef __RX600__
SREG = interruptSave;
#else
if (!interruptSave) {
interrupts();
}
#endif
}
}
}
// Disable the SPI bus
static void end();
// This function is deprecated. New applications should use
// beginTransaction() to configure SPI settings.
inline static void setBitOrder(uint8_t bitOrder) {
#ifndef __RX600__
if (bitOrder == LSBFIRST) SPCR |= _BV(DORD);
else SPCR &= ~(_BV(DORD));
#else
RSPI1.SPCR.BIT.SPE = 0; //Stop SPI
if(bitOrder == LSBFIRST) {
RSPI1.SPCMD0.WORD |= (1 << 12);
} else {
RSPI1.SPCMD0.WORD &= ~(1 << 12);
}
RSPI1.SPCR.BIT.SPE = 1; //Start SPI
#endif
}
// This function is deprecated. New applications should use
// beginTransaction() to configure SPI settings.
inline static void setDataMode(uint8_t dataMode) {
#ifndef __RX600__
SPCR = (SPCR & ~SPI_MODE_MASK) | dataMode;
#else
RSPI1.SPCR.BIT.SPE = 0; //Stop SPI
RSPI1.SPCMD0.WORD = (RSPI0.SPCMD0.WORD & ~SPI_MODE_MASK) | ((uint16_t)dataMode);
RSPI1.SPCR.BIT.SPE = 1; //Start SPI
#endif
}
// This function is deprecated. New applications should use
// beginTransaction() to configure SPI settings.
inline static void setClockDivider(uint8_t clockDiv) {
#ifndef __RX600__
SPCR = (SPCR & ~SPI_CLOCK_MASK) | (clockDiv & SPI_CLOCK_MASK);
SPSR = (SPSR & ~SPI_2XCLOCK_MASK) | ((clockDiv >> 2) & SPI_2XCLOCK_MASK);
#else
RSPI1.SPCR.BIT.SPE = 0; //Stop SPI
RSPI1.SPBR = clockDiv;
RSPI1.SPCR.BIT.SPE = 1; //Start SPI
#endif
}
// These undocumented functions should not be used. SPI.transfer()
// polls the hardware flag which is automatically cleared as the
// AVR responds to SPI's interrupt
#ifndef __RX600__
inline static void attachInterrupt() { SPCR |= _BV(SPIE); }
inline static void detachInterrupt() { SPCR &= ~_BV(SPIE); }
#endif
private:
static uint8_t initialized;
static uint8_t interruptMode; // 0=none, 1=mask, 2=global
static uint8_t interruptMask; // which interrupts to mask
static uint8_t interruptSave; // temp storage, to restore state
#ifdef SPI_TRANSACTION_MISMATCH_LED
static uint8_t inTransactionFlag;
#endif
};
extern SPIClass SPI;
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.