SPI Slave Sender Example
Note
This document is automatically translated using AI. Please excuse any detailed errors. The official English version is still in progress.
Example Description
This example demonstrates how to use the SPI master driver in ESP-IDF to communicate with another device. The master sends data while receiving information returned by the slave, and initiates transactions only when the slave is ready, ensured by a handshake signal. For information about SPI master mode, refer to SPI Master Driver. Before studying this example, it is recommended to understand the related content of FreeRTOS semaphores and queues. The basic description and API usage examples can be found in the Semaphores and Mutexes and Queue Management documents.
Running Method
This example needs to be used in conjunction with the Receiver Example. Both are burned into two ESP32 series chips and the signal lines are correctly connected to achieve complete data exchange. Although the example name contains Slave, the Sender is actually configured as an SPI master, taking on the role of generating clocks and controlling chip selection in communication, and driving the Receiver (SPI slave) to complete transmission and reception.
The complete code of the example can be found in the SPI Slave Sender Example. For instructions on configuration before running, corresponding GPIO pins, build and burn process, please refer to the README.md file in the spi_slave
directory.
For custom configuration items and default values, refer to the Macro Definition Explanation section.
Header File Description
The header files used in this example cover FreeRTOS task management and synchronization, log output, SPI master driver, and GPIO and timer control modules, providing support for SPI master initialization, data transmission, and task scheduling.
The header files are categorized by function as follows:
FreeRTOS Task Scheduling: Provides task creation and scheduling, semaphore and queue functions for concurrent task management and communication between multiple tasks.
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"
System Tools: Provides standard input and output, data type definitions, and log output functions.
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include "esp_log.h"
SPI Slave Driver: Provides initialization, device configuration, and data transmission functions for SPI master mode.
#include "driver/spi_master.h"
GPIO Control: Provides GPIO pin configuration and level control functions.
#include "driver/gpio.h"
Timer: Provides timing and callback functions with microsecond precision, used for task delay or periodic operations.
#include "esp_timer.h"
Macro Definition Explanation
The macro definitions involved in this example are mainly used for configuring the pins of the SPI peripherals, controller selection, and handshake signal pins, which are convenient for subsequent SPI data transmission and task scheduling.
The macro definitions are classified by function as follows:
SPI Host Configuration
Defines the controller used by the SPI host, this example uses
SPI2_HOST
.#define RCV_HOST SPI2_HOST
Defines the key pins of SPI, including handshake, data input/output, clock, and chip select pins.
#define GPIO_HANDSHAKE 2 // Handshake signal #define GPIO_MOSI 12 // Master output, slave input #define GPIO_MISO 13 // Master input, slave output #define GPIO_SCLK 15 // SPI clock #define GPIO_CS 14 // Chip select signal
Note
The handshake pin is used for data synchronization control between the host and the slave, and the appropriate GPIO can be selected according to the hardware design.
The chip select pin is used to select the SPI slave to ensure correct communication of multiple slaves on the bus.
Task Function Explanation
This example defines an Interrupt Service Routine (ISR), which responds to the interrupt triggered by the handshake GPIO. When the slave sends a ready signal through the handshake pin, this interrupt is triggered, and the host knows through this function that the slave is ready and can transmit data.
static void IRAM_ATTR gpio_handshake_isr_handler( void* arg );
Note
The function uses the
IRAM_ATTR
modifier to ensure that the function is placed in internal RAM, improving interrupt response speed.The function argument is a user-defined argument pointer, often used to pass trigger source or context information. In this example, this function does not access or modify the argument content, it is only used to meet the function prototype requirements of the callback function.
The execution logic of this function is:
Interrupt Debounce Filtering: To avoid interference or signal bounce on the handshake GPIO causing repeated triggering of interrupts, the function records the last interrupt time and compares it with the current interrupt time. If the interval between two interrupts is less than the set threshold (1 ms), this interrupt is ignored.
Define the variable
lasthandshaketime_us
to record the time of the last interrupt.Call
esp_timer_get_time()
to get the current time (microsecond level) and save it to the variablecurrtime_us
. For further introduction and parameter explanation, please refer to ESP Timer.Get the time interval
diff
between two interrupt triggers through the above two variables.Determine whether the time interval is less than the set threshold (1 ms). If it is less, return directly and ignore this interrupt; otherwise, update the time of the last interrupt to the current time and continue to perform subsequent operations.
Issue Synchronization Signal: In order to notify the waiting task that the slave is ready and can start SPI data transmission, the function uses the FreeRTOS semaphore mechanism to safely release the semaphore from the ISR and trigger task switching when necessary.
Define the flag variable
mustYield
, which indicates whether it is necessary to switch tasks immediately after the interrupt ends, initialized asfalse
.Invoke
xSemaphoreGiveFromISR()
to release the semaphore. For further introduction and parameter description, please refer to Release Semaphore Resources.Determine whether
mustYield
istrue
. If so, callportYIELD_FROM_ISR()
to switch to the execution of higher priority tasks.
Main Function Description
This example demonstrates the initialization of the SPI host, establishing communication with the slave device and transmitting data, as well as the steps to release resources.
Define variables:
Define the return value variable
ret
to save the return status of SPI peripheral related functions, judge whether the operation is successful, and perform error handling or print prompt information according to the return value.Define the device handle variable
handle
, which is used to identify and manage the slave devices registered on the SPI bus.
Configure handshake signal GPIO: In the communication between the SPI slave and the host, in order to achieve reliable data transmission, a handshake signal (handshake line) is usually used to notify the host that the slave is ready to send or receive data.
Configure handshake line Interrupt Service.
Install and bind the interrupt service so that the corresponding processing can be triggered when the handshake signal changes.
Before binding the interrupt service, explicitly set the interrupt trigger mode of the pin to
GPIO_INTR_POSEDGE
again to prevent the hardware initialization or driver library from modifying the interrupt configuration.Note
Setting the interrupt trigger mode again is a redundant but safe operation, ensuring that the interrupt trigger type of the handshake GPIO is indeed triggered by the rising edge, thus ensuring the reliability of the master-slave communication.
Preparation for SPI host send/receive transaction:
Define the count variable
n
and initialize it to 0, which is used to record or identify the current number of transmissions, convenient for debugging or generating sending data.Define data buffer arrays to provide storage space for SPI host data sending and receiving, ensuring that each transmission has a definite storage area:
Define the variable
sendbuf
and initialize it to 0, used as the send data buffer.Define the variable
recvbuf
and initialize it to 0, used as the receive data buffer.The array length is set to 128, indicating that the buffer can store up to 128 bytes of data, and the byte capacity can be adjusted according to actual application needs.
Invoke
xSemaphoreCreateBinary()
to create a semaphore and save its handle to the variablerdySem
. For further introduction and parameter description, refer to Creating Binary Semaphore.Invoke
xSemaphoreGive()
to send an “available” signal to the semaphorerdySem
, notifying the waiting task that the slave is ready or data has arrived, and subsequent operations can be performed. For further introduction and parameter description, refer to Releasing Semaphore Resources.
Construct the data to be sent to the SPI slave, and record the transmission number and the data returned by the previous slave in the data.
Invoke
snprintf()
to write the formatted string into the send buffersendbuf
, ensuring no overflow.Check the return value, if the sent data exceeds the buffer size, print
Data truncated
to prevent overflow.
Before executing the transaction, you need to call
xSemaphoreTake()
to block and wait for the semaphore until the slave indicates that the next transmission can be performed through the handshake signal. The timeout is set toportMAX_DELAY
which means waiting indefinitely. For further introduction and parameter description, refer to Acquiring Semaphore Resources.
Print the data returned by the slave and update the transmission count for this time.
After the loop ends, call
spi_bus_remove_device()
to release the SPI bus resources, disconnect the association between the host and the slave, and check the return value to ensure success. For further introduction and parameter description, refer to SPI Master Driver.
Note
In actual applications, the SPI data transmission task is usually a long-term or resident task, so this step is generally not executed, but it can be used to prevent potential program exceptions or memory leaks.