Skip to content

Instantly share code, notes, and snippets.

@GOROman
Last active January 2, 2025 03:49
Show Gist options
  • Select an option

  • Save GOROman/9ae00eacc297a8e6676d29a5884ac502 to your computer and use it in GitHub Desktop.

Select an option

Save GOROman/9ae00eacc297a8e6676d29a5884ac502 to your computer and use it in GitHub Desktop.
ESP32S3 + esp-idf + ESP PSRAM64H (QPI接続) で読み書きしよう!
// ESP-IDF v5.5.0
// ESP32S3
// SPI PSRAM (ESP-PSRAM64H) is used.
#include <stdio.h>
#include "driver/spi_master.h"
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include <string.h>
// GPIO Pin Assignments
#define SPI_MOSI 4 // 5:SI/SIO[0] I/O Serial input
#define SPI_MISO 5 // 2:SO/SIO[1] I/O Serial output
#define SPI_QUADWP 6 // 3: SIO[2]
#define SPI_QUADHD 7 // 7: SIO[3]
#define SPI_SCK 8 // 6:CLK Input Clock signa
#define SPI_CS 9 // 1:CS Input Chip select signal(Active low)
// ESP-PSRAM64 Command
#define PSRAM_READ 0x03
#define PSRAM_FAST_READ 0x0B
#define PSRAM_FAST_READ_QUAD 0xEB
#define PSRAM_WRITE 0x02
#define PSRAM_QUAD_WRITE 0x38
#define PSRAM_ENTER_QMODE 0x35
#define PSRAM_EXIT_QMODE 0xF5
#define PSRAM_RESET_EN 0x66
#define PSRAM_RESET 0x99
#define PSRAM_SET_BURST_LEN 0xC0
#define PSRAM_DEVICE_ID 0x9F
#define PSRAM_CLOCK 1
//(10 * 1000 * 1000) // 10MHz
#define SPI_DEF SPI2_HOST
spi_device_handle_t psram_spi;
// PSRAMのリセット
void psram_reset() {
printf("Resetting PSRAM\n");
spi_transaction_t t = {
.length = 8,
.tx_data = {PSRAM_RESET_EN},
.flags = SPI_TRANS_USE_TXDATA
};
ESP_ERROR_CHECK(spi_device_polling_transmit(psram_spi, &t));
t.tx_data[0] = PSRAM_RESET;
ESP_ERROR_CHECK(spi_device_polling_transmit(psram_spi, &t));
}
// Quad Mode に入る
void psram_enter_qmode() {
printf("Entering Quad Mode\n");
uint8_t tx_buffer[1] = {PSRAM_ENTER_QMODE};
// トランザクション設定
spi_transaction_t t = {
.length = 8 * sizeof(tx_buffer), // ビット単位で長さを指定
.tx_buffer = tx_buffer,
};
// トランザクション送信
ESP_ERROR_CHECK(spi_device_polling_transmit(psram_spi, &t));
}
// Quad Mode から出る
void psram_exit_qmode() {
printf("Exit Quad Mode\n");
//PSRAM_ENTER_QMODE
uint8_t tx_buffer[1] = {PSRAM_EXIT_QMODE};
spi_transaction_t t = {
.length = 8 * sizeof(tx_buffer),
.tx_buffer = tx_buffer,
.flags = SPI_TRANS_MODE_QIO,
};
ESP_ERROR_CHECK(spi_device_polling_transmit(psram_spi, &t));
}
void psram_read_id() {
printf("Reading PSRAM ID\n");
// 送信用バッファ(コマンドのみ)
uint8_t tx_buffer[4] = {PSRAM_DEVICE_ID, 0x00, 0x00, 0x00}; // コマンド + ダミーバイト
// 受信用バッファ(IDは最大8バイト)
uint8_t rx_buffer[8];
// トランザクション設定
spi_transaction_t t = {
.length = 8 * (4 + 8), // コマンド(4バイト) + 受信データ(8バイト)
.tx_buffer = tx_buffer,
.rx_buffer = rx_buffer
};
// トランザクション送信
ESP_ERROR_CHECK(spi_device_polling_transmit(psram_spi, &t));
// 受信データを出力
printf("PSRAM ID:[ ");
for (int i = 0; i < 8; i++) {
printf("%02X ", rx_buffer[4+i]);
}
printf("]\n");
}
void psram_write(uint32_t address, const uint8_t *data, size_t size) {
printf("Writing to PSRAM at address 0x%06lx\n", (unsigned long)address);
uint8_t tx_buffer[4 + size]; // コマンド+アドレス(3バイト) + データサイズ
// コマンドとアドレスをバッファにコピー
tx_buffer[0] = PSRAM_WRITE;
tx_buffer[1] = (address >> 16) & 0xFF; // アドレス上位バイト
tx_buffer[2] = (address >> 8) & 0xFF; // アドレス中位バイト
tx_buffer[3] = address & 0xFF; // アドレス下位バイト
// 書き込むデータをバッファにコピー
memcpy(&tx_buffer[4], data, size);
// トランザクションの設定
spi_transaction_t t = {
.length = 8 * (4 + size), // ビット単位で長さを指定
.tx_buffer = tx_buffer // データ送信用バッファ
};
// トランザクション送信
ESP_ERROR_CHECK(spi_device_polling_transmit(psram_spi, &t));
for( int i = 0; i < size; i++) {
printf("[SPI] Write : 0x%06lx: [0x%02X]\n", address + i, data[i]);
}
}
void psram_write_quad(uint32_t address, const uint8_t *data, size_t size) {
printf("Writing to PSRAM(Quad) at address 0x%06lx\n", (unsigned long)address);
#if 0 // こっちでも良い
uint8_t tx_buffer[4 + size]; // コマンド+アドレス(3バイト) + データサイズ
// コマンドとアドレスをバッファにコピー
tx_buffer[0] = PSRAM_WRITE;
tx_buffer[1] = (address >> 16) & 0xFF; // アドレス上位バイト
tx_buffer[2] = (address >> 8) & 0xFF; // アドレス中位バイト
tx_buffer[3] = address & 0xFF; // アドレス下位バイト
// 書き込むデータをバッファにコピー
memcpy(&tx_buffer[4], data, size);
// トランザクションの設定
spi_transaction_t t = {
.length = 8 * (4 + size), // ビット単位で長さを指定
.tx_buffer = tx_buffer, // データ送信用バッファ
.flags = SPI_TRANS_MODE_QIO
};
// トランザクション送信
ESP_ERROR_CHECK(spi_device_polling_transmit(psram_spi, &t));
#else
spi_transaction_t t = {
.cmd = PSRAM_WRITE, // コマンド
.addr = address, // アドレス
.length = 8 * (size), // ビット単位のデータ長
.tx_buffer = data, // 送信バッファ
.flags = SPI_TRANS_MODE_QIO
| SPI_TRANS_MULTILINE_CMD // 重要⭐️
| SPI_TRANS_MULTILINE_ADDR // 重要⭐️
| SPI_TRANS_VARIABLE_CMD
| SPI_TRANS_VARIABLE_ADDR,
};
spi_transaction_ext_t ext = {
.base = t,
.command_bits = 8,
.address_bits = 24,
.dummy_bits = 0,
};
esp_err_t ret = spi_device_polling_transmit(psram_spi, (spi_transaction_t*)&ext);
if (ret != ESP_OK) {
printf("SPI transaction failed: %s\n", esp_err_to_name(ret));
return;
}
#endif
for( int i = 0; i < size; i++) {
printf("[QPI] Write : 0x%06lx: [0x%02X]\n", address + i, data[i]);
}
}
void psram_read(uint32_t address, uint8_t *data, size_t size) {
printf("Reading from PSRAM at address 0x%06lx\n", (unsigned long)address);
uint8_t rx_buffer[4 + size]; // コマンド+アドレス(3バイト) + データサイズ
uint8_t tx_buffer[4] = {
PSRAM_READ, // コマンド
(address >> 16) & 0xFF, // アドレス上位バイト
(address >> 8) & 0xFF, // アドレス中位バイト
address & 0xFF // アドレス下位バイト
};
spi_transaction_t t = {
.length = 8 * (4+size), // ビット単位で長さを指定
.tx_buffer = tx_buffer, // コマンドとアドレス
.rx_buffer = rx_buffer, // 受信データバッファ
};
// トランザクション送信
ESP_ERROR_CHECK(spi_device_polling_transmit(psram_spi, &t));
memcpy(data, &rx_buffer[4], size);
}
void psram_read_quad(uint32_t address, uint8_t *data, size_t size) {
printf("Reading from PSRAM(Quad) at address 0x%06lx size:%d\n", (unsigned long)address, size);
// QPI で読み込む
spi_transaction_ext_t ext = {
.base = {
.cmd = PSRAM_FAST_READ_QUAD,
.addr = address,
.length = 0,
.rxlength = 8 * (size), // 受信データ長(半二重)
.tx_buffer = NULL, // 送信バッファ
.rx_buffer = data, // 受信バッファ
.flags = SPI_TRANS_MODE_QIO // QIOモード
| SPI_DEVICE_HALFDUPLEX // 半二重
// | SPI_TRANS_MODE_DIOQIO_ADDR // アドレスをDIO/QIOモードで送信
| SPI_TRANS_MULTILINE_CMD // 重要⭐️
| SPI_TRANS_MULTILINE_ADDR // 重要⭐️
| SPI_TRANS_VARIABLE_CMD
| SPI_TRANS_VARIABLE_ADDR // アドレスのビット数を指定
| SPI_TRANS_VARIABLE_DUMMY, // ダミービット数を指定
},
.command_bits = 8,
.address_bits = 24, // 8(data) + 24(address)
.dummy_bits = 6 // PSRAM64H データシートより
};
esp_err_t ret = spi_device_polling_transmit(psram_spi, (spi_transaction_t *)&ext);
if (ret != ESP_OK) {
printf("SPI transaction failed: %s\n", esp_err_to_name(ret));
return;
}
}
void app_main(void) {
printf("Initializing SPI for PSRAM\n");
esp_log_level_set("*", ESP_LOG_DEBUG);
{
spi_bus_config_t buscfg = {
.sclk_io_num = SPI_SCK,
.mosi_io_num = SPI_MOSI,
.miso_io_num = SPI_MISO,
.quadwp_io_num = SPI_QUADWP,
.quadhd_io_num = SPI_QUADHD,
.data0_io_num = SPI_MOSI,
.data1_io_num = SPI_MISO,
.data2_io_num = SPI_QUADWP,
.data3_io_num = SPI_QUADHD,
.max_transfer_sz = 4096,
.flags = SPICOMMON_BUSFLAG_MASTER | SPICOMMON_BUSFLAG_QUAD,
};
ESP_ERROR_CHECK(spi_bus_initialize(SPI_DEF, &buscfg, SPI_DMA_CH_AUTO));
spi_device_interface_config_t devcfg = {
.clock_speed_hz = PSRAM_CLOCK,
.mode = 0,
.spics_io_num = SPI_CS,
.queue_size = 7,
.flags = 0
};
ESP_ERROR_CHECK(spi_bus_add_device(SPI_DEF, &devcfg, &psram_spi));
psram_reset();
psram_read_id();
const uint8_t DATA[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xD0, 0xF0, 0x0D};
uint32_t address = 0x000000;
uint8_t read_buffer[sizeof(DATA)];
memset(read_buffer, 0x00, sizeof(DATA));
psram_write(address, DATA, sizeof(DATA));
vTaskDelay(pdMS_TO_TICKS(100));
psram_read(address, read_buffer, sizeof(read_buffer));
for (size_t i = 0; i < sizeof(DATA); i++) {
printf("[SPI] Verify: 0x%06lx: [0x%02X] == [0x%02X] %s\n", address + i, DATA[i], read_buffer[i], DATA[i] == read_buffer[i] ? "😃" : "😖");
}
psram_read_id();
psram_enter_qmode();
}
ESP_ERROR_CHECK(spi_bus_remove_device(psram_spi));
spi_device_interface_config_t devcfg_quad = {
.clock_speed_hz = PSRAM_CLOCK,
.mode = 0,
.spics_io_num = SPI_CS, // CS ピン
.queue_size = 7,
.flags = SPI_DEVICE_HALFDUPLEX,
};
ESP_ERROR_CHECK(spi_bus_add_device(SPI_DEF, &devcfg_quad, &psram_spi));
const uint8_t DATA[] = {0x01, 0x02, 0x03, 0x04 , 0xBA, 0xD0, 0xF0, 0x0D};
uint32_t address = 0x000000;
uint8_t read_buffer[sizeof(DATA)];
memset(read_buffer, 0x00, sizeof(DATA));
psram_write_quad(address, DATA, sizeof(DATA));
vTaskDelay(pdMS_TO_TICKS(100));
psram_read_quad(address, read_buffer, sizeof(read_buffer));
for (size_t i = 0; i < sizeof(DATA); i++) {
printf("[QPI] Verify: 0x%06lx: [0x%02X] == [0x%02X] %s\n", address + i, DATA[i], read_buffer[i], DATA[i] == read_buffer[i] ? "😃" : "😖");
}
psram_exit_qmode();
ESP_ERROR_CHECK(spi_bus_remove_device(psram_spi));
{
spi_device_interface_config_t devcfg = {
.clock_speed_hz = PSRAM_CLOCK,
.mode = 0,
.spics_io_num = SPI_CS,
.queue_size = 7
};
ESP_ERROR_CHECK(spi_bus_add_device(SPI_DEF, &devcfg, &psram_spi));
psram_read_id();
}
ESP_ERROR_CHECK(spi_bus_remove_device(psram_spi));
ESP_ERROR_CHECK(spi_bus_free(SPI_DEF));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment