Skip to content

Instantly share code, notes, and snippets.

@ircmaxell
Last active February 24, 2017 22:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ircmaxell/6ff5cea15357aaf4186c6c03488d31f4 to your computer and use it in GitHub Desktop.
Save ircmaxell/6ff5cea15357aaf4186c6c03488d31f4 to your computer and use it in GitHub Desktop.
#include "pins_arduino.h"
#include "SPI_USART.h"
uint8_t SPIUsartClass::ss[SPIUSART_COUNT];
void SPIUsartClass::begin(uint8_t _usart, uint8_t _ss) {
if (_usart > SPIUSART_COUNT) {
return;
}
ss[_usart] = _ss;
digitalWrite(ss[_usart], HIGH);
pinMode(ss[_usart], OUTPUT);
#define SPIUSART_INIT_CASE(_n) \
UBRR ## _n = 0; \
UCSR ## _n ## A = _BV(TXC ## _n); \
UCSR ## _n ## C = _BV(UMSEL ## _n ## 0) | _BV(UMSEL ## _n ## 1); \
UCSR ## _n ## B = _BV(TXEN ## _n) | _BV(RXEN ## _n);
SPIUSART_SWITCH(_usart, SPIUSART_INIT_CASE);
SPIUsart.beginTransaction(_usart, SPIUsartSettings());
}
#ifndef _SPIUSART_H_INCLUDED
#define _SPIUSART_H_INCLUDED
#include <Arduino.h>
#include <avr/pgmspace.h>
#include <SPI.h>
#define SPIUSART_SET_BIT(_dest, _bit) _dest |= _BV(_bit)
#define SPIUSART_CLEAR_BIT(_dest, _bit) _dest &= ~_BV(_bit)
#if defined(UBRR3)
#define SPIUSART0 0
#define SPIUSART1 1
#define SPIUSART2 2
#define SPIUSART3 3
#define SPIUSART_COUNT 4
#define SPIUSART_SWITCH(_v, _c) switch(_v) { \
case 0: \
_c(0); \
break; \
case 1: \
_c(1); \
break; \
case 2: \
_c(2); \
break; \
case 3: \
_c(3); \
break; \
}
#elif defined(UBRR2)
#define SPIUSART0 0
#define SPIUSART1 1
#define SPIUSART2 2
#define SPIUSART_COUNT 3
#define SPIUSART_SWITCH(_v, _c) switch(_v) { \
case 0: \
_c(0); \
break; \
case 1: \
_c(1); \
break; \
case 2: \
_c(2); \
break; \
}
#elif defined(UBRR1)
#define SPIUSART0 0
#define SPIUSART1 1
#define SPIUSART_COUNT 2
#define SPIUSART_SWITCH(_v, _c) switch(_v) { \
case 0: \
_c(0); \
break; \
case 1: \
_c(1); \
break; \
}
#elif defined(UBRR0)
#define SPIUSART0 0
#define SPIUSART_COUNT 1
#define SPIUSART_SWITCH(_v, _c) switch(_v) { \
case 0: \
_c(0); \
break; \
}
#else
#define SPIUSART_COUNT 0
#define SPIUSART_SWITCH(_v, _c) {}
#endif
#define SPIUSART_UCSRnC_MASK (_BV(UDORD0) | _BV(UCPOL0) | _BV(UCPHA0))
class SPIUsartSettings {
public:
SPIUsartSettings() {
SPIUsartSettings(4000000, MSBFIRST, SPI_MODE0);
}
SPIUsartSettings(uint32_t clock, uint8_t bitOrder, uint8_t dataMode) {
ubbr_value = (F_CPU / (2 * clock)) - 1;
if (clock > F_CPU / 2) {
ubbr_value = 0; // clamp to max
}
ucsrc_or_mask = 0;
if (bitOrder == LSBFIRST) {
ucsrc_or_mask |= _BV(UDORD0);
}
switch (dataMode) {
case SPI_MODE0:
break; // all pins are cleared
case SPI_MODE1:
ucsrc_or_mask |= _BV(UCPHA0);
break;
case SPI_MODE2:
ucsrc_or_mask |= _BV(UCPOL0);
break;
case SPI_MODE3:
ucsrc_or_mask |= _BV(UCPOL0) | _BV(UCPHA0);
break;
}
}
private:
uint8_t ucsrc_or_mask = 0;
uint16_t ubbr_value = 0;
friend class SPIUsartClass;
};
class SPIUsartClass {
public:
inline uint8_t transfer(uint8_t);
inline uint8_t transfer(uint8_t, uint8_t);
inline uint8_t transfer16(uint8_t _usart, uint16_t _value) {
uint16_t result = _value;
SPIUsartClass::transferRev(_usart, &result, sizeof(result));
return result;
}
inline static void transfer(uint8_t, void*, size_t);
inline static void transferRev(uint8_t, void*, size_t);
inline static void beginTransaction(uint8_t _usart, SPIUsartSettings settings) {
#define SPIUSART_BEGIN_TRANSACTION_CASE(_n) \
UCSR ## _n ## C = (UCSR ## _n ## C & SPIUSART_UCSRnC_MASK) | settings.ucsrc_or_mask; \
UBRR ## _n = settings.ubbr_value;
SPIUSART_SWITCH(_usart, SPIUSART_BEGIN_TRANSACTION_CASE);
}
inline static void endTransaction(uint8_t _usart) {
}
static void begin(uint8_t, uint8_t); // SS
static void end(uint8_t);
private:
static uint8_t ss[SPIUSART_COUNT];
static inline uint8_t transfer_raw(uint8_t, uint8_t);
};
extern SPIUsartClass SPIUsart;
inline uint8_t SPIUsartClass::transfer(uint8_t _usart, uint8_t _data) {
uint8_t val;
digitalWrite(SPIUsartClass::ss[_usart], LOW);
val = SPIUsartClass::transfer_raw(_usart, _data);
digitalWrite(SPIUsartClass::ss[_usart], HIGH);
return val;
}
inline void SPIUsartClass::transfer(uint8_t _usart, void *buf, size_t count) {
register uint8_t *p = (uint8_t *) buf;
digitalWrite(SPIUsartClass::ss[_usart], LOW);
// duff's device
register size_t n = (count + 7) / 8;
switch (count % 8) {
do {
case 0:
*p = SPIUsartClass::transfer_raw(_usart, *p);
p++;
case 7:
*p = SPIUsartClass::transfer_raw(_usart, *p);
p++;
case 6:
*p = SPIUsartClass::transfer_raw(_usart, *p);
p++;
case 5:
*p = SPIUsartClass::transfer_raw(_usart, *p);
p++;
case 4:
*p = SPIUsartClass::transfer_raw(_usart, *p);
p++;
case 3:
*p = SPIUsartClass::transfer_raw(_usart, *p);
p++;
case 2:
*p = SPIUsartClass::transfer_raw(_usart, *p);
p++;
case 1:
*p = SPIUsartClass::transfer_raw(_usart, *p);
p++;
} while (--n > 0);
}
digitalWrite(SPIUsartClass::ss[_usart], HIGH);
}
inline void SPIUsartClass::transferRev(uint8_t _usart, void *buf, size_t count) {
register uint8_t *p = (uint8_t *) buf + count - 1;
digitalWrite(SPIUsartClass::ss[_usart], LOW);
// duff's device
register size_t n = (count + 7) / 8;
switch (count % 8) {
do {
case 0:
*p = SPIUsartClass::transfer_raw(_usart, *p);
p--;
case 7:
*p = SPIUsartClass::transfer_raw(_usart, *p);
p--;
case 6:
*p = SPIUsartClass::transfer_raw(_usart, *p);
p--;
case 5:
*p = SPIUsartClass::transfer_raw(_usart, *p);
p--;
case 4:
*p = SPIUsartClass::transfer_raw(_usart, *p);
p--;
case 3:
*p = SPIUsartClass::transfer_raw(_usart, *p);
p--;
case 2:
*p = SPIUsartClass::transfer_raw(_usart, *p);
p--;
case 1:
*p = SPIUsartClass::transfer_raw(_usart, *p);
p--;
} while (--n > 0);
}
digitalWrite(SPIUsartClass::ss[_usart], HIGH);
}
inline uint8_t SPIUsartClass::transfer_raw(uint8_t _usart, uint8_t _data) {
#define SPIUSART_TRANSFER_RAW_CASE(_n) \
UDR ## _n = _data; \
asm volatile("nop"); \
loop_until_bit_is_set(UCSR ## _n ## A, RXC ## _n); \
return UDR ## _n;
SPIUSART_SWITCH(_usart, SPIUSART_TRANSFER_RAW_CASE);
return 0;
}
#endif
#include "SPI_USART.h"
#include <Arduino.h>
void setup() {
SPIUsart.begin(SPIUSART0, 5);
pinMode(13, OUTPUT);
digitalWrite(13, LOW);
SPIUsart.transfer16(SPIUSART0, ((B10101010 << 8) | (B01010101)));
delay(200);
SPIUsart.transfer16(SPIUSART0, ((B10101010) | (B01010101 << 8)));
delay(200);
}
void loop() {
uint32_t i;
SPIUsart.beginTransaction(SPIUSART0, SPIUsartSettings());
for (i = 0; i < (1u<<15); i++) {
SPIUsart.transfer16(SPIUSART0, (uint16_t) i);
}
SPIUsart.endTransaction(SPIUSART0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment