Skip to content

Instantly share code, notes, and snippets.

@phoddie
Created October 20, 2020 23:59
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save phoddie/d5a7d99d76814d863305e28815c57ba9 to your computer and use it in GitHub Desktop.
Save phoddie/d5a7d99d76814d863305e28815c57ba9 to your computer and use it in GitHub Desktop.
SPI Module for ESP8266 and ESP32
{
"include": [
"$(MODULES)/pins/spi/manifest.json"
],
"modules": {
"pins/spi": "$(MODDABLEPROJECTS)/modules/pins/spi/spi"
},
"preload": "pins/spi",
"platforms": {
"esp": {
"modules": {
"commodetto/Bitmap": "$(COMMODETTO)/commodettoBitmap"
}
}
}
}
/*
* Copyright (c) 2019 Moddable Tech, Inc.
*
* This file is part of the Moddable SDK Runtime.
*
* The Moddable SDK Runtime 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 3 of the License, or
* (at your option) any later version.
*
* The Moddable SDK Runtime 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 the Moddable SDK Runtime. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "xsPlatform.h"
#include "xsmc.h"
#include "modSPI.h"
#include "modGPIO.h"
#include "mc.xs.h"
#include "mc.defines.h"
#ifndef MODDEF_SPI_PORT
#if ESP32
#define MODDEF_SPI_PORT HSPI_HOST
#else
#define MODDEF_SPI_PORT "HSPI"
#endif
#endif
#define DEFAULT_CS_PORT NULL
struct xsSPIRecord {
uint8_t buffer[100];
modSPIConfigurationRecord spiConfig;
modGPIOConfigurationRecord csPin;
uint8_t activeValue;
};
typedef struct xsSPIRecord xsSPIRecord;
typedef struct xsSPIRecord *xsSPI;
void spi_chipSelect(uint8_t active, modSPIConfiguration config);
void xs_spi(xsMachine *the)
{
xsmcVars(2);
int argc = xsmcArgc;
xsSPI spi;
int csPin, hz;
char* csPort = DEFAULT_CS_PORT;
uint8_t mode = 0;
spi = c_calloc(1, sizeof(xsSPIRecord));
xsmcSetHostData(xsThis, spi);
spi->activeValue = 0;
if (xsmcHas(xsArg(0), xsID_cs)) {
xsmcGet(xsVar(0), xsArg(0), xsID_cs);
if (xsmcHas(xsVar(0), xsID_pin)) {
xsmcGet(xsVar(1), xsVar(0), xsID_pin);
csPin = xsmcToInteger(xsVar(1));
}else{
xsUnknownError("pin missing from cs object in dictionary");
}
if (xsmcHas(xsVar(0), xsID_port)) {
xsmcGet(xsVar(1), xsVar(0), xsID_port);
csPort = xsmcToString(xsVar(1));
}
if (xsmcHas(xsVar(0), xsID_active)) {
xsmcGet(xsVar(0), xsArg(0), xsID_active);
if (0 == c_strcmp(xsmcToString(xsVar(0)), "high")){
spi->activeValue = 1;
}else if (0 == c_strcmp(xsmcToString(xsVar(0)), "low")){
spi->activeValue = 0;
}else{
xsUnknownError("invalid cs.active parameter in dictionary");
}
}
}else{
xsUnknownError("cs missing from dictionary");
}
if (xsmcHas(xsArg(0), xsID_hz)) {
xsmcGet(xsVar(0), xsArg(0), xsID_hz);
hz = xsmcToInteger(xsVar(0));
}else{
xsUnknownError("hz missing from dictionary");
}
modSPIConfig(spi->spiConfig, hz, MODDEF_SPI_PORT, csPort, -1, spi_chipSelect);
modGPIOInit(&spi->csPin, csPort, csPin, kModGPIOOutput);
modGPIOWrite(&spi->csPin, !(spi->activeValue));
modSPIInit(&spi->spiConfig);
}
void xs_spi_TxRx(xsMachine *the)
{
xsSPI spi;
int bufferByteLength, count;
uint8_t *data;
int argc = xsmcArgc;
// unsigned char buffer[64];
spi = xsmcGetHostData(xsThis);
xsResult = xsArg(0);
bufferByteLength = xsGetArrayBufferLength(xsResult);
data = (uint8_t*)xsmcToArrayBuffer(xsResult);
if (argc >= 2){
count = xsmcToInteger(xsArg(1));
if (count > bufferByteLength) xsUnknownError("Asked to transfer more bytes than are allocated in the ArrayBuffer");
}else{
count = bufferByteLength;
}
if (argc >= 3){
int offset = xsmcToInteger(xsArg(2));
if (offset + count > bufferByteLength) xsUnknownError("offset + count overruns end of the ArrayBuffer");
data += offset;
}
if (3 & (int)data) {
xsUnknownError("SPI offset must be long aligned.");
return;
}
if (count > 64) xsUnknownError("SPI.transfer is limited to 64 byte buffers");
//c_memmove(buffer, data, bufferByteLength);
modSPITxRx(&spi->spiConfig, data, count);
// c_memmove(data, buffer, bufferByteLength);
}
void xs_spi_Tx(xsMachine *the)
{
xsSPI spi;
uint8_t *data;
int argc = xsmcArgc;
int bufferByteLength, count;
uint8_t swap16 = false;
spi = xsmcGetHostData(xsThis);
if (xsmcIsInstanceOf(xsArg(0), xsArrayBufferPrototype)) {
data = (uint8_t*)xsmcToArrayBuffer(xsArg(0));
bufferByteLength = xsGetArrayBufferLength(xsArg(0));
} else {
data = xsmcGetHostData(xsArg(0));
xsmcVars(1);
xsmcGet(xsVar(0), xsArg(0), xsID_byteLength);
bufferByteLength = xsmcToInteger(xsVar(0));
}
if (argc >= 2){
count = xsmcToInteger(xsArg(1));
if (count > bufferByteLength) xsUnknownError("Asked to send more bytes than are allocated in the ArrayBuffer");
}else{
count = bufferByteLength;
}
if (argc >= 3){
int offset = xsmcToInteger(xsArg(2));
if (offset + count > bufferByteLength) xsUnknownError("offset + count overruns end of the ArrayBuffer");
data += offset;
}
if (argc >= 4){
swap16 = xsmcToBoolean(xsArg(3));
}
if (3 & (int)data) {
xsUnknownError("SPI offset must be long aligned");
return;
}
if (swap16){
modSPITxSwap16(&spi->spiConfig, (void *)data, count);
}else{
modSPITx(&spi->spiConfig, (void *)data, count);
}
}
void xs_spi_destructor(void *data)
{
xsSPI spi = data;
if (spi){
modGPIOUninit(&spi->csPin);
modSPIUninit(&spi->spiConfig);
c_free(data);
}
}
void xs_spi_deactivateChipSelect(xsMachine *the)
{
modSPIActivateConfiguration(NULL);
}
void xs_spi_flush(xsMachine *the)
{
modSPIFlush();
}
void spi_chipSelect(uint8_t active, modSPIConfiguration config)
{
xsSPI spi = (xsSPI)(((char *)config) - offsetof(xsSPIRecord, spiConfig));
modGPIOWrite(&spi->csPin, active ? (spi->activeValue) : !(spi->activeValue));
}
/*
* Copyright (c) 2019 Moddable Tech, Inc.
*
* This file is part of the Moddable SDK Runtime.
*
* The Moddable SDK Runtime 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 3 of the License, or
* (at your option) any later version.
*
* The Moddable SDK Runtime 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 the Moddable SDK Runtime. If not, see <http://www.gnu.org/licenses/>.
*
*/
export class SPI @ "xs_spi_destructor" {
constructor(dictionary) @ "xs_spi";
transfer(buffer, count, offset) @ "xs_spi_TxRx";
send(buffer, count, offset, swapByteOrder) @ "xs_spi_Tx";
deactivate() @ "xs_spi_deactivateChipSelect";
flush() @ "xs_spi_flush";
}
Object.freeze(SPI.prototype);
export default SPI;

class SPI

The SPI class provides access to the SPI bus connected to Clock, MISO, and MOSI pins along with a designated Chip Select pin.

import SPI from "pins/spi";

constructor(dictionary)

The SPI constructor takes a dictionary which contains at least the frequency of the SPI device in Hertz (hz) and a sub-object (cs) containing the chip select pin number (pin).

let device = new SPI({ hz:1000000, cs:{pin: 0} });

The constructor dictionary has three optional properties. The base dictionary object can contain a port property which sets the SPI port (defaults to "HSPI" on the ESP8266 and to HSPI_HOST on the ESP32). The cs sub-object can contain port for the chip select GPIO port (defaults to NULL), and activeHigh to specify whether the chip select pin should be "high" or "low" when the SPI device is selected (defaults to "low").

let device = new SPI({ hz: 1000000, port: "HSPI", cs:{pin: 0, port: "PORT1", active: "high"} });

transfer(buffer [, count, offset])

The transfer function simultaneously writes and reads bytes to/from the target device. The buffer parameter is required and must be an ArrayBuffer. The provided buffer will be modified in place with the results of the transfer operation and returned as the result. This operation is limited to 64 bytes per transfer.

By default, this function transfers the entire length of the provided buffer. An optional count parameter specifies that only count bytes of the buffer should be included in the transaction. If the bytes to be transferred do not come at the beginning of the buffer, the optional offset parameter can be used to specify where in the buffer to begin. Offsets must be long-aligned (i.e. offset % 4 == 0).

let data = new Uint8Array(10);
device.transfer(data.buffer, 4, 2);

send(buffer [, count, offset, swapByteOrder])

The send function writes bytes to the target device while discarding any data written back on to the SPI bus by the target device. The buffer parameter is required and must be an ArrayBuffer.

By default, this function transfers the entire length of the provided buffer. An optional count parameter specifies that only count bytes of the buffer should be sent. If the bytes to be sent do not come at the beginning of the buffer, the optional offset parameter can be used to specify where in the buffer to begin. Offsets must be long-aligned (i.e. offset % 4 == 0). The optional swapByteOrder parameter will cause pairs of bytes to be swapped when sent (that is, the endianness of each two-byte word will be changed).

let data = new Uint8Array(10);
device.transfer(data.buffer, 4, 2, true);

flush()

The flush function synchronously flushes the SPI bus.

device.flush();

deactivate()

The deactivate function causes the SPI device's chip select pin to be deactivated. This is useful for devices that require a chip select deactivation between individual transactions.

device.deactivate();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment