kopia lustrzana https://github.com/Jean-MarcHarvengt/MCUME
477 wiersze
15 KiB
C
Executable File
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
|