| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174 | ; RP2040 PIO program for implementing SD card access in SDIO mode; Run "pioasm rp2040_sdio.pio rp2040_sdio.pio.h" to regenerate the C header from this.;; Copyright (c) 2022 Rabbit Hole Computing™; Copyright (c) 2011-2024 Bill Greiman; Copyright (c) 2024 Tech by Androda, LLC; This file is part of the SdFat library for SD memory cards.; Portions from Bill Greiman use the MIT License:; MIT License;; 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.; The RP2040 official work-in-progress code at; https://github.com/raspberrypi/pico-extras/tree/master/src/rp2_common/pico_sd_card; may be useful reference, but this is independent implementation.;; For official SDIO specifications, refer to:; https://www.sdcard.org/downloads/pls/; "SDIO Physical Layer Simplified Specification Version 8.00"; Clock settings; For 3.3V communication the available speeds are:; - Default speed: max. 25 MHz clock; - High speed:    max. 50 MHz clock;; From the default RP2040 clock speed of 125 MHz, the closest dividers; are 3 for 41.7 MHz and 5 for 25 MHz. The CPU can apply further divider; through state machine registers for the initial handshake.;; Because data is written on the falling edge and read on the rising; edge, it is preferrable to have a long 0 state and short 1 state.;.define CLKDIV 3.define CLKDIV 5.define D0 (((CLKDIV + 1) /2) - 1).define D1 ((CLKDIV/2) - 1).define SDIO_CLK_GPIO 10.define public SDIO_IRQ 7; State Machine 0 is for the Command / Response; This State Machine will stall with clock low after sending a command and receiving the response; Note that the FIFOs are set to 8 bit mode here, because 8 bits evenly divides all command and response sizes.program cmd_rsp.side_set 1 opt.wrap_targetcmd_begin:send_cmd:    out pins, 1         side 0 [1]  ; When TX FIFO is empty, this command will stall with clock low    jmp X-- send_cmd    side 1 [1]    jmp !Y cmd_begin    side 0 [1]  ; If no response, go back to the beginning and stall    set pindirs, 0      side 1 [3]wait_resp:    nop                 side 0 [3]    nop                 side 1 [2]    jmp PIN wait_resp               ; Run the SD clock until CMD pin goes low (First bit of response)    read_resp:    in pins, 1              push iffull block   side 0 [2]  ; Read command response    jmp Y-- read_resp   side 1 [1].wrap% c-sdk {static inline pio_sm_config pio_cmd_rsp_program_config(uint offset, uint cmd_pin, uint clk_pin, uint16_t div_int, uint8_t div_frac) {    pio_sm_config c = cmd_rsp_program_get_default_config(offset);    sm_config_set_sideset_pins(&c, clk_pin);    sm_config_set_out_pins(&c, cmd_pin, 1);    sm_config_set_in_pins(&c, cmd_pin);    sm_config_set_set_pins(&c, cmd_pin, 1);    sm_config_set_jmp_pin(&c, cmd_pin);    sm_config_set_in_shift(&c, false, false, 8);    sm_config_set_out_shift(&c, false, true, 8);    sm_config_set_clkdiv_int_frac(&c, div_int, div_frac);    return c;}%}; Program which reads data and provides its own clock signal; Use direct-execute PIO instructions to place the number of 4-bit nibbles to receive; into the X register before enabling the state machine.program rd_data_w_clock.side_set 1mov X, Y                side 0      ; Reinitialize number of nibbles to receivewait_d0:    nop                 side 0 [3]  ; Run the clock...    jmp PIN wait_d0     side 1 [3]  ; Until the first response nibble (all zeroes)    nop                 side 0 [2]  ; Clock transition low to make the SD card write out the first actual data nibble    nop                 side 1 [1]  ; Transition clock high to stick data valueread_loop:    in pins, 4          side 0 [2]  ; Read in the nibble and transition the clock low    push iffull block   side 1      ; Transition the clock high and block execution if rx fifo is full    jmp X--, read_loop  side 1      ; No delays here or previous instruction, because instr [1] = two instr execution time% c-sdk {static inline pio_sm_config pio_rd_data_w_clock_program_config(uint offset, uint d0_pin, uint clk_pin, float clk_div) {  pio_sm_config c = rd_data_w_clock_program_get_default_config(offset);  sm_config_set_sideset_pins(&c, clk_pin);  sm_config_set_in_pins(&c, d0_pin);  sm_config_set_jmp_pin(&c, d0_pin);  sm_config_set_in_shift(&c, false, false, 32);  sm_config_set_out_shift(&c, false, true, 32);  sm_config_set_clkdiv(&c, clk_div);  return c;}%}; Data transmission program;; Before running this program, pindirs should be set as output; and register X should be initialized with the number of nibbles; to send minus 1 (typically 8 + 1024 + 16 + 1 - 1 = 1048);; Register Y must be set to the number of CRC bits to receive (8/32);; Words written to TX FIFO must be:; - Word 0: start token 0xFFFFFFF0; - Word 1-128: transmitted data (512 bytes); - Word 129-130: CRC checksum; - Word 131: end token 0xFFFFFFFF.program sdio_tx_w_clock.side_set 1 opttx_loop:    out PINS, 4             side 0 [2]      ; Write nibble value and transition clock low    jmp X-- tx_loop         side 1 [1]      ; Transition clock high, and check if more data needs to be sent    set pindirs, 0          side 1 [2]      ; Set input mode to receive CRC token, without changing clock phasecrc_get:    in pins, 1              side 1 [4]      ; Input the first bit of CRC response    jmp Y-- crc_get         side 0 [4]      ; Read the CRC bitsbsy_wait:    jmp PIN done            side 1 [4]    jmp bsy_wait            side 0 [4]      ; Clock until no longer BSYdone:.wrap_target    push iffull noblock     side 0         ; Unconditional, just push the response token.wrap% c-sdk {static inline pio_sm_config pio_sdio_tx_w_clock_program_config(uint offset, uint data_pin, uint clk_pin, int clk_div) {    pio_sm_config c = sdio_tx_w_clock_program_get_default_config(offset);    sm_config_set_sideset_pins(&c, clk_pin);    sm_config_set_out_pins(&c, data_pin, 4);    sm_config_set_in_pins(&c, data_pin);    sm_config_set_set_pins(&c, data_pin, 4);    sm_config_set_in_shift(&c, false, false, 8);    sm_config_set_out_shift(&c, false, true, 32);    sm_config_set_jmp_pin(&c, data_pin);    sm_config_set_clkdiv_int_frac(&c, clk_div, 0);    return c;}%}
 |