diff --git a/README.md b/README.md index f7f7609..1eb53dd 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,9 @@ bus managers and device driver support. the library contains bus manager implementations for hardware resources (AVR SPI and USI) and software using Ardino-GPIO. +The design of this library allows multiple bus managers both hardware +and software, and device drivers directly support bus managers. + Version: 1.8 ## Classes @@ -19,41 +22,57 @@ Version: 1.8 * [ShiftIn](./examples/ShiftIn) * [ShiftOut](./examples/ShiftOut) +[ATtiny](./examples/ATtiny) variants. + ## Benchmarks -Benchmarks include bus acquire-release (multitasking support), and -slave select pin handling. +Benchmarks measurements below do not include bus acquire-release +(multitasking support), and slave select pin handling. The +measurements are the transfer time in micro-seconds. #### Software SPI Bus Manager -Operation (LSB @ 570 kHz) | Bytes | us +Operation (LSB) | Bytes | us ----------|-------|---- -transfer | 1 | 16.62 -read | 4 | 70.19 -write | 4 | 68.75 -transfer | 4 | 70.75 +transfer | 1 | 13.6 +transfer | 10 | 147.9 +transfer | 100 | 1491.8 +read | 100 | 1511.7 +write | 100 | 1459.6 -Operation (MSB @ 760 kHz) | Bytes | us +Operation (MSB) | Bytes | us ----------|-------|---- -transfer | 1 | 13.81 -read | 4 | 55.94 -write | 4 | 56.31 -transfer | 4 | 58.5 +transfer | 1 | 12.8 +transfer | 10 | 139.0 +transfer | 100 | 1345.4 +read | 100 | 1304.2 +write | 100 | 1313.2 + +Wiring (MSB) | us | SPI | us | Xn +------ |----|------|----|---- +shiftIn | 86.340 | spi >> var | 12.892 | 6.7 +shiftOut | 109.288 | spi << val | 13.144 | 8.3 -Wiring | us | SPI | us | Xn +Wiring (MSB) | us | SPR | us | Xn ------ |----|------|----|---- -shiftIn | 84.19 | var = spi.transfer(0) | 15.75 | 5.35 -shiftOut | 108.9 | spi.transfer(var) | 15.00 | 7.26 +shiftIn | 86.340 | srpi >> var | 4.844 | 17.8 +shiftOut | 109.288 | srpo << val | 8.676 | 12.6 #### Hardware SPI Bus Manager Operation (LSB/MSB @ 8 MHz) | Bytes | us ----------|-------|---- -transfer | 1 | 3.44 -read | 4 | 8.50 -write | 4 | 8.44 -transfer | 4 | 8.96 +transfer | 1 | 1.8 +transfer | 10 | 15.3 +transfer | 100 | 139.8 +read | 100 | 133.1 +write | 100 | 145.6 + +Wiring (MSB) | us | SPI | us | Xn +------ |----|------|----|---- +shiftIn | 86.340 | spi >> var | 2.832 | 30 +shiftOut | 109.288 | spi << val | 2.832 | 38 ## Dependencies -* [General Purpose Input/Output library for Arduino, GPIO](https://github.com/mikaelpatel/Arduino-GPIO) +* [Arduino-GPIO](https://github.com/mikaelpatel/Arduino-GPIO) diff --git a/examples/Benchmark/Benchmark.ino b/examples/Benchmark/Benchmark.ino index 068ab83..e4e0dbc 100644 --- a/examples/Benchmark/Benchmark.ino +++ b/examples/Benchmark/Benchmark.ino @@ -1,24 +1,19 @@ #include "GPIO.h" #include "SPI.h" +#include "benchmark.h" // Configuration: SPI/BITORDER -#define USE_SOFTWARE_SPI -// #define USE_HARDWARE_SPI +// #define USE_SOFTWARE_SPI // #define BITORDER LSBFIRST #define BITORDER MSBFIRST #if defined(USE_SOFTWARE_SPI) #include "Software/SPI.h" -#if defined(ARDUINO_attiny) -GPIO ss; -Software::SPI spi; -SPI::Device<0, BITORDER, SPI::MAX_FREQ, BOARD::D0> dev(spi); -#else GPIO ss; Software::SPI spi; SPI::Device<0, BITORDER, SPI::MAX_FREQ, BOARD::D10> dev(spi); -#endif -#elif defined(USE_HARDWARE_SPI) + +#else #include "Hardware/SPI.h" GPIO ss; Hardware::SPI spi; @@ -27,45 +22,136 @@ SPI::Device<0, BITORDER, SPI::MAX_FREQ, BOARD::D10> dev(spi); void setup() { + Serial.begin(57600); + while (!Serial); + BENCHMARK_BASELINE(1000); } void loop() { - // 32-bit/4 byte test data - static uint32_t value = 0; - uint32_t res; + // Measure SPI bus manager performance with and without slave select + // and device setting + + const uint8_t PRESCALE = spi.prescale(SPI::MAX_FREQ); + uint8_t src[100]; + uint8_t dest[100]; - // SPI bus manager serial data transfer ss.toggle(); - spi.acquire(0, BITORDER, SPI::MIN_CLOCK_SCALE); - spi.transfer(value); + spi.acquire(0, BITORDER, PRESCALE); + BENCHMARK("1a. SPI bus manager data transfer only (1 byte)", 1000) { + spi.transfer(src[0]); + } + + BENCHMARK("1b. - transfer (10 byte)", 1000) { + spi.transfer(dest, src, 10); + } + + BENCHMARK("1c. - transfer (100 byte)", 1000) { + spi.transfer(dest, src, 100); + } + + BENCHMARK("1d. - read (100 byte)", 1000) { + spi.read(dest, 100); + } + + BENCHMARK("1e. - write only (100 byte)", 1000) { + spi.write(src, 100); + } spi.release(); ss.toggle(); - delayMicroseconds(20); - // SPI device driver serial data transfer - dev.acquire(); - dev.transfer(value); - dev.release(); - delayMicroseconds(10); + BENCHMARK("2a. SPI bus manager data transfer (1 byte)", 1000) { + ss.toggle(); + spi.acquire(0, BITORDER, PRESCALE); + spi.transfer(src[0]); + spi.release(); + ss.toggle(); + } - // SPI device driver serial buffer read - dev.acquire(); - dev.read(&res, sizeof(value)); - dev.release(); - delayMicroseconds(10); + BENCHMARK("2b. - transfer (10 byte)", 1000) { + ss.toggle(); + spi.acquire(0, BITORDER, PRESCALE); + spi.transfer(dest, src, 10); + spi.release(); + ss.toggle(); + } - // SPI device driver serial buffer write - dev.acquire(); - dev.write(&value, sizeof(value)); - dev.release(); - delayMicroseconds(10); + BENCHMARK("2c. - transfer (100 byte)", 1000) { + ss.toggle(); + spi.acquire(0, BITORDER, PRESCALE); + spi.transfer(dest, src, 100); + spi.release(); + ss.toggle(); + } + + BENCHMARK("2d. - read (100 byte)", 1000) { + ss.toggle(); + spi.acquire(0, BITORDER, PRESCALE); + spi.read(dest, 100); + spi.release(); + ss.toggle(); + } + + BENCHMARK("2e. - write (100 byte)", 1000) { + ss.toggle(); + spi.acquire(0, BITORDER, PRESCALE); + spi.write(dest, 100); + spi.release(); + ss.toggle(); + } - // SPI device driver serial buffer transfer dev.acquire(); - dev.transfer(&res, &value, sizeof(value)); + BENCHMARK("3a. SPI device driver data transfer only (1 byte)", 1000) { + dev.transfer(src[0]); + } + + BENCHMARK("3b. - transfer (10 bytes)", 1000) { + dev.transfer(dest, src, 10); + } + + BENCHMARK("3c. - transfer (100 bytes)", 1000) { + dev.transfer(dest, src, 100); + } + + BENCHMARK("3d. - read (100 bytes)", 1000) { + dev.read(dest, 100); + } + + BENCHMARK("3e. - write (100 bytes)", 1000) { + dev.write(src, 100); + } dev.release(); - delayMicroseconds(100); - value++; + BENCHMARK("4a. SPI device driver data transfer (1 byte)", 1000) { + dev.acquire(); + dev.transfer(src[0]); + dev.release(); + } + + BENCHMARK("4b. - transfer (10 bytes)", 1000) { + dev.acquire(); + dev.transfer(dest, src, 10); + dev.release(); + } + + BENCHMARK("4c. - transfer (100 bytes)", 1000) { + dev.acquire(); + dev.transfer(dest, src, 100); + dev.release(); + } + + BENCHMARK("4d. - read (100 bytes)", 1000) { + dev.acquire(); + dev.read(dest, 100); + dev.release(); + } + + BENCHMARK("4e. - write (100 bytes)", 1000) { + dev.acquire(); + dev.write(src, 100); + dev.release(); + } + + Serial.println(); + delay(2000); } diff --git a/examples/ShiftIn/ShiftIn.ino b/examples/ShiftIn/ShiftIn.ino index abced88..0586462 100644 --- a/examples/ShiftIn/ShiftIn.ino +++ b/examples/ShiftIn/ShiftIn.ino @@ -1,69 +1,61 @@ #include "GPIO.h" #include "SRPI.h" #include "SPI.h" +#include "benchmark.h" // Configuration: SPI/BITORDER #define USE_SOFTWARE_SPI -// #define USE_HARDWARE_SPI // #define BITORDER LSBFIRST #define BITORDER MSBFIRST #if defined(USE_SOFTWARE_SPI) #include "Software/SPI.h" -#if defined(ARDUINO_attiny) -GPIO ss; -Software::SPI spi; -SPI::Device<0, BITORDER, SPI::MAX_FREQ, BOARD::D0> dev(spi); -SRPI srpi; -#else -GPIO ss; Software::SPI spi; -SPI::Device<0, BITORDER, SPI::MAX_FREQ, BOARD::D10> dev(spi); -SRPI srpi; -#endif -#elif defined(USE_HARDWARE_SPI) +#else #include "Hardware/SPI.h" -GPIO ss; Hardware::SPI spi; +#endif + +GPIO ss; SPI::Device<0, BITORDER, SPI::MAX_FREQ, BOARD::D10> dev(spi); SRPI srpi; -#endif -#if defined(ARDUINO_attiny) -#define MOSI_PIN 1 -#define MISO_PIN 2 -#define SCK_PIN 3 -#else +#define SS_PIN 10 #define MOSI_PIN 11 #define MISO_PIN 12 #define SCK_PIN 13 -#endif void setup() { + Serial.begin(57600); + while (!Serial); ss.output(); - spi.acquire(0, BITORDER, SPI::MIN_CLOCK_SCALE); + spi.acquire(0, BITORDER, spi.prescale(SPI::MAX_FREQ)); + BENCHMARK_BASELINE(1000); } void loop() { uint8_t value; - // 84 us, 95 kHz - ss.toggle(); - value = shiftIn(MISO_PIN, SCK_PIN, BITORDER); - ss.toggle(); - delayMicroseconds(10); - - // 13 us, 762 kHz - ss.toggle(); - spi >> value; - ss.toggle(); - delayMicroseconds(10); - - // 4.7 us, 1.78 HHz - ss.toggle(); - srpi >> value; - ss.toggle(); - delayMicroseconds(100); + BENCHMARK("1. Arduino core shiftIn", 1000) { + ss.toggle(); + value = shiftIn(MISO_PIN, SCK_PIN, BITORDER); + ss.toggle(); + } + + BENCHMARK("2. SPI input operator", 1000) { + ss.toggle(); + spi >> value; + ss.toggle(); + } + + BENCHMARK("3, SRPI input operator", 1000) { + ss.toggle(); + srpi >> value; + ss.toggle(); + } + + Serial.println(); + delay(2000); } diff --git a/examples/ShiftOut/ShiftOut.ino b/examples/ShiftOut/ShiftOut.ino index 1df3989..5cb52c2 100644 --- a/examples/ShiftOut/ShiftOut.ino +++ b/examples/ShiftOut/ShiftOut.ino @@ -1,69 +1,61 @@ #include "GPIO.h" #include "SRPO.h" #include "SPI.h" +#include "benchmark.h" // Configuration: SPI/BITORDER #define USE_SOFTWARE_SPI -// #define USE_HARDWARE_SPI // #define BITORDER LSBFIRST #define BITORDER MSBFIRST #if defined(USE_SOFTWARE_SPI) #include "Software/SPI.h" -#if defined(ARDUINO_attiny) -GPIO ss; -Software::SPI spi; -SPI::Device<0, BITORDER, SPI::MAX_FREQ, BOARD::D0> dev(spi); -SRPO srpo; -#else -GPIO ss; Software::SPI spi; -SPI::Device<0, BITORDER, SPI::MAX_FREQ, BOARD::D10> dev(spi); -SRPO srpo; -#endif -#elif defined(USE_HARDWARE_SPI) +#else #include "Hardware/SPI.h" -GPIO ss; Hardware::SPI spi; +#endif + +GPIO ss; SPI::Device<0, BITORDER, SPI::MAX_FREQ, BOARD::D10> dev(spi); SRPO srpo; -#endif -#if defined(ARDUINO_attiny) -#define MOSI_PIN 1 -#define MISO_PIN 2 -#define SCK_PIN 3 -#else +#define SS_PIN 10 #define MOSI_PIN 11 #define MISO_PIN 12 #define SCK_PIN 13 -#endif void setup() { + Serial.begin(57600); + while (!Serial); ss.output(); - spi.acquire(0, BITORDER, SPI::MIN_CLOCK_SCALE); + spi.acquire(0, BITORDER, spi.prescale(SPI::MAX_FREQ)); + BENCHMARK_BASELINE(1000); } void loop() { uint8_t value = 0xa5; - // 108 us, 72 kHz - ss.toggle(); - shiftOut(MOSI_PIN, SCK_PIN, BITORDER, value); - ss.toggle(); - delayMicroseconds(10); - - // 13 us, 762 kHz - ss.toggle(); - spi << value; - ss.toggle(); - delayMicroseconds(10); - - // 8 us, 1.07 MHz - ss.toggle(); - srpo << value; - ss.toggle(); - delayMicroseconds(100); + BENCHMARK("1. Arduino core shiftOut", 1000) { + ss.toggle(); + shiftOut(MOSI_PIN, SCK_PIN, BITORDER, value); + ss.toggle(); + } + + BENCHMARK("2. SPI output operator", 1000) { + ss.toggle(); + spi << value; + ss.toggle(); + } + + BENCHMARK("3, SRPO output operator", 1000) { + ss.toggle(); + srpo << value; + ss.toggle(); + } + + Serial.println(); + delay(2000); } diff --git a/src/Hardware/AVR/SPI.h b/src/Hardware/AVR/SPI.h new file mode 100644 index 0000000..880b705 --- /dev/null +++ b/src/Hardware/AVR/SPI.h @@ -0,0 +1,260 @@ +/** + * @file Hardware/AVR/SPI.h + * @version 1.1 + * + * @section License + * Copyright (C) 2017, Mikael Patel + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + */ + +#ifndef HARDWARE_AVR_SPI_H +#define HARDWARE_AVR_SPI_H + +#include "GPIO.h" +#include "SPI.h" + +/** + * Hardware Serial Perpheral Interface (SPI) class. + */ +namespace Hardware { + +#if defined(SPDR) +class SPI : public ::SPI { +public: + /** + * Serial Perpheral Interface (SPI) constructor. + */ + SPI() : + ::SPI() + { + m_ss.output(); + m_sck.output(); + m_mosi.output(); + m_miso.input(); + } + + /** + * @override{SPI} + * Calculate bus prescale from device frequency. + * @param[in] frequency device access. + * @return prescale + */ + virtual uint8_t prescale(uint32_t frequency) + { + uint16_t scale = F_CPU / frequency; + if (scale <= 2) return (0b100); + if (scale <= 4) return (0b000); + if (scale <= 8) return (0b101); + if (scale <= 16) return (0b001); + if (scale <= 32) return (0b110); + if (scale <= 64) return (0b111); + return (0b011); + } + + /** + * @override{SPI} + * Acquire bus access. Yield until bus is released. + * @param[in] mode of access. + * @param[in] bitorder of serial data. + * @param[in] prescale clock prescale. + */ + virtual void acquire(uint8_t mode, uint8_t bitorder, uint8_t prescale) + { + // Wait for bus manager to be released + lock(); + + // Set control registers: mode, bitorder and clock + SPCR = _BV(SPE) + | _BV(MSTR) + | (bitorder == LSBFIRST ? _BV(DORD) : 0) + | ((mode & 0x03) << CPHA) + | (prescale & 0x03); + SPSR = ((prescale >> 2) & 0x01); + } + + /** + * @override{SPI} + * Send given byte to device. Returns byte received from device. + * @param[in] value to send to device. + * @return received value. + */ + virtual uint8_t transfer(uint8_t value) + __attribute__((always_inline)) + { + SPDR = value; + __asm__ __volatile__("nop" ::: "memory"); + loop_until_bit_is_set(SPSR, SPIF); + return (SPDR); + } + + /** + * @override{SPI} + * Transfer given number of bytes from source buffer to + * device. Store received bytes in destination buffer. + * @param[in] dest destination buffer. + * @param[in] src source buffer. + * @param[in] count number of bytes to transfer. + */ + virtual void transfer(void* dest, const void* src, size_t count) + { + if (count == 0 || dest == NULL || src == NULL) return; + const uint8_t* sp = (const uint8_t*) src; + uint8_t* dp = (uint8_t*) dest; + uint8_t value = *sp++; + SPDR = value; + while (--count) { + value = *sp++; + __asm__ __volatile__("" ::: "memory"); + loop_until_bit_is_set(SPSR, SPIF); + SPDR = value; + __asm__ __volatile__("" ::: "memory"); + *dp++ = SPDR; + } + loop_until_bit_is_set(SPSR, SPIF); + *dp = SPDR; + } + + /** + * @override{SPI} + * Read given number of bytes from device and store in buffer. + * @param[in] buf buffer pointer. + * @param[in] count number of bytes. + */ + virtual void read(void* buf, size_t count) + { + if (count == 0 || buf == NULL) return; + uint8_t* bp = (uint8_t*) buf; + SPDR = 0; + while (--count) { + __asm__ __volatile__("nop" ::: "memory"); + loop_until_bit_is_set(SPSR, SPIF); + SPDR = 0; + __asm__ __volatile__("" ::: "memory"); + *bp++ = SPDR; + } + loop_until_bit_is_set(SPSR, SPIF); + *bp = SPDR; + } + + /** + * @override{SPI} + * Write given number of bytes from buffer to device. + * @param[in] buf buffer pointer. + * @param[in] count number of bytes. + */ + virtual void write(const void* buf, size_t count) + { + if (count == 0 || buf == NULL) return; + uint8_t* bp = (uint8_t*) buf; + uint8_t value = *bp++; + SPDR = value; + while (--count) { + value = *bp++; + __asm__ __volatile__("" ::: "memory"); + loop_until_bit_is_set(SPSR, SPIF); + SPDR = value; + __asm__ __volatile__("" ::: "memory"); + } + loop_until_bit_is_set(SPSR, SPIF); + } + +protected: + /** Slave select pin. */ + GPIO m_ss; + + /** Clock pin. */ + GPIO m_sck; + + /** Master Output Slave Input pin. */ + GPIO m_mosi; + + /** Master Input Slave Output pin. */ + GPIO m_miso; +}; + +#elif defined(USIDR) + +class SPI : public ::SPI { +public: + /** + * Serial Perpheral Interface (SPI) constructor. + */ + SPI() : + ::SPI() + { + m_ss.output(); + m_sck.output(); + m_mosi.output(); + m_miso.input(); + } + + /** + * @override{SPI} + * Acquire bus access. Yield until bus is released. + * @param[in] mode of access. + * @param[in] bitorder of serial data. + * @param[in] prescale clock prescale. + */ + virtual void acquire(uint8_t mode, uint8_t bitorder, uint8_t prescale) + { + // Wait for bus manager to be released + lock(); + + // Not used: only MSBFIRST bitorder and max frequency + (void) bitorder; + (void) prescale; + + // Set clock polarity + m_sck.write(mode & 0x02); + + // Cache clocking command + m_usicr = (_BV(USIWM0) | _BV(USICS1) | _BV(USICLK) | _BV(USITC)); + if (mode == 1 || mode == 2) m_usicr |= _BV(USICS0); + } + + /** + * @override{SPI} + * Send given byte to device. Returns byte received from device. + * @param[in] value to send to device. + * @return received value. + */ + virtual uint8_t transfer(uint8_t value) + { + USIDR = value; + USISR = _BV(USIOIF); + register uint8_t cntl = m_usicr; + do { + USICR = cntl; + } while ((USISR & _BV(USIOIF)) == 0); + return (USIDR); + } + +protected: + /** Slave select pin. */ + GPIO m_ss; + + /** Clock pin. */ + GPIO m_sck; + + /** Master Output Slave Input pin. */ + GPIO m_mosi; + + /** Master Input Slave Output pin. */ + GPIO m_miso; + + /** USI clock command. */ + uint8_t m_usicr; +}; +#endif + +}; +#endif diff --git a/src/Hardware/SAM/SPI.h b/src/Hardware/SAM/SPI.h new file mode 100644 index 0000000..a270ce5 --- /dev/null +++ b/src/Hardware/SAM/SPI.h @@ -0,0 +1,102 @@ +/** + * @file Hardware/SAM/SPI.h + * @version 1.0 + * + * @section License + * Copyright (C) 2017, Mikael Patel + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + */ + +#ifndef HARDWARE_SAM_SPI_H +#define HARDWARE_SAM_SPI_H + +#include "GPIO.h" +#include "SPI.h" + +/** + * Hardware Serial Perpheral Interface (SPI) class. + */ +namespace Hardware { + +class SPI : public ::SPI { +public: + /** + * Serial Perpheral Interface (SPI) constructor. + */ + SPI() : ::SPI() {} + + /** + * @override{SPI} + * Acquire bus access. Yield until bus is released. + * @param[in] mode of access. + * @param[in] bitorder of serial data. + * @param[in] scale clock frequency. + */ + virtual void acquire(uint8_t mode, uint8_t bitorder, uint8_t scale) + { + (void) mode; + (void) bitorder; + (void) scale; + } + + /** + * @override{SPI} + * Send given byte to device. Returns byte received from device. + * @param[in] value to send to device. + * @return received value. + */ + virtual uint8_t transfer(uint8_t value) + __attribute__((always_inline)) + { + (void) value; + return (0); + } + + /** + * @override{SPI} + * Transfer given number of bytes from source buffer to + * device. Store received bytes in destination buffer. + * @param[in] dest destination buffer. + * @param[in] src source buffer. + * @param[in] count number of bytes to transfer. + */ + virtual void transfer(void* dest, const void* src, size_t count) + { + if (count == 0 || dest == NULL || src == NULL) return; + } + + /** + * @override{SPI} + * Read given number of bytes from device and store in buffer. + * @param[in] buf buffer pointer. + * @param[in] count number of bytes. + */ + virtual void read(void* buf, size_t count) + { + if (count == 0 || buf == NULL) return; + } + + /** + * @override{SPI} + * Write given number of bytes from buffer to device. + * @param[in] buf buffer pointer. + * @param[in] count number of bytes. + */ + virtual void write(const void* buf, size_t count) + { + if (count == 0 || buf == NULL) return; + } + +protected: +}; +}; +#endif diff --git a/src/Hardware/SPI.h b/src/Hardware/SPI.h index c735216..ce8bc77 100644 --- a/src/Hardware/SPI.h +++ b/src/Hardware/SPI.h @@ -1,6 +1,6 @@ /** * @file Hardware/SPI.h - * @version 1.1 + * @version 1.0 * * @section License * Copyright (C) 2017, Mikael Patel @@ -18,254 +18,9 @@ #ifndef HARDWARE_SPI_H #define HARDWARE_SPI_H - -#include "GPIO.h" -#include "SPI.h" - -/** - * Hardware Serial Perpheral Interface (SPI) class. - */ -namespace Hardware { - -#if defined(SPDR) -class SPI : public ::SPI { -public: - /** - * Serial Perpheral Interface (SPI) constructor. - */ - SPI() : - m_busy(false) - { - m_ss.output(); - m_sck.output(); - m_mosi.output(); - m_miso.input(); - } - - /** - * @override{SPI} - * Acquire bus access. Yield until bus is released. - * @param[in] mode of access. - * @param[in] bitorder of serial data. - * @param[in] scale clock frequency. - */ - virtual void acquire(uint8_t mode, uint8_t bitorder, uint8_t scale) - { - // Wait for bus manager to be released - while (m_busy) yield(); - m_busy = true; - - // Calculate clock setting for given scale - uint8_t spr = 0; - scale >>= 2; - while (scale != 0) { - spr++; - scale >>= 1; - } - if (scale > 7) scale = 7; - - // Set control registers: mode, bitorder and clock - SPCR = _BV(SPE) - | _BV(MSTR) - | (bitorder == LSBFIRST ? _BV(DORD) : 0) - | ((mode & 0x03) << CPHA) - | ((spr >> 1) & 3); - SPSR = ((spr & 0x01) == 0); - } - - /** - * @override{SPI} - * Release bus access. - */ - virtual void release() - { - m_busy = false; - } - - /** - * @override{SPI} - * Send given byte to device. Returns byte received from device. - * @param[in] value to send to device. - * @return received value. - */ - virtual uint8_t transfer(uint8_t value) - __attribute__((always_inline)) - { - SPDR = value; - __asm__ __volatile__("nop"); - loop_until_bit_is_set(SPSR, SPIF); - return (SPDR); - } - - /** - * @override{SPI} - * Transfer given number of bytes from source buffer to - * device. Store received bytes in destination buffer. - * @param[in] dest destination buffer. - * @param[in] src source buffer. - * @param[in] count number of bytes to transfer. - */ - virtual void transfer(void* dest, const void* src, size_t count) - { - if (count == 0 || dest == NULL || src == NULL) return; - const uint8_t* sp = (const uint8_t*) src; - uint8_t* dp = (uint8_t*) dest; - uint8_t value = *sp++; - SPDR = value; - while (--count) { - value = *sp++; - loop_until_bit_is_set(SPSR, SPIF); - SPDR = value; - *dp++ = SPDR; - } - loop_until_bit_is_set(SPSR, SPIF); - *dp = SPDR; - } - - /** - * @override{SPI} - * Read given number of bytes from device and store in buffer. - * @param[in] buf buffer pointer. - * @param[in] count number of bytes. - */ - virtual void read(void* buf, size_t count) - { - if (count == 0 || buf == NULL) return; - uint8_t* bp = (uint8_t*) buf; - SPDR = 0; - while (--count) { - __asm__ __volatile__("nop"); - loop_until_bit_is_set(SPSR, SPIF); - SPDR = 0; - *bp++ = SPDR; - } - loop_until_bit_is_set(SPSR, SPIF); - *bp = SPDR; - } - - /** - * @override{SPI} - * Write given number of bytes from buffer to device. - * @param[in] buf buffer pointer. - * @param[in] count number of bytes. - */ - virtual void write(const void* buf, size_t count) - { - if (count == 0 || buf == NULL) return; - uint8_t* bp = (uint8_t*) buf; - uint8_t value = *bp++; - SPDR = value; - while (--count) { - value = *bp++; - loop_until_bit_is_set(SPSR, SPIF); - SPDR = value; - } - loop_until_bit_is_set(SPSR, SPIF); - } - -protected: - /** Bus manager semaphore. */ - volatile bool m_busy; - - /** Slave select pin. */ - GPIO m_ss; - - /** Clock pin. */ - GPIO m_sck; - - /** Master Output Slave Input pin. */ - GPIO m_mosi; - - /** Master Input Slave Output pin. */ - GPIO m_miso; -}; - -#elif defined(USIDR) -class SPI : public ::SPI { -public: - /** - * Serial Perpheral Interface (SPI) constructor. - */ - SPI() : - m_busy(false) - { - m_ss.output(); - m_sck.output(); - m_mosi.output(); - m_miso.input(); - } - - /** - * @override{SPI} - * Acquire bus access. Yield until bus is released. - * @param[in] mode of access. - * @param[in] bitorder of serial data. - * @param[in] scale clock frequency. - */ - virtual void acquire(uint8_t mode, uint8_t bitorder, uint8_t scale) - { - // Wait for bus manager to be released - while (m_busy) yield(); - m_busy = true; - - // Not used: only MSBFIRST bitorder and max frequency - (void) bitorder; - (void) scale; - - // Set clock polarity - m_sck.write(mode & 0x02); - - // Cache clocking command - m_usicr = (_BV(USIWM0) | _BV(USICS1) | _BV(USICLK) | _BV(USITC)); - if (mode == 1 || mode == 2) m_usicr |= _BV(USICS0); - } - - /** - * @override{SPI} - * Release bus access. - */ - virtual void release() - { - m_busy = false; - } - - /** - * @override{SPI} - * Send given byte to device. Returns byte received from device. - * @param[in] value to send to device. - * @return received value. - */ - virtual uint8_t transfer(uint8_t value) - { - USIDR = value; - USISR = _BV(USIOIF); - register uint8_t cntl = m_usicr; - do { - USICR = cntl; - } while ((USISR & _BV(USIOIF)) == 0); - return (USIDR); - } - -protected: - /** Bus manager semaphore. */ - volatile bool m_busy; - - /** Slave select pin. */ - GPIO m_ss; - - /** Clock pin. */ - GPIO m_sck; - - /** Master Output Slave Input pin. */ - GPIO m_mosi; - - /** Master Input Slave Output pin. */ - GPIO m_miso; - - /** USI clock command. */ - uint8_t m_usicr; -}; +#if defined(AVR) +#include "Hardware/AVR/SPI.h" +#elif defined(SAM) +#include "Hardware/SAM/SPI.h" #endif - -}; #endif diff --git a/src/SPI.h b/src/SPI.h index 9125d4a..203b09f 100644 --- a/src/SPI.h +++ b/src/SPI.h @@ -1,6 +1,6 @@ /** * @file SPI.h - * @version 1.4 + * @version 1.5 * * @section License * Copyright (C) 2017, Mikael Patel @@ -26,26 +26,48 @@ */ class SPI { public: - /** Minimum clock frequency scale. */ - static const uint8_t MIN_CLOCK_SCALE = 2; - /** Maximum clock frequency. */ - static const uint32_t MAX_FREQ = F_CPU / MIN_CLOCK_SCALE; + static const uint32_t MAX_FREQ = F_CPU / 2; + + /** + * Construct and initiate bus manager. + */ + SPI() : m_busy(false) {} + + /** + * @override{SPI} + * Calculate clock prescale from device frequency. + * @param[in] frequency device access. + * @return prescale + */ + virtual uint8_t prescale(uint32_t frequency) + { + return (F_CPU / frequency); + } /** * @override{SPI} * Acquire bus access with given mode. * @param[in] mode of access. * @param[in] bitorder of serial data. - * @param[in] scale clock frequency. + * @param[in] prescale for device. */ - virtual void acquire(uint8_t mode, uint8_t bitorder, uint8_t scale) = 0; + virtual void acquire(uint8_t mode, uint8_t bitorder, uint8_t prescale) + { + (void) mode; + (void) bitorder; + (void) prescale; + lock(); + } /** * @override{SPI} * Release bus access. */ - virtual void release() = 0; + virtual void release() + { + unlock(); + } /** * @override{SPI} @@ -136,7 +158,8 @@ class SPI { * @param[in] ss initial slave select pin state (default HIGH). */ Device(SPI& spi, bool ss = HIGH) : - m_spi(spi) + m_spi(spi), + PRESCALE(spi.prescale(FREQ)) { m_ss.output(); m_ss = ss; @@ -148,7 +171,7 @@ class SPI { void acquire() __attribute__((always_inline)) { - m_spi.acquire(MODE, BITORDER, F_CPU / FREQ); + m_spi.acquire(MODE, BITORDER, PRESCALE); m_ss.toggle(); } @@ -214,6 +237,32 @@ class SPI { /** Slave select pin. */ GPIO m_ss; + + /** Bus clock prescale. */ + const uint8_t PRESCALE; }; + +protected: + /** Bus manager semaphore. */ + volatile bool m_busy; + + /** + * Lock bus manager. + */ + void lock() + __attribute__((always_inline)) + { + while (m_busy) yield(); + m_busy = true; + } + + /** + * Unlock bus manager. + */ + void unlock() + __attribute__((always_inline)) + { + m_busy = false; + } }; #endif diff --git a/src/Software/SPI.h b/src/Software/SPI.h index 6b7e646..bc03332 100644 --- a/src/Software/SPI.h +++ b/src/Software/SPI.h @@ -42,9 +42,9 @@ class SPI : public ::SPI { * Serial Perpheral Interface (SPI) constructor. Initiate pin * input/output mode. */ - SPI() + SPI() : + ::SPI() { - m_busy = false; m_sck.output(); m_mosi.output(); m_miso.input(); @@ -60,8 +60,7 @@ class SPI : public ::SPI { virtual void acquire(uint8_t mode, uint8_t bitorder, uint8_t scale) { (void) scale; - while (m_busy) yield(); - m_busy = true; + lock(); m_sck = mode & 2; m_cpha = mode & 1; m_bitorder = bitorder; @@ -73,8 +72,8 @@ class SPI : public ::SPI { */ virtual void release() { - m_busy = false; m_mosi = LOW; + unlock(); } /** @@ -110,6 +109,8 @@ class SPI : public ::SPI { return (value); } + using ::SPI::transfer; + protected: /** Clock pin. */ GPIO m_sck; @@ -120,11 +121,8 @@ class SPI : public ::SPI { /** Master Input Slave Output pin. */ GPIO m_miso; - /** Bus manager semaphore. */ - volatile bool m_busy; - /** Clock phase flag. */ - uint8_t m_cpha; + bool m_cpha; /** Serial data bitorder flag. */ uint8_t m_bitorder; pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy