#include "spi_slave_server.h"
// spi
#include <SPI.h>
#include <ESP32SPISlave.h>
#include "helper.h"
ESP32SPISlave slave; // 创建ESP32SPISlave对象
#define SPI_SPLAVE_ENABLE 1
// Adafruit_SPIDevice spi_dev = Adafruit_SPIDevice(SPIDEVICE_CS, 1000000, SPI_BITORDER_MSBFIRST, SPI_MODE0);
#define MY_CS 12
#define MY_SCK 13
#define MY_MOSI 14
#define MY_MISO 11 // 自定义spi引脚
// SPIClass my_spi(HSPI); // 创建SPIClass对象my_spi
#define SPI_READ_TIMEROUT 999999 // 10 // 50ms
static constexpr size_t SPI_BUFFER_SIZE = 16 * 2; // 上位机 测试42 字节数据正常,模式1,定义SPI通信缓冲区大小为32字节
static constexpr size_t QUEUE_SIZE = 1;
static uint8_t spi_tx_buf[SPI_BUFFER_SIZE]{1, 2, 3, 4, 5, 6, 7, 8};//发送缓冲区,预置测试数据1-8
static uint8_t spi_rx_buf[SPI_BUFFER_SIZE]{0, 0, 0, 0, 0, 0, 0, 0};//接收缓冲区,初始化为全0
static uint8_t spi_tx_cmd_buf[SPI_BUFFER_SIZE]{0, 0, 0, 0, 0, 0, 0, 0};//命令发送缓冲区,初始化为全0
#define SPI_TX_CACHE_BUF_LEN 1024//发送缓存总大小为1024字节
#define SPI_TX_PAGE_BUF_LEN (SPI_BUFFER_SIZE - 3) // spi 回复数据的最大长度,单次SPI通信有效数据长度
#define SPI_USER_CMD_NULL 0//空指令
#define SPI_USER_CMD_READ 1//读数据
#define SPI_USER_CMD_WRITE 2//写数据
static volatile uint8_t spi_tx_cache_buf[SPI_TX_CACHE_BUF_LEN]{0, 0, 0, 0, 0, 0, 0, 0};
static volatile int spi_tx_cache_buf_cnt = 0; // spi 发送缓存区数据长度
// static int spi_tx_page_cnt=0;//spi 发送缓存区数据长度
static volatile int spi_current_cmd = 0, spi_current_cmd_len = 0;
static uint8_t spi_send_busy = 0;//发送忙标志位
static uint8_t spi_send_mode = 1; // 发送模式选择发串口
int spi_slave_data_cache_add(uint8_t *data, uint16_t len)
{
if (len > SPI_TX_CACHE_BUF_LEN)
{
/* code */
len = SPI_TX_CACHE_BUF_LEN;
}
for (int i = 0; i < len; i++)
{
/* code */
spi_tx_cache_buf[i] = data[i];
}
spi_tx_cache_buf_cnt = len;
return 0;
}
void IRAM_ATTR my_post_setup_cb(spi_slave_transaction_t *trans)//强制将函数编译到内部RAM,确保中断快速响应
{
static uint8_t test_send = 0x11;//静态测试变量
int over_len = spi_tx_cache_buf_cnt - spi_current_cmd_len * SPI_TX_PAGE_BUF_LEN; // 剩余待发送数据量 = 总缓存量 - 已发送页数×单页容量
// Serial.printf("spi slave read over_len = %d cache= %d\r\n", over_len,spi_tx_cache_buf_cnt);
if (over_len < 0)//数据已全部发送完成时的保护判断
{
return;
}
if (over_len > SPI_TX_PAGE_BUF_LEN)//单包数据超限时截断处理
{
over_len = SPI_TX_PAGE_BUF_LEN;
}
memset((void *)spi_tx_buf, 0, SPI_BUFFER_SIZE);//清空发送缓冲区(32字节全置0)
spi_tx_buf[0] = 0xab;
spi_tx_buf[1] = over_len; // 实际长度
spi_tx_buf[2] = spi_current_cmd_len; // 包号
for (int i = 0; i < over_len; i++)//将缓存数据拷贝到发送缓冲区(偏移3字节协议头)
{
/* code */
spi_tx_buf[3 + i] = spi_tx_cache_buf[spi_current_cmd_len * SPI_TX_PAGE_BUF_LEN + i];
}
spi_current_cmd_len++;//包序号自增
if (spi_current_cmd_len >= 3)//包序号归零
{
/* code */
spi_current_cmd_len = 0;
}
// Serial.printf("spi slave read cmd_len = %d ,%02x %02x %02x\n", over_len,spi_tx_buf[1], spi_tx_buf[2], spi_tx_buf[3]);
}
/***
* spi slave 任务
* 模式设置4 字节
* spi cmd: 0xaa 0x01 ,切换模式1,发串口1数据
*
* 数据读取 64字节,发aa 01 0xff ......
* spi ack: 0xaa 0x01 ,剩余字节全部回复串口数据
*/
static void spi_slave_task(void *pvParameters)//FreeRTOS任务函数标准定义
{
size_t received_bytes; // 记录接收到的数据字节数
while (1)//无限循环,保持任务持续运行
{
/* code */
// initializeBuffers(tx_buf, rx_buf, BUFFER_SIZE);
int cs = digitalRead(MY_CS);//读取片选信号(CS)引脚状态,检测主设备是否选中本从设备
#if SPI_SPLAVE_ENABLE//条件编译开关,控制SPI从设备功能是否启用
if (cs == 0)//当片选信号为低电平时(表示被主设备选中)执行通信
{
memset(spi_rx_buf, 0, SPI_BUFFER_SIZE);//清空接受缓存区
// start and wait to complete one BIG transaction (same data will be received from slave)
received_bytes = slave.transfer(spi_tx_buf, spi_rx_buf, SPI_BUFFER_SIZE, SPI_READ_TIMEROUT);
//tx_buf:发送缓冲区,rx_buf:接收缓冲区,BUFFER_SIZE:传输长度,READ_TIMEROUT:超时时间
// verify and dump difference with received data
if (received_bytes > 0)//成功接收到数据时处理数据
{
printf_log_hex("slave", spi_rx_buf, received_bytes);
printf_log_hex("slave", spi_tx_buf, received_bytes);
if (spi_rx_buf[1] == 0xaa && spi_rx_buf[2] == 0x01) // 当收到0xAA01时设置为读取模式,读取数据cmd
{
spi_current_cmd = SPI_USER_CMD_READ;
// spi_current_cmd_len = spi_rx_buf[3];
}
my_post_setup_cb(NULL);//调用之前分析的发送回调函数
}
}
#endif
vTaskDelay(5 / portTICK_PERIOD_MS);//任务延时5ms
}
}
/**
* spi slave 初始化
* SPI_MODE1
* sck 13
* miso 14
* mosi 11
* ss 12
*
*/
void spi_slave_server_init(void)
{
#if SPI_SPLAVE_ENABLE
slave.setDataMode(SPI_MODE3); // default: SPI_MODE1
slave.setQueueSize(QUEUE_SIZE); // default: 1
slave.begin(HSPI, 13, 11, 14, 12); // default: HSPI (please refer README for pin assignments)
// pinMode(10, INPUT);
// slave.setPostSetupCb(my_post_setup_cb);
xTaskCreate(spi_slave_task, "spi_slave_task", 8 * 1024, NULL, 2, NULL);
#endif
}
仿照上述格式,写spi主机代码