1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-04-19 23:22:16 +03:00

Initial SPI Slave implementation and examples (#2234)

* Initial SPI Slave implementation and examples

* Update style and info
This commit is contained in:
Me No Dev 2016-07-06 05:58:45 +03:00 committed by Ivan Grokhotkov
parent 2364ad4dd0
commit 4217e49b54
9 changed files with 704 additions and 0 deletions

View File

@ -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 <SPI.h>
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?");
}

View File

@ -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 <SPI.h>
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?");
}

View File

@ -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() {}

View File

@ -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)
#######################################

View File

@ -0,0 +1,9 @@
name=SPISlave
version=1.0
author=Hristo Gochkov
maintainer=Hristo Gochkov <hristo@espressif.com>
sentence=SPI Slave library for ESP8266
paragraph=
category=Signal Input/Output
url=
architectures=esp8266

View File

@ -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<SPISlaveClass*>(arg)->_data_rx(data,len);
}
void SPISlaveClass::_s_status_rx(void *arg, uint32_t data)
{
reinterpret_cast<SPISlaveClass*>(arg)->_status_rx(data);
}
void SPISlaveClass::_s_data_tx(void *arg)
{
reinterpret_cast<SPISlaveClass*>(arg)->_data_tx();
}
void SPISlaveClass::_s_status_tx(void *arg)
{
reinterpret_cast<SPISlaveClass*>(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;

View File

@ -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 <functional>
typedef std::function<void(uint8_t *data, size_t len)> SpiSlaveDataHandler;
typedef std::function<void(uint32_t status)> SpiSlaveStatusHandler;
typedef std::function<void(void)> 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

View File

@ -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<len)?(data[i] << (bi * 8)):0;
bi++;
bi &= 3;
if(!bi) {
SPI1W(wi) = out;
out = 0;
wi++;
}
}
}
void hspi_slave_onData(void (*rxd_cb)(void *, uint8_t *, uint8_t))
{
_hspi_slave_rx_data_cb = rxd_cb;
}
void hspi_slave_onDataSent(void (*txd_cb)(void *))
{
_hspi_slave_tx_data_cb = txd_cb;
}
void hspi_slave_onStatus(void (*rxs_cb)(void *, uint32_t))
{
_hspi_slave_rx_status_cb = rxs_cb;
}
void hspi_slave_onStatusSent(void (*txs_cb)(void *))
{
_hspi_slave_tx_status_cb = txs_cb;
}

View File

@ -0,0 +1,41 @@
/*
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 _HSPISLAVE_H_INCLUDED
#define _HSPISLAVE_H_INCLUDED
#include "Arduino.h"
//Start SPI SLave
void hspi_slave_begin(uint8_t status_len, void * arg);
//set the status register so the master can read it
void hspi_slave_setStatus(uint32_t status);
//set the data registers (max 32 bytes at a time)
void hspi_slave_setData(uint8_t *data, uint8_t len);
//set the callbacks
void hspi_slave_onData(void (*rxd_cb)(void *, uint8_t *, uint8_t));
void hspi_slave_onDataSent(void (*txd_cb)(void *));
void hspi_slave_onStatus(void (*rxs_cb)(void *, uint32_t));
void hspi_slave_onStatusSent(void (*txs_cb)(void *));
#endif