rp2040-psram 1.0.0
A header-only library to allow access to SPI PSRAM via PIO on the RP2040 microcontroller.
Loading...
Searching...
No Matches
/Users/ian/src/rp2040-psram/psram_spi.h
Go to the documentation of this file.
1/******************************************************************************
2
3rp2040-psram
4
5Copyright © 2023 Ian Scott
6
7Permission is hereby granted, free of charge, to any person obtaining a copy of
8this software and associated documentation files (the “Software”), to deal in
9the Software without restriction, including without limitation the rights to
10use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
11of the Software, and to permit persons to whom the Software is furnished to do
12so, subject to the following conditions:
13
14The above copyright notice and this permission notice shall be included in all
15copies or substantial portions of the Software.
16
17THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23SOFTWARE.
24
25******************************************************************************/
26
49#pragma once
50
51#include "hardware/pio.h"
52#include "hardware/gpio.h"
53#include "hardware/timer.h"
54#include "hardware/dma.h"
55#if defined(PSRAM_MUTEX)
56#include "pico/mutex.h"
57#endif
58#include "stdio.h"
59
60#include "psram_spi.pio.h"
61
62#ifdef __cplusplus
63extern "C" {
64#endif
65
72typedef struct psram_spi_inst {
73 PIO pio;
74 uint sm;
75#if defined(PSRAM_MUTEX)
76 mutex_t mtx;
77#endif
78 int write_dma_chan;
79 dma_channel_config write_dma_chan_config;
80 int read_dma_chan;
81 dma_channel_config read_dma_chan_config;
82 int async_dma_chan;
83 dma_channel_config async_dma_chan_config;
85
86static psram_spi_inst_t* async_spi_inst;
87
103__force_inline extern void __time_critical_func(pio_spi_write_read_blocking)(
104 psram_spi_inst_t* spi,
105 const uint8_t* src, const size_t src_len,
106 uint8_t* dst, const size_t dst_len
107) {
108 size_t tx_remain = src_len, rx_remain = dst_len;
109
110#if defined(PSRAM_MUTEX)
111 mutex_enter_blocking(&spi->mtx);
112#endif
113 io_rw_8 *txfifo = (io_rw_8 *) &spi->pio->txf[spi->sm];
114 while (tx_remain) {
115 if (!pio_sm_is_tx_fifo_full(spi->pio, spi->sm)) {
116 *txfifo = *src++;
117 --tx_remain;
118 }
119 }
120
121 io_rw_8 *rxfifo = (io_rw_8 *) &spi->pio->rxf[spi->sm];
122 while (rx_remain) {
123 if (!pio_sm_is_rx_fifo_empty(spi->pio, spi->sm)) {
124 *dst++ = *rxfifo;
125 --rx_remain;
126 }
127 }
128
129#if defined(PSRAM_MUTEX)
130 mutex_exit(&spi->mtx);
131#endif
132}
133
146__force_inline extern void __time_critical_func(pio_spi_write_dma_blocking)(
147 psram_spi_inst_t* spi,
148 const uint8_t* src, const size_t src_len
149) {
150#ifdef PSRAM_MUTEX
151 mutex_enter_blocking(&spi->mtx);
152#endif
153 dma_channel_transfer_from_buffer_now(spi->write_dma_chan, src, src_len);
154 dma_channel_wait_for_finish_blocking(spi->write_dma_chan);
155#ifdef PSRAM_MUTEX
156 mutex_exit(&spi->mtx);
157#endif
158}
159
176__force_inline extern void __time_critical_func(pio_spi_write_read_dma_blocking)(
177 psram_spi_inst_t* spi,
178 const uint8_t* src, const size_t src_len,
179 uint8_t* dst, const size_t dst_len
180) {
181#ifdef PSRAM_MUTEX
182 mutex_enter_blocking(&spi->mtx);
183#endif
184 dma_channel_transfer_from_buffer_now(spi->write_dma_chan, src, src_len);
185 dma_channel_transfer_to_buffer_now(spi->read_dma_chan, dst, dst_len);
186 dma_channel_wait_for_finish_blocking(spi->write_dma_chan);
187 dma_channel_wait_for_finish_blocking(spi->read_dma_chan);
188#ifdef PSRAM_MUTEX
189 mutex_exit(&spi->mtx);
190#endif
191}
192
194void __isr dma_complete_handler() {
195 dma_hw->ints1 = 1u << async_spi_inst->async_dma_chan;
196#ifdef PSRAM_MUTEX
197 mutex_exit(&async_spi_inst->mtx);
198#endif
199}
200
212__force_inline extern void __time_critical_func(pio_spi_write_async)(
213 psram_spi_inst_t* spi,
214 const uint8_t* src, const size_t src_len
215) {
216#ifdef PSRAM_MUTEX
217 mutex_enter_blocking(&spi->mtx);
218#endif
219
220 dma_channel_wait_for_finish_blocking(spi->async_dma_chan);
221 async_spi_inst = spi;
222
223 dma_channel_transfer_from_buffer_now(spi->async_dma_chan, src, src_len);
224}
225
226
235 uint spi_offset = pio_add_program(pio1, &spi_fudge_program);
236 uint spi_sm = pio_claim_unused_sm(pio1, true);
238 spi.pio = pio1;
239 spi.sm = spi_sm;
240#if defined(PSRAM_MUTEX)
241 mutex_init(&spi.mtx);
242#endif
243
244 gpio_set_drive_strength(PSRAM_PIN_CS, GPIO_DRIVE_STRENGTH_2MA);
245 gpio_set_drive_strength(PSRAM_PIN_SCK, GPIO_DRIVE_STRENGTH_2MA);
246 gpio_set_drive_strength(PSRAM_PIN_MOSI, GPIO_DRIVE_STRENGTH_2MA);
247 gpio_set_slew_rate(PSRAM_PIN_CS, GPIO_SLEW_RATE_FAST);
248 gpio_set_slew_rate(PSRAM_PIN_SCK, GPIO_SLEW_RATE_FAST);
249 gpio_set_slew_rate(PSRAM_PIN_MOSI, GPIO_SLEW_RATE_FAST);
250
251 pio_spi_fudge_cs_init(pio1, spi_sm, spi_offset, 8 /*n_bits*/, 1 /*clkdiv*/, PSRAM_PIN_CS, PSRAM_PIN_MOSI, PSRAM_PIN_MISO);
252
253 // Write DMA channel setup
254 spi.write_dma_chan = dma_claim_unused_channel(true);
255 spi.write_dma_chan_config = dma_channel_get_default_config(spi.write_dma_chan);
256 channel_config_set_transfer_data_size(&spi.write_dma_chan_config, DMA_SIZE_8);
257 channel_config_set_read_increment(&spi.write_dma_chan_config, true);
258 channel_config_set_write_increment(&spi.write_dma_chan_config, false);
259 channel_config_set_dreq(&spi.write_dma_chan_config, pio_get_dreq(spi.pio, spi.sm, true));
260 dma_channel_set_write_addr(spi.write_dma_chan, &spi.pio->txf[spi.sm], false);
261 dma_channel_set_config(spi.write_dma_chan, &spi.write_dma_chan_config, false);
262
263 // Read DMA channel setup
264 spi.read_dma_chan = dma_claim_unused_channel(true);
265 spi.read_dma_chan_config = dma_channel_get_default_config(spi.read_dma_chan);
266 channel_config_set_transfer_data_size(&spi.read_dma_chan_config, DMA_SIZE_8);
267 channel_config_set_read_increment(&spi.read_dma_chan_config, false);
268 channel_config_set_write_increment(&spi.read_dma_chan_config, true);
269 channel_config_set_dreq(&spi.read_dma_chan_config, pio_get_dreq(spi.pio, spi.sm, false));
270 dma_channel_set_read_addr(spi.read_dma_chan, &spi.pio->rxf[spi.sm], false);
271 dma_channel_set_config(spi.read_dma_chan, &spi.read_dma_chan_config, false);
272
273 // Asynchronous DMA channel setup
274 spi.async_dma_chan = dma_claim_unused_channel(true);
275 spi.async_dma_chan_config = dma_channel_get_default_config(spi.async_dma_chan);
276 channel_config_set_transfer_data_size(&spi.async_dma_chan_config, DMA_SIZE_8);
277 channel_config_set_read_increment(&spi.async_dma_chan_config, true);
278 channel_config_set_write_increment(&spi.async_dma_chan_config, false);
279 channel_config_set_dreq(&spi.async_dma_chan_config, pio_get_dreq(spi.pio, spi.sm, true));
280 dma_channel_set_write_addr(spi.async_dma_chan, &spi.pio->txf[spi.sm], false);
281 dma_channel_set_config(spi.async_dma_chan, &spi.async_dma_chan_config, false);
282 irq_set_exclusive_handler(DMA_IRQ_1, dma_complete_handler);
283 dma_irqn_set_channel_enabled(1, spi.async_dma_chan, true);
284 irq_set_enabled(DMA_IRQ_1, true);
285
286 uint8_t psram_reset_en_cmd[] = {
287 8, // 8 bits to write
288 0, // 0 bits to read
289 0x66u // Reset enable command
290 };
291 pio_spi_write_read_dma_blocking(&spi, psram_reset_en_cmd, 3, 0, 0);
292 busy_wait_us(50);
293 uint8_t psram_reset_cmd[] = {
294 8, // 8 bits to write
295 0, // 0 bits to read
296 0x99u // Reset command
297 };
298 pio_spi_write_read_dma_blocking(&spi, psram_reset_cmd, 3, 0, 0);
299 busy_wait_us(100);
300
301 return spi;
302};
303
304
305static uint8_t write8_command[] = {
306 40, // 40 bits write
307 0, // 0 bits read
308 0x02u, // Write command
309 0, 0, 0, // Address
310 0 // 8 bits data
311};
323__force_inline extern void psram_write8_async(psram_spi_inst_t* spi, uint32_t addr, uint8_t val) {
324 write8_command[3] = addr >> 16;
325 write8_command[4] = addr >> 8;
326 write8_command[5] = addr;
327 write8_command[6] = val;
328
329 pio_spi_write_async(spi, write8_command, sizeof(write8_command));
330};
331
332
345__force_inline extern void psram_write8(psram_spi_inst_t* spi, uint32_t addr, uint8_t val) {
346 write8_command[3] = addr >> 16;
347 write8_command[4] = addr >> 8;
348 write8_command[5] = addr;
349 write8_command[6] = val;
350
351 pio_spi_write_dma_blocking(spi, write8_command, sizeof(write8_command));
352};
353
354
355static uint8_t read8_command[] = {
356 40, // 40 bits write
357 8, // 8 bits read
358 0x0bu, // Fast read command
359 0, 0, 0, // Address
360 0 // 8 delay cycles
361};
374__force_inline extern uint8_t psram_read8(psram_spi_inst_t* spi, uint32_t addr) {
375 read8_command[3] = addr >> 16;
376 read8_command[4] = addr >> 8;
377 read8_command[5] = addr;
378
379 uint8_t val;
380 pio_spi_write_read_dma_blocking(spi, read8_command, sizeof(read8_command), &val, 1);
381 return val;
382};
383
384
385static uint8_t write16_command[] = {
386 48, // 48 bits write
387 0, // 0 bits read
388 0x02u, // Write command
389 0, 0, 0, // Address
390 0, 0 // 16 bits data
391};
404__force_inline extern void psram_write16(psram_spi_inst_t* spi, uint32_t addr, uint16_t val) {
405 write16_command[3] = addr >> 16;
406 write16_command[4] = addr >> 8;
407 write16_command[5] = addr;
408 write16_command[6] = val;
409 write16_command[7] = val >> 8;
410
411 pio_spi_write_dma_blocking(spi, write16_command, sizeof(write16_command));
412};
413
414
415static uint8_t read16_command[] = {
416 40, // 40 bits write
417 16, // 16 bits read
418 0x0bu, // Fast read command
419 0, 0, 0, // Address
420 0 // 8 delay cycles
421};
434__force_inline extern uint16_t psram_read16(psram_spi_inst_t* spi, uint32_t addr) {
435 read16_command[3] = addr >> 16;
436 read16_command[4] = addr >> 8;
437 read16_command[5] = addr;
438
439 uint16_t val;
440 pio_spi_write_read_dma_blocking(spi, read16_command, sizeof(read16_command), (unsigned char*)&val, 2);
441 return val;
442};
443
444
445static uint8_t write32_command[] = {
446 64, // 64 bits write
447 0, // 0 bits read
448 0x02u, // Write command
449 0, 0, 0, // Address
450 0, 0, 0, 0 // 32 bits data
451};
464__force_inline extern void psram_write32(psram_spi_inst_t* spi, uint32_t addr, uint32_t val) {
465 // Break the address into three bytes and send read command
466 write32_command[3] = addr >> 16;
467 write32_command[4] = addr >> 8;
468 write32_command[5] = addr;
469 write32_command[6] = val;
470 write32_command[7] = val >> 8;
471 write32_command[8] = val >> 16;
472 write32_command[9] = val >> 24;
473
474 pio_spi_write_dma_blocking(spi, write32_command, sizeof(write32_command));
475};
476
477
478static uint8_t read32_command[] = {
479 40, // 40 bits write
480 32, // 32 bits read
481 0x0bu, // Fast read command
482 0, 0, 0, // Address
483 0 // 8 delay cycles
484};
497__force_inline extern uint32_t psram_read32(psram_spi_inst_t* spi, uint32_t addr) {
498 read32_command[3] = addr >> 16;
499 read32_command[4] = addr >> 8;
500 read32_command[5] = addr;
501
502 uint32_t val;
503 pio_spi_write_read_dma_blocking(spi, read32_command, sizeof(read32_command), (unsigned char*)&val, 4);
504 return val;
505};
506
507
508static uint8_t write_command[] = {
509 0, // n bits write
510 0, // 0 bits read
511 0x02u, // Fast write command
512 0, 0, 0 // Address
513};
524__force_inline extern void psram_write(psram_spi_inst_t* spi, const uint32_t addr, const uint8_t* src, const size_t count) {
525 // Break the address into three bytes and send read command
526 write_command[0] = (4 + count) * 8;
527 write_command[3] = addr >> 16;
528 write_command[4] = addr >> 8;
529 write_command[5] = addr;
530
531 pio_spi_write_dma_blocking(spi, write_command, sizeof(write_command));
532 pio_spi_write_dma_blocking(spi, src, count);
533};
534
535
536static uint8_t read_command[] = {
537 40, // 40 bits write
538 0, // n bits read
539 0x0bu, // Fast read command
540 0, 0, 0, // Address
541 0 // 8 delay cycles
542};
553__force_inline extern void psram_read(psram_spi_inst_t* spi, const uint32_t addr, uint8_t* dst, const size_t count) {
554 read_command[1] = count * 8;
555 read_command[3] = addr >> 16;
556 read_command[4] = addr >> 8;
557 read_command[5] = addr;
558
559 pio_spi_write_read_dma_blocking(spi, read_command, sizeof(read_command), dst, count);
560};
561
562#ifdef __cplusplus
563}
564#endif
__force_inline 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)
Write and read raw data to the PSRAM SPI PIO, driven by DMA without CPU involvement.
Definition: psram_spi.h:176
__force_inline void __time_critical_func() pio_spi_write_read_blocking(psram_spi_inst_t *spi, const uint8_t *src, const size_t src_len, uint8_t *dst, const size_t dst_len)
Write and read raw data to the PSRAM SPI PIO, driven by the CPU without DMA. This can be used if DMA ...
Definition: psram_spi.h:103
__force_inline void __time_critical_func() pio_spi_write_dma_blocking(psram_spi_inst_t *spi, const uint8_t *src, const size_t src_len)
Write raw data to the PSRAM SPI PIO, driven by DMA without CPU involvement.
Definition: psram_spi.h:146
__force_inline uint16_t psram_read16(psram_spi_inst_t *spi, uint32_t addr)
Read 16 bits of data from a given address to the PSRAM SPI PIO, driven by DMA without CPU involvement...
Definition: psram_spi.h:434
__force_inline uint8_t psram_read8(psram_spi_inst_t *spi, uint32_t addr)
Read 8 bits of data from a given address to the PSRAM SPI PIO, driven by DMA without CPU involvement,...
Definition: psram_spi.h:374
__force_inline void psram_write8(psram_spi_inst_t *spi, uint32_t addr, uint8_t val)
Write 8 bits of data to a given address to the PSRAM SPI PIO, driven by DMA without CPU involvement,...
Definition: psram_spi.h:345
__force_inline void psram_write32(psram_spi_inst_t *spi, uint32_t addr, uint32_t val)
Write 32 bits of data to a given address to the PSRAM SPI PIO, driven by DMA without CPU involvement,...
Definition: psram_spi.h:464
__force_inline void psram_read(psram_spi_inst_t *spi, const uint32_t addr, uint8_t *dst, const size_t count)
Read count bits of data from a given address to the PSRAM SPI PIO, driven by DMA without CPU involvem...
Definition: psram_spi.h:553
__force_inline void psram_write(psram_spi_inst_t *spi, const uint32_t addr, const uint8_t *src, const size_t count)
Write count bytes of data to a given address to the PSRAM SPI PIO, driven by DMA without CPU involvem...
Definition: psram_spi.h:524
struct psram_spi_inst psram_spi_inst_t
A struct that holds the configuration for the PSRAM interface.
__force_inline void __time_critical_func() pio_spi_write_async(psram_spi_inst_t *spi, const uint8_t *src, const size_t src_len)
Write raw data asynchronously to the PSRAM SPI PIO, driven by DMA without CPU involvement.
Definition: psram_spi.h:212
psram_spi_inst_t psram_spi_init(void)
Initialize the PSRAM over SPI. This function must be called before accessing PSRAM.
Definition: psram_spi.h:234
__force_inline uint32_t psram_read32(psram_spi_inst_t *spi, uint32_t addr)
Read 32 bits of data from a given address to the PSRAM SPI PIO, driven by DMA without CPU involvement...
Definition: psram_spi.h:497
__force_inline void psram_write8_async(psram_spi_inst_t *spi, uint32_t addr, uint8_t val)
Write 8 bits of data to a given address asynchronously to the PSRAM SPI PIO, driven by DMA without CP...
Definition: psram_spi.h:323
__force_inline void psram_write16(psram_spi_inst_t *spi, uint32_t addr, uint16_t val)
Write 16 bits of data to a given address to the PSRAM SPI PIO, driven by DMA without CPU involvement,...
Definition: psram_spi.h:404
A struct that holds the configuration for the PSRAM interface.
Definition: psram_spi.h:72