feature(spi_ram): add spi ram

This commit is contained in:
XiongYu
2019-04-10 20:18:55 +08:00
parent f3c97bb74a
commit 0b0d82ca19
6 changed files with 974 additions and 4 deletions

View File

@ -113,10 +113,10 @@ typedef volatile struct {
uint32_t reserved8: 2; /*reserved*/
uint32_t rd_byte_order: 1; /*In read-data (MISO) phase 1: big-endian 0: little_endian*/
uint32_t wr_byte_order: 1; /*In command address write-data (MOSI) phases 1: big-endian 0: litte_endian*/
uint32_t fwrite_dual: 1; /*In the write operations read-data phase apply 2 signals*/
uint32_t fwrite_quad: 1; /*In the write operations read-data phase apply 4 signals*/
uint32_t fwrite_dio: 1; /*In the write operations address phase and read-data phase apply 2 signals.*/
uint32_t fwrite_qio: 1; /*In the write operations address phase and read-data phase apply 4 signals.*/
uint32_t fwrite_dual: 1; /*In the write operations write-data phase apply 2 signals*/
uint32_t fwrite_quad: 1; /*In the write operations write-data phase apply 4 signals*/
uint32_t fwrite_dio: 1; /*In the write operations address phase and write-data phase apply 2 signals.*/
uint32_t fwrite_qio: 1; /*In the write operations address phase and write-data phase apply 4 signals.*/
uint32_t sio: 1; /*Set the bit to enable 3-line half duplex communication mosi and miso signals share the same pin. 1: enable 0: disable.*/
uint32_t reserved17: 7; /*reserved*/
uint32_t usr_miso_highpart: 1; /*read-data phase only access to high-part of the buffer spi_w8~spi_w15. 1: enable 0: disable.*/

View File

View File

@ -0,0 +1,168 @@
// Copyright 2018-2025 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.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <stdint.h>
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
/* spi ram spi mode default cmd definition */
#define SPI_RAM_SPI_MODE_DEFAULT_CMD 0x000B0200 /* EXIT: 0x00, READ: 0x0B, WRITE: 0x02, START: 0x00*/
/* spi ram spi mode default read wait cycle definition */
#define SPI_RAM_SPI_MODE_DEFAULT_READ_WAIT_CYCLE 8
/* spi ram qpi mode default cmd definition */
#define SPI_RAM_QPI_MODE_DEFAULT_CMD 0xF5EB3835 /* EXIT: 0xF5, READ: 0xEB, WRITE: 0x38, START: 0x35*/
/* spi ram qpi mode default read wait cycle definition */
#define SPI_RAM_QPI_MODE_DEFAULT_READ_WAIT_CYCLE 6
/**
* @brief spi ram number
*
* @note SPI_RAM_NUM_0 use hardware CS (IO0) and SPI_RAM_NUM_1 use GPIO to simulate CS (IO5).
*/
typedef enum {
SPI_RAM_NUM_0 = 0x0,
SPI_RAM_NUM_1,
SPI_RAM_NUM_MAX
} spi_ram_num_t;
/**
* @brief spi ram clock division factor enumeration
*/
typedef enum {
SPI_RAM_2MHz_DIV = 40,
SPI_RAM_4MHz_DIV = 20,
SPI_RAM_5MHz_DIV = 16,
SPI_RAM_8MHz_DIV = 10,
SPI_RAM_10MHz_DIV = 8,
SPI_RAM_16MHz_DIV = 5,
SPI_RAM_20MHz_DIV = 4,
SPI_RAM_40MHz_DIV = 2,
SPI_RAM_80MHz_DIV = 1,
} spi_ram_clk_div_t;
typedef union {
struct {
uint32_t start: 8;
uint32_t write: 8;
uint32_t read: 8;
uint32_t exit: 8;
};
uint32_t val;
} spi_ram_cmd_t;
/**
* @brief spi ram initialization parameter structure type definition
*/
typedef struct {
uint32_t size;
uint32_t read_wait_cycle;
spi_ram_cmd_t cmd;
spi_ram_clk_div_t clk_div;
} spi_ram_config_t;
/**
* @brief spi ram check function
*
* @param num spi ram number
* - SPI_RAM_NUM_0
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_FAIL spi ram error
*/
esp_err_t spi_ram_check(spi_ram_num_t num);
/**
* @brief Set the spi ram clock division factor
*
* @param num spi ram number
* - SPI_RAM_NUM_0
* @param data Pointer to the spi ram clock division factor
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_FAIL spi ram not installed yet
*/
esp_err_t spi_ram_set_clk_div(spi_ram_num_t num, spi_ram_clk_div_t *clk_div);
/**
* @brief spi ram data write function
*
* @param num spi ram number
* - SPI_RAM_NUM_0
* @param addr spi ram write address
* @param data Pointer to the write data buffer
* @param len Length of write data, range [1, 64]
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_FAIL spi ram not installed yet
*/
esp_err_t spi_ram_write(spi_ram_num_t num, uint32_t addr, uint8_t *data, int len);
/**
* @brief spi ram data read function
*
* @param num spi ram number
* - SPI_RAM_NUM_0
* @param addr spi ram read address
* @param data Pointer to the read data buffer
* @param len Length of read data, range [1, 64]
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_FAIL spi ram not installed yet
*/
esp_err_t spi_ram_read(spi_ram_num_t num, uint32_t addr, uint8_t *data, int len);
/**
* @brief Deinit the spi ram
*
* @param num spi ram number
* - SPI_RAM_NUM_0
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_FAIL spi ram not installed yet
*/
esp_err_t spi_ram_deinit(spi_ram_num_t num);
/**
* @brief Initialize the spi ram
*
* @param num spi ram number
* - SPI_RAM_NUM_0
* @param config Pointer to deliver initialize configuration parameter
*
* @return
* - ESP_OK Success
* - ESP_ERR_NO_MEM malloc fail
* - ESP_FAIL spi ram has been initialized
*/
esp_err_t spi_ram_init(spi_ram_num_t num, spi_ram_config_t *config);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,158 @@
// Copyright 2018-2025 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.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <stdint.h>
#include "esp_err.h"
#include "spi_ram.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef void *spi_ram_fifo_handle_t;
/**
* @brief spi ram fifo initialization parameter structure type definition
*/
typedef struct {
spi_ram_num_t ram_num;
uint32_t start_addr;
uint32_t total_size;
} spi_ram_fifo_config_t;
/**
* @brief spi ram fifo write function
*
* @note spi ram fifo may block waiting for free space in fifo
*
* @param handle spi ram fifo handle
* @param data Pointer to the write data buffer
* @param len Length of write data, range: len > 0
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_FAIL spi ram fifo not created yet
*/
esp_err_t spi_ram_fifo_write(spi_ram_fifo_handle_t handle, uint8_t *data, int len);
/**
* @brief spi ram fifo read function
*
* @note spi ram fifo may block waiting for data in fifo
*
* @param handle spi ram fifo handle
* @param data Pointer to the read data buffer
* @param len Length of read data, range: len > 0
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_FAIL spi ram fifo not created yet
*/
esp_err_t spi_ram_fifo_read(spi_ram_fifo_handle_t handle, uint8_t *data, int len);
/**
* @brief Get spi ram fifo filled length
*
* @param handle spi ram fifo handle
* @param len Pointer to the fifo filled length
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_FAIL spi ram fifo not created yet
*/
esp_err_t spi_ram_fifo_get_fill(spi_ram_fifo_handle_t handle, uint32_t *len);
/**
* @brief Get spi ram fifo free length
*
* @param handle spi ram fifo handle
* @param len Pointer to the fifo free length
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_FAIL spi ram fifo not created yet
*/
esp_err_t spi_ram_fifo_get_free(spi_ram_fifo_handle_t handle, uint32_t *len);
/**
* @brief Get spi ram fifo total length
*
* @param handle spi ram fifo handle
* @param len Pointer to the fifo total length
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_FAIL spi ram fifo not created yet
*/
esp_err_t spi_ram_fifo_get_total(spi_ram_fifo_handle_t handle, uint32_t *len);
/**
* @brief Get spi ram fifo overflow number of times
*
* @param handle spi ram fifo handle
* @param num Pointer to the fifo overflow number of times
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_FAIL spi ram fifo not created yet
*/
esp_err_t spi_ram_fifo_get_overflow(spi_ram_fifo_handle_t handle, uint32_t *num);
/**
* @brief Get spi ram fifo underrun number of times
*
* @param handle spi ram fifo handle
* @param num Pointer to the fifo underrun number of times
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_FAIL spi ram fifo not created yet
*/
esp_err_t spi_ram_fifo_get_underrun(spi_ram_fifo_handle_t handle, uint32_t *num);
/**
* @brief Delete the spi ram fifo
*
* @param handle spi ram fifo handle
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
* - ESP_FAIL spi ram fifo not created yet
*/
esp_err_t spi_ram_fifo_delete(spi_ram_fifo_handle_t handle);
/**
* @brief Create the spi ram fifo
*
* @param config Pointer to the configuration parameter
*
* @return
* - spi ram fifo handle
*/
spi_ram_fifo_handle_t spi_ram_fifo_create(spi_ram_fifo_config_t *config);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,404 @@
// Copyright 2018-2025 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.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "esp8266/eagle_soc.h"
#include "esp8266/spi_struct.h"
#include "esp8266/pin_mux_register.h"
#include "esp8266/spi_register.h"
#include "esp8266/gpio_struct.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "spi_ram.h"
#define SPI_RAM_ENTER_CRITICAL() portENTER_CRITICAL()
#define SPI_RAM_EXIT_CRITICAL() portEXIT_CRITICAL()
static const char *TAG = "spi_ram";
#define SPI_RAM_CHECK(a, str, ret_val) \
if (!(a)) { \
ESP_LOGE(TAG,"%s(%d): %s", __FUNCTION__, __LINE__, str); \
return (ret_val); \
}
typedef struct {
uint32_t size;
uint32_t read_wait_cycle;
spi_ram_cmd_t cmd;
uint32_t clock;
bool qpi_flag;
SemaphoreHandle_t mux;
} spi_ram_obj_t;
spi_ram_obj_t *spi_ram_obj[SPI_RAM_NUM_MAX] = {NULL};
static esp_err_t IRAM_ATTR spi_ram_write_cmd(spi_ram_num_t num, uint8_t cmd)
{
SPI_RAM_CHECK((num < SPI_RAM_NUM_MAX), "spi ram num error", ESP_ERR_INVALID_ARG);
SPI_RAM_CHECK(spi_ram_obj[num], "spi ram not installed yet", ESP_FAIL);
xSemaphoreTake(spi_ram_obj[num]->mux, (portTickType)portMAX_DELAY);
SPI_RAM_ENTER_CRITICAL();
while (SPI1.cmd.usr);
if (spi_ram_obj[num]->qpi_flag == false) {
SPI1.user.val |= SPI_CS_SETUP | SPI_CS_HOLD | SPI_USR_COMMAND;
SPI1.user.val &= ~(SPI_FLASH_MODE | SPI_USR_ADDR | SPI_USR_DUMMY | SPI_USR_MOSI | SPI_USR_MISO | SPI_FWRITE_QIO);
// SPI_FLASH_USER2 bit28-31 is cmd length,cmd bit length is value(0-15)+1,
// bit15-0 is cmd value.
SPI1.user2.val = ((7 & SPI_USR_COMMAND_BITLEN) << SPI_USR_COMMAND_BITLEN_S) | ((uint32_t)cmd);
SPI1.ctrl.val &= ~(SPI_QIO_MODE | SPI_FASTRD_MODE);
} else {
SPI1.user.val |= SPI_CS_SETUP | SPI_CS_HOLD | SPI_USR_MOSI | SPI_FWRITE_QIO;
SPI1.user.val &= ~(SPI_FLASH_MODE | SPI_USR_COMMAND | SPI_USR_ADDR | SPI_USR_DUMMY | SPI_USR_MISO );
SPI1.user1.usr_mosi_bitlen = 7; // 8bits cmd
SPI1.data_buf[0] = cmd & 0xFF; // write cmd
SPI1.ctrl.val |= SPI_QIO_MODE | SPI_FASTRD_MODE;
}
if (SPI_RAM_NUM_1 == num) {
SPI1.pin.cs2_dis = true;
while (SPI0.cmd.usr);
GPIO.out_w1tc |= GPIO_Pin_5;
SPI1.cmd.usr = true;
while (SPI1.cmd.usr);
GPIO.out_w1ts |= GPIO_Pin_5;
} else {
SPI1.pin.cs2_dis = false;
SPI1.cmd.usr = true;
}
SPI_RAM_EXIT_CRITICAL();
xSemaphoreGive(spi_ram_obj[num]->mux);
return ESP_OK;
}
esp_err_t IRAM_ATTR spi_ram_write(spi_ram_num_t num, uint32_t addr, uint8_t *data, int len)
{
SPI_RAM_CHECK((num < SPI_RAM_NUM_MAX), "spi ram num error", ESP_ERR_INVALID_ARG);
SPI_RAM_CHECK(spi_ram_obj[num], "spi ram not installed yet", ESP_FAIL);
SPI_RAM_CHECK(data, "param null", ESP_ERR_INVALID_ARG);
SPI_RAM_CHECK(addr + len <= spi_ram_obj[num]->size, "Address out of range", ESP_ERR_INVALID_ARG);
SPI_RAM_CHECK((len > 0) && (len <= 64), "len error", ESP_ERR_INVALID_ARG);
uint32_t i;
uint32_t buf;
xSemaphoreTake(spi_ram_obj[num]->mux, (portTickType)portMAX_DELAY);
SPI_RAM_ENTER_CRITICAL();
while (SPI1.cmd.usr);
SPI1.clock.val = spi_ram_obj[num]->clock;
if (spi_ram_obj[num]->qpi_flag == false) {
SPI1.user.val |= SPI_CS_SETUP | SPI_CS_HOLD | SPI_USR_COMMAND | SPI_USR_ADDR | SPI_USR_MOSI;
SPI1.user.val &= ~(SPI_FLASH_MODE | SPI_USR_MISO | SPI_USR_DUMMY | SPI_FWRITE_QIO);
SPI1.user1.val = ((((8 * len) - 1)&SPI_USR_MOSI_BITLEN) << SPI_USR_MOSI_BITLEN_S) | // len bitsbits of data out
((0 & SPI_USR_MISO_BITLEN) << SPI_USR_MISO_BITLEN_S) | // no data in
((23 & SPI_USR_ADDR_BITLEN) << SPI_USR_ADDR_BITLEN_S); // address is 24 bits A0-A23
SPI1.addr = addr << 8; // write address
SPI1.user2.val = (((7 & SPI_USR_COMMAND_BITLEN) << SPI_USR_COMMAND_BITLEN_S) | spi_ram_obj[num]->cmd.write);
SPI1.ctrl.val &= ~(SPI_QIO_MODE | SPI_FASTRD_MODE);
} else {
SPI1.user.val |= SPI_CS_SETUP | SPI_CS_HOLD | SPI_USR_ADDR | SPI_USR_MOSI | SPI_FWRITE_QIO;
SPI1.user.val &= ~(SPI_FLASH_MODE | SPI_USR_MISO | SPI_USR_COMMAND | SPI_USR_DUMMY);
SPI1.user1.val = ((((8 * len) - 1)&SPI_USR_MOSI_BITLEN) << SPI_USR_MOSI_BITLEN_S) | // len bitsbits of data out
((0 & SPI_USR_MISO_BITLEN) << SPI_USR_MISO_BITLEN_S) | // no data i
((31 & SPI_USR_ADDR_BITLEN) << SPI_USR_ADDR_BITLEN_S); // 8bits command+address is 24 bits A0-A23
SPI1.addr = addr | (spi_ram_obj[num]->cmd.write<<24); // write address
SPI1.ctrl.val |= SPI_QIO_MODE | SPI_FASTRD_MODE;
}
// Assume unaligned src: Copy byte-wise.
for (i = 0; i < (len + 3) / 4; i++) {
buf = *(uint32_t *)(data + i*4);
SPI1.data_buf[i] = buf;
}
if (SPI_RAM_NUM_1 == num) {
SPI1.pin.cs2_dis = true;
while (SPI0.cmd.usr);
GPIO.out_w1tc |= GPIO_Pin_5;
SPI1.cmd.usr = true;
while (SPI1.cmd.usr);
GPIO.out_w1ts |= GPIO_Pin_5;
} else {
SPI1.pin.cs2_dis = false;
SPI1.cmd.usr = true;
}
SPI_RAM_EXIT_CRITICAL();
xSemaphoreGive(spi_ram_obj[num]->mux);
return ESP_OK;
}
esp_err_t IRAM_ATTR spi_ram_read(spi_ram_num_t num, uint32_t addr, uint8_t *data, int len)
{
SPI_RAM_CHECK((num < SPI_RAM_NUM_MAX), "spi ram num error", ESP_ERR_INVALID_ARG);
SPI_RAM_CHECK(spi_ram_obj[num], "spi ram not installed yet", ESP_FAIL);
SPI_RAM_CHECK(data, "param null", ESP_ERR_INVALID_ARG);
SPI_RAM_CHECK(addr + len <= spi_ram_obj[num]->size, "Address out of range", ESP_ERR_INVALID_ARG);
SPI_RAM_CHECK((len > 0) && (len <= 64), "len error", ESP_ERR_INVALID_ARG);
uint32_t buf = 0;
int i = 0;
xSemaphoreTake(spi_ram_obj[num]->mux, (portTickType)portMAX_DELAY);
SPI_RAM_ENTER_CRITICAL();
while (SPI1.cmd.usr);
SPI1.clock.val = spi_ram_obj[num]->clock;
if (spi_ram_obj[num]->qpi_flag == false) {
SPI1.user.val |= SPI_CS_SETUP | SPI_CS_HOLD | SPI_USR_COMMAND | SPI_USR_ADDR | SPI_USR_MISO;
SPI1.user.val &= ~(SPI_FLASH_MODE | SPI_USR_MOSI | SPI_FWRITE_QIO);
SPI1.user1.val = ((0 & SPI_USR_MOSI_BITLEN) << SPI_USR_MOSI_BITLEN_S) | // no data out
((((8 * len) - 1)&SPI_USR_MISO_BITLEN) << SPI_USR_MISO_BITLEN_S) | // len bits of data in
((23 & SPI_USR_ADDR_BITLEN) << SPI_USR_ADDR_BITLEN_S); // address is 24 bits A0-A23
if (spi_ram_obj[num]->read_wait_cycle != 0x0) {
SPI1.user.usr_dummy = true;
SPI1.user1.usr_dummy_cyclelen = spi_ram_obj[num]->read_wait_cycle - 1; // 1 dummy cycle = 1 spi clk cycle
} else {
SPI1.user.usr_dummy = false;
}
SPI1.addr = addr << 8; // write address
SPI1.user2.val = (((7 & SPI_USR_COMMAND_BITLEN) << SPI_USR_COMMAND_BITLEN_S) | spi_ram_obj[num]->cmd.read);
SPI1.ctrl.val &= ~(SPI_QIO_MODE | SPI_FASTRD_MODE);
} else {
SPI1.user.val |= SPI_CS_SETUP | SPI_CS_HOLD | SPI_USR_ADDR | SPI_USR_MISO | SPI_USR_DUMMY | SPI_FWRITE_QIO;
SPI1.user.val &= ~(SPI_FLASH_MODE | SPI_USR_MOSI | SPI_USR_COMMAND);
SPI1.user1.val = ((0 & SPI_USR_MOSI_BITLEN) << SPI_USR_MOSI_BITLEN_S) | // no data out
((((8 * len) - 1)&SPI_USR_MISO_BITLEN) << SPI_USR_MISO_BITLEN_S) | // len bits of data in
((31 & SPI_USR_ADDR_BITLEN) << SPI_USR_ADDR_BITLEN_S); // 8bits command+address is 24 bits A0-A23
if (spi_ram_obj[num]->read_wait_cycle != 0x0) {
SPI1.user.usr_dummy = true;
SPI1.user1.usr_dummy_cyclelen = spi_ram_obj[num]->read_wait_cycle - 1; // 1 dummy cycle = 1 spi clk cycle
} else {
SPI1.user.usr_dummy = false;
}
SPI1.addr = addr | (spi_ram_obj[num]->cmd.read<<24); // write address
SPI1.ctrl.val |= SPI_QIO_MODE | SPI_FASTRD_MODE;
}
if (SPI_RAM_NUM_1 == num) {
SPI1.pin.cs2_dis = true;
while (SPI0.cmd.usr);
GPIO.out_w1tc |= GPIO_Pin_5;
SPI1.cmd.usr = true;
while (SPI1.cmd.usr);
GPIO.out_w1ts |= GPIO_Pin_5;
} else {
SPI1.pin.cs2_dis = false;
SPI1.cmd.usr = true;
while (SPI1.cmd.usr);
}
// Unaligned dest address. Copy 8bit at a time
while (len > 0) {
buf = SPI1.data_buf[i];
data[i * 4 + 0] = (buf >> 0) & 0xff;
if (len > 1) {
data[i * 4 + 1] = (buf >> 8) & 0xff;
}
if (len > 2) {
data[i * 4 + 2] = (buf >> 16) & 0xff;
}
if (len > 3) {
data[i * 4 + 3] = (buf >> 24) & 0xff;
}
len -= 4;
i++;
}
SPI_RAM_EXIT_CRITICAL();
xSemaphoreGive(spi_ram_obj[num]->mux);
return ESP_OK;
}
esp_err_t spi_ram_check(spi_ram_num_t num)
{
SPI_RAM_CHECK((num < SPI_RAM_NUM_MAX), "spi ram num error", ESP_ERR_INVALID_ARG);
SPI_RAM_CHECK(spi_ram_obj[num], "spi ram not installed yet", ESP_FAIL);
int x;
int err = 0;
uint8_t a[64];
uint8_t b[64];
uint8_t aa, bb;
for (x = 0; x < 64; x++) {
a[x] = x ^ (x << 2);
b[x] = 0xaa ^ x;
}
spi_ram_write(num, 0x0, a, 64);
spi_ram_write(num, 0x100, b, 64);
spi_ram_read(num, 0x0, a, 64);
spi_ram_read(num, 0x100, b, 64);
for (x = 0; x < 64; x++) {
aa = x ^ (x << 2);
bb = 0xaa ^ x;
if (aa != a[x]) {
err = 1;
ESP_LOGE(TAG, "num[%d]: aa: 0x%x != 0x%x\n", num, aa, a[x]);
}
if (bb != b[x]) {
err = 1;
ESP_LOGE(TAG, "num[%d]: bb: 0x%x != 0x%x\n", num, bb, b[x]);
}
}
if (err) {
return ESP_FAIL;
} else {
return ESP_OK;
}
}
esp_err_t spi_ram_set_clk_div(spi_ram_num_t num, spi_ram_clk_div_t *clk_div)
{
SPI_RAM_CHECK((num < SPI_RAM_NUM_MAX), "spi ram num error", ESP_ERR_INVALID_ARG);
SPI_RAM_CHECK(spi_ram_obj[num], "spi ram not installed yet", ESP_FAIL);
SPI_RAM_CHECK(clk_div && *clk_div > 0, "parameter pointer is empty", ESP_ERR_INVALID_ARG);
#ifdef CONFIG_ESPTOOLPY_FLASHFREQ_80M
SPI_RAM_CHECK(SPI_RAM_80MHz_DIV == *clk_div, "Since the SPI Flash clock frequency is set to 80MHz, the SPI ram clock frequency also needs to be set to 80MHz.", ESP_ERR_INVALID_ARG);
#else
SPI_RAM_CHECK(SPI_RAM_80MHz_DIV != *clk_div, "SPI Flash clock frequency also need to be set to 80MHz", ESP_ERR_INVALID_ARG);
#endif
SPI_RAM_ENTER_CRITICAL();
if (SPI_RAM_80MHz_DIV == *clk_div) {
SET_PERI_REG_MASK(PERIPHS_IO_MUX_CONF_U, SPI0_CLK_EQU_SYS_CLK);
SPI1.clock.clk_equ_sysclk = true;
} else {
CLEAR_PERI_REG_MASK(PERIPHS_IO_MUX_CONF_U, SPI0_CLK_EQU_SYS_CLK);
// FRE(SCLK) = clk_equ_sysclk ? 80MHz : APB_CLK(80MHz) / clkdiv_pre / clkcnt
SPI1.clock.clk_equ_sysclk = false;
SPI1.clock.clkdiv_pre = 0;
SPI1.clock.clkcnt_n = *clk_div - 1;
// In the master mode clkcnt_h = floor((clkcnt_n+1)/2-1). In the slave mode it must be 0
SPI1.clock.clkcnt_h = *clk_div / 2 - 1;
// In the master mode clkcnt_l = clkcnt_n. In the slave mode it must be 0
SPI1.clock.clkcnt_l = *clk_div - 1;
}
spi_ram_obj[num]->clock = SPI1.clock.val;
SPI_RAM_EXIT_CRITICAL();
return ESP_OK;
}
esp_err_t spi_ram_deinit(spi_ram_num_t num)
{
SPI_RAM_CHECK((num < SPI_RAM_NUM_MAX), "spi ram num error", ESP_ERR_INVALID_ARG);
SPI_RAM_CHECK(spi_ram_obj[num], "spi ram not installed yet", ESP_FAIL);
if (spi_ram_obj[num]->qpi_flag == true) {
spi_ram_write_cmd(num, spi_ram_obj[num]->cmd.exit);
spi_ram_obj[num]->qpi_flag = false;
}
if (spi_ram_obj[num]->mux) {
xSemaphoreTake(spi_ram_obj[num]->mux, (portTickType)portMAX_DELAY);
}
vSemaphoreDelete(spi_ram_obj[num]->mux);
heap_caps_free(spi_ram_obj[num]);
spi_ram_obj[num] = NULL;
return ESP_OK;
}
esp_err_t spi_ram_init(spi_ram_num_t num, spi_ram_config_t *config)
{
SPI_RAM_CHECK((num < SPI_RAM_NUM_MAX), "spi ram num error", ESP_ERR_INVALID_ARG);
SPI_RAM_CHECK(config, "param null", ESP_ERR_INVALID_ARG);
SPI_RAM_CHECK(config->cmd.val != 0, "cmd error", ESP_ERR_INVALID_ARG);
SPI_RAM_CHECK(spi_ram_obj[num] == NULL, "spi ram has been initialized", ESP_FAIL);
spi_ram_obj[num] = (spi_ram_obj_t *)heap_caps_zalloc(sizeof(spi_ram_obj_t), MALLOC_CAP_8BIT);
SPI_RAM_CHECK(spi_ram_obj[num], "malloc spi ram obj error", ESP_ERR_NO_MEM);
spi_ram_obj[num]->mux = xSemaphoreCreateMutex();
if (NULL == spi_ram_obj[num]->mux) {
spi_ram_deinit(num);
return ESP_ERR_NO_MEM;
}
spi_ram_obj[num]->size = config->size;
spi_ram_obj[num]->cmd = config->cmd;
spi_ram_obj[num]->read_wait_cycle = config->read_wait_cycle;
uint8_t dummy[64];
spi_ram_set_clk_div(num, &config->clk_div);
SPI_RAM_ENTER_CRITICAL();
// hspi overlap to spi, two spi masters on cspi
SET_PERI_REG_MASK(HOST_INF_SEL, PERI_IO_CSPI_OVERLAP);
// set higher priority for spi than hspi
SPI0.ext3.val = 0x1;
SPI1.ext3.val = 0x3;
SPI1.user.cs_setup = true;
SPI1.user.usr_mosi_highpart = false;
SPI1.user.usr_miso_highpart = false;
if (SPI_RAM_NUM_1 == num) {
// Using GPIO5 to simulate CS
WRITE_PERI_REG(PERIPHS_IO_MUX_GPIO5_U, FUNC_GPIO5);
GPIO.out_w1ts |= GPIO_Pin_5;
GPIO.enable_w1ts |= GPIO_Pin_5;
SPI1.pin.val |= SPI_CS0_DIS | SPI_CS1_DIS | SPI_CS2_DIS;
} else {
// select HSPI CS2 ,disable HSPI CS0 and CS1
SPI1.pin.cs2_dis = false;
SPI1.pin.val |= SPI_CS0_DIS | SPI_CS1_DIS;
// SET IO MUX FOR GPIO0 , SELECT PIN FUNC AS SPI CS2
// IT WORK AS HSPI CS2 AFTER OVERLAP(THERE IS NO PIN OUT FOR NATIVE HSPI CS1/2)
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_SPICS2);
}
if (spi_ram_obj[num]->cmd.start != 0x0) {
spi_ram_write_cmd(num, spi_ram_obj[num]->cmd.start);
spi_ram_obj[num]->qpi_flag = true;
}
SPI_RAM_EXIT_CRITICAL();
//Dummy read to clear any weird state the SPI ram chip may be in
spi_ram_read(num, 0x0, dummy, 64);
return ESP_OK;
}

View File

@ -0,0 +1,240 @@
// Copyright 2018-2025 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.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "esp_log.h"
#include "spi_ram_fifo.h"
#include "spi_ram.h"
#define SPI_FIFO_SIZE 64
static const char *TAG = "spi_ram_fifo";
typedef struct {
uint32_t read_pos;
uint32_t write_pos;
uint32_t fill_len;
uint32_t overflow_num;
uint32_t underrun_num;
spi_ram_num_t ram_num;
uint32_t start_addr;
uint32_t total_size;
SemaphoreHandle_t mux;
SemaphoreHandle_t read_sem;
SemaphoreHandle_t write_sem;
} spi_ram_fifo_obj_t;
#define SPI_RAM_FIFO_CHECK(a, str, ret_val) \
if (!(a)) { \
ESP_LOGE(TAG,"%s(%d): %s", __FUNCTION__, __LINE__, str); \
return (ret_val); \
}
esp_err_t spi_ram_fifo_write(spi_ram_fifo_handle_t handle, uint8_t *data, int len)
{
spi_ram_fifo_obj_t *obj = (spi_ram_fifo_obj_t *)handle;
int n;
SPI_RAM_FIFO_CHECK(handle, "spi ram fifo not created yet", ESP_FAIL);
SPI_RAM_FIFO_CHECK(data, "param null", ESP_ERR_INVALID_ARG);
while (len > 0) {
n = len;
if (n > SPI_FIFO_SIZE) {
n = SPI_FIFO_SIZE; // don't read more than SPI_FIFO_SIZE
}
if (n > (obj->total_size - obj->write_pos)) {
n = obj->total_size - obj->write_pos; // don't read past end of buffer
}
xSemaphoreTake(obj->mux, portMAX_DELAY);
if ((obj->total_size - obj->fill_len) < n) {
// Not enough free room in FIFO. Wait till there's some read and try again.
obj->overflow_num++;
xSemaphoreGive(obj->mux);
xSemaphoreTake(obj->write_sem, portMAX_DELAY);
} else {
//Write the data.
spi_ram_write(obj->ram_num, obj->start_addr + obj->write_pos, data, n);
data += n;
len -= n;
obj->fill_len += n;
obj->write_pos += n;
if (obj->write_pos >= obj->total_size) {
obj->write_pos = 0;
}
xSemaphoreGive(obj->mux);
xSemaphoreGive(obj->read_sem); // Tell reader thread there's some data in the fifo.
}
}
return ESP_OK;
}
esp_err_t spi_ram_fifo_read(spi_ram_fifo_handle_t handle, uint8_t *data, int len)
{
spi_ram_fifo_obj_t *obj = (spi_ram_fifo_obj_t *)handle;
int n;
SPI_RAM_FIFO_CHECK(handle, "spi ram fifo not created yet", ESP_FAIL);
SPI_RAM_FIFO_CHECK(data, "param null", ESP_ERR_INVALID_ARG);
while (len > 0) {
n = len;
if (n > SPI_FIFO_SIZE) {
n = SPI_FIFO_SIZE; // don't read more than SPI_FIFO_SIZE
}
if (n > (obj->total_size - obj->read_pos)) {
n = obj->total_size - obj->read_pos; // don't read past end of buffer
}
xSemaphoreTake(obj->mux, portMAX_DELAY);
if (obj->fill_len < n) {
// Not enough data in FIFO. Wait till there's some written and try again.
obj->underrun_num++;
xSemaphoreGive(obj->mux);
xSemaphoreTake(obj->read_sem, portMAX_DELAY);
} else {
// Read the data.
spi_ram_read(obj->ram_num, obj->start_addr + obj->read_pos, data, n);
data += n;
len -= n;
obj->fill_len -= n;
obj->read_pos += n;
if (obj->read_pos >= obj->total_size) {
obj->read_pos = 0;
}
xSemaphoreGive(obj->mux);
xSemaphoreGive(obj->write_sem); // Indicate writer thread there's some free space in the fifo
}
}
return ESP_OK;
}
esp_err_t spi_ram_fifo_get_fill(spi_ram_fifo_handle_t handle, uint32_t *len)
{
spi_ram_fifo_obj_t *obj = (spi_ram_fifo_obj_t *)handle;
SPI_RAM_FIFO_CHECK(handle, "spi ram fifo not created yet", ESP_FAIL);
SPI_RAM_FIFO_CHECK(len, "param null", ESP_ERR_INVALID_ARG);
xSemaphoreTake(obj->mux, portMAX_DELAY);
*len = obj->fill_len;
xSemaphoreGive(obj->mux);
return ESP_OK;
}
esp_err_t spi_ram_fifo_get_free(spi_ram_fifo_handle_t handle, uint32_t *len)
{
spi_ram_fifo_obj_t *obj = (spi_ram_fifo_obj_t *)handle;
SPI_RAM_FIFO_CHECK(handle, "spi ram fifo not created yet", ESP_FAIL);
SPI_RAM_FIFO_CHECK(len, "param null", ESP_ERR_INVALID_ARG);
xSemaphoreTake(obj->mux, portMAX_DELAY);
*len = obj->total_size - obj->fill_len;
xSemaphoreGive(obj->mux);
return ESP_OK;
}
esp_err_t spi_ram_fifo_get_total(spi_ram_fifo_handle_t handle, uint32_t *len)
{
spi_ram_fifo_obj_t *obj = (spi_ram_fifo_obj_t *)handle;
SPI_RAM_FIFO_CHECK(handle, "spi ram fifo not created yet", ESP_FAIL);
SPI_RAM_FIFO_CHECK(len, "param null", ESP_ERR_INVALID_ARG);
xSemaphoreTake(obj->mux, portMAX_DELAY);
*len = obj->total_size;
xSemaphoreGive(obj->mux);
return ESP_OK;
}
esp_err_t spi_ram_fifo_get_overflow(spi_ram_fifo_handle_t handle, uint32_t *num)
{
spi_ram_fifo_obj_t *obj = (spi_ram_fifo_obj_t *)handle;
SPI_RAM_FIFO_CHECK(handle, "spi ram fifo not created yet", ESP_FAIL);
SPI_RAM_FIFO_CHECK(num, "param null", ESP_ERR_INVALID_ARG);
xSemaphoreTake(obj->mux, portMAX_DELAY);
*num = obj->overflow_num;
xSemaphoreGive(obj->mux);
return ESP_OK;
}
esp_err_t spi_ram_fifo_get_underrun(spi_ram_fifo_handle_t handle, uint32_t *num)
{
spi_ram_fifo_obj_t *obj = (spi_ram_fifo_obj_t *)handle;
SPI_RAM_FIFO_CHECK(handle, "spi ram fifo not created yet", ESP_FAIL);
SPI_RAM_FIFO_CHECK(num, "param null", ESP_ERR_INVALID_ARG);
xSemaphoreTake(obj->mux, portMAX_DELAY);
*num = obj->underrun_num;
xSemaphoreGive(obj->mux);
return ESP_OK;
}
esp_err_t spi_ram_fifo_delete(spi_ram_fifo_handle_t handle)
{
spi_ram_fifo_obj_t *obj = (spi_ram_fifo_obj_t *)handle;
SPI_RAM_FIFO_CHECK(handle, "spi ram fifo not created yet", ESP_FAIL);
if (obj->mux) {
xSemaphoreTake(obj->mux, portMAX_DELAY);
}
vSemaphoreDelete(obj->write_sem);
vSemaphoreDelete(obj->read_sem);
vSemaphoreDelete(obj->mux);
heap_caps_free(obj);
return ESP_OK;
}
spi_ram_fifo_handle_t spi_ram_fifo_create(spi_ram_fifo_config_t *config)
{
spi_ram_fifo_obj_t *obj = NULL;
if (NULL == config) {
ESP_LOGE(TAG, "config null");
return NULL;
}
obj = (spi_ram_fifo_obj_t *)heap_caps_zalloc(sizeof(spi_ram_fifo_obj_t), MALLOC_CAP_8BIT);
if (NULL == obj) {
ESP_LOGE(TAG, "obj malloc fail");
return NULL;
}
obj->mux = xSemaphoreCreateMutex();
obj->read_sem = xSemaphoreCreateBinary();
obj->write_sem = xSemaphoreCreateBinary();
if (NULL == obj->mux || NULL == obj->read_sem || NULL == obj->read_sem) {
ESP_LOGE(TAG, "obj create fail");
spi_ram_fifo_delete((spi_ram_fifo_handle_t)obj);
return NULL;
}
obj->ram_num = config->ram_num;
obj->start_addr = config->start_addr;
obj->total_size = config->total_size;
return (spi_ram_fifo_handle_t)obj;
}