175 lines
7.0 KiB
C++
Vendored
175 lines
7.0 KiB
C++
Vendored
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Name: FSPI.h
|
|
// Created: Jan 2025
|
|
// Version: v1.1
|
|
// Author(s): Philip Smart
|
|
// Description: Class definition to encapsulate the Espressif Fast SPI Interface.
|
|
// v1.1: Binary IPC frame protocol support. Fixed max_transfer_sz (was 32, now
|
|
// IPCF_MAX_FRAME_SIZE = 8260). DMA-capable buffers for command/response
|
|
// frames. receiveBinaryCmd() / sendBinaryResp() added.
|
|
// Credits:
|
|
// Copyright: (c) 2019-2026 Philip Smart <philip.smart@net2net.org>
|
|
//
|
|
// History: v1.00 Jan 2025 - Initial write.
|
|
// v1.10 Mar 2026 - Binary IPC protocol, fix max_transfer_sz, burst support.
|
|
//
|
|
// Notes: See Makefile to enable/disable conditional components
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// This source file is free software: you can redistribute it and#or modify
|
|
// it under the terms of the GNU General Public License as published
|
|
// by the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This source file 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 General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#ifndef FSPI_H
|
|
#define FSPI_H
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <iostream>
|
|
#include <vector>
|
|
#include <map>
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/task.h"
|
|
#include "freertos/semphr.h"
|
|
#include "driver/spi_slave.h"
|
|
#include "esp_system.h"
|
|
#include "esp_log.h"
|
|
#include "sdmmc_cmd.h"
|
|
#include "driver/sdspi_host.h"
|
|
#include "driver/spi_common.h"
|
|
#include "esp_vfs_fat.h"
|
|
#include <string>
|
|
#include <functional>
|
|
#include "ipc_protocol.h"
|
|
|
|
// Define the SPI host and protocol constants.
|
|
class FSPI
|
|
{
|
|
#define FSPI_CMD_SIZE 4
|
|
#define FSPI_RESP_SIZE 4
|
|
#define FSPI_PKTCTRL_SIZE 4
|
|
#define FSPI_DATABLOCK_SIZE 512
|
|
#define FSPI_FILENAME_LENGTH 28
|
|
#define FSPI_PARAM_LENGTH 128
|
|
#define FSPI_DATABLOCK_OFFSET FSPI_RESP_SIZE + FSPI_PKTCTRL_SIZE - 1
|
|
#define FSPI_CHKSUM_OFFSET FSPI_RESP_SIZE + FSPI_PKTCTRL_SIZE + FSPI_DATABLOCK_SIZE
|
|
#define FSPI_CMDMSG_SIZE FSPI_CMD_SIZE + FSPI_FILENAME_LENGTH
|
|
#define FSPI_DATAMSG_SIZE FSPI_RESP_SIZE + FSPI_PKTCTRL_SIZE + FSPI_DATABLOCK_SIZE
|
|
#define FSPI_HOST SPI2_HOST
|
|
#define FSPI_RESP_ACK 0xE0
|
|
#define FSPI_RESP_NAK 0xEE
|
|
#define FSPI_RESP_ABORT 0xEF
|
|
#define FSPI_RESULT_CODE_SUCCESS 0xF0
|
|
#define FSPI_RESULT_CODE_NEXTBLK 0xF1
|
|
#define FSPI_RESULT_CODE_LASTBLK 0xF2
|
|
#define FSPI_RESULT_CODE_CHKSUM 0xFC
|
|
#define FSPI_RESULT_CODE_BADFILE 0xFD
|
|
#define FSPI_RESULT_CODE_TIMEOUT 0xFE
|
|
#define FSPI_RESULT_CODE_ERROR 0xFF
|
|
#define FSPI_SPI_TICK_TIMEOUT 1000
|
|
#define FSPI_SPI_XACT_TIMEOUT portMAX_DELAY
|
|
|
|
// Macros.
|
|
#define NUMELEM(a) (sizeof(a) / sizeof(a[0]))
|
|
|
|
// Constants.
|
|
#define FSPI_VERSION 1.10
|
|
|
|
public:
|
|
// Prototypes.
|
|
FSPI(void);
|
|
virtual ~FSPI(void) {};
|
|
bool init();
|
|
|
|
// Legacy SPI transactions (kept for backward compatibility with INF data path).
|
|
bool receiveCommand(char *cmd, size_t timeout);
|
|
esp_err_t sendData(const uint8_t *data, size_t length, size_t timeout);
|
|
esp_err_t receiveData(uint8_t *data, size_t length, size_t timeout);
|
|
esp_err_t sendResponse(uint8_t resultCode, size_t length, size_t timeout);
|
|
bool decodeParam(const char *paramStr, std::vector<std::string> *paramVec);
|
|
uint8_t getResponse(size_t timeout);
|
|
esp_err_t sendACK(size_t timeout);
|
|
esp_err_t sendNAK(size_t timeout);
|
|
esp_err_t sendABORT(size_t timeout);
|
|
uint8_t calculateChecksum(uint8_t *data, size_t length);
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Binary IPC frame protocol (v1.1)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// Receive a 64-byte command header from the RP2350 master.
|
|
// Blocks until a transaction completes or the timeout expires.
|
|
// timeout: FreeRTOS ticks (use portMAX_DELAY to wait forever).
|
|
// Returns ESP_OK on success.
|
|
esp_err_t receiveBinaryCmd(uint8_t *cmdBuf, TickType_t timeout = portMAX_DELAY);
|
|
|
|
// Send a binary response frame: 64-byte header + payload + CRC32.
|
|
// hdr - Pre-filled response header (frameType, command, status, payloadLen).
|
|
// payload - Pointer to payload data (may be NULL if payloadLen == 0).
|
|
// payloadLen - Bytes of payload to include.
|
|
// respSize - Total frame size to transmit (header + payload + CRC32); the SPI
|
|
// slave always sends exactly this many bytes so the master can pre-size
|
|
// its receive buffer. Must not exceed IPCF_MAX_FRAME_SIZE.
|
|
// timeout - FreeRTOS ticks.
|
|
esp_err_t sendBinaryResp(const t_IpcFrameHdr *hdr, const uint8_t *payload, uint32_t payloadLen, uint32_t respSize, TickType_t timeout = portMAX_DELAY);
|
|
|
|
// CRC32 — standard IEEE 802.3, matches FSPI_crc32() on RP2350.
|
|
// Uses esp_rom_crc32_le() for hardware acceleration.
|
|
static uint32_t crc32(const uint8_t *data, size_t len);
|
|
|
|
// Public buffers (DMA-capable, allocated in init()).
|
|
// ipcCmdBuf: receives command frames from RP2350 master.
|
|
// ipcRespBuf: transmits response frames to RP2350 master.
|
|
// Also used as dataBuf alias for the legacy single-block path.
|
|
uint8_t *ipcCmdBuf; // IPCF_MAX_FRAME_SIZE bytes
|
|
uint8_t *ipcRespBuf; // IPCF_MAX_FRAME_SIZE bytes
|
|
|
|
// Legacy public data buffer alias (first 512 bytes of ipcCmdBuf).
|
|
uint8_t dataBuf[FSPI_DATABLOCK_SIZE];
|
|
|
|
// Helper method to identify the sub class.
|
|
const char *getClassName(const std::string &prettyFunction)
|
|
{
|
|
size_t colons = prettyFunction.find("::");
|
|
if (colons == std::string::npos)
|
|
return "::";
|
|
size_t begin = prettyFunction.substr(0, colons).rfind(" ") + 1;
|
|
size_t end = colons - begin;
|
|
return (prettyFunction.substr(begin, end).c_str());
|
|
}
|
|
|
|
void replaceExt(std::string &fileName, const std::string &newExt)
|
|
{
|
|
std::string::size_type extPos = fileName.rfind('.', fileName.length());
|
|
if (extPos != std::string::npos)
|
|
fileName.replace(extPos + 1, newExt.length(), newExt);
|
|
}
|
|
|
|
template <typename E> constexpr typename std::underlying_type<E>::type to_underlying(E e) noexcept
|
|
{
|
|
return static_cast<typename std::underlying_type<E>::type>(e);
|
|
}
|
|
|
|
virtual float version(void)
|
|
{
|
|
return (FSPI_VERSION);
|
|
}
|
|
|
|
protected:
|
|
private:
|
|
// No per-instance private state beyond the public DMA buffers.
|
|
};
|
|
#endif // FSPI_H
|