diff --git a/components/esp8266/include/esp8266/spi_struct.h b/components/esp8266/include/esp8266/spi_struct.h index e61d7066..fd60cd27 100644 --- a/components/esp8266/include/esp8266/spi_struct.h +++ b/components/esp8266/include/esp8266/spi_struct.h @@ -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.*/ diff --git a/components/spi_ram/component.mk b/components/spi_ram/component.mk new file mode 100644 index 00000000..e69de29b diff --git a/components/spi_ram/include/spi_ram.h b/components/spi_ram/include/spi_ram.h new file mode 100644 index 00000000..4c17686b --- /dev/null +++ b/components/spi_ram/include/spi_ram.h @@ -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 +#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 \ No newline at end of file diff --git a/components/spi_ram/include/spi_ram_fifo.h b/components/spi_ram/include/spi_ram_fifo.h new file mode 100644 index 00000000..55b93546 --- /dev/null +++ b/components/spi_ram/include/spi_ram_fifo.h @@ -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 +#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 \ No newline at end of file diff --git a/components/spi_ram/spi_ram.c b/components/spi_ram/spi_ram.c new file mode 100644 index 00000000..054076c2 --- /dev/null +++ b/components/spi_ram/spi_ram.c @@ -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 +#include +#include +#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; +} \ No newline at end of file diff --git a/components/spi_ram/spi_ram_fifo.c b/components/spi_ram/spi_ram_fifo.c new file mode 100644 index 00000000..f0aea1d8 --- /dev/null +++ b/components/spi_ram/spi_ram_fifo.c @@ -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 +#include +#include +#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; +} \ No newline at end of file