From 4217e49b548bf613eb1ee042cc38797f67009af2 Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Wed, 6 Jul 2016 05:58:45 +0300 Subject: [PATCH] Initial SPI Slave implementation and examples (#2234) * Initial SPI Slave implementation and examples * Update style and info --- .../SPISlave_Master/SPISlave_Master.ino | 118 +++++++++++++++ .../SPISlave_SafeMaster.ino | 124 +++++++++++++++ .../examples/SPISlave_Test/SPISlave_Test.ino | 72 +++++++++ libraries/SPISlave/keywords.txt | 25 +++ libraries/SPISlave/library.properties | 9 ++ libraries/SPISlave/src/SPISlave.cpp | 103 +++++++++++++ libraries/SPISlave/src/SPISlave.h | 69 +++++++++ libraries/SPISlave/src/hspi_slave.c | 143 ++++++++++++++++++ libraries/SPISlave/src/hspi_slave.h | 41 +++++ 9 files changed, 704 insertions(+) create mode 100644 libraries/SPISlave/examples/SPISlave_Master/SPISlave_Master.ino create mode 100644 libraries/SPISlave/examples/SPISlave_SafeMaster/SPISlave_SafeMaster.ino create mode 100644 libraries/SPISlave/examples/SPISlave_Test/SPISlave_Test.ino create mode 100644 libraries/SPISlave/keywords.txt create mode 100644 libraries/SPISlave/library.properties create mode 100644 libraries/SPISlave/src/SPISlave.cpp create mode 100644 libraries/SPISlave/src/SPISlave.h create mode 100644 libraries/SPISlave/src/hspi_slave.c create mode 100644 libraries/SPISlave/src/hspi_slave.h diff --git a/libraries/SPISlave/examples/SPISlave_Master/SPISlave_Master.ino b/libraries/SPISlave/examples/SPISlave_Master/SPISlave_Master.ino new file mode 100644 index 000000000..107467cc4 --- /dev/null +++ b/libraries/SPISlave/examples/SPISlave_Master/SPISlave_Master.ino @@ -0,0 +1,118 @@ +/* + SPI Master Demo Sketch + Connect the SPI Master device to the following pins on the esp8266: + + GPIO NodeMCU Name | Uno + =================================== + 15 D8 SS | D10 + 13 D7 MOSI | D11 + 12 D6 MISO | D12 + 14 D5 SCK | D13 + + Note: If the ESP is booting at a moment when the SPI Master has the Select line HIGH (deselected) + the ESP8266 WILL FAIL to boot! + See SPISlave_SafeMaster example for possible workaround + +*/ +#include + +class ESPMaster +{ +private: + uint8_t _ss_pin; + +public: + ESPMaster(uint8_t pin):_ss_pin(pin) {} + void begin() + { + pinMode(_ss_pin, OUTPUT); + digitalWrite(_ss_pin, HIGH); + } + + uint32_t readStatus() + { + digitalWrite(_ss_pin, LOW); + SPI.transfer(0x04); + uint32_t status = (SPI.transfer(0) | ((uint32_t)(SPI.transfer(0)) << 8) | ((uint32_t)(SPI.transfer(0)) << 16) | ((uint32_t)(SPI.transfer(0)) << 24)); + digitalWrite(_ss_pin, HIGH); + return status; + } + + void writeStatus(uint32_t status) + { + digitalWrite(_ss_pin, LOW); + SPI.transfer(0x01); + SPI.transfer(status & 0xFF); + SPI.transfer((status >> 8) & 0xFF); + SPI.transfer((status >> 16) & 0xFF); + SPI.transfer((status >> 24) & 0xFF); + digitalWrite(_ss_pin, HIGH); + } + + void readData(uint8_t * data) + { + digitalWrite(_ss_pin, LOW); + SPI.transfer(0x03); + SPI.transfer(0x00); + for(uint8_t i=0; i<32; i++) { + data[i] = SPI.transfer(0); + } + digitalWrite(_ss_pin, HIGH); + } + + void writeData(uint8_t * data, size_t len) + { + uint8_t i=0; + digitalWrite(_ss_pin, LOW); + SPI.transfer(0x02); + SPI.transfer(0x00); + while(len-- && i < 32) { + SPI.transfer(data[i++]); + } + while(i++ < 32) { + SPI.transfer(0); + } + digitalWrite(_ss_pin, HIGH); + } + + String readData() + { + char data[33]; + data[32] = 0; + readData((uint8_t *)data); + return String(data); + } + + void writeData(const char * data) + { + writeData((uint8_t *)data, strlen(data)); + } +}; + +ESPMaster esp(SS); + +void send(const char * message) +{ + Serial.print("Master: "); + Serial.println(message); + esp.writeData(message); + delay(10); + Serial.print("Slave: "); + Serial.println(esp.readData()); + Serial.println(); +} + +void setup() +{ + Serial.begin(115200); + SPI.begin(); + esp.begin(); + delay(1000); + send("Hello Slave!"); +} + +void loop() +{ + delay(1000); + send("Are you alive?"); +} diff --git a/libraries/SPISlave/examples/SPISlave_SafeMaster/SPISlave_SafeMaster.ino b/libraries/SPISlave/examples/SPISlave_SafeMaster/SPISlave_SafeMaster.ino new file mode 100644 index 000000000..6d3e55cd7 --- /dev/null +++ b/libraries/SPISlave/examples/SPISlave_SafeMaster/SPISlave_SafeMaster.ino @@ -0,0 +1,124 @@ +/* + SPI Safe Master Demo Sketch + Connect the SPI Master device to the following pins on the esp8266: + + GPIO NodeMCU Name | Uno + =================================== + 15 D8 SS | D10 + 13 D7 MOSI | D11 + 12 D6 MISO | D12 + 14 D5 SCK | D13 + + Note: If the ESP is booting at a moment when the SPI Master has the Select line HIGH (deselected) + the ESP8266 WILL FAIL to boot! + This sketch tries to go around this issue by only pulsing the Slave Select line to reset the command + and keeping the line LOW all other time. + +*/ +#include + +class ESPSafeMaster +{ +private: + uint8_t _ss_pin; + void _pulseSS() + { + digitalWrite(_ss_pin, HIGH); + delayMicroseconds(5); + digitalWrite(_ss_pin, LOW); + } +public: + ESPSafeMaster(uint8_t pin):_ss_pin(pin) {} + void begin() + { + pinMode(_ss_pin, OUTPUT); + _pulseSS(); + } + + uint32_t readStatus() + { + _pulseSS(); + SPI.transfer(0x04); + uint32_t status = (SPI.transfer(0) | ((uint32_t)(SPI.transfer(0)) << 8) | ((uint32_t)(SPI.transfer(0)) << 16) | ((uint32_t)(SPI.transfer(0)) << 24)); + _pulseSS(); + return status; + } + + void writeStatus(uint32_t status) + { + _pulseSS(); + SPI.transfer(0x01); + SPI.transfer(status & 0xFF); + SPI.transfer((status >> 8) & 0xFF); + SPI.transfer((status >> 16) & 0xFF); + SPI.transfer((status >> 24) & 0xFF); + _pulseSS(); + } + + void readData(uint8_t * data) + { + _pulseSS(); + SPI.transfer(0x03); + SPI.transfer(0x00); + for(uint8_t i=0; i<32; i++) { + data[i] = SPI.transfer(0); + } + _pulseSS(); + } + + void writeData(uint8_t * data, size_t len) + { + uint8_t i=0; + _pulseSS(); + SPI.transfer(0x02); + SPI.transfer(0x00); + while(len-- && i < 32) { + SPI.transfer(data[i++]); + } + while(i++ < 32) { + SPI.transfer(0); + } + _pulseSS(); + } + + String readData() + { + char data[33]; + data[32] = 0; + readData((uint8_t *)data); + return String(data); + } + + void writeData(const char * data) + { + writeData((uint8_t *)data, strlen(data)); + } +}; + +ESPSafeMaster esp(SS); + +void send(const char * message) +{ + Serial.print("Master: "); + Serial.println(message); + esp.writeData(message); + delay(10); + Serial.print("Slave: "); + Serial.println(esp.readData()); + Serial.println(); +} + +void setup() +{ + Serial.begin(115200); + SPI.begin(); + esp.begin(); + delay(1000); + send("Hello Slave!"); +} + +void loop() +{ + delay(1000); + send("Are you alive?"); +} diff --git a/libraries/SPISlave/examples/SPISlave_Test/SPISlave_Test.ino b/libraries/SPISlave/examples/SPISlave_Test/SPISlave_Test.ino new file mode 100644 index 000000000..0f6a544bd --- /dev/null +++ b/libraries/SPISlave/examples/SPISlave_Test/SPISlave_Test.ino @@ -0,0 +1,72 @@ +/* + SPI Slave Demo Sketch + Connect the SPI Master device to the following pins on the esp8266: + + GPIO NodeMCU Name | Uno + =================================== + 15 D8 SS | D10 + 13 D7 MOSI | D11 + 12 D6 MISO | D12 + 14 D5 SCK | D13 + + Note: If the ESP is booting at a moment when the SPI Master has the Select line HIGH (deselected) + the ESP8266 WILL FAIL to boot! + See SPISlave_SafeMaster example for possible workaround + +*/ + +#include "SPISlave.h" + +void setup() +{ + Serial.begin(115200); + Serial.setDebugOutput(true); + + // data has been received from the master. Beware that len is always 32 + // and the buffer is autofilled with zeroes if data is less than 32 bytes long + // It's up to the user to implement protocol for handling data length + SPISlave.onData([](uint8_t * data, size_t len) { + String message = String((char *)data); + if(message.equals("Hello Slave!")) { + SPISlave.setData("Hello Master!"); + } else if(message.equals("Are you alive?")) { + char answer[33]; + sprintf(answer,"Alive for %u seconds!", millis() / 1000); + SPISlave.setData(answer); + } else { + SPISlave.setData("Say what?"); + } + Serial.printf("Question: %s\n", (char *)data); + }); + + // The master has read out outgoing data buffer + // that buffer can be set with SPISlave.setData + SPISlave.onDataSent([]() { + Serial.println("Answer Sent"); + }); + + // status has been received from the master. + // The status register is a special register that bot the slave and the master can write to and read from. + // Can be used to exchange small data or status information + SPISlave.onStatus([](uint32_t data) { + Serial.printf("Status: %u\n", data); + SPISlave.setStatus(millis()); //set next status + }); + + // The master has read the status register + SPISlave.onStatusSent([]() { + Serial.println("Status Sent"); + }); + + // Setup SPI Slave registers and pins + SPISlave.begin(); + + // Set the status register (if the master reads it, it will read this value) + SPISlave.setStatus(millis()); + + // Sets the data registers. Limited to 32 bytes at a time. + // SPISlave.setData(uint8_t * data, size_t len); is also available with the same limitation + SPISlave.setData("Ask me a question!"); +} + +void loop() {} diff --git a/libraries/SPISlave/keywords.txt b/libraries/SPISlave/keywords.txt new file mode 100644 index 000000000..dfa7974fb --- /dev/null +++ b/libraries/SPISlave/keywords.txt @@ -0,0 +1,25 @@ +####################################### +# Syntax Coloring Map SPI Slave +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +SPISlave KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +begin KEYWORD2 +setData KEYWORD2 +setStatus KEYWORD2 +onData KEYWORD2 +onDataSent KEYWORD2 +onStatus KEYWORD2 +onStatusSent KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### diff --git a/libraries/SPISlave/library.properties b/libraries/SPISlave/library.properties new file mode 100644 index 000000000..50d81f3fb --- /dev/null +++ b/libraries/SPISlave/library.properties @@ -0,0 +1,9 @@ +name=SPISlave +version=1.0 +author=Hristo Gochkov +maintainer=Hristo Gochkov +sentence=SPI Slave library for ESP8266 +paragraph= +category=Signal Input/Output +url= +architectures=esp8266 diff --git a/libraries/SPISlave/src/SPISlave.cpp b/libraries/SPISlave/src/SPISlave.cpp new file mode 100644 index 000000000..054355783 --- /dev/null +++ b/libraries/SPISlave/src/SPISlave.cpp @@ -0,0 +1,103 @@ +/* + SPISlave library for esp8266 + + Copyright (c) 2015 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + 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. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "SPISlave.h" +extern "C" { +#include "hspi_slave.h" +} + +void SPISlaveClass::_data_rx(uint8_t * data, uint8_t len) +{ + if(_data_cb) { + _data_cb(data, len); + } +} +void SPISlaveClass::_status_rx(uint32_t data) +{ + if(_status_cb) { + _status_cb(data); + } +} +void SPISlaveClass::_data_tx(void) +{ + if(_data_sent_cb) { + _data_sent_cb(); + } +} +void SPISlaveClass::_status_tx(void) +{ + if(_status_sent_cb) { + _status_sent_cb(); + } +} +void SPISlaveClass::_s_data_rx(void *arg, uint8_t * data, uint8_t len) +{ + reinterpret_cast(arg)->_data_rx(data,len); +} +void SPISlaveClass::_s_status_rx(void *arg, uint32_t data) +{ + reinterpret_cast(arg)->_status_rx(data); +} +void SPISlaveClass::_s_data_tx(void *arg) +{ + reinterpret_cast(arg)->_data_tx(); +} +void SPISlaveClass::_s_status_tx(void *arg) +{ + reinterpret_cast(arg)->_status_tx(); +} + +void SPISlaveClass::begin() +{ + hspi_slave_onData(&_s_data_rx); + hspi_slave_onDataSent(&_s_data_tx); + hspi_slave_onStatus(&_s_status_rx); + hspi_slave_onStatusSent(&_s_status_tx); + hspi_slave_begin(4, this); +} +void SPISlaveClass::setData(uint8_t * data, size_t len) +{ + if(len > 32) { + len = 32; + } + hspi_slave_setData(data, len); +} +void SPISlaveClass::setStatus(uint32_t status) +{ + hspi_slave_setStatus(status); +} +void SPISlaveClass::onData(SpiSlaveDataHandler cb) +{ + _data_cb = cb; +} +void SPISlaveClass::onDataSent(SpiSlaveSentHandler cb) +{ + _data_sent_cb = cb; +} +void SPISlaveClass::onStatus(SpiSlaveStatusHandler cb) +{ + _status_cb = cb; +} +void SPISlaveClass::onStatusSent(SpiSlaveSentHandler cb) +{ + _status_sent_cb = cb; +} + +SPISlaveClass SPISlave; diff --git a/libraries/SPISlave/src/SPISlave.h b/libraries/SPISlave/src/SPISlave.h new file mode 100644 index 000000000..5f9a566eb --- /dev/null +++ b/libraries/SPISlave/src/SPISlave.h @@ -0,0 +1,69 @@ +/* + SPISlave library for esp8266 + + Copyright (c) 2015 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + 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. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#ifndef _SPISLAVE_H_INCLUDED +#define _SPISLAVE_H_INCLUDED + +#include "Arduino.h" +#include + +typedef std::function SpiSlaveDataHandler; +typedef std::function SpiSlaveStatusHandler; +typedef std::function SpiSlaveSentHandler; + +class SPISlaveClass +{ +protected: + SpiSlaveDataHandler _data_cb; + SpiSlaveStatusHandler _status_cb; + SpiSlaveSentHandler _data_sent_cb; + SpiSlaveSentHandler _status_sent_cb; + void _data_rx(uint8_t * data, uint8_t len); + void _status_rx(uint32_t data); + void _data_tx(void); + void _status_tx(void); + static void _s_data_rx(void *arg, uint8_t * data, uint8_t len); + static void _s_status_rx(void *arg, uint32_t data); + static void _s_data_tx(void *arg); + static void _s_status_tx(void *arg); +public: + SPISlaveClass() + : _data_cb(NULL) + , _status_cb(NULL) + , _data_sent_cb(NULL) + , _status_sent_cb(NULL) + {} + ~SPISlaveClass() {} + void begin(); + void setData(uint8_t * data, size_t len); + void setData(const char * data) + { + setData((uint8_t *)data, strlen(data)); + } + void setStatus(uint32_t status); + void onData(SpiSlaveDataHandler cb); + void onDataSent(SpiSlaveSentHandler cb); + void onStatus(SpiSlaveStatusHandler cb); + void onStatusSent(SpiSlaveSentHandler cb); +}; + +extern SPISlaveClass SPISlave; + +#endif diff --git a/libraries/SPISlave/src/hspi_slave.c b/libraries/SPISlave/src/hspi_slave.c new file mode 100644 index 000000000..b3e0d6b5d --- /dev/null +++ b/libraries/SPISlave/src/hspi_slave.c @@ -0,0 +1,143 @@ +/* + SPISlave library for esp8266 + + Copyright (c) 2015 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + 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. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "hspi_slave.h" +#include "esp8266_peri.h" +#include "ets_sys.h" + +static void (*_hspi_slave_rx_data_cb)(void * arg, uint8_t * data, uint8_t len) = NULL; +static void (*_hspi_slave_tx_data_cb)(void * arg) = NULL; +static void (*_hspi_slave_rx_status_cb)(void * arg, uint32_t data) = NULL; +static void (*_hspi_slave_tx_status_cb)(void * arg) = NULL; +static uint8_t _hspi_slave_buffer[33]; + +void ICACHE_RAM_ATTR _hspi_slave_isr_handler(void *arg) +{ + uint32_t status; + uint32_t istatus; + + istatus = SPIIR; + + if(istatus & (1 << SPII1)) { //SPI1 ISR + status = SPI1S; + SPI1S &= ~(0x3E0);//disable interrupts + SPI1S |= SPISSRES;//reset + SPI1S &= ~(0x1F);//clear interrupts + SPI1S |= (0x3E0);//enable interrupts + + if((status & SPISRBIS) != 0 && (_hspi_slave_tx_data_cb)) { + _hspi_slave_tx_data_cb(arg); + } + if((status & SPISRSIS) != 0 && (_hspi_slave_tx_status_cb)) { + _hspi_slave_tx_status_cb(arg); + } + if((status & SPISWSIS) != 0 && (_hspi_slave_rx_status_cb)) { + uint32_t s = SPI1WS; + _hspi_slave_rx_status_cb(arg, s); + } + if((status & SPISWBIS) != 0 && (_hspi_slave_rx_data_cb)) { + uint8_t i; + uint32_t data; + uint8_t buffer[33]; + _hspi_slave_buffer[32] = 0; + for(i=0; i<8; i++) { + data=SPI1W(i); + _hspi_slave_buffer[i<<2] = data & 0xff; + _hspi_slave_buffer[(i<<2)+1] = (data >> 8) & 0xff; + _hspi_slave_buffer[(i<<2)+2] = (data >> 16) & 0xff; + _hspi_slave_buffer[(i<<2)+3] = (data >> 24) & 0xff; + } + _hspi_slave_rx_data_cb(arg, &_hspi_slave_buffer[0], 32); + } + } else if(istatus & (1 << SPII0)) { //SPI0 ISR + SPI0S &= ~(0x3ff);//clear SPI ISR + } else if(istatus & (1 << SPII2)) {} //I2S ISR +} + +void hspi_slave_begin(uint8_t status_len, void * arg) +{ + status_len &= 7; + if(status_len > 4) { + status_len == 4; //max 32 bits + } + if(status_len == 0) { + status_len == 1; //min 8 bits + } + + pinMode(SS, SPECIAL); + pinMode(SCK, SPECIAL); + pinMode(MISO, SPECIAL); + pinMode(MOSI, SPECIAL); + + SPI1S = SPISE | SPISBE | 0x3E0; + SPI1U = SPIUMISOH | SPIUCOMMAND | SPIUSSE; + SPI1CLK = 0; + SPI1U2 = (7 << SPILCOMMAND); + SPI1S1 = (((status_len * 8) - 1) << SPIS1LSTA) | (0xff << SPIS1LBUF) | (7 << SPIS1LWBA) | (7 << SPIS1LRBA) | SPIS1RSTA; + SPI1P = (1 << 19); + SPI1CMD = SPIBUSY; + + ETS_SPI_INTR_ATTACH(_hspi_slave_isr_handler,arg); + ETS_SPI_INTR_ENABLE(); +} + +void hspi_slave_setStatus(uint32_t status) +{ + SPI1WS = status; +} + +void hspi_slave_setData(uint8_t *data, uint8_t len) +{ + uint8_t i; + uint32_t out = 0; + uint8_t bi = 0; + uint8_t wi = 8; + + for(i=0; i<32; i++) { + out |= (i