generated from pimoroni/pico-boilerplate
-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathpio_spi.c
99 lines (88 loc) · 3.9 KB
/
pio_spi.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
/**
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <hardware/dma.h>
#include <hardware/sync.h>
#include "pio_spi.h"
// Just 8 bit functions provided here. The PIO program supports any frame size
// 1...32, but the software to do the necessary FIFO shuffling is left as an
// exercise for the reader :)
//
// Likewise we only provide MSB-first here. To do LSB-first, you need to
// - Do shifts when reading from the FIFO, for general case n != 8, 16, 32
// - Do a narrow read at a one halfword or 3 byte offset for n == 16, 8
// in order to get the read data correctly justified.
void __time_critical_func(pio_spi_write8_blocking)(const pio_spi_inst_t *spi, const uint8_t *src, size_t len) {
size_t tx_remain = len, rx_remain = len;
// Do 8 bit accesses on FIFO, so that write data is byte-replicated. This
// gets us the left-justification for free (for MSB-first shift-out)
io_rw_8 *txfifo = (io_rw_8 *) &spi->pio->txf[spi->sm];
io_rw_8 *rxfifo = (io_rw_8 *) &spi->pio->rxf[spi->sm];
while (tx_remain || rx_remain) {
if (tx_remain && !pio_sm_is_tx_fifo_full(spi->pio, spi->sm)) {
*txfifo = *src++;
--tx_remain;
}
if (rx_remain && !pio_sm_is_rx_fifo_empty(spi->pio, spi->sm)) {
(void) *rxfifo;
--rx_remain;
}
}
}
void __time_critical_func(pio_spi_read8_blocking)(const pio_spi_inst_t *spi, uint8_t *dst, size_t len) {
size_t tx_remain = len, rx_remain = len;
io_rw_8 *txfifo = (io_rw_8 *) &spi->pio->txf[spi->sm];
io_rw_8 *rxfifo = (io_rw_8 *) &spi->pio->rxf[spi->sm];
while (tx_remain || rx_remain) {
if (tx_remain && !pio_sm_is_tx_fifo_full(spi->pio, spi->sm)) {
*txfifo = 0;
--tx_remain;
}
if (rx_remain && !pio_sm_is_rx_fifo_empty(spi->pio, spi->sm)) {
*dst++ = *rxfifo;
--rx_remain;
}
}
}
static uint tx_channel;
static uint rx_channel;
void __time_critical_func(pio_spi_write8_read8_blocking)(const pio_spi_inst_t *spi, uint8_t *src, uint8_t *dst,
size_t len) {
uint32_t interrupt_status = save_and_disable_interrupts();
dma_channel_transfer_from_buffer_now(tx_channel, src, len);
dma_channel_transfer_to_buffer_now(rx_channel, dst, len);
restore_interrupts(interrupt_status);
dma_channel_wait_for_finish_blocking(rx_channel);
}
void pio_spi_setup(const pio_spi_inst_t *spi) {
rx_channel = dma_claim_unused_channel(true);
tx_channel = dma_claim_unused_channel(true);
dma_channel_config c = dma_channel_get_default_config(rx_channel);
channel_config_set_transfer_data_size(&c, DMA_SIZE_8);
channel_config_set_read_increment(&c, false);
channel_config_set_write_increment(&c, true);
channel_config_set_dreq(&c, pio_get_dreq(spi->pio, spi->sm, false));
dma_channel_configure(
rx_channel, // Channel to be configured
&c, // The configuration we just created
NULL, // The initial write address
&spi->pio->rxf[spi->sm], // The initial read address
0, // Number of transfers; in this case each is 1 byte.
false // Start immediately.
);
c = dma_channel_get_default_config(tx_channel);
channel_config_set_transfer_data_size(&c, DMA_SIZE_8);
channel_config_set_read_increment(&c, true);
channel_config_set_write_increment(&c, false);
channel_config_set_dreq(&c, pio_get_dreq(spi->pio, spi->sm, true));
dma_channel_configure(
tx_channel, // Channel to be configured
&c, // The configuration we just created
&spi->pio->txf[spi->sm], // The initial write address
NULL, // The initial read address
0, // Number of transfers; in this case each is 1 byte.
false // Start immediately.
);
}