kopia lustrzana https://github.com/pimoroni/pimoroni-pico
Merge pull request #188 from pimoroni/patch-sdcard-pio
Add support for SD over PIO SPIpull/195/head
commit
0b7076d751
|
@ -9,4 +9,5 @@ if (NOT TARGET fatfs)
|
||||||
|
|
||||||
target_link_libraries(fatfs INTERFACE pico_stdlib hardware_clocks hardware_spi)
|
target_link_libraries(fatfs INTERFACE pico_stdlib hardware_clocks hardware_spi)
|
||||||
target_include_directories(fatfs INTERFACE ${CMAKE_CURRENT_LIST_DIR})
|
target_include_directories(fatfs INTERFACE ${CMAKE_CURRENT_LIST_DIR})
|
||||||
|
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void __time_critical_func(pio_spi_write8_read8_blocking)(const pio_spi_inst_t *spi, uint8_t *src, 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 = *src++;
|
||||||
|
--tx_remain;
|
||||||
|
}
|
||||||
|
if (rx_remain && !pio_sm_is_rx_fifo_empty(spi->pio, spi->sm)) {
|
||||||
|
*dst++ = *rxfifo;
|
||||||
|
--rx_remain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void __time_critical_func(pio_spi_repeat8_read8_blocking)(const pio_spi_inst_t *spi, uint8_t src, 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 = src;
|
||||||
|
--tx_remain;
|
||||||
|
}
|
||||||
|
if (rx_remain && !pio_sm_is_rx_fifo_empty(spi->pio, spi->sm)) {
|
||||||
|
*dst++ = *rxfifo;
|
||||||
|
--rx_remain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*/
|
||||||
|
#ifndef _PIO_SPI_H
|
||||||
|
#define _PIO_SPI_H
|
||||||
|
|
||||||
|
#include "hardware/pio.h"
|
||||||
|
#include "spi.pio.h"
|
||||||
|
|
||||||
|
typedef struct pio_spi_inst {
|
||||||
|
PIO pio;
|
||||||
|
uint sm;
|
||||||
|
uint cs_pin;
|
||||||
|
} pio_spi_inst_t;
|
||||||
|
|
||||||
|
void pio_spi_write8_blocking(const pio_spi_inst_t *spi, const uint8_t *src, size_t len);
|
||||||
|
|
||||||
|
void pio_spi_read8_blocking(const pio_spi_inst_t *spi, uint8_t *dst, size_t len);
|
||||||
|
|
||||||
|
void pio_spi_write8_read8_blocking(const pio_spi_inst_t *spi, uint8_t *src, uint8_t *dst, size_t len);
|
||||||
|
|
||||||
|
void pio_spi_repeat8_read8_blocking(const pio_spi_inst_t *spi, uint8_t src, uint8_t *dst, size_t len);
|
||||||
|
|
||||||
|
#endif
|
|
@ -3,7 +3,11 @@
|
||||||
#include "pico.h"
|
#include "pico.h"
|
||||||
#include "pico/stdlib.h"
|
#include "pico/stdlib.h"
|
||||||
#include "hardware/clocks.h"
|
#include "hardware/clocks.h"
|
||||||
|
#ifndef SDCARD_PIO
|
||||||
#include "hardware/spi.h"
|
#include "hardware/spi.h"
|
||||||
|
#else
|
||||||
|
#include "pio_spi.h"
|
||||||
|
#endif
|
||||||
#include "hardware/gpio.h"
|
#include "hardware/gpio.h"
|
||||||
//#include "hardware/gpio_ex.h"
|
//#include "hardware/gpio_ex.h"
|
||||||
|
|
||||||
|
@ -55,6 +59,13 @@ DSTATUS Stat = STA_NOINIT; /* Physical drive status */
|
||||||
static
|
static
|
||||||
BYTE CardType; /* Card type flags */
|
BYTE CardType; /* Card type flags */
|
||||||
|
|
||||||
|
#ifdef SDCARD_PIO
|
||||||
|
pio_spi_inst_t pio_spi = {
|
||||||
|
.pio = SDCARD_PIO,
|
||||||
|
.sm = SDCARD_PIO_SM
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
static inline uint32_t _millis(void)
|
static inline uint32_t _millis(void)
|
||||||
{
|
{
|
||||||
return to_ms_since_boot(get_absolute_time());
|
return to_ms_since_boot(get_absolute_time());
|
||||||
|
@ -78,12 +89,16 @@ static inline void cs_deselect(uint cs_pin) {
|
||||||
|
|
||||||
static void FCLK_SLOW(void)
|
static void FCLK_SLOW(void)
|
||||||
{
|
{
|
||||||
spi_set_baudrate(spi0, CLK_SLOW);
|
#ifndef SDCARD_PIO
|
||||||
|
spi_set_baudrate(SDCARD_SPI_BUS, CLK_SLOW);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static void FCLK_FAST(void)
|
static void FCLK_FAST(void)
|
||||||
{
|
{
|
||||||
spi_set_baudrate(spi0, CLK_FAST);
|
#ifndef SDCARD_PIO
|
||||||
|
spi_set_baudrate(SDCARD_SPI_BUS, CLK_FAST);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static void CS_HIGH(void)
|
static void CS_HIGH(void)
|
||||||
|
@ -107,18 +122,15 @@ void init_spi(void)
|
||||||
//gpio_pull_up(SDCARD_PIN_SPI0_SCK);
|
//gpio_pull_up(SDCARD_PIN_SPI0_SCK);
|
||||||
//gpio_set_drive_strength(SDCARD_PIN_SPI0_SCK, PADS_BANK0_GPIO0_DRIVE_VALUE_4MA); // 2mA, 4mA (default), 8mA, 12mA
|
//gpio_set_drive_strength(SDCARD_PIN_SPI0_SCK, PADS_BANK0_GPIO0_DRIVE_VALUE_4MA); // 2mA, 4mA (default), 8mA, 12mA
|
||||||
//gpio_set_slew_rate(SDCARD_PIN_SPI0_SCK, 0); // 0: SLOW (default), 1: FAST
|
//gpio_set_slew_rate(SDCARD_PIN_SPI0_SCK, 0); // 0: SLOW (default), 1: FAST
|
||||||
gpio_set_function(SDCARD_PIN_SPI0_SCK, GPIO_FUNC_SPI);
|
|
||||||
|
|
||||||
gpio_init(SDCARD_PIN_SPI0_MISO);
|
gpio_init(SDCARD_PIN_SPI0_MISO);
|
||||||
gpio_pull_up(SDCARD_PIN_SPI0_MISO);
|
gpio_pull_up(SDCARD_PIN_SPI0_MISO);
|
||||||
//gpio_set_schmitt(SDCARD_PIN_SPI0_MISO, 1); // 0: Off, 1: On (default)
|
//gpio_set_schmitt(SDCARD_PIN_SPI0_MISO, 1); // 0: Off, 1: On (default)
|
||||||
gpio_set_function(SDCARD_PIN_SPI0_MISO, GPIO_FUNC_SPI);
|
|
||||||
|
|
||||||
gpio_init(SDCARD_PIN_SPI0_MOSI);
|
gpio_init(SDCARD_PIN_SPI0_MOSI);
|
||||||
gpio_pull_up(SDCARD_PIN_SPI0_MOSI);
|
gpio_pull_up(SDCARD_PIN_SPI0_MOSI);
|
||||||
//gpio_set_drive_strength(SDCARD_PIN_SPI0_MOSI, PADS_BANK0_GPIO0_DRIVE_VALUE_4MA); // 2mA, 4mA (default), 8mA, 12mA
|
//gpio_set_drive_strength(SDCARD_PIN_SPI0_MOSI, PADS_BANK0_GPIO0_DRIVE_VALUE_4MA); // 2mA, 4mA (default), 8mA, 12mA
|
||||||
//gpio_set_slew_rate(SDCARD_PIN_SPI0_MOSI, 0); // 0: SLOW (default), 1: FAST
|
//gpio_set_slew_rate(SDCARD_PIN_SPI0_MOSI, 0); // 0: SLOW (default), 1: FAST
|
||||||
gpio_set_function(SDCARD_PIN_SPI0_MOSI, GPIO_FUNC_SPI);
|
|
||||||
|
|
||||||
gpio_init(SDCARD_PIN_SPI0_CS);
|
gpio_init(SDCARD_PIN_SPI0_CS);
|
||||||
//gpio_pull_up(SDCARD_PIN_SPI0_CS);
|
//gpio_pull_up(SDCARD_PIN_SPI0_CS);
|
||||||
|
@ -129,15 +141,40 @@ void init_spi(void)
|
||||||
/* chip _select invalid*/
|
/* chip _select invalid*/
|
||||||
CS_HIGH();
|
CS_HIGH();
|
||||||
|
|
||||||
spi_init(spi0, CLK_SLOW);
|
#ifndef SDCARD_PIO
|
||||||
|
gpio_set_function(SDCARD_PIN_SPI0_SCK, GPIO_FUNC_SPI);
|
||||||
|
gpio_set_function(SDCARD_PIN_SPI0_MISO, GPIO_FUNC_SPI);
|
||||||
|
gpio_set_function(SDCARD_PIN_SPI0_MOSI, GPIO_FUNC_SPI);
|
||||||
|
|
||||||
|
spi_init(SDCARD_SPI_BUS, CLK_SLOW);
|
||||||
|
|
||||||
/* SPI0 parameter config */
|
/* SPI0 parameter config */
|
||||||
spi_set_format(spi0,
|
spi_set_format(SDCARD_SPI_BUS,
|
||||||
8, /* data_bits */
|
8, /* data_bits */
|
||||||
SPI_CPOL_0, /* cpol */
|
SPI_CPOL_0, /* cpol */
|
||||||
SPI_CPHA_0, /* cpha */
|
SPI_CPHA_0, /* cpha */
|
||||||
SPI_MSB_FIRST /* order */
|
SPI_MSB_FIRST /* order */
|
||||||
);
|
);
|
||||||
|
#else
|
||||||
|
gpio_set_dir(SDCARD_PIN_SPI0_SCK, GPIO_OUT);
|
||||||
|
gpio_set_dir(SDCARD_PIN_SPI0_MISO, GPIO_OUT);
|
||||||
|
gpio_set_dir(SDCARD_PIN_SPI0_MOSI, GPIO_OUT);
|
||||||
|
|
||||||
|
float clkdiv = 3.0f;
|
||||||
|
int cpol = 0;
|
||||||
|
int cpha = 0;
|
||||||
|
uint cpha0_prog_offs = pio_add_program(pio_spi.pio, &spi_cpha0_program);
|
||||||
|
pio_spi_init(pio_spi.pio, pio_spi.sm,
|
||||||
|
cpha0_prog_offs,
|
||||||
|
8, // 8 bits per SPI frame
|
||||||
|
clkdiv,
|
||||||
|
cpha,
|
||||||
|
cpol,
|
||||||
|
SDCARD_PIN_SPI0_SCK,
|
||||||
|
SDCARD_PIN_SPI0_MOSI,
|
||||||
|
SDCARD_PIN_SPI0_MISO
|
||||||
|
);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Exchange a byte */
|
/* Exchange a byte */
|
||||||
|
@ -147,7 +184,11 @@ BYTE xchg_spi (
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
uint8_t *buff = (uint8_t *) &dat;
|
uint8_t *buff = (uint8_t *) &dat;
|
||||||
spi_write_read_blocking(spi0, buff, buff, 1);
|
#ifndef SDCARD_PIO
|
||||||
|
spi_write_read_blocking(SDCARD_SPI_BUS, buff, buff, 1);
|
||||||
|
#else
|
||||||
|
pio_spi_write8_read8_blocking(&pio_spi, buff, buff, 1);
|
||||||
|
#endif
|
||||||
return (BYTE) *buff;
|
return (BYTE) *buff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,7 +201,11 @@ void rcvr_spi_multi (
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
uint8_t *b = (uint8_t *) buff;
|
uint8_t *b = (uint8_t *) buff;
|
||||||
spi_read_blocking(spi0, 0xff, b, btr);
|
#ifndef SDCARD_PIO
|
||||||
|
spi_read_blocking(SDCARD_SPI_BUS, 0xff, b, btr);
|
||||||
|
#else
|
||||||
|
pio_spi_repeat8_read8_blocking(&pio_spi, 0xff, b, btr);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -427,7 +472,11 @@ void xmit_spi_multi (
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
const uint8_t *b = (const uint8_t *) buff;
|
const uint8_t *b = (const uint8_t *) buff;
|
||||||
spi_write_blocking(spi0, b, btx);
|
#ifndef SDCARD_PIO
|
||||||
|
spi_write_blocking(SDCARD_SPI_BUS, b, btx);
|
||||||
|
#else
|
||||||
|
pio_spi_write8_blocking(&pio_spi, b, btx);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/*-----------------------------------------------------------------------*/
|
/*-----------------------------------------------------------------------*/
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
if (NOT TARGET sdcard)
|
if (NOT TARGET sdcard)
|
||||||
add_library(sdcard INTERFACE)
|
add_library(sdcard INTERFACE)
|
||||||
|
|
||||||
|
pico_generate_pio_header(sdcard ${CMAKE_CURRENT_LIST_DIR}/spi.pio)
|
||||||
|
|
||||||
target_sources(sdcard INTERFACE
|
target_sources(sdcard INTERFACE
|
||||||
${CMAKE_CURRENT_LIST_DIR}/sdcard.c
|
${CMAKE_CURRENT_LIST_DIR}/sdcard.c
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/pio_spi.c
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(sdcard INTERFACE fatfs pico_stdlib hardware_clocks hardware_spi)
|
target_link_libraries(sdcard INTERFACE fatfs pico_stdlib hardware_clocks hardware_spi hardware_pio)
|
||||||
target_include_directories(sdcard INTERFACE ${CMAKE_CURRENT_LIST_DIR})
|
target_include_directories(sdcard INTERFACE ${CMAKE_CURRENT_LIST_DIR})
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -4,6 +4,10 @@
|
||||||
/* SPI pin assignment */
|
/* SPI pin assignment */
|
||||||
|
|
||||||
/* Pico Wireless */
|
/* Pico Wireless */
|
||||||
|
#ifndef SDCARD_SPI_BUS
|
||||||
|
#define SDCARD_SPI_BUS spi0
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef SDCARD_PIN_SPI0_CS
|
#ifndef SDCARD_PIN_SPI0_CS
|
||||||
#define SDCARD_PIN_SPI0_CS 22
|
#define SDCARD_PIN_SPI0_CS 22
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,168 @@
|
||||||
|
;
|
||||||
|
; Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||||
|
;
|
||||||
|
; SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
;
|
||||||
|
|
||||||
|
; These programs implement full-duplex SPI, with a SCK period of 4 clock
|
||||||
|
; cycles. A different program is provided for each value of CPHA, and CPOL is
|
||||||
|
; achieved using the hardware GPIO inversion available in the IO controls.
|
||||||
|
;
|
||||||
|
; Transmit-only SPI can go twice as fast -- see the ST7789 example!
|
||||||
|
|
||||||
|
|
||||||
|
.program spi_cpha0
|
||||||
|
.side_set 1
|
||||||
|
|
||||||
|
; Pin assignments:
|
||||||
|
; - SCK is side-set pin 0
|
||||||
|
; - MOSI is OUT pin 0
|
||||||
|
; - MISO is IN pin 0
|
||||||
|
;
|
||||||
|
; Autopush and autopull must be enabled, and the serial frame size is set by
|
||||||
|
; configuring the push/pull threshold. Shift left/right is fine, but you must
|
||||||
|
; justify the data yourself. This is done most conveniently for frame sizes of
|
||||||
|
; 8 or 16 bits by using the narrow store replication and narrow load byte
|
||||||
|
; picking behaviour of RP2040's IO fabric.
|
||||||
|
|
||||||
|
; Clock phase = 0: data is captured on the leading edge of each SCK pulse, and
|
||||||
|
; transitions on the trailing edge, or some time before the first leading edge.
|
||||||
|
|
||||||
|
out pins, 1 side 0 [1] ; Stall here on empty (sideset proceeds even if
|
||||||
|
in pins, 1 side 1 [1] ; instruction stalls, so we stall with SCK low)
|
||||||
|
|
||||||
|
.program spi_cpha1
|
||||||
|
.side_set 1
|
||||||
|
|
||||||
|
; Clock phase = 1: data transitions on the leading edge of each SCK pulse, and
|
||||||
|
; is captured on the trailing edge.
|
||||||
|
|
||||||
|
out x, 1 side 0 ; Stall here on empty (keep SCK deasserted)
|
||||||
|
mov pins, x side 1 [1] ; Output data, assert SCK (mov pins uses OUT mapping)
|
||||||
|
in pins, 1 side 0 ; Input data, deassert SCK
|
||||||
|
|
||||||
|
% c-sdk {
|
||||||
|
#include "hardware/gpio.h"
|
||||||
|
static inline void pio_spi_init(PIO pio, uint sm, uint prog_offs, uint n_bits,
|
||||||
|
float clkdiv, bool cpha, bool cpol, uint pin_sck, uint pin_mosi, uint pin_miso) {
|
||||||
|
pio_sm_config c = cpha ? spi_cpha1_program_get_default_config(prog_offs) : spi_cpha0_program_get_default_config(prog_offs);
|
||||||
|
sm_config_set_out_pins(&c, pin_mosi, 1);
|
||||||
|
sm_config_set_in_pins(&c, pin_miso);
|
||||||
|
sm_config_set_sideset_pins(&c, pin_sck);
|
||||||
|
// Only support MSB-first in this example code (shift to left, auto push/pull, threshold=nbits)
|
||||||
|
sm_config_set_out_shift(&c, false, true, n_bits);
|
||||||
|
sm_config_set_in_shift(&c, false, true, n_bits);
|
||||||
|
sm_config_set_clkdiv(&c, clkdiv);
|
||||||
|
|
||||||
|
// MOSI, SCK output are low, MISO is input
|
||||||
|
pio_sm_set_pins_with_mask(pio, sm, 0, (1u << pin_sck) | (1u << pin_mosi));
|
||||||
|
pio_sm_set_pindirs_with_mask(pio, sm, (1u << pin_sck) | (1u << pin_mosi), (1u << pin_sck) | (1u << pin_mosi) | (1u << pin_miso));
|
||||||
|
pio_gpio_init(pio, pin_mosi);
|
||||||
|
pio_gpio_init(pio, pin_miso);
|
||||||
|
pio_gpio_init(pio, pin_sck);
|
||||||
|
|
||||||
|
// The pin muxes can be configured to invert the output (among other things
|
||||||
|
// and this is a cheesy way to get CPOL=1
|
||||||
|
gpio_set_outover(pin_sck, cpol ? GPIO_OVERRIDE_INVERT : GPIO_OVERRIDE_NORMAL);
|
||||||
|
// SPI is synchronous, so bypass input synchroniser to reduce input delay.
|
||||||
|
hw_set_bits(&pio->input_sync_bypass, 1u << pin_miso);
|
||||||
|
|
||||||
|
pio_sm_init(pio, sm, prog_offs, &c);
|
||||||
|
pio_sm_set_enabled(pio, sm, true);
|
||||||
|
}
|
||||||
|
%}
|
||||||
|
|
||||||
|
; SPI with Chip Select
|
||||||
|
; -----------------------------------------------------------------------------
|
||||||
|
;
|
||||||
|
; For your amusement, here are some SPI programs with an automatic chip select
|
||||||
|
; (asserted once data appears in TX FIFO, deasserts when FIFO bottoms out, has
|
||||||
|
; a nice front/back porch).
|
||||||
|
;
|
||||||
|
; The number of bits per FIFO entry is configured via the Y register
|
||||||
|
; and the autopush/pull threshold. From 2 to 32 bits.
|
||||||
|
;
|
||||||
|
; Pin assignments:
|
||||||
|
; - SCK is side-set bit 0
|
||||||
|
; - CSn is side-set bit 1
|
||||||
|
; - MOSI is OUT bit 0 (host-to-device)
|
||||||
|
; - MISO is IN bit 0 (device-to-host)
|
||||||
|
;
|
||||||
|
; This program only supports one chip select -- use GPIO if more are needed
|
||||||
|
;
|
||||||
|
; Provide a variation for each possibility of CPHA; for CPOL we can just
|
||||||
|
; invert SCK in the IO muxing controls (downstream from PIO)
|
||||||
|
|
||||||
|
|
||||||
|
; CPHA=0: data is captured on the leading edge of each SCK pulse (including
|
||||||
|
; the first pulse), and transitions on the trailing edge
|
||||||
|
|
||||||
|
.program spi_cpha0_cs
|
||||||
|
.side_set 2
|
||||||
|
|
||||||
|
.wrap_target
|
||||||
|
bitloop:
|
||||||
|
out pins, 1 side 0x0 [1]
|
||||||
|
in pins, 1 side 0x1
|
||||||
|
jmp x-- bitloop side 0x1
|
||||||
|
|
||||||
|
out pins, 1 side 0x0
|
||||||
|
mov x, y side 0x0 ; Reload bit counter from Y
|
||||||
|
in pins, 1 side 0x1
|
||||||
|
jmp !osre bitloop side 0x1 ; Fall-through if TXF empties
|
||||||
|
|
||||||
|
nop side 0x0 [1] ; CSn back porch
|
||||||
|
public entry_point: ; Must set X,Y to n-2 before starting!
|
||||||
|
pull ifempty side 0x2 [1] ; Block with CSn high (minimum 2 cycles)
|
||||||
|
.wrap ; Note ifempty to avoid time-of-check race
|
||||||
|
|
||||||
|
; CPHA=1: data transitions on the leading edge of each SCK pulse, and is
|
||||||
|
; captured on the trailing edge
|
||||||
|
|
||||||
|
.program spi_cpha1_cs
|
||||||
|
.side_set 2
|
||||||
|
|
||||||
|
.wrap_target
|
||||||
|
bitloop:
|
||||||
|
out pins, 1 side 0x1 [1]
|
||||||
|
in pins, 1 side 0x0
|
||||||
|
jmp x-- bitloop side 0x0
|
||||||
|
|
||||||
|
out pins, 1 side 0x1
|
||||||
|
mov x, y side 0x1
|
||||||
|
in pins, 1 side 0x0
|
||||||
|
jmp !osre bitloop side 0x0
|
||||||
|
|
||||||
|
public entry_point: ; Must set X,Y to n-2 before starting!
|
||||||
|
pull ifempty side 0x2 [1] ; Block with CSn high (minimum 2 cycles)
|
||||||
|
nop side 0x0 [1]; CSn front porch
|
||||||
|
.wrap
|
||||||
|
|
||||||
|
% c-sdk {
|
||||||
|
#include "hardware/gpio.h"
|
||||||
|
static inline void pio_spi_cs_init(PIO pio, uint sm, uint prog_offs, uint n_bits, float clkdiv, bool cpha, bool cpol,
|
||||||
|
uint pin_sck, uint pin_mosi, uint pin_miso) {
|
||||||
|
pio_sm_config c = cpha ? spi_cpha1_cs_program_get_default_config(prog_offs) : spi_cpha0_cs_program_get_default_config(prog_offs);
|
||||||
|
sm_config_set_out_pins(&c, pin_mosi, 1);
|
||||||
|
sm_config_set_in_pins(&c, pin_miso);
|
||||||
|
sm_config_set_sideset_pins(&c, pin_sck);
|
||||||
|
sm_config_set_out_shift(&c, false, true, n_bits);
|
||||||
|
sm_config_set_in_shift(&c, false, true, n_bits);
|
||||||
|
sm_config_set_clkdiv(&c, clkdiv);
|
||||||
|
|
||||||
|
pio_sm_set_pins_with_mask(pio, sm, (2u << pin_sck), (3u << pin_sck) | (1u << pin_mosi));
|
||||||
|
pio_sm_set_pindirs_with_mask(pio, sm, (3u << pin_sck) | (1u << pin_mosi), (3u << pin_sck) | (1u << pin_mosi) | (1u << pin_miso));
|
||||||
|
pio_gpio_init(pio, pin_mosi);
|
||||||
|
pio_gpio_init(pio, pin_miso);
|
||||||
|
pio_gpio_init(pio, pin_sck);
|
||||||
|
pio_gpio_init(pio, pin_sck + 1);
|
||||||
|
gpio_set_outover(pin_sck, cpol ? GPIO_OVERRIDE_INVERT : GPIO_OVERRIDE_NORMAL);
|
||||||
|
hw_set_bits(&pio->input_sync_bypass, 1u << pin_miso);
|
||||||
|
|
||||||
|
uint entry_point = prog_offs + (cpha ? spi_cpha1_cs_offset_entry_point : spi_cpha0_cs_offset_entry_point);
|
||||||
|
pio_sm_init(pio, sm, entry_point, &c);
|
||||||
|
pio_sm_exec(pio, sm, pio_encode_set(pio_x, n_bits - 2));
|
||||||
|
pio_sm_exec(pio, sm, pio_encode_set(pio_y, n_bits - 2));
|
||||||
|
pio_sm_set_enabled(pio, sm, true);
|
||||||
|
}
|
||||||
|
%}
|
Ładowanie…
Reference in New Issue