sdcardio: implement new library for SD card I/O
Testing performed: That a card is successfully mounted on Pygamer with the built in SD card slot This module is enabled for most FULL_BUILD boards, but is disabled for samd21 ("M0"), litex, and pca10100 for various reasons.
This commit is contained in:
parent
12df4ce221
commit
57fde2e07b
|
@ -8,7 +8,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2020-06-25 11:44-0500\n"
|
"POT-Creation-Date: 2020-06-26 11:50-0500\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
@ -417,6 +417,10 @@ msgstr ""
|
||||||
msgid "Buffer length %d too big. It must be less than %d"
|
msgid "Buffer length %d too big. It must be less than %d"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: shared-module/sdcardio/SDCard.c
|
||||||
|
msgid "Buffer length must be a multiple of 512"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: shared-bindings/bitbangio/I2C.c shared-bindings/busio/I2C.c
|
#: shared-bindings/bitbangio/I2C.c shared-bindings/busio/I2C.c
|
||||||
msgid "Buffer must be at least length 1"
|
msgid "Buffer must be at least length 1"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@ -698,7 +702,8 @@ msgstr ""
|
||||||
msgid "Error in regex"
|
msgid "Error in regex"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: shared-bindings/aesio/aes.c shared-bindings/microcontroller/Pin.c
|
#: shared-bindings/aesio/aes.c shared-bindings/busio/SPI.c
|
||||||
|
#: shared-bindings/microcontroller/Pin.c
|
||||||
#: shared-bindings/neopixel_write/__init__.c shared-bindings/pulseio/PulseOut.c
|
#: shared-bindings/neopixel_write/__init__.c shared-bindings/pulseio/PulseOut.c
|
||||||
#: shared-bindings/terminalio/Terminal.c
|
#: shared-bindings/terminalio/Terminal.c
|
||||||
msgid "Expected a %q"
|
msgid "Expected a %q"
|
||||||
|
@ -1358,6 +1363,10 @@ msgstr ""
|
||||||
msgid "Running in safe mode! Not running saved code.\n"
|
msgid "Running in safe mode! Not running saved code.\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: shared-module/sdcardio/SDCard.c
|
||||||
|
msgid "SD card CSD format not supported"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: ports/atmel-samd/common-hal/busio/I2C.c
|
#: ports/atmel-samd/common-hal/busio/I2C.c
|
||||||
#: ports/mimxrt10xx/common-hal/busio/I2C.c ports/nrf/common-hal/busio/I2C.c
|
#: ports/mimxrt10xx/common-hal/busio/I2C.c ports/nrf/common-hal/busio/I2C.c
|
||||||
msgid "SDA or SCL needs a pull up"
|
msgid "SDA or SCL needs a pull up"
|
||||||
|
@ -1979,6 +1988,10 @@ msgstr ""
|
||||||
msgid "can't send non-None value to a just-started generator"
|
msgid "can't send non-None value to a just-started generator"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: shared-module/sdcardio/SDCard.c
|
||||||
|
msgid "can't set 512 block size"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: py/objnamedtuple.c
|
#: py/objnamedtuple.c
|
||||||
msgid "can't set attribute"
|
msgid "can't set attribute"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@ -2105,6 +2118,10 @@ msgstr ""
|
||||||
msgid "could not invert Vandermonde matrix"
|
msgid "could not invert Vandermonde matrix"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: shared-module/sdcardio/SDCard.c
|
||||||
|
msgid "couldn't determine SD card version"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: extmod/ulab/code/approx.c
|
#: extmod/ulab/code/approx.c
|
||||||
msgid "data must be iterable"
|
msgid "data must be iterable"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@ -2662,6 +2679,10 @@ msgstr ""
|
||||||
msgid "negative shift count"
|
msgid "negative shift count"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: shared-module/sdcardio/SDCard.c
|
||||||
|
msgid "no SD card"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: py/vm.c
|
#: py/vm.c
|
||||||
msgid "no active exception to reraise"
|
msgid "no active exception to reraise"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@ -2683,6 +2704,10 @@ msgstr ""
|
||||||
msgid "no reset pin available"
|
msgid "no reset pin available"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: shared-module/sdcardio/SDCard.c
|
||||||
|
msgid "no response from SD card"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: py/runtime.c
|
#: py/runtime.c
|
||||||
msgid "no such attribute"
|
msgid "no such attribute"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@ -3073,6 +3098,14 @@ msgstr ""
|
||||||
msgid "timeout must be >= 0.0"
|
msgid "timeout must be >= 0.0"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: shared-module/sdcardio/SDCard.c
|
||||||
|
msgid "timeout waiting for v1 card"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: shared-module/sdcardio/SDCard.c
|
||||||
|
msgid "timeout waiting for v2 card"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: shared-bindings/time/__init__.c
|
#: shared-bindings/time/__init__.c
|
||||||
msgid "timestamp out of range for platform time_t"
|
msgid "timestamp out of range for platform time_t"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
|
@ -37,6 +37,8 @@ ifndef CIRCUITPY_TOUCHIO_USE_NATIVE
|
||||||
CIRCUITPY_TOUCHIO_USE_NATIVE = 1
|
CIRCUITPY_TOUCHIO_USE_NATIVE = 1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
CIRCUITPY_SDCARDIO ?= 0
|
||||||
|
|
||||||
# SAMD21 needs separate endpoint pairs for MSC BULK IN and BULK OUT, otherwise it's erratic.
|
# SAMD21 needs separate endpoint pairs for MSC BULK IN and BULK OUT, otherwise it's erratic.
|
||||||
USB_MSC_EP_NUM_OUT = 1
|
USB_MSC_EP_NUM_OUT = 1
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ CIRCUITPY_AUDIOIO = 0
|
||||||
CIRCUITPY_BITBANGIO = 0
|
CIRCUITPY_BITBANGIO = 0
|
||||||
CIRCUITPY_BOARD = 0
|
CIRCUITPY_BOARD = 0
|
||||||
CIRCUITPY_BUSIO = 0
|
CIRCUITPY_BUSIO = 0
|
||||||
|
CIRCUITPY_COUNTIO = 0
|
||||||
CIRCUITPY_DISPLAYIO = 0
|
CIRCUITPY_DISPLAYIO = 0
|
||||||
CIRCUITPY_FREQUENCYIO = 0
|
CIRCUITPY_FREQUENCYIO = 0
|
||||||
CIRCUITPY_I2CPERIPHERAL = 0
|
CIRCUITPY_I2CPERIPHERAL = 0
|
||||||
|
@ -25,7 +26,7 @@ CIRCUITPY_NVM = 0
|
||||||
CIRCUITPY_PULSEIO = 0
|
CIRCUITPY_PULSEIO = 0
|
||||||
CIRCUITPY_ROTARYIO = 0
|
CIRCUITPY_ROTARYIO = 0
|
||||||
CIRCUITPY_RTC = 0
|
CIRCUITPY_RTC = 0
|
||||||
CIRCUITPY_COUNTIO = 0
|
CIRCUITPY_SDCARDIO = 0
|
||||||
# Enable USB support
|
# Enable USB support
|
||||||
CIRCUITPY_USB_HID = 1
|
CIRCUITPY_USB_HID = 1
|
||||||
CIRCUITPY_USB_MIDI = 1
|
CIRCUITPY_USB_MIDI = 1
|
||||||
|
|
|
@ -21,6 +21,7 @@ CIRCUITPY_PIXELBUF = 0
|
||||||
CIRCUITPY_RGBMATRIX = 0
|
CIRCUITPY_RGBMATRIX = 0
|
||||||
CIRCUITPY_ROTARYIO = 0
|
CIRCUITPY_ROTARYIO = 0
|
||||||
CIRCUITPY_RTC = 1
|
CIRCUITPY_RTC = 1
|
||||||
|
CIRCUITPY_SDCARDIO = 0
|
||||||
CIRCUITPY_TOUCHIO = 0
|
CIRCUITPY_TOUCHIO = 0
|
||||||
CIRCUITPY_ULAB = 0
|
CIRCUITPY_ULAB = 0
|
||||||
|
|
||||||
|
|
|
@ -213,6 +213,9 @@ endif
|
||||||
ifeq ($(CIRCUITPY_SAMD),1)
|
ifeq ($(CIRCUITPY_SAMD),1)
|
||||||
SRC_PATTERNS += samd/%
|
SRC_PATTERNS += samd/%
|
||||||
endif
|
endif
|
||||||
|
ifeq ($(CIRCUITPY_SDCARDIO),1)
|
||||||
|
SRC_PATTERNS += sdcardio/%
|
||||||
|
endif
|
||||||
ifeq ($(CIRCUITPY_STAGE),1)
|
ifeq ($(CIRCUITPY_STAGE),1)
|
||||||
SRC_PATTERNS += _stage/%
|
SRC_PATTERNS += _stage/%
|
||||||
endif
|
endif
|
||||||
|
@ -384,6 +387,8 @@ SRC_SHARED_MODULE_ALL = \
|
||||||
fontio/__init__.c \
|
fontio/__init__.c \
|
||||||
framebufferio/FramebufferDisplay.c \
|
framebufferio/FramebufferDisplay.c \
|
||||||
framebufferio/__init__.c \
|
framebufferio/__init__.c \
|
||||||
|
sdcardio/SDCard.c \
|
||||||
|
sdcardio/__init__.c \
|
||||||
gamepad/GamePad.c \
|
gamepad/GamePad.c \
|
||||||
gamepad/__init__.c \
|
gamepad/__init__.c \
|
||||||
gamepadshift/GamePadShift.c \
|
gamepadshift/GamePadShift.c \
|
||||||
|
|
|
@ -537,6 +537,13 @@ extern const struct _mp_obj_module_t samd_module;
|
||||||
#define SAMD_MODULE
|
#define SAMD_MODULE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if CIRCUITPY_SDCARDIO
|
||||||
|
extern const struct _mp_obj_module_t sdcardio_module;
|
||||||
|
#define SDCARDIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_sdcardio), (mp_obj_t)&sdcardio_module },
|
||||||
|
#else
|
||||||
|
#define SDCARDIO_MODULE
|
||||||
|
#endif
|
||||||
|
|
||||||
#if CIRCUITPY_STAGE
|
#if CIRCUITPY_STAGE
|
||||||
extern const struct _mp_obj_module_t stage_module;
|
extern const struct _mp_obj_module_t stage_module;
|
||||||
#define STAGE_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR__stage), (mp_obj_t)&stage_module },
|
#define STAGE_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR__stage), (mp_obj_t)&stage_module },
|
||||||
|
@ -709,6 +716,7 @@ extern const struct _mp_obj_module_t watchdog_module;
|
||||||
ROTARYIO_MODULE \
|
ROTARYIO_MODULE \
|
||||||
RTC_MODULE \
|
RTC_MODULE \
|
||||||
SAMD_MODULE \
|
SAMD_MODULE \
|
||||||
|
SDCARDIO_MODULE \
|
||||||
STAGE_MODULE \
|
STAGE_MODULE \
|
||||||
STORAGE_MODULE \
|
STORAGE_MODULE \
|
||||||
STRUCT_MODULE \
|
STRUCT_MODULE \
|
||||||
|
|
|
@ -166,6 +166,9 @@ CFLAGS += -DCIRCUITPY_RTC=$(CIRCUITPY_RTC)
|
||||||
CIRCUITPY_SAMD ?= 0
|
CIRCUITPY_SAMD ?= 0
|
||||||
CFLAGS += -DCIRCUITPY_SAMD=$(CIRCUITPY_SAMD)
|
CFLAGS += -DCIRCUITPY_SAMD=$(CIRCUITPY_SAMD)
|
||||||
|
|
||||||
|
CIRCUITPY_SDCARDIO ?= $(CIRCUITPY_FULL_BUILD)
|
||||||
|
CFLAGS += -DCIRCUITPY_SDCARDIO=$(CIRCUITPY_SDCARDIO)
|
||||||
|
|
||||||
# Currently always off.
|
# Currently always off.
|
||||||
CIRCUITPY_STAGE ?= 0
|
CIRCUITPY_STAGE ?= 0
|
||||||
CFLAGS += -DCIRCUITPY_STAGE=$(CIRCUITPY_STAGE)
|
CFLAGS += -DCIRCUITPY_STAGE=$(CIRCUITPY_STAGE)
|
||||||
|
|
|
@ -418,3 +418,10 @@ const mp_obj_type_t busio_spi_type = {
|
||||||
.make_new = busio_spi_make_new,
|
.make_new = busio_spi_make_new,
|
||||||
.locals_dict = (mp_obj_dict_t*)&busio_spi_locals_dict,
|
.locals_dict = (mp_obj_dict_t*)&busio_spi_locals_dict,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
busio_spi_obj_t *validate_obj_is_spi_bus(mp_obj_t obj) {
|
||||||
|
if (!MP_OBJ_IS_TYPE(obj, &busio_spi_type)) {
|
||||||
|
mp_raise_TypeError_varg(translate("Expected a %q"), busio_spi_type.name);
|
||||||
|
}
|
||||||
|
return MP_OBJ_TO_PTR(obj);
|
||||||
|
}
|
||||||
|
|
|
@ -70,4 +70,6 @@ uint8_t common_hal_busio_spi_get_polarity(busio_spi_obj_t* self);
|
||||||
// This is used by the supervisor to claim SPI devices indefinitely.
|
// This is used by the supervisor to claim SPI devices indefinitely.
|
||||||
extern void common_hal_busio_spi_never_reset(busio_spi_obj_t *self);
|
extern void common_hal_busio_spi_never_reset(busio_spi_obj_t *self);
|
||||||
|
|
||||||
|
extern busio_spi_obj_t *validate_obj_is_spi_bus(mp_obj_t obj_in);
|
||||||
|
|
||||||
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BUSIO_SPI_H
|
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BUSIO_SPI_H
|
||||||
|
|
|
@ -0,0 +1,184 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the Micro Python project, http://micropython.org/
|
||||||
|
*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020 Jeff Epler for Adafruit Industries
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "py/obj.h"
|
||||||
|
#include "py/objproperty.h"
|
||||||
|
#include "py/runtime.h"
|
||||||
|
#include "py/objarray.h"
|
||||||
|
|
||||||
|
#include "shared-bindings/sdcardio/SDCard.h"
|
||||||
|
#include "shared-module/sdcardio/SDCard.h"
|
||||||
|
#include "common-hal/busio/SPI.h"
|
||||||
|
#include "shared-bindings/busio/SPI.h"
|
||||||
|
#include "shared-bindings/microcontroller/Pin.h"
|
||||||
|
#include "supervisor/flash.h"
|
||||||
|
|
||||||
|
//| class SDCard:
|
||||||
|
//| """SD Card Block Interface
|
||||||
|
//|
|
||||||
|
//| Controls an SD card over SPI. This built-in module has higher read
|
||||||
|
//| performance than the library adafruit_sdcard, but it is only compatible with
|
||||||
|
//| `busio.SPI`, not `bitbangio.SPI`. Usually an SDCard object is used
|
||||||
|
//| with ``storage.VfsFat`` to allow file I/O to an SD card."""
|
||||||
|
//|
|
||||||
|
//| def __init__(bus:busio.SPI, cs=digitalio.DigitalInOut, baudrate=8000000):
|
||||||
|
//| """Construct an SPI SD Card object with the given properties
|
||||||
|
//|
|
||||||
|
//| :param busio.SPI spi: The SPI bus
|
||||||
|
//| :param microcontroller.Pin cs: The chip select connected to the card
|
||||||
|
//| :param int baudrate: The SPI data rate to use after card setup
|
||||||
|
//| :param busio.SDIO sdio: The SDIO bus. Mutually exclusive with spi and cs.
|
||||||
|
//|
|
||||||
|
//| Note that during detection and configuration, a hard-coded low baudrate is used.
|
||||||
|
//| Data transfers use the specified baurate (rounded down to one that is supported by
|
||||||
|
//| the microcontroller)
|
||||||
|
//|
|
||||||
|
//| Example usage:
|
||||||
|
//|
|
||||||
|
//| .. code-block:: python
|
||||||
|
//|
|
||||||
|
//| import os
|
||||||
|
//|
|
||||||
|
//| import board
|
||||||
|
//| import sdcardio
|
||||||
|
//| import storage
|
||||||
|
//|
|
||||||
|
//| sd = sdcardio.SDCard(board.SPI(), board.SD_CS)
|
||||||
|
//| vfs = storage.VfsFat(sd)
|
||||||
|
//| storage.mount(vfs, '/sd')
|
||||||
|
//| os.listdir('/sd')"""
|
||||||
|
|
||||||
|
STATIC mp_obj_t sdcardio_sdcard_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||||
|
enum { ARG_spi, ARG_cs, ARG_baudrate, ARG_sdio, NUM_ARGS };
|
||||||
|
static const mp_arg_t allowed_args[] = {
|
||||||
|
{ MP_QSTR_spi, MP_ARG_OBJ, {.u_obj = mp_const_none } },
|
||||||
|
{ MP_QSTR_cs, MP_ARG_OBJ, {.u_obj = mp_const_none } },
|
||||||
|
{ MP_QSTR_baudrate, MP_ARG_INT, {.u_int = 8000000} },
|
||||||
|
{ MP_QSTR_sdio, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_int = 8000000} },
|
||||||
|
};
|
||||||
|
MP_STATIC_ASSERT( MP_ARRAY_SIZE(allowed_args) == NUM_ARGS );
|
||||||
|
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||||
|
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||||
|
|
||||||
|
busio_spi_obj_t *spi = validate_obj_is_spi_bus(args[ARG_spi].u_obj);
|
||||||
|
mcu_pin_obj_t *cs = validate_obj_is_free_pin(args[ARG_cs].u_obj);
|
||||||
|
|
||||||
|
sdcardio_sdcard_obj_t *self = m_new_obj(sdcardio_sdcard_obj_t);
|
||||||
|
self->base.type = &sdcardio_SDCard_type;
|
||||||
|
|
||||||
|
common_hal_sdcardio_sdcard_construct(self, spi, cs, args[ARG_baudrate].u_int);
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//| def count() -> int:
|
||||||
|
//| """Returns the total number of sectors
|
||||||
|
//|
|
||||||
|
//| Due to technical limitations, this is a function and not a property.
|
||||||
|
//|
|
||||||
|
//| :return: The number of 512-byte blocks, as a number"""
|
||||||
|
//|
|
||||||
|
mp_obj_t sdcardio_sdcard_count(mp_obj_t self_in) {
|
||||||
|
sdcardio_sdcard_obj_t *self = (sdcardio_sdcard_obj_t*)self_in;
|
||||||
|
return mp_obj_new_int_from_ull(common_hal_sdcardio_sdcard_get_blockcount(self));
|
||||||
|
}
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_1(sdcardio_sdcard_count_obj, sdcardio_sdcard_count);
|
||||||
|
|
||||||
|
//| def deinit() -> None:
|
||||||
|
//| """Disable permanently.
|
||||||
|
//|
|
||||||
|
//| :return: None"""
|
||||||
|
//|
|
||||||
|
mp_obj_t sdcardio_sdcard_deinit(mp_obj_t self_in) {
|
||||||
|
sdcardio_sdcard_obj_t *self = (sdcardio_sdcard_obj_t*)self_in;
|
||||||
|
common_hal_sdcardio_sdcard_deinit(self);
|
||||||
|
return mp_const_none;
|
||||||
|
}
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_1(sdcardio_sdcard_deinit_obj, sdcardio_sdcard_deinit);
|
||||||
|
|
||||||
|
|
||||||
|
//| def readblocks(start_block: int, buf: bytearray) -> None:
|
||||||
|
//|
|
||||||
|
//| """Read one or more blocks from the card
|
||||||
|
//|
|
||||||
|
//| :param int start_block: The block to start reading from
|
||||||
|
//| :param bytearray buf: The buffer to write into. Length must be multiple of 512.
|
||||||
|
//|
|
||||||
|
//| :return: None"""
|
||||||
|
//|
|
||||||
|
|
||||||
|
mp_obj_t sdcardio_sdcard_readblocks(mp_obj_t self_in, mp_obj_t start_block_in, mp_obj_t buf_in) {
|
||||||
|
uint32_t start_block = mp_obj_get_int(start_block_in);
|
||||||
|
mp_buffer_info_t bufinfo;
|
||||||
|
mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_WRITE);
|
||||||
|
sdcardio_sdcard_obj_t *self = (sdcardio_sdcard_obj_t*)self_in;
|
||||||
|
int result = common_hal_sdcardio_sdcard_readblocks(self, start_block, &bufinfo);
|
||||||
|
if (result < 0) {
|
||||||
|
mp_raise_OSError(-result);
|
||||||
|
}
|
||||||
|
return mp_const_none;
|
||||||
|
}
|
||||||
|
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_3(sdcardio_sdcard_readblocks_obj, sdcardio_sdcard_readblocks);
|
||||||
|
|
||||||
|
//| def writeblocks(start_block: int, buf: bytearray) -> None:
|
||||||
|
//|
|
||||||
|
//| """Write one or more blocks to the card
|
||||||
|
//|
|
||||||
|
//| :param int start_block: The block to start writing from
|
||||||
|
//| :param bytearray buf: The buffer to read from. Length must be multiple of 512.
|
||||||
|
//|
|
||||||
|
//| :return: None"""
|
||||||
|
//|
|
||||||
|
|
||||||
|
mp_obj_t sdcardio_sdcard_writeblocks(mp_obj_t self_in, mp_obj_t start_block_in, mp_obj_t buf_in) {
|
||||||
|
uint32_t start_block = mp_obj_get_int(start_block_in);
|
||||||
|
mp_buffer_info_t bufinfo;
|
||||||
|
mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_READ);
|
||||||
|
sdcardio_sdcard_obj_t *self = (sdcardio_sdcard_obj_t*)self_in;
|
||||||
|
int result = common_hal_sdcardio_sdcard_writeblocks(self, start_block, &bufinfo);
|
||||||
|
if (result < 0) {
|
||||||
|
mp_raise_OSError(-result);
|
||||||
|
}
|
||||||
|
return mp_const_none;
|
||||||
|
}
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_3(sdcardio_sdcard_writeblocks_obj, sdcardio_sdcard_writeblocks);
|
||||||
|
|
||||||
|
STATIC const mp_rom_map_elem_t sdcardio_sdcard_locals_dict_table[] = {
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_count), MP_ROM_PTR(&sdcardio_sdcard_count_obj) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&sdcardio_sdcard_deinit_obj) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_readblocks), MP_ROM_PTR(&sdcardio_sdcard_readblocks_obj) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_writeblocks), MP_ROM_PTR(&sdcardio_sdcard_writeblocks_obj) },
|
||||||
|
};
|
||||||
|
STATIC MP_DEFINE_CONST_DICT(sdcardio_sdcard_locals_dict, sdcardio_sdcard_locals_dict_table);
|
||||||
|
|
||||||
|
const mp_obj_type_t sdcardio_SDCard_type = {
|
||||||
|
{ &mp_type_type },
|
||||||
|
.name = MP_QSTR_SDCard,
|
||||||
|
.make_new = sdcardio_sdcard_make_new,
|
||||||
|
.locals_dict = (mp_obj_dict_t*)&sdcardio_sdcard_locals_dict,
|
||||||
|
};
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the Micro Python project, http://micropython.org/
|
||||||
|
*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2017, 2018 Scott Shawcroft for Adafruit Industries
|
||||||
|
* Copyright (c) 2020 Jeff Epler for Adafruit Industries
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
extern const mp_obj_type_t sdcardio_SDCard_type;
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the MicroPython project, http://micropython.org/
|
||||||
|
*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020 Jeff Epler for Adafruit Industries
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "py/obj.h"
|
||||||
|
#include "py/runtime.h"
|
||||||
|
|
||||||
|
#include "shared-bindings/sdcardio/SDCard.h"
|
||||||
|
|
||||||
|
//| """Interface to an SD card via the SPI bus"""
|
||||||
|
|
||||||
|
STATIC const mp_rom_map_elem_t sdcardio_module_globals_table[] = {
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_sdcardio) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_SDCard), MP_ROM_PTR(&sdcardio_SDCard_type) },
|
||||||
|
};
|
||||||
|
|
||||||
|
STATIC MP_DEFINE_CONST_DICT(sdcardio_module_globals, sdcardio_module_globals_table);
|
||||||
|
|
||||||
|
const mp_obj_module_t sdcardio_module = {
|
||||||
|
.base = { &mp_type_module },
|
||||||
|
.globals = (mp_obj_dict_t*)&sdcardio_module_globals,
|
||||||
|
};
|
|
@ -0,0 +1,466 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the Micro Python project, http://micropython.org/
|
||||||
|
*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020 Jeff Epler for Adafruit Industries
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This implementation largely follows the structure of adafruit_sdcard.py
|
||||||
|
|
||||||
|
#include "shared-bindings/busio/SPI.h"
|
||||||
|
#include "shared-bindings/digitalio/DigitalInOut.h"
|
||||||
|
#include "shared-bindings/time/__init__.h"
|
||||||
|
#include "shared-bindings/util.h"
|
||||||
|
#include "shared-module/sdcardio/SDCard.h"
|
||||||
|
|
||||||
|
#include "py/mperrno.h"
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
#define DEBUG_PRINT(...) ((void)mp_printf(&mp_plat_print, ## __VA_ARGS__))
|
||||||
|
#else
|
||||||
|
#define DEBUG_PRINT(...) ((void)0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define CMD_TIMEOUT (200)
|
||||||
|
|
||||||
|
#define R1_IDLE_STATE (1<<0)
|
||||||
|
#define R1_ILLEGAL_COMMAND (1<<2)
|
||||||
|
|
||||||
|
#define TOKEN_CMD25 (0xFC)
|
||||||
|
#define TOKEN_STOP_TRAN (0xFD)
|
||||||
|
#define TOKEN_DATA (0xFE)
|
||||||
|
|
||||||
|
STATIC bool lock_and_configure_bus(sdcardio_sdcard_obj_t *self) {
|
||||||
|
if (!common_hal_busio_spi_try_lock(self->bus)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
common_hal_busio_spi_configure(self->bus, self->baudrate, 0, 0, 8);
|
||||||
|
common_hal_digitalio_digitalinout_set_value(&self->cs, false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
STATIC void lock_bus_or_throw(sdcardio_sdcard_obj_t *self) {
|
||||||
|
if (!lock_and_configure_bus(self)) {
|
||||||
|
mp_raise_OSError(EAGAIN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
STATIC void clock_card(sdcardio_sdcard_obj_t *self, int bytes) {
|
||||||
|
uint8_t buf[] = {0xff};
|
||||||
|
common_hal_digitalio_digitalinout_set_value(&self->cs, true);
|
||||||
|
for (int i=0; i<bytes; i++) {
|
||||||
|
common_hal_busio_spi_write(self->bus, buf, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
STATIC void extraclock_and_unlock_bus(sdcardio_sdcard_obj_t *self) {
|
||||||
|
clock_card(self, 1);
|
||||||
|
common_hal_busio_spi_unlock(self->bus);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t CRC7(const uint8_t* data, uint8_t n) {
|
||||||
|
uint8_t crc = 0;
|
||||||
|
for (uint8_t i = 0; i < n; i++) {
|
||||||
|
uint8_t d = data[i];
|
||||||
|
for (uint8_t j = 0; j < 8; j++) {
|
||||||
|
crc <<= 1;
|
||||||
|
if ((d & 0x80) ^ (crc & 0x80)) {
|
||||||
|
crc ^= 0x09;
|
||||||
|
}
|
||||||
|
d <<= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (crc << 1) | 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define READY_TIMEOUT_NS (300 * 1000 * 1000) // 300ms
|
||||||
|
STATIC void wait_for_ready(sdcardio_sdcard_obj_t *self) {
|
||||||
|
uint64_t deadline = common_hal_time_monotonic_ns() + READY_TIMEOUT_NS;
|
||||||
|
while (common_hal_time_monotonic_ns() < deadline) {
|
||||||
|
uint8_t b;
|
||||||
|
common_hal_busio_spi_read(self->bus, &b, 1, 0xff);
|
||||||
|
if (b == 0xff) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// In Python API, defaults are response=None, data_block=True, wait=True
|
||||||
|
STATIC int cmd(sdcardio_sdcard_obj_t *self, int cmd, int arg, void *response_buf, size_t response_len, bool data_block, bool wait) {
|
||||||
|
DEBUG_PRINT("cmd % 3d [%02x] arg=% 11d [%08x] len=%d%s%s\n", cmd, cmd, arg, arg, response_len, data_block ? " data" : "", wait ? " wait" : "");
|
||||||
|
uint8_t cmdbuf[6];
|
||||||
|
cmdbuf[0] = cmd | 0x40;
|
||||||
|
cmdbuf[1] = (arg >> 24) & 0xff;
|
||||||
|
cmdbuf[2] = (arg >> 16) & 0xff;
|
||||||
|
cmdbuf[3] = (arg >> 8) & 0xff;
|
||||||
|
cmdbuf[4] = arg & 0xff;
|
||||||
|
cmdbuf[5] = CRC7(cmdbuf, 5);
|
||||||
|
|
||||||
|
if (wait) {
|
||||||
|
wait_for_ready(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
common_hal_busio_spi_write(self->bus, cmdbuf, sizeof(cmdbuf));
|
||||||
|
|
||||||
|
// Wait for the response (response[7] == 0)
|
||||||
|
bool response_received = false;
|
||||||
|
for (int i=0; i<CMD_TIMEOUT; i++) {
|
||||||
|
common_hal_busio_spi_read(self->bus, cmdbuf, 1, 0xff);
|
||||||
|
if ((cmdbuf[0] & 0x80) == 0) {
|
||||||
|
response_received = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!response_received) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response_buf) {
|
||||||
|
|
||||||
|
if (data_block) {
|
||||||
|
cmdbuf[1] = 0xff;
|
||||||
|
do {
|
||||||
|
// Wait for the start block byte
|
||||||
|
common_hal_busio_spi_read(self->bus, cmdbuf+1, 1, 0xff);
|
||||||
|
} while (cmdbuf[1] != 0xfe);
|
||||||
|
}
|
||||||
|
|
||||||
|
common_hal_busio_spi_read(self->bus, response_buf, response_len, 0xff);
|
||||||
|
|
||||||
|
if (data_block) {
|
||||||
|
// Read and discard the CRC-CCITT checksum
|
||||||
|
common_hal_busio_spi_read(self->bus, cmdbuf+1, 2, 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmdbuf[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
STATIC int block_cmd(sdcardio_sdcard_obj_t *self, int cmd_, int block, void *response_buf, size_t response_len, bool data_block, bool wait) {
|
||||||
|
return cmd(self, cmd_, block * self->cdv, response_buf, response_len, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
STATIC bool cmd_nodata(sdcardio_sdcard_obj_t* self, int cmd, int response) {
|
||||||
|
uint8_t cmdbuf[2] = {cmd, 0xff};
|
||||||
|
|
||||||
|
common_hal_busio_spi_write(self->bus, cmdbuf, sizeof(cmdbuf));
|
||||||
|
|
||||||
|
// Wait for the response (response[7] == response)
|
||||||
|
for (int i=0; i<CMD_TIMEOUT; i++) {
|
||||||
|
common_hal_busio_spi_read(self->bus, cmdbuf, 1, 0xff);
|
||||||
|
if (cmdbuf[0] == response) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
STATIC const compressed_string_t *init_card_v1(sdcardio_sdcard_obj_t *self) {
|
||||||
|
for (int i=0; i<CMD_TIMEOUT; i++) {
|
||||||
|
if (cmd(self, 41, 0, NULL, 0, true, true) == 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return translate("timeout waiting for v1 card");
|
||||||
|
}
|
||||||
|
|
||||||
|
STATIC const compressed_string_t *init_card_v2(sdcardio_sdcard_obj_t *self) {
|
||||||
|
for (int i=0; i<CMD_TIMEOUT; i++) {
|
||||||
|
uint8_t ocr[4];
|
||||||
|
common_hal_time_delay_ms(50);
|
||||||
|
cmd(self, 58, 0, ocr, sizeof(ocr), false, true);
|
||||||
|
cmd(self, 55, 0, NULL, 0, true, true);
|
||||||
|
if (cmd(self, 41, 0x40000000, NULL, 0, true, true) == 0) {
|
||||||
|
cmd(self, 58, 0, ocr, sizeof(ocr), false, true);
|
||||||
|
if ((ocr[0] & 0x40) != 0) {
|
||||||
|
self->cdv = 1;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return translate("timeout waiting for v2 card");
|
||||||
|
}
|
||||||
|
|
||||||
|
STATIC const compressed_string_t *init_card(sdcardio_sdcard_obj_t *self) {
|
||||||
|
clock_card(self, 10);
|
||||||
|
|
||||||
|
common_hal_digitalio_digitalinout_set_value(&self->cs, false);
|
||||||
|
|
||||||
|
// CMD0: init card: should return _R1_IDLE_STATE (allow 5 attempts)
|
||||||
|
{
|
||||||
|
bool reached_idle_state = false;
|
||||||
|
for (int i=0; i<5; i++) {
|
||||||
|
if (cmd(self, 0, 0, NULL, 0, true, true) == R1_IDLE_STATE) {
|
||||||
|
reached_idle_state = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!reached_idle_state) {
|
||||||
|
return translate("no SD card");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CMD8: determine card version
|
||||||
|
{
|
||||||
|
uint8_t rb7[4];
|
||||||
|
int response = cmd(self, 8, 0x1AA, rb7, sizeof(rb7), false, true);
|
||||||
|
if (response == R1_IDLE_STATE) {
|
||||||
|
const compressed_string_t *result =init_card_v2(self);
|
||||||
|
if (result != NULL) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
} else if (response == (R1_IDLE_STATE | R1_ILLEGAL_COMMAND)) {
|
||||||
|
const compressed_string_t *result =init_card_v1(self);
|
||||||
|
if (result != NULL) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return translate("couldn't determine SD card version");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CMD9: get number of sectors
|
||||||
|
{
|
||||||
|
uint8_t csd[16];
|
||||||
|
int response = cmd(self, 9, 0, csd, sizeof(csd), true, true);
|
||||||
|
if (response != 0) {
|
||||||
|
return translate("no response from SD card");
|
||||||
|
}
|
||||||
|
int csd_version = (csd[0] & 0xC0) >> 6;
|
||||||
|
if (csd_version >= 2) {
|
||||||
|
return translate("SD card CSD format not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (csd_version == 1) {
|
||||||
|
self->sectors = ((csd[8] << 8 | csd[9]) + 1) * 1024;
|
||||||
|
} else {
|
||||||
|
uint32_t block_length = 1 << (csd[5] & 0xF);
|
||||||
|
uint32_t c_size = ((csd[6] & 0x3) << 10) | (csd[7] << 2) | ((csd[8] & 0xC) >> 6);
|
||||||
|
uint32_t mult = 1 << (((csd[9] & 0x3) << 1 | (csd[10] & 0x80) >> 7) + 2);
|
||||||
|
self->sectors = block_length / 512 * mult * (c_size + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CMD16: set block length to 512 bytes
|
||||||
|
{
|
||||||
|
int response = cmd(self, 16, 512, NULL, 0, true, true);
|
||||||
|
if (response != 0) {
|
||||||
|
return translate("can't set 512 block size");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void common_hal_sdcardio_sdcard_construct(sdcardio_sdcard_obj_t *self, busio_spi_obj_t *bus, mcu_pin_obj_t *cs, int baudrate) {
|
||||||
|
self->bus = bus;
|
||||||
|
common_hal_digitalio_digitalinout_construct(&self->cs, cs);
|
||||||
|
common_hal_digitalio_digitalinout_switch_to_output(&self->cs, true, DRIVE_MODE_PUSH_PULL);
|
||||||
|
|
||||||
|
self->cdv = 512;
|
||||||
|
self->sectors = 0;
|
||||||
|
self->baudrate = 250000;
|
||||||
|
|
||||||
|
lock_bus_or_throw(self);
|
||||||
|
const compressed_string_t *result = init_card(self);
|
||||||
|
extraclock_and_unlock_bus(self);
|
||||||
|
|
||||||
|
if (result != NULL) {
|
||||||
|
common_hal_digitalio_digitalinout_deinit(&self->cs);
|
||||||
|
mp_raise_OSError_msg(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
self->baudrate = baudrate;
|
||||||
|
}
|
||||||
|
|
||||||
|
void common_hal_sdcardio_sdcard_deinit(sdcardio_sdcard_obj_t *self) {
|
||||||
|
if (!self->bus) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self->bus = 0;
|
||||||
|
common_hal_digitalio_digitalinout_deinit(&self->cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void common_hal_sdcardio_check_for_deinit(sdcardio_sdcard_obj_t *self) {
|
||||||
|
if (!self->bus) {
|
||||||
|
raise_deinited_error();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int common_hal_sdcardio_sdcard_get_blockcount(sdcardio_sdcard_obj_t *self) {
|
||||||
|
common_hal_sdcardio_check_for_deinit(self);
|
||||||
|
return self->sectors;
|
||||||
|
}
|
||||||
|
|
||||||
|
int readinto(sdcardio_sdcard_obj_t *self, void *buf, size_t size) {
|
||||||
|
uint8_t aux[2] = {0, 0};
|
||||||
|
while (aux[0] != 0xfe) {
|
||||||
|
common_hal_busio_spi_read(self->bus, aux, 1, 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
common_hal_busio_spi_read(self->bus, buf, size, 0xff);
|
||||||
|
|
||||||
|
// Read checksum and throw it away
|
||||||
|
common_hal_busio_spi_read(self->bus, aux, sizeof(aux), 0xff);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int readblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf) {
|
||||||
|
uint32_t nblocks = buf->len / 512;
|
||||||
|
if (nblocks == 1) {
|
||||||
|
// Use CMD17 to read a single block
|
||||||
|
return block_cmd(self, 17, start_block, buf->buf, buf->len, true, true);
|
||||||
|
} else {
|
||||||
|
// Use CMD18 to read multiple blocks
|
||||||
|
int r = block_cmd(self, 18, start_block, NULL, 0, true, true);
|
||||||
|
if (r < 0) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *ptr = buf->buf;
|
||||||
|
while (nblocks--) {
|
||||||
|
r = readinto(self, ptr, 512);
|
||||||
|
if (r < 0) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
ptr += 512;
|
||||||
|
}
|
||||||
|
|
||||||
|
// End the multi-block read
|
||||||
|
r = cmd(self, 12, 0, NULL, 0, true, false);
|
||||||
|
|
||||||
|
// Return first status 0 or last before card ready (0xff)
|
||||||
|
while (r != 0) {
|
||||||
|
uint8_t single_byte;
|
||||||
|
common_hal_busio_spi_read(self->bus, &single_byte, 1, 0xff);
|
||||||
|
if (single_byte & 0x80) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
r = single_byte;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int common_hal_sdcardio_sdcard_readblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf) {
|
||||||
|
common_hal_sdcardio_check_for_deinit(self);
|
||||||
|
if (buf->len % 512 != 0) {
|
||||||
|
mp_raise_ValueError(translate("Buffer length must be a multiple of 512"));
|
||||||
|
}
|
||||||
|
|
||||||
|
lock_and_configure_bus(self);
|
||||||
|
int r = readblocks(self, start_block, buf);
|
||||||
|
extraclock_and_unlock_bus(self);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _write(sdcardio_sdcard_obj_t *self, uint8_t token, void *buf, size_t size) {
|
||||||
|
wait_for_ready(self);
|
||||||
|
|
||||||
|
uint8_t cmd[2];
|
||||||
|
cmd[0] = token;
|
||||||
|
|
||||||
|
common_hal_busio_spi_write(self->bus, cmd, 1);
|
||||||
|
common_hal_busio_spi_write(self->bus, buf, size);
|
||||||
|
|
||||||
|
cmd[0] = cmd[1] = 0xff;
|
||||||
|
common_hal_busio_spi_write(self->bus, cmd, 2);
|
||||||
|
|
||||||
|
// Check the response
|
||||||
|
// This differs from the traditional adafruit_sdcard handling,
|
||||||
|
// but adafruit_sdcard also ignored the return value of SDCard._write(!)
|
||||||
|
// so nobody noticed
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Response is as follows:
|
||||||
|
// x x x 0 STAT 1
|
||||||
|
// 7 6 5 4 3..1 0
|
||||||
|
// with STATUS 010 indicating "data accepted", and other status bit
|
||||||
|
// combinations indicating failure.
|
||||||
|
// In practice, I was seeing cmd[0] as 0xe5, indicating success
|
||||||
|
for (int i=0; i<CMD_TIMEOUT; i++) {
|
||||||
|
common_hal_busio_spi_read(self->bus, cmd, 1, 0xff);
|
||||||
|
DEBUG_PRINT("i=%02d cmd[0] = 0x%02x\n", i, cmd[0]);
|
||||||
|
if ((cmd[0] & 0b00010001) == 0b00000001) {
|
||||||
|
if ((cmd[0] & 0x1f) != 0x5) {
|
||||||
|
return -EIO;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the write to finish
|
||||||
|
do {
|
||||||
|
common_hal_busio_spi_read(self->bus, cmd, 1, 0xff);
|
||||||
|
} while (cmd[0] == 0);
|
||||||
|
|
||||||
|
// Success
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int writeblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf) {
|
||||||
|
common_hal_sdcardio_check_for_deinit(self);
|
||||||
|
uint32_t nblocks = buf->len / 512;
|
||||||
|
if (nblocks == 1) {
|
||||||
|
// Use CMD24 to write a single block
|
||||||
|
int r = block_cmd(self, 24, start_block, NULL, 0, true, true);
|
||||||
|
if (r < 0) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
r = _write(self, TOKEN_DATA, buf->buf, buf->len);
|
||||||
|
if (r < 0) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Use CMD25 to write multiple block
|
||||||
|
int r = block_cmd(self, 25, start_block, NULL, 0, true, true);
|
||||||
|
if (r < 0) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *ptr = buf->buf;
|
||||||
|
while (nblocks--) {
|
||||||
|
r = _write(self, TOKEN_CMD25, ptr, 512);
|
||||||
|
if (r < 0) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
ptr += 512;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_nodata(self, TOKEN_STOP_TRAN, 0);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int common_hal_sdcardio_sdcard_writeblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf) {
|
||||||
|
common_hal_sdcardio_check_for_deinit(self);
|
||||||
|
if (buf->len % 512 != 0) {
|
||||||
|
mp_raise_ValueError(translate("Buffer length must be a multiple of 512"));
|
||||||
|
}
|
||||||
|
lock_and_configure_bus(self);
|
||||||
|
int r = writeblocks(self, start_block, buf);
|
||||||
|
extraclock_and_unlock_bus(self);
|
||||||
|
return r;
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* This file is part of the Micro Python project, http://micropython.org/
|
||||||
|
*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020 Jeff Epler for Adafruit Industries
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "py/obj.h"
|
||||||
|
#include "py/objproperty.h"
|
||||||
|
#include "py/runtime.h"
|
||||||
|
#include "py/objarray.h"
|
||||||
|
|
||||||
|
#include "common-hal/busio/SPI.h"
|
||||||
|
#include "common-hal/digitalio/DigitalInOut.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
mp_obj_base_t base;
|
||||||
|
busio_spi_obj_t *bus;
|
||||||
|
digitalio_digitalinout_obj_t cs;
|
||||||
|
int cdv;
|
||||||
|
int baudrate;
|
||||||
|
uint32_t sectors;
|
||||||
|
} sdcardio_sdcard_obj_t;
|
||||||
|
|
||||||
|
void common_hal_sdcardio_sdcard_construct(sdcardio_sdcard_obj_t *self, busio_spi_obj_t *spi, mcu_pin_obj_t *cs, int baudrate);
|
||||||
|
void common_hal_sdcardio_sdcard_deinit(sdcardio_sdcard_obj_t *self);
|
||||||
|
void common_hal_sdcardio_sdcard_check_for_deinit(sdcardio_sdcard_obj_t *self);
|
||||||
|
int common_hal_sdcardio_sdcard_get_blockcount(sdcardio_sdcard_obj_t *self);
|
||||||
|
int common_hal_sdcardio_sdcard_readblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf);
|
||||||
|
int common_hal_sdcardio_sdcard_writeblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf);
|
Loading…
Reference in New Issue