Add spi master.
This commit is contained in:
parent
9e7eb48fef
commit
819284530e
|
@ -135,6 +135,12 @@ config LUA_MODULE_SIGMA_DELTA
|
||||||
Includes the sigma_delta module. This module provides access to the
|
Includes the sigma_delta module. This module provides access to the
|
||||||
sigma-delta hardware.
|
sigma-delta hardware.
|
||||||
|
|
||||||
|
config LUA_MODULE_SPI
|
||||||
|
bool "SPI module"
|
||||||
|
default "n"
|
||||||
|
help
|
||||||
|
Includes the spi module.
|
||||||
|
|
||||||
config LUA_MODULE_STRUCT
|
config LUA_MODULE_STRUCT
|
||||||
bool "Struct module"
|
bool "Struct module"
|
||||||
default "n"
|
default "n"
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
// Module for interfacing with the SPI interface
|
||||||
|
|
||||||
|
#include "module.h"
|
||||||
|
#include "lauxlib.h"
|
||||||
|
|
||||||
|
#include "spi_common.h"
|
||||||
|
#include "driver/spi_common.h"
|
||||||
|
|
||||||
|
|
||||||
|
static const LUA_REG_TYPE lspi_map[] = {
|
||||||
|
{ LSTRKEY( "master" ), LFUNCVAL( lspi_master ) },
|
||||||
|
// { LSTRKEY( "slave" ), LFUNCVAL( lspi_slave ) },
|
||||||
|
{ LSTRKEY( "SPI" ), LNUMVAL( SPI_HOST ) },
|
||||||
|
{ LSTRKEY( "HSPI" ), LNUMVAL( HSPI_HOST ) },
|
||||||
|
{ LSTRKEY( "VSPI" ), LNUMVAL( VSPI_HOST ) },
|
||||||
|
{LNILKEY, LNILVAL}
|
||||||
|
};
|
||||||
|
|
||||||
|
int luaopen_spi( lua_State *L ) {
|
||||||
|
luaopen_spi_master( L );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
NODEMCU_MODULE(SPI, "spi", lspi_map, luaopen_spi);
|
|
@ -0,0 +1,14 @@
|
||||||
|
|
||||||
|
#ifndef _NODEMCU_SPI_COMMON_H_
|
||||||
|
#define _NODEMCU_SPI_COMMON_H_
|
||||||
|
|
||||||
|
#include "lauxlib.h"
|
||||||
|
|
||||||
|
// ***************************************************************************
|
||||||
|
// SPI master
|
||||||
|
//
|
||||||
|
int luaopen_spi_master( lua_State *L );
|
||||||
|
int lspi_master( lua_State *L );
|
||||||
|
|
||||||
|
|
||||||
|
#endif /*_NODEMCU_SPI_COMMON_H_*/
|
|
@ -0,0 +1,337 @@
|
||||||
|
// Module for interfacing with the SPI master hardware
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include "module.h"
|
||||||
|
#include "lauxlib.h"
|
||||||
|
#include "lextra.h"
|
||||||
|
|
||||||
|
#include "driver/spi_master.h"
|
||||||
|
#include "esp_heap_alloc_caps.h"
|
||||||
|
|
||||||
|
#include "esp_log.h"
|
||||||
|
|
||||||
|
#define SPI_MASTER_TAG "spi.master"
|
||||||
|
|
||||||
|
#define UD_HOST_STR "spi.master"
|
||||||
|
#define UD_DEVICE_STR "spi.device"
|
||||||
|
|
||||||
|
|
||||||
|
static int no_err( esp_err_t err )
|
||||||
|
{
|
||||||
|
switch (err)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
ESP_LOGI(SPI_MASTER_TAG, "unknown error");
|
||||||
|
return 0;
|
||||||
|
case ESP_ERR_INVALID_ARG:
|
||||||
|
ESP_LOGI(SPI_MASTER_TAG, "invalid argument");
|
||||||
|
return 0;
|
||||||
|
case ESP_ERR_INVALID_STATE:
|
||||||
|
ESP_LOGI(SPI_MASTER_TAG, "internal logic error");
|
||||||
|
return 0;
|
||||||
|
case ESP_ERR_NO_MEM:
|
||||||
|
ESP_LOGI(SPI_MASTER_TAG, "no memory");
|
||||||
|
return 0;
|
||||||
|
case ESP_OK:
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ****************************************************************************
|
||||||
|
// Device related functions
|
||||||
|
//
|
||||||
|
typedef struct {
|
||||||
|
spi_device_handle_t device;
|
||||||
|
int host_ref, host;
|
||||||
|
} lspi_device_t;
|
||||||
|
|
||||||
|
#define GET_UD_DEVICE \
|
||||||
|
lspi_device_t *ud = (lspi_device_t *)luaL_checkudata( L, 1, UD_DEVICE_STR );
|
||||||
|
|
||||||
|
#define CONFIG_TRANS_FROM_FIELD(field) \
|
||||||
|
lua_getfield( L, stack, #field ); \
|
||||||
|
trans.field = luaL_optint( L, -1, 0 );
|
||||||
|
|
||||||
|
static int lspi_device_free( lua_State *L )
|
||||||
|
{
|
||||||
|
GET_UD_DEVICE;
|
||||||
|
|
||||||
|
if (ud->device) {
|
||||||
|
spi_bus_remove_device( ud->device );
|
||||||
|
ud->device = NULL;
|
||||||
|
|
||||||
|
// unref host to unblock automatic gc from this object
|
||||||
|
luaL_unref( L, LUA_REGISTRYINDEX, ud->host_ref );
|
||||||
|
ud->host_ref = LUA_NOREF;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lua:
|
||||||
|
// recv = dev:transfer(trans_desc)
|
||||||
|
// recv = dev:transfer(data)
|
||||||
|
static int lspi_device_transfer( lua_State *L )
|
||||||
|
{
|
||||||
|
GET_UD_DEVICE;
|
||||||
|
int stack = 1;
|
||||||
|
|
||||||
|
luaL_argcheck( L, ud->device, stack, "no device" );
|
||||||
|
|
||||||
|
spi_transaction_t trans;
|
||||||
|
memset( &trans, 0, sizeof( trans ) );
|
||||||
|
size_t data_len, rx_len;
|
||||||
|
const char *data;
|
||||||
|
|
||||||
|
int type = lua_type( L, ++stack );
|
||||||
|
luaL_argcheck( L, type == LUA_TSTRING || type == LUA_TTABLE, stack, "string or table expected" );
|
||||||
|
|
||||||
|
if (type == LUA_TSTRING) {
|
||||||
|
|
||||||
|
data = luaL_checklstring( L, stack, &data_len );
|
||||||
|
rx_len = data_len;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
const char * const options[] = {"std", "dio", "qio"};
|
||||||
|
const uint32_t options_flags[] = {0, SPI_TRANS_MODE_DIO, SPI_TRANS_MODE_QIO};
|
||||||
|
|
||||||
|
CONFIG_TRANS_FROM_FIELD(command);
|
||||||
|
CONFIG_TRANS_FROM_FIELD(address);
|
||||||
|
//
|
||||||
|
lua_getfield( L, stack, "rxlen" );
|
||||||
|
rx_len = luaL_optint( L, -1, 0 );
|
||||||
|
//
|
||||||
|
lua_getfield( L, stack, "addr_mode" );
|
||||||
|
trans.flags |= luaL_optbool( L, -1, false ) ? SPI_TRANS_MODE_DIOQIO_ADDR : 0;
|
||||||
|
//
|
||||||
|
lua_getfield( L, stack, "mode" );
|
||||||
|
trans.flags |= options_flags[ luaL_checkoption( L, -1, options[0], options ) ];
|
||||||
|
//
|
||||||
|
lua_getfield( L, stack, "txdata" );
|
||||||
|
data = luaL_optlstring( L, -1, "", &data_len );
|
||||||
|
|
||||||
|
lua_settop( L, stack );
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *msg = NULL;
|
||||||
|
|
||||||
|
trans.length = data_len * 8;
|
||||||
|
if (data_len == 0) {
|
||||||
|
//no MOSI phase requested
|
||||||
|
trans.tx_buffer = NULL;
|
||||||
|
} else if (data_len <=4 ) {
|
||||||
|
// use local tx data buffer
|
||||||
|
trans.flags |= SPI_TRANS_USE_TXDATA;
|
||||||
|
memcpy( trans.tx_data, data, data_len );
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// use DMA'able buffer
|
||||||
|
if ((trans.tx_buffer = pvPortMallocCaps( data_len, MALLOC_CAP_DMA ))) {
|
||||||
|
memcpy( (void *)trans.tx_buffer, data, data_len );
|
||||||
|
} else {
|
||||||
|
msg = "no memory";
|
||||||
|
goto free_mem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trans.rxlength = rx_len * 8;
|
||||||
|
if (rx_len == 0) {
|
||||||
|
// no MISO phase requested
|
||||||
|
trans.rx_buffer = NULL;
|
||||||
|
|
||||||
|
} else if (rx_len <= 4) {
|
||||||
|
// use local rx data buffer
|
||||||
|
trans.flags |= SPI_TRANS_USE_RXDATA;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// use DMA'able buffer
|
||||||
|
if (!(trans.rx_buffer = pvPortMallocCaps( rx_len, MALLOC_CAP_DMA ))) {
|
||||||
|
msg = "no mem";
|
||||||
|
goto free_mem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// finally perform the transaction
|
||||||
|
if (no_err( spi_device_transmit( ud->device, &trans ) )) {
|
||||||
|
// evaluate receive data
|
||||||
|
if (trans.flags & SPI_TRANS_USE_RXDATA) {
|
||||||
|
lua_pushlstring( L, (const char *)&(trans.rx_data[0]), rx_len );
|
||||||
|
} else {
|
||||||
|
lua_pushlstring( L, trans.rx_buffer, rx_len );
|
||||||
|
}
|
||||||
|
|
||||||
|
} else
|
||||||
|
msg = "transfer failed";
|
||||||
|
|
||||||
|
free_mem:
|
||||||
|
if (!(trans.flags & SPI_TRANS_USE_TXDATA) && trans.tx_buffer)
|
||||||
|
free( (void *)trans.tx_buffer );
|
||||||
|
if (!(trans.flags & SPI_TRANS_USE_RXDATA) && trans.rx_buffer)
|
||||||
|
free( (void *)trans.rx_buffer );
|
||||||
|
|
||||||
|
if (msg)
|
||||||
|
return luaL_error( L, msg );
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const LUA_REG_TYPE lspi_device_map[] = {
|
||||||
|
{ LSTRKEY( "transfer" ), LFUNCVAL( lspi_device_transfer ) },
|
||||||
|
{ LSTRKEY( "remove" ), LFUNCVAL( lspi_device_free ) },
|
||||||
|
{ LSTRKEY( "__gc" ), LFUNCVAL( lspi_device_free ) },
|
||||||
|
{ LSTRKEY( "__index" ), LROVAL( lspi_device_map ) },
|
||||||
|
{LNILKEY, LNILVAL}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// ****************************************************************************
|
||||||
|
// Host related functions
|
||||||
|
//
|
||||||
|
typedef struct {
|
||||||
|
int host;
|
||||||
|
} lspi_host_t;
|
||||||
|
|
||||||
|
#define GET_UD_HOST \
|
||||||
|
lspi_host_t *ud = (lspi_host_t *)luaL_checkudata( L, 1, UD_HOST_STR );
|
||||||
|
//
|
||||||
|
#define CONFIG_BUS_PIN_FROM_FIELD(pin) \
|
||||||
|
lua_getfield( L, stack, #pin ); \
|
||||||
|
config.pin ## _io_num = luaL_optint( L, -1, -1 );
|
||||||
|
//
|
||||||
|
#define CONFIG_DEVICE_FROM_INT_FIELD(field) \
|
||||||
|
lua_getfield( L, stack, #field ); \
|
||||||
|
config.field = luaL_optint( L, -1, 0 );
|
||||||
|
#define CONFIG_DEVICE_FROM_BOOL_FIELD(field, mask) \
|
||||||
|
lua_getfield( L, stack, #field ); \
|
||||||
|
config.flags |= luaL_optbool( L, -1, false ) ? mask : 0;
|
||||||
|
|
||||||
|
static int lspi_host_free( lua_State *L )
|
||||||
|
{
|
||||||
|
GET_UD_HOST;
|
||||||
|
|
||||||
|
if (ud->host >= 0) {
|
||||||
|
spi_bus_free( ud->host );
|
||||||
|
ud->host = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lua: master = spi.master(host, config)
|
||||||
|
int lspi_master( lua_State *L )
|
||||||
|
{
|
||||||
|
int stack = 0;
|
||||||
|
|
||||||
|
int host = luaL_checkint( L, ++stack );
|
||||||
|
luaL_argcheck( L,
|
||||||
|
host == SPI_HOST || host == HSPI_HOST || host == VSPI_HOST,
|
||||||
|
stack,
|
||||||
|
"invalid host" );
|
||||||
|
|
||||||
|
luaL_checktype( L, ++stack, LUA_TTABLE );
|
||||||
|
|
||||||
|
spi_bus_config_t config;
|
||||||
|
memset( &config, 0, sizeof( config ) );
|
||||||
|
//
|
||||||
|
CONFIG_BUS_PIN_FROM_FIELD(sclk);
|
||||||
|
CONFIG_BUS_PIN_FROM_FIELD(mosi);
|
||||||
|
CONFIG_BUS_PIN_FROM_FIELD(miso);
|
||||||
|
CONFIG_BUS_PIN_FROM_FIELD(quadwp);
|
||||||
|
CONFIG_BUS_PIN_FROM_FIELD(quadhd);
|
||||||
|
lua_settop( L, stack );
|
||||||
|
//
|
||||||
|
if (no_err( spi_bus_initialize( host, &config, 1 ) )) {
|
||||||
|
lspi_host_t *ud = (lspi_host_t *)lua_newuserdata( L, sizeof( lspi_host_t ) );
|
||||||
|
luaL_getmetatable( L, UD_HOST_STR );
|
||||||
|
lua_setmetatable( L, -2 );
|
||||||
|
ud->host = host;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return luaL_error( L, "bus init failed" );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lua: dev = master:device(config)
|
||||||
|
static int lspi_host_device( lua_State *L )
|
||||||
|
{
|
||||||
|
GET_UD_HOST;
|
||||||
|
int stack = 1;
|
||||||
|
|
||||||
|
luaL_argcheck( L, ud->host >= 0, stack, "no active bus host" );
|
||||||
|
|
||||||
|
luaL_checktype( L, ++stack, LUA_TTABLE );
|
||||||
|
|
||||||
|
spi_device_interface_config_t config;
|
||||||
|
memset( &config, 0, sizeof( config ) );
|
||||||
|
|
||||||
|
// mandatory fields
|
||||||
|
lua_getfield( L, stack, "cs" );
|
||||||
|
config.spics_io_num = luaL_optint( L, -1, -1 );
|
||||||
|
//
|
||||||
|
lua_getfield( L, stack, "mode" );
|
||||||
|
int mode = luaL_optint( L, -1, -1 );
|
||||||
|
luaL_argcheck( L, mode >= 0, stack, "mode setting missing" );
|
||||||
|
config.mode = (uint8_t)mode;
|
||||||
|
//
|
||||||
|
lua_getfield( L, stack, "freq" );
|
||||||
|
int freq = luaL_optint( L, -1, -1 );
|
||||||
|
luaL_argcheck( L, freq >= 0, stack, "freq setting missing" );
|
||||||
|
config.clock_speed_hz = freq;
|
||||||
|
//
|
||||||
|
// optional fields
|
||||||
|
CONFIG_DEVICE_FROM_INT_FIELD(command_bits);
|
||||||
|
CONFIG_DEVICE_FROM_INT_FIELD(address_bits);
|
||||||
|
CONFIG_DEVICE_FROM_INT_FIELD(dummy_bits);
|
||||||
|
CONFIG_DEVICE_FROM_INT_FIELD(cs_ena_pretrans);
|
||||||
|
CONFIG_DEVICE_FROM_INT_FIELD(cs_ena_posttrans);
|
||||||
|
CONFIG_DEVICE_FROM_INT_FIELD(duty_cycle_pos);
|
||||||
|
CONFIG_DEVICE_FROM_BOOL_FIELD(tx_lsb_first, SPI_DEVICE_TXBIT_LSBFIRST);
|
||||||
|
CONFIG_DEVICE_FROM_BOOL_FIELD(rx_lsb_first, SPI_DEVICE_RXBIT_LSBFIRST);
|
||||||
|
CONFIG_DEVICE_FROM_BOOL_FIELD(wire3, SPI_DEVICE_3WIRE);
|
||||||
|
CONFIG_DEVICE_FROM_BOOL_FIELD(positive_cs, SPI_DEVICE_POSITIVE_CS);
|
||||||
|
CONFIG_DEVICE_FROM_BOOL_FIELD(halfduplex, SPI_DEVICE_HALFDUPLEX);
|
||||||
|
CONFIG_DEVICE_FROM_BOOL_FIELD(clk_as_cs, SPI_DEVICE_CLK_AS_CS);
|
||||||
|
lua_settop( L, stack );
|
||||||
|
//
|
||||||
|
// fill remaining config entries
|
||||||
|
config.queue_size = 1;
|
||||||
|
|
||||||
|
lspi_device_t *dev = (lspi_device_t *)lua_newuserdata( L, sizeof( lspi_device_t ) );
|
||||||
|
dev->device = NULL;
|
||||||
|
if (no_err( spi_bus_add_device( ud->host, &config, &(dev->device) ) )) {
|
||||||
|
luaL_getmetatable( L, UD_DEVICE_STR );
|
||||||
|
lua_setmetatable( L, -2 );
|
||||||
|
|
||||||
|
// reference host object to avoid automatic gc
|
||||||
|
lua_pushvalue( L, 1 );
|
||||||
|
dev->host_ref = luaL_ref( L, LUA_REGISTRYINDEX );
|
||||||
|
dev->host = ud->host;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_pop( L, 1 );
|
||||||
|
|
||||||
|
return luaL_error( L, "failed to add device" );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const LUA_REG_TYPE lspi_master_map[] = {
|
||||||
|
{ LSTRKEY( "device" ), LFUNCVAL( lspi_host_device ) },
|
||||||
|
{ LSTRKEY( "close" ), LFUNCVAL( lspi_host_free ) },
|
||||||
|
{ LSTRKEY( "__gc" ), LFUNCVAL( lspi_host_free ) },
|
||||||
|
{ LSTRKEY( "__index" ), LROVAL( lspi_master_map ) },
|
||||||
|
{LNILKEY, LNILVAL}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// ****************************************************************************
|
||||||
|
// Generic
|
||||||
|
//
|
||||||
|
int luaopen_spi_master( lua_State *L ) {
|
||||||
|
luaL_rometatable(L, UD_HOST_STR, (void *)lspi_master_map);
|
||||||
|
luaL_rometatable(L, UD_DEVICE_STR, (void *)lspi_device_map);
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,157 @@
|
||||||
|
# SPI Module
|
||||||
|
| Since | Origin / Contributor | Maintainer | Source |
|
||||||
|
| :----- | :-------------------- | :---------- | :------ |
|
||||||
|
| 2017-05-04 | [Arnim Läuger](https://github.com/devsaurus) | [Arnim Läuger](https://github.com/devsaurus) | [spi.c](../../../components/modules/spi.c)|
|
||||||
|
|
||||||
|
# SPI Bus
|
||||||
|
The ESP32 contains 4 SPI bus hosts called `SPI`, `SPI1`, `HSPI`, and `VSPI`. `SPI` is locked to flash communication and is not available for the application. `SPI1` is currently also tied to flash support, but might be available in the future. Applications can currently only use the `HSPI` and `VSPI` hosts.
|
||||||
|
|
||||||
|
The host signals can be mapped to any suitable GPIO pins.
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
|
||||||
|
The API on ESP32 differs from the API on ESP8266. For backwards compatibility please refer to [`lua_compat/spi_compat.lua`](../../../lua_compat/spi_compat.lua`).
|
||||||
|
|
||||||
|
|
||||||
|
## spi.master()
|
||||||
|
Initializes a bus in master mode and returns a bus master object.
|
||||||
|
|
||||||
|
#### Syntax
|
||||||
|
`spi.master(host, config)`
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
- `host` id, one of
|
||||||
|
- `spi.SPI1`. not supported yet
|
||||||
|
- `spi.HSPI`
|
||||||
|
- `spi.VSPI`
|
||||||
|
- `config` table listing the assigned GPIOs. All signal assignment are optional.
|
||||||
|
- `sclk`
|
||||||
|
- `mosi`
|
||||||
|
- `miso`
|
||||||
|
- `quadwp`
|
||||||
|
- `quadhd`
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
SPI bus master object
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
```lua
|
||||||
|
busmaster_config = {sclk = 19, mosi = 23, miso = 25}
|
||||||
|
busmaster = spi.master(spi.HSPI, busmaster_config)
|
||||||
|
```
|
||||||
|
|
||||||
|
# Bus Master Object
|
||||||
|
|
||||||
|
## spi.master:close()
|
||||||
|
Close the bus host. This fails if there are still devices registered on this bus.
|
||||||
|
|
||||||
|
!!! caution
|
||||||
|
|
||||||
|
The bus is also closed when the bus master object is automatically destroyed during garbage collection. Registered devices inherently prevent garbage collection of the bus master object.
|
||||||
|
|
||||||
|
#### Syntax
|
||||||
|
`busmaster:close()`
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
none
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
`nil`
|
||||||
|
|
||||||
|
## spi.master:device()
|
||||||
|
Adds a device on the given master bus. Up to three devices per bus are supported.
|
||||||
|
|
||||||
|
#### Syntax
|
||||||
|
`busmaster:device(config)`
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`config` table describing the device parameters:
|
||||||
|
|
||||||
|
- `cs` GPIO connected to device's chip-select pin, optional
|
||||||
|
- `mode` SPI mode used for this device (0-3), mandatory
|
||||||
|
- `freq` clock frequency used for this device [Hz], mandatory
|
||||||
|
- `command_bits` amount of bits in command phase (0-16), defaults to 0 if omitted
|
||||||
|
- `address_bits` amount of bits in address phase (0-64), defaults to 0 if omitted
|
||||||
|
- `dummy_bits` amount of dummy bits to insert address and data phase, defaults to 0 if omitted
|
||||||
|
- `cs_ena_pretrans`, optional
|
||||||
|
- `cs_ena_posttrans`, optional
|
||||||
|
- `duty_cycle_pos`, optional
|
||||||
|
- `tx_lsb_first` transmit command/address/data LSB first if `true`, MSB first otherwise
|
||||||
|
- `rx_lsb_first` receive data LSB first if `true`, MSB first otherwise
|
||||||
|
- `wire3` use spiq for both transmit and receive if `true`, use mosi and miso otherwise
|
||||||
|
- `positive_cs` chip-select is active high during a transaction if `true`, cs is active low otherwise
|
||||||
|
- `halfduplex` transmit data before receiving data if `true`, transmit and receive simultaneously otherwise
|
||||||
|
- `clk_as_cs` output clock on cs line when cs is active if `true`
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
SPI device object
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
```lua
|
||||||
|
device_config = {mode = 0, freq = 1000000}
|
||||||
|
device_config.cs = 22
|
||||||
|
dev1 = busmaster:device(device_config)
|
||||||
|
|
||||||
|
device_config.cs = 4
|
||||||
|
dev2 = busmaster:device(device_config)
|
||||||
|
```
|
||||||
|
|
||||||
|
# Device Object
|
||||||
|
|
||||||
|
## spi.master:device:remove()
|
||||||
|
Removes a device from the related bus master.
|
||||||
|
|
||||||
|
!!! caution
|
||||||
|
|
||||||
|
The device is also removed when the device object is automatically destroyed during garbage collection.
|
||||||
|
|
||||||
|
#### Syntax
|
||||||
|
`device:remove()`
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
none
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
`nil`
|
||||||
|
|
||||||
|
## spi.master:device:transfer()
|
||||||
|
Initiate an SPI transaction consisting of
|
||||||
|
|
||||||
|
1. assertion of cs signal
|
||||||
|
2. optional sending of command bits
|
||||||
|
3. optional sending of address bits
|
||||||
|
4. optional sending of dummy bits
|
||||||
|
5. sending of tx data
|
||||||
|
6. concurrent or appended reception of rx data, optional
|
||||||
|
7. de-assertion of cs signal
|
||||||
|
|
||||||
|
The function returns after the transaction is completed.
|
||||||
|
|
||||||
|
#### Syntax
|
||||||
|
```lua
|
||||||
|
device:transfer(trans)
|
||||||
|
device:transfer(txdata)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`trans` table containing the elements of the transaction:
|
||||||
|
|
||||||
|
- `command` data for command phase, amount of bits was defined during device creation, optional
|
||||||
|
- `address` data for address phase, amount of bits was defined during device creation, optional
|
||||||
|
- `txdata` string of data to be sent to the device, optional
|
||||||
|
- `rxlen` number of bytes to be received, optional
|
||||||
|
- `mode` optional, one of
|
||||||
|
- `sio` transmit in SIO mode, default if omitted
|
||||||
|
- `dio` transmit in DIO mode
|
||||||
|
- `qio` transmit in QIO mode
|
||||||
|
- `addr_mode` transmit address also in selected `mode` if `true`, transmit address in SIO otherwise.
|
||||||
|
|
||||||
|
`txdata` string of data to be sent to the device
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
String of `rxlen` length, or `#txdata` length if `rxlen` is omitted.
|
||||||
|
|
||||||
|
|
||||||
|
## spi.slave()
|
||||||
|
Initializes a bus in slave mode and returns a slave object.
|
||||||
|
Not yet supported.
|
|
@ -0,0 +1,107 @@
|
||||||
|
-- ****************************************************************************
|
||||||
|
--
|
||||||
|
-- Compatability wrapper for mapping ESP8266's spi module API to ESP32
|
||||||
|
--
|
||||||
|
-- Usage:
|
||||||
|
--
|
||||||
|
-- spi = require("spi_compat")([pin_sclk], [pin_mosi], [pin_miso], [pin_cs])
|
||||||
|
--
|
||||||
|
-- pin_sclk: GPIO pin for SCLK, optional
|
||||||
|
-- pin_mosi: GPIO pin for MOSI, optional
|
||||||
|
-- pin_miso: GPIO pin for MISO, optional
|
||||||
|
-- pin_cs: GPIO pin for CS, optional
|
||||||
|
--
|
||||||
|
-- ****************************************************************************
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
local _pin_sclk, _pin_mosi, _pin_miso, _pin_cs
|
||||||
|
|
||||||
|
local _spi
|
||||||
|
|
||||||
|
local _duplex
|
||||||
|
local _device
|
||||||
|
|
||||||
|
|
||||||
|
-- ****************************************************************************
|
||||||
|
-- Implement esp8266 compatability API
|
||||||
|
--
|
||||||
|
function M.setup(id, mode, cpol, cpha, databits, clock_div, duplex_mode)
|
||||||
|
if databits ~= 8 then
|
||||||
|
error("only 8 bits per item supported")
|
||||||
|
end
|
||||||
|
|
||||||
|
local bus_master = _spi.master(_spi.HSPI, {sclk = _pin_sclk, mosi = _pin_mosi, miso = _pin_miso})
|
||||||
|
|
||||||
|
local dev_config = {}
|
||||||
|
dev_config.cs = _pin_cs
|
||||||
|
dev_config.mode = cpol * 2 + cpha
|
||||||
|
dev_config.freq = 80000000 / clock_div
|
||||||
|
|
||||||
|
_device = bus_master:device(dev_config)
|
||||||
|
|
||||||
|
_duplex = duplex_mode or M.HALFDUPLEX
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.send(id, ...)
|
||||||
|
local results = {}
|
||||||
|
local wrote = 0
|
||||||
|
|
||||||
|
for idx = 1, select("#", ...) do
|
||||||
|
local arg = select(idx, ...)
|
||||||
|
if type(arg) == "number" then
|
||||||
|
table.insert(results, _device:transfer(string.char(arg)):byte(1))
|
||||||
|
wrote = wrote + 1
|
||||||
|
elseif type(arg) == "string" then
|
||||||
|
table.insert(results, _device:transfer(arg))
|
||||||
|
wrote = wrote + #arg
|
||||||
|
elseif type(arg) == "table" then
|
||||||
|
local rtab = {}
|
||||||
|
for i, data in ipairs(arg) do
|
||||||
|
table.insert(rtab, _device:transfer(string.char(data)):byte(1))
|
||||||
|
wrote = wrote + 1
|
||||||
|
end
|
||||||
|
table.insert(results, rtab)
|
||||||
|
else
|
||||||
|
error("wrong argument type")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if _duplex == M.FULLDUPLEX then
|
||||||
|
return wrote, unpack(results)
|
||||||
|
else
|
||||||
|
return wrote
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.recv(id, size, default_data)
|
||||||
|
local def = default_data or 0xff
|
||||||
|
return _device:transfer(string.char(def):rep(size))
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
return function (pin_sclk, pin_mosi, pin_miso, pin_cs)
|
||||||
|
-- cache built-in module
|
||||||
|
_spi = spi
|
||||||
|
-- invalidate built-in module
|
||||||
|
spi = nil
|
||||||
|
|
||||||
|
-- forward unchanged functions
|
||||||
|
|
||||||
|
-- forward constant definitions
|
||||||
|
M.MASTER = 0
|
||||||
|
M.CPOL_LOW = 0
|
||||||
|
M.CPOL_HIGH = 1
|
||||||
|
M.CPHA_LOW = 0
|
||||||
|
M.CPHA_HIGH = 1
|
||||||
|
M.HALFDUPLEX = 0
|
||||||
|
M.FULLDUPLEX = 1
|
||||||
|
|
||||||
|
_pin_sclk = pin_sclk
|
||||||
|
_pin_mosi = pin_mosi
|
||||||
|
_pin_miso = pin_miso
|
||||||
|
_pin_cs = pin_cs
|
||||||
|
|
||||||
|
return M
|
||||||
|
end
|
|
@ -44,6 +44,7 @@ pages:
|
||||||
- 'ow (1-Wire)': 'en/modules/ow.md'
|
- 'ow (1-Wire)': 'en/modules/ow.md'
|
||||||
- 'sdmmc': 'en/modules/sdmmc.md'
|
- 'sdmmc': 'en/modules/sdmmc.md'
|
||||||
- 'sigma delta': 'en/modules/sigma-delta.md'
|
- 'sigma delta': 'en/modules/sigma-delta.md'
|
||||||
|
- 'spi': 'en/modules/spi.md'
|
||||||
- 'struct': 'en/modules/struct.md'
|
- 'struct': 'en/modules/struct.md'
|
||||||
- 'tmr': 'en/modules/tmr.md'
|
- 'tmr': 'en/modules/tmr.md'
|
||||||
- 'uart': 'en/modules/uart.md'
|
- 'uart': 'en/modules/uart.md'
|
||||||
|
|
Loading…
Reference in New Issue