MCUME/MCUME_pico2/psram/psram_spi.h

477 wiersze
15 KiB
C
Executable File

/******************************************************************************
rp2040-psram
Copyright © 2023 Ian Scott
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the “Software”), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
******************************************************************************/
/**
* @file psram_spi.h
*
* \mainpage
*
* The interface to this file is defined in psram_spi.h. Please see the
* documentation for this file.
*
* The following defines _MUST_ be defined:
*
* - @c PSRAM_PIN_CS - GPIO number of the chip select pin
* - @c PSRAM_PIN_SCK - GPIO number of the clock pin
* - @c PSRAM_PIN_MOSI - GPIO number of the MOSI pin
* - @c PSRAM_PIN_MISO - GPIO number of the MISO pin
*
* Optional define:
* - @c PSRAM_MUTEX - Define this to put PSRAM access behind a mutex. This must
* be used if the PSRAM is to be used by multiple cores.
*
* Project homepage: https://github.com/polpo/rp2040-psram
*/
#pragma once
#include "iopins.h"
#include "hardware/pio.h"
#include "hardware/gpio.h"
#include "hardware/timer.h"
#include "hardware/dma.h"
#include <string.h>
#include "psram_spi.pio.h"
#ifdef __cplusplus
extern "C" {
#endif
#define QSPIONLY 1
//#define SPIONLY 1
#define PSRAM_WAITDMA 1
static uint8_t wrbuffer[32];
#define WRITE_CMD_QSPI 0x38u
#define WRITE_CMD_SPI 0x02u
#define READ_CMD_QSPI 0xebu
#define READ_CMD_SPI 0x0bu
/**
* @brief A struct that holds the configuration for the PSRAM interface.
*
* This struct is generated by psram_spi_init() and must be passed to all calls to
* the psram access functions.
*/
typedef struct psram_spi_inst {
PIO pio;
int sm;
//uint offset;
int write_dma_chan;
dma_channel_config write_dma_chan_config;
int read_dma_chan;
dma_channel_config read_dma_chan_config;
bool init_done;
bool qspi;
} psram_spi_inst_t;
/**
* @brief Write raw data to the PSRAM SPI PIO, driven by DMA without CPU
* involvement.
* @param spi The PSRAM configuration instance returned from psram_spi_init().
* @param command is the command to write.
*/
__force_inline static void __time_critical_func(pio_spi_cmd_dma_blocking)(
psram_spi_inst_t* spi,
const uint8_t command
) {
#if defined(PSRAM_WAITDMA)
dma_channel_wait_for_finish_blocking(spi->write_dma_chan);
dma_channel_wait_for_finish_blocking(spi->read_dma_chan);
#endif // PSRAM_WAITDMA
int k=0;
#ifdef QSPIONLY
wrbuffer[k++]=8;
wrbuffer[k++]=0;
uint8_t d=0;
if (command & 0b10000000) d |= 0b00010000;
if (command & 0b01000000) d |= 0b00000001;
wrbuffer[k++] = d;
d=0;
if (command & 0b00100000) d |= 0b00010000;
if (command & 0b00010000) d |= 0b00000001;
wrbuffer[k++] = d;
d=0;
if (command & 0b00001000) d |= 0b00010000;
if (command & 0b00000100) d |= 0b00000001;
wrbuffer[k++] = d;
d=0;
if (command & 0b00000010) d |= 0b00010000;
if (command & 0b00000001) d |= 0b00000001;
wrbuffer[k++] = d;
#else
if (spi->qspi) {
wrbuffer[k++]=2;
wrbuffer[k++]=0;
wrbuffer[k++]=command;
}
else {
wrbuffer[k++]=8;
wrbuffer[k++]=0;
wrbuffer[k++]=command;
}
#endif
dma_channel_transfer_from_buffer_now(spi->write_dma_chan, wrbuffer, k);
dma_channel_wait_for_finish_blocking(spi->write_dma_chan);
}
/**
* @brief Write raw data to the PSRAM SPI PIO, driven by DMA without CPU
* involvement.
*
* It's recommended to use DMA when possible as it's higher speed. Used to send
* raw commands to the PSRAM. This function is faster than
* pio_spi_write_read_dma_blocking() if no data is to be read.
*
* @param spi The PSRAM configuration instance returned from psram_spi_init().
* @param src Pointer to the source data to write.
* @param src_len Length of the source data in bytes.
*/
__force_inline static void __time_critical_func(pio_spi_write_dma_blocking)(
psram_spi_inst_t* spi,
const uint8_t* src, const size_t src_len
) {
#if defined(PSRAM_WAITDMA)
dma_channel_wait_for_finish_blocking(spi->write_dma_chan);
dma_channel_wait_for_finish_blocking(spi->read_dma_chan);
#endif // PSRAM_WAITDMA
dma_channel_transfer_from_buffer_now(spi->write_dma_chan, src, src_len);
dma_channel_wait_for_finish_blocking(spi->write_dma_chan);
}
/**
* @brief Write and read raw data to the PSRAM SPI PIO, driven by DMA without CPU
* involvement.
*
* It's recommended to use DMA when possible as it's higher speed. Used to send
* raw commands and receive data from the PSRAM. Usually the @c psram_write* and
* @c psram_read* commands should be used instead.
*
* @param spi The PSRAM configuration instance returned from psram_spi_init().
* @param src Pointer to the source data to write.
* @param src_len Length of the source data in bytes.
* @param dst Pointer to the destination for read data, if any. Set to 0 or NULL
* if no data is to be read.
* @param dst_len Length of the destination data in bytes. Set to 0 if no data
* is to be read.
*/
__force_inline static void __time_critical_func(pio_spi_write_read_dma_blocking)(
psram_spi_inst_t* spi,
const uint8_t* src, const size_t src_len,
uint8_t* dst, const size_t dst_len
) {
#if defined(PSRAM_WAITDMA)
dma_channel_wait_for_finish_blocking(spi->write_dma_chan);
dma_channel_wait_for_finish_blocking(spi->read_dma_chan);
#endif // PSRAM_WAITDMA
dma_channel_transfer_from_buffer_now(spi->write_dma_chan, src, src_len);
dma_channel_wait_for_finish_blocking(spi->write_dma_chan);
dma_channel_transfer_to_buffer_now(spi->read_dma_chan, dst, dst_len);
dma_channel_wait_for_finish_blocking(spi->read_dma_chan);
}
/**
* @brief Initialize the PSRAM over SPI. This function must be called before
* accessing PSRAM.
*
* @param pio The PIO instance to use (PIO0 or PIO1).
* @param sm The state machine number in the PIO module to use. If -1 is given,
* will use the first available state machine.
* @param clkdiv Clock divisor for the state machine. At RP2040 speeds greater
* than 280MHz, a clkdiv >1.0 is needed. For example, at 400MHz, a clkdiv of
* 1.6 is recommended.
* @param fudge Whether to insert an extra "fudge factor" of one clock cycle
* before reading from the PSRAM. Depending on your PCB layout or PSRAM type,
* you may need to do this.
*
* @return The PSRAM configuration instance. This instance should be passed to
* all PSRAM access functions.
*/
psram_spi_inst_t psram_spi_init_clkdiv(PIO pio, int sm, float clkdiv, bool fudge);
/**
* @brief Initialize the PSRAM over SPI. This function must be called before
* accessing PSRAM.
*
* Defaults to a clkdiv of 1.0. This function is provided for backwards
* compatibility. Use psram_spi_init_clkdiv instead if you want a clkdiv other
* than 1.0.
*
* @param pio The PIO instance to use (PIO0 or PIO1).
* @param sm The state machine number in the PIO module to use. If -1 is given,
* will use the first available state machine.
*
* @return The PSRAM configuration instance. This instance should be passed to
* all PSRAM access functions.
*/
psram_spi_inst_t psram_spi_init(PIO pio, int sm);
static uint8_t write8_command[] = {
40, // 40 bits write
0, // 0 bits read
WRITE_CMD_SPI, // Write command
0, 0, 0, // Address
0 // 8 bits data
};
/**
* @brief Write 8 bits of data to a given address to the PSRAM SPI PIO,
* driven by DMA without CPU involvement, blocking until the write is
* complete.
*
* This function is optimized to write 8 bits as quickly as possible to the
* PSRAM as opposed to the more general-purpose psram_write() function.
*
* @param spi The PSRAM configuration instance returned from psram_spi_init().
* @param addr Address to write to.
* @param val Value to write.
*/
__force_inline static void psram_write8(psram_spi_inst_t* spi, uint32_t addr, uint8_t val) {
if (spi->qspi) {
write8_command[0] = 40/4;
write8_command[1] = 0;
write8_command[2] = WRITE_CMD_QSPI;
}
else {
write8_command[0] = 40;
write8_command[1] = 0;
write8_command[2] = WRITE_CMD_SPI;
}
write8_command[3] = addr >> 16;
write8_command[4] = addr >> 8;
write8_command[5] = addr;
write8_command[6] = val;
pio_spi_write_dma_blocking(spi, write8_command, sizeof(write8_command));
};
static uint8_t read8_command[] = {
32, // 32 bits write
8, // 8 bits read
READ_CMD_SPI, // Fast read command
0, 0, 0, // Address
0
};
/**
* @brief Read 8 bits of data from a given address to the PSRAM SPI PIO,
* driven by DMA without CPU involvement, blocking until the read is
* complete.
*
* This function is optimized to read 8 bits as quickly as possible from the
* PSRAM as opposed to the more general-purpose psram_read() function.
*
* @param spi The PSRAM configuration instance returned from psram_spi_init().
* @param addr Address to read from.
* @return The data at the specified address.
*/
__force_inline static uint8_t psram_read8(psram_spi_inst_t* spi, uint32_t addr) {
uint8_t val=0;
if (spi->qspi) {
read8_command[0] = (32+8)/4;
read8_command[1] = 8/4; //8/8;
read8_command[2] = READ_CMD_QSPI;
read8_command[6] = 0xff;
}
else {
read8_command[0] = 32+8;
read8_command[1] = 8;
read8_command[2] = READ_CMD_SPI;
read8_command[6] = 0x00;
}
read8_command[3] = addr >> 16;
read8_command[4] = addr >> 8;
read8_command[5] = addr;
pio_spi_write_read_dma_blocking(spi, read8_command, sizeof(read8_command), &val, 1);
return val;
};
static uint8_t write16_command[] = {
48, // 48 bits write
0, // 0 bits read
WRITE_CMD_SPI, // Write command
0, 0, 0, // Address
0, 0 // 16 bits data
};
/**
* @brief Write 16 bits of data to a given address to the PSRAM SPI PIO,
* driven by DMA without CPU involvement, blocking until the write is
* complete.
*
* This function is optimized to write 16 bits as quickly as possible to the
* PSRAM as opposed to the more general-purpose psram_write() function.
*
* @param spi The PSRAM configuration instance returned from psram_spi_init().
* @param addr Address to write to.
* @param val Value to write.
*/
__force_inline static void psram_write16(psram_spi_inst_t* spi, uint32_t addr, uint16_t val) {
if (spi->qspi) {
write16_command[0] = 48/4;
write16_command[1] = 0;
write16_command[2] = WRITE_CMD_QSPI;
}
else {
write16_command[0] = 48;
write16_command[1] = 0;
write16_command[2] = WRITE_CMD_SPI;
}
write16_command[3] = addr >> 16;
write16_command[4] = addr >> 8;
write16_command[5] = addr;
write16_command[6] = val;
write16_command[7] = val >> 8;
pio_spi_write_dma_blocking(spi, write16_command, sizeof(write16_command));
};
static uint8_t read16_command[] = {
32, // 32 bits write
16, // 16 bits read
READ_CMD_SPI, // Fast read command
0, 0, 0, // Address
0
};
/**
* @brief Read 16 bits of data from a given address to the PSRAM SPI PIO,
* driven by DMA without CPU involvement, blocking until the read is
* complete.
*
* This function is optimized to read 16 bits as quickly as possible from the
* PSRAM as opposed to the more general-purpose psram_read() function.
*
* @param spi The PSRAM configuration instance returned from psram_spi_init().
* @param addr Address to read from.
* @return The data at the specified address.
*/
__force_inline static uint16_t psram_read16(psram_spi_inst_t* spi, uint32_t addr) {
if (spi->qspi) {
read16_command[0] = (32+8)/4;
read16_command[1] = 16/4;
read16_command[2] = READ_CMD_QSPI;
read16_command[6] = 0xff;
}
else {
read16_command[0] = (32+8);
read16_command[1] = 16;
read16_command[2] = READ_CMD_SPI;
read16_command[6] = 0;
}
read16_command[3] = addr >> 16;
read16_command[4] = addr >> 8;
read16_command[5] = addr;
uint16_t val=0;
pio_spi_write_read_dma_blocking(spi, read16_command, sizeof(read16_command), (unsigned char*)&val, 2);
return val;
};
/*
static uint8_t write_command[] = {
0, // n bits write
0, // 0 bits read
#ifdef QSPI
0x38u, // Write QUAD command
#else
0x02u, // Write command
#endif
0, 0, 0 // Address
};
*/
/**
* @brief Write @c count bytes of data to a given address to the PSRAM SPI PIO,
* driven by DMA without CPU involvement, blocking until the write is
* complete.
*
* @param spi The PSRAM configuration instance returned from psram_spi_init().
* @param addr Address to write to.
* @param src Pointer to the source data to write.
* @param count Number of bytes to write.
*/
/*
__force_inline static void psram_writen(psram_spi_inst_t* spi, const uint32_t addr, const uint8_t* src, const size_t count) {
// Break the address into three bytes and send read command
write_command[0] = (4 + count) * 8;
write_command[3] = addr >> 16;
write_command[4] = addr >> 8;
write_command[5] = addr;
pio_spi_write_dma_blocking(spi, write_command, sizeof(write_command));
//pio_spi_write_dma_blocking(spi, src, count);
};
*/
static uint8_t readn_command[] = {
40, // 40 bits write
0, // n bits read
READ_CMD_SPI, // Fast read command
0, 0, 0, // Address
0 // 8 delay cycles
};
/**
* @brief Read @c count bits of data from a given address to the PSRAM SPI PIO,
* driven by DMA without CPU involvement, blocking until the read is
* complete.
*
* @param spi The PSRAM configuration instance returned from psram_spi_init().
* @param addr Address to read from.
* @param dst Pointer to the destination for the read data.
* @param count Number of bytes to read.
*/
__force_inline static void psram_readn(psram_spi_inst_t* spi, const uint32_t addr, uint8_t* dst, const size_t count) {
if (spi->qspi) {
readn_command[0] = (32+8)/4;
readn_command[1] = (count * 8)/4;
readn_command[2] = READ_CMD_QSPI;
readn_command[6] = 0xff;
}
else {
readn_command[0] = (32+8);
readn_command[1] = count * 8;
readn_command[2] = READ_CMD_SPI;
readn_command[6] = 0;
}
readn_command[3] = addr >> 16;
readn_command[4] = addr >> 8;
readn_command[5] = addr;
pio_spi_write_read_dma_blocking(spi, readn_command, sizeof(readn_command), dst, count);
};
#ifdef __cplusplus
}
#endif