-// Copyright 2010-2016 Espressif Systems (Shanghai) PTE LTD
+// Copyright 2010-2017 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
bool spicommon_periph_free(spi_host_device_t host);
-#define SPICOMMON_BUSFLAG_SLAVE 0 ///< Initialize I/O in slave mode
-#define SPICOMMON_BUSFLAG_MASTER 1 ///< Initialize I/O in master mode
-#define SPICOMMON_BUSFLAG_QUAD 2 ///< Also initialize WP/HD pins, if specified
+#define SPICOMMON_BUSFLAG_SLAVE 0 ///< Initialize I/O in slave mode
+#define SPICOMMON_BUSFLAG_MASTER (1<<0) ///< Initialize I/O in master mode
+#define SPICOMMON_BUSFLAG_QUAD (1<<1) ///< Also initialize WP/HD pins, if specified
/**
* @brief Connect a SPI peripheral to GPIO pins
* @param bus_config Pointer to a spi_bus_config struct detailing the GPIO pins
* @param dma_chan DMA-channel (1 or 2) to use, or 0 for no DMA.
* @param flags Combination of SPICOMMON_BUSFLAG_* flags
- * @param is_native A value of 'true' will be written to this address if the GPIOs can be
+ * @param[out] is_native A value of 'true' will be written to this address if the GPIOs can be
* routed using the IO_mux, 'false' if the GPIO matrix is used.
* @return
* - ESP_ERR_INVALID_ARG if parameter is invalid
* - ESP_OK on success
*/
-esp_err_t spicommon_bus_initialize_io(spi_host_device_t host, spi_bus_config_t *bus_config, int dma_chan, int flags, bool *is_native);
+esp_err_t spicommon_bus_initialize_io(spi_host_device_t host, const spi_bus_config_t *bus_config, int dma_chan, int flags, bool *is_native);
/**
* @brief Free the IO used by a SPI peripheral
/**
- * @note V0 and V1 of the ESP32 silicon has a bug where in some (well-known) cases a SPI DMA channel will get confused. This can be remedied
+ * @note In some (well-defined) cases in the ESP32 (at least rev v.0 and v.1), a SPI DMA channel will get confused. This can be remedied
* by resetting the SPI DMA hardware in case this happens. Unfortunately, the reset knob used for thsi will reset _both_ DMA channels, and
* as such can only done safely when both DMA channels are idle. These functions coordinate this.
*
/**
* @brief Request a reset for a certain DMA channel
*
- * @param host The SPI host
+ * @param dmachan DMA channel associated with the SPI host that needs a reset
* @param cb Callback to call in case DMA channel cannot be reset immediately
* @param arg Argument to the callback
*
-// Copyright 2010-2016 Espressif Systems (Shanghai) PTE LTD
+// Copyright 2010-2017 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
#define SPI_SLAVE_TXBIT_LSBFIRST (1<<0) ///< Transmit command/address/data LSB first instead of the default MSB first
#define SPI_SLAVE_RXBIT_LSBFIRST (1<<1) ///< Receive data LSB first instead of the default MSB first
#define SPI_SLAVE_BIT_LSBFIRST (SPI_TXBIT_LSBFIRST|SPI_RXBIT_LSBFIRST); ///< Transmit and receive LSB first
-#define SPI_SLAVE_POSITIVE_CS (1<<3) ///< Make CS positive during a transaction instead of negative
typedef struct spi_slave_transaction_t spi_slave_transaction_t;
* @brief This is a configuration for a SPI host acting as a slave device.
*/
typedef struct {
- int spics_io_num; ///< CS GPIO pin for this device, or -1 if not used
- uint32_t flags; ///< Bitwise OR of SPI_DEVICE_* flags
- int queue_size; ///< Transaction queue size. This sets how many transactions can be 'in the air' (queued using spi_device_queue_trans but not yet finished using spi_device_get_trans_result) at the same time
+ int spics_io_num; ///< CS GPIO pin for this device
+ uint32_t flags; ///< Bitwise OR of SPI_SLAVE_* flags
+ int queue_size; ///< Transaction queue size. This sets how many transactions can be 'in the air' (queued using spi_slave_queue_trans but not yet finished using spi_slave_get_trans_result) at the same time
uint8_t mode; ///< SPI mode (0-3)
slave_transaction_cb_t post_setup_cb; ///< Callback called after the SPI registers are loaded with new data
slave_transaction_cb_t post_trans_cb; ///< Callback called after a transaction is done
size_t length; ///< Total data length, in bits
const void *tx_buffer; ///< Pointer to transmit buffer, or NULL for no MOSI phase
void *rx_buffer; ///< Pointer to receive buffer, or NULL for no MISO phase
+ void *user; ///< User-defined variable. Can be used to store eg transaction ID.
};
/**
* - ESP_ERR_NO_MEM if out of memory
* - ESP_OK on success
*/
-esp_err_t spi_slave_initialize(spi_host_device_t host, spi_bus_config_t *bus_config, spi_slave_interface_config_t *slave_config, int dma_chan);
+esp_err_t spi_slave_initialize(spi_host_device_t host, const spi_bus_config_t *bus_config, const spi_slave_interface_config_t *slave_config, int dma_chan);
/**
* @brief Free a SPI bus claimed as a SPI slave interface
* unhandled transactions before it and the master initiates a SPI transaction by pulling down CS and sending out
* clock signals.
*
- * @param handle Device handle obtained using spi_host_add_dev
- * @param trans_desc Description of transaction to execute
+ * @param host SPI peripheral that is acting as a slave
+ * @param trans_desc Description of transaction to execute. Not const because we may want to write status back
+ * into the transaction description.
* @param ticks_to_wait Ticks to wait until there's room in the queue; use portMAX_DELAY to
* never time out.
* @return
* - ESP_ERR_INVALID_ARG if parameter is invalid
* - ESP_OK on success
*/
-esp_err_t spi_slave_queue_trans(spi_host_device_t host, spi_slave_transaction_t *trans_desc, TickType_t ticks_to_wait);
+esp_err_t spi_slave_queue_trans(spi_host_device_t host, const spi_slave_transaction_t *trans_desc, TickType_t ticks_to_wait);
/**
* @brief Get the result of a SPI transaction queued earlier
*
* This routine will wait until a transaction to the given device (queued earlier with
- * spi_device_queue_trans) has succesfully completed. It will then return the description of the
+ * spi_slave_queue_trans) has succesfully completed. It will then return the description of the
* completed transaction so software can inspect the result and e.g. free the memory or
* re-use the buffers.
*
- * @param handle Device handle obtained using spi_host_add_dev
+ * @param host SPI peripheral to that is acting as a slave
* @param trans_desc Pointer to variable able to contain a pointer to the description of the
* transaction that is executed
* @param ticks_to_wait Ticks to wait until there's a returned item; use portMAX_DELAY to never time
/**
* @brief Do a SPI transaction
*
- * Essentially does the same as spi_device_queue_trans followed by spi_device_get_trans_result. Do
+ * Essentially does the same as spi_slave_queue_trans followed by spi_slave_get_trans_result. Do
* not use this when there is still a transaction queued that hasn't been finalized
- * using spi_device_get_trans_result.
+ * using spi_slave_get_trans_result.
*
- * @param handle Device handle obtained using spi_host_add_dev
+ * @param host SPI peripheral to that is acting as a slave
* @param trans_desc Pointer to variable able to contain a pointer to the description of the
- * transaction that is executed
+ * transaction that is executed. Not const because we may want to write status back
+ * into the transaction description.
* @param ticks_to_wait Ticks to wait until there's a returned item; use portMAX_DELAY to never time
* out.
* @return
#include "soc/spi_reg.h"
#include "soc/dport_reg.h"
#include "soc/spi_struct.h"
-#include "soc/rtc_cntl_reg.h"
#include "rom/ets_sys.h"
#include "esp_types.h"
#include "esp_attr.h"
#include "esp_intr_alloc.h"
#include "esp_log.h"
#include "esp_err.h"
-#include "freertos/FreeRTOS.h"
-#include "freertos/semphr.h"
-#include "freertos/xtensa_api.h"
-#include "freertos/task.h"
-#include "freertos/ringbuf.h"
#include "soc/soc.h"
#include "soc/dport_reg.h"
-#include "soc/uart_struct.h"
#include "rom/lldesc.h"
-#include "driver/uart.h"
#include "driver/gpio.h"
#include "driver/periph_ctrl.h"
#include "esp_heap_alloc_caps.h"
bus config struct and it'll set up the GPIO matrix and enable the device. It will set is_native to 1 if the bus
config can be done using the IOMUX instead of using the GPIO matrix.
*/
-esp_err_t spicommon_bus_initialize_io(spi_host_device_t host, spi_bus_config_t *bus_config, int dma_chan, int flags, bool *is_native)
+esp_err_t spicommon_bus_initialize_io(spi_host_device_t host, const spi_bus_config_t *bus_config, int dma_chan, int flags, bool *is_native)
{
bool native=true;
bool is_master=(flags&SPICOMMON_BUSFLAG_MASTER)?true:false;
#include "soc/spi_reg.h"
#include "soc/dport_reg.h"
#include "soc/spi_struct.h"
-#include "soc/rtc_cntl_reg.h"
#include "rom/ets_sys.h"
#include "esp_types.h"
#include "esp_attr.h"
#include "freertos/ringbuf.h"
#include "soc/soc.h"
#include "soc/dport_reg.h"
-#include "soc/uart_struct.h"
#include "rom/lldesc.h"
-#include "driver/uart.h"
#include "driver/gpio.h"
#include "driver/periph_ctrl.h"
#include "esp_heap_alloc_caps.h"
#include "soc/spi_reg.h"
#include "soc/dport_reg.h"
#include "soc/spi_struct.h"
-#include "soc/rtc_cntl_reg.h"
#include "rom/ets_sys.h"
#include "esp_types.h"
#include "esp_attr.h"
#include "freertos/ringbuf.h"
#include "soc/soc.h"
#include "soc/dport_reg.h"
-#include "soc/uart_struct.h"
#include "rom/lldesc.h"
-#include "driver/uart.h"
#include "driver/gpio.h"
#include "driver/periph_ctrl.h"
#include "esp_heap_alloc_caps.h"
return (ret_val); \
}
-#define VALID_HOST(x) (host>SPI_HOST && host<=VSPI_HOST)
+#define VALID_HOST(x) (x>SPI_HOST && x<=VSPI_HOST)
typedef struct {
spi_slave_interface_config_t cfg;
static void IRAM_ATTR spi_intr(void *arg);
-esp_err_t spi_slave_initialize(spi_host_device_t host, spi_bus_config_t *bus_config, spi_slave_interface_config_t *slave_config, int dma_chan)
+esp_err_t spi_slave_initialize(spi_host_device_t host, const spi_bus_config_t *bus_config, const spi_slave_interface_config_t *slave_config, int dma_chan)
{
bool native, claimed;
//We only support HSPI/VSPI, period.
}
-esp_err_t spi_slave_queue_trans(spi_host_device_t host, spi_slave_transaction_t *trans_desc, TickType_t ticks_to_wait)
+esp_err_t spi_slave_queue_trans(spi_host_device_t host, const spi_slave_transaction_t *trans_desc, TickType_t ticks_to_wait)
{
BaseType_t r;
SPI_CHECK(VALID_HOST(host), "invalid host", ESP_ERR_INVALID_ARG);
host->hw->user.usr_miso_highpart=0;
host->hw->user.usr_mosi_highpart=0;
if (trans->tx_buffer) {
- uint32_t *data=host->cur_trans->tx_buffer;
+ const uint32_t *data=host->cur_trans->tx_buffer;
for (int x=0; x<trans->length; x+=32) {
uint32_t word;
memcpy(&word, &data[x/32], 4);
int from=x-16;
if (from<0) from=0;
printf("Error at %d! Sent vs recved: (starting from %d)\n" , x, from);
- for (int i=0; i<32; i++) {
- if (i+from<num_bytes) printf("%02X ", sendbuf[from+i]);
- }
- printf("\n");
- for (int i=0; i<32; i++) {
- if (i+from<num_bytes) printf("%02X ", recvbuf[from+i]);
- }
- printf("\n");
+ for (int i=0; i<32; i++) {
+ if (i+from<num_bytes) printf("%02X ", sendbuf[from+i]);
+ }
+ printf("\n");
+ for (int i=0; i<32; i++) {
+ if (i+from<num_bytes) printf("%02X ", recvbuf[from+i]);
+ }
+ printf("\n");
// TEST_ASSERT(0);
}
.queue_size=3,
};
spi_device_handle_t handle1=setup_spi_bus(80000, true);
- spi_device_handle_t handle2;
- spi_bus_add_device(HSPI_HOST, &devcfg, &handle2);
+ spi_device_handle_t handle2;
+ spi_bus_add_device(HSPI_HOST, &devcfg, &handle2);
- printf("Sending to dev 1\n");
+ printf("Sending to dev 1\n");
spi_test(handle1, 7);
- printf("Sending to dev 1\n");
+ printf("Sending to dev 1\n");
spi_test(handle1, 15);
- printf("Sending to dev 2\n");
+ printf("Sending to dev 2\n");
spi_test(handle2, 15);
- printf("Sending to dev 1\n");
+ printf("Sending to dev 1\n");
spi_test(handle1, 32);
- printf("Sending to dev 2\n");
+ printf("Sending to dev 2\n");
spi_test(handle2, 32);
- printf("Sending to dev 1\n");
+ printf("Sending to dev 1\n");
spi_test(handle1, 63);
- printf("Sending to dev 2\n");
+ printf("Sending to dev 2\n");
spi_test(handle2, 63);
- printf("Sending to dev 1\n");
+ printf("Sending to dev 1\n");
spi_test(handle1, 5000);
- printf("Sending to dev 2\n");
+ printf("Sending to dev 2\n");
spi_test(handle2, 5000);
the master to the slave on the MOSI line and vice versa on the MISO line. At the end of the transaction,
the master makes CS high again.
-Using the spi_master driver
+Using the spi_slave driver
^^^^^^^^^^^^^^^^^^^^^^^^^^^
- Initialize a SPI peripheral as a slave by calling ``spi_slave_initialize``. Make sure to set the
will be sent and received. In case the transmission length is shorter than the buffer length, only data up
to the length of the buffer will be exchanged.
-Warning: Due to a design peculiarity in the V0 and V1 silicon of the ESP32, if the amount of bytes sent
-by the master or the length of the transmission sent to the slave driver is not both larger than eight and
-dividable by four, the SPI hardware can fail to write the last one to seven bytes to the receive buffer.
+Warning: Due to a design peculiarity in the ESP32, if the amount of bytes sent by the master or the length
+of the transmission queues in the slave driver, in bytes, is not both larger than eight and dividable by
+four, the SPI hardware can fail to write the last one to seven bytes to the receive buffer.
Application Example
These two projects illustrate the SPI Slave driver. They're supposed to be flashed into two separate ESP32s connected to
eachother using the SPI pins defined in app_main.c. Once connected and flashed, they will use the spi master and spi slave
driver to communicate with eachother. The example also includes a handshaking line to allow the master to only poll the
-slave when it is actually ready to parse a transaction.
\ No newline at end of file
+slave when it is actually ready to parse a transaction.
+
+Please run wires between the following GPIOs between the slave and master to make the example function:
+
+========= ====== =======
+Signal Slave Master
+========= ====== =======
+Handshake GPIO2 GPIO2
+MOSI GPIO12 GPIO12
+MISO GPIO13 GPIO13
+SCLK GPIO15 GPIO15
+CS GPIO14 GPIO14
+========= ====== =======
+
+Be aware that the example by default uses lines normally reserved for JTAG. If this is an issue, either because of hardwired
+JTAG hardware or because of the need to do JTAG debugging, feel free to change the GPIO settings by editing defines in the top
+of main.c in the master/slave source code.
\ No newline at end of file
# project subdirectory.
#
-PROJECT_NAME := app-template
+PROJECT_NAME := spi-slave-receiver
include $(IDF_PATH)/make/project.mk
# project subdirectory.
#
-PROJECT_NAME := app-template
+PROJECT_NAME := spi-slave-sender
include $(IDF_PATH)/make/project.mk
# CONFIG_FREERTOS_UNICORE is not set
CONFIG_FREERTOS_CORETIMER_0=y
# CONFIG_FREERTOS_CORETIMER_1 is not set
-CONFIG_FREERTOS_HZ=100
+CONFIG_FREERTOS_HZ=1000
CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION=y
# CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE is not set
# CONFIG_FREERTOS_CHECK_STACKOVERFLOW_PTRVAL is not set