Skip to content

Commit 57fde2e

Browse files
committed
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.
1 parent 12df4ce commit 57fde2e

File tree

17 files changed

+843
-3
lines changed

17 files changed

+843
-3
lines changed

locale/circuitpython.pot

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ msgid ""
88
msgstr ""
99
"Project-Id-Version: PACKAGE VERSION\n"
1010
"Report-Msgid-Bugs-To: \n"
11-
"POT-Creation-Date: 2020-06-25 11:44-0500\n"
11+
"POT-Creation-Date: 2020-06-26 11:50-0500\n"
1212
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
1313
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1414
"Language-Team: LANGUAGE <[email protected]>\n"
@@ -417,6 +417,10 @@ msgstr ""
417417
msgid "Buffer length %d too big. It must be less than %d"
418418
msgstr ""
419419

420+
#: shared-module/sdcardio/SDCard.c
421+
msgid "Buffer length must be a multiple of 512"
422+
msgstr ""
423+
420424
#: shared-bindings/bitbangio/I2C.c shared-bindings/busio/I2C.c
421425
msgid "Buffer must be at least length 1"
422426
msgstr ""
@@ -698,7 +702,8 @@ msgstr ""
698702
msgid "Error in regex"
699703
msgstr ""
700704

701-
#: shared-bindings/aesio/aes.c shared-bindings/microcontroller/Pin.c
705+
#: shared-bindings/aesio/aes.c shared-bindings/busio/SPI.c
706+
#: shared-bindings/microcontroller/Pin.c
702707
#: shared-bindings/neopixel_write/__init__.c shared-bindings/pulseio/PulseOut.c
703708
#: shared-bindings/terminalio/Terminal.c
704709
msgid "Expected a %q"
@@ -1358,6 +1363,10 @@ msgstr ""
13581363
msgid "Running in safe mode! Not running saved code.\n"
13591364
msgstr ""
13601365

1366+
#: shared-module/sdcardio/SDCard.c
1367+
msgid "SD card CSD format not supported"
1368+
msgstr ""
1369+
13611370
#: ports/atmel-samd/common-hal/busio/I2C.c
13621371
#: ports/mimxrt10xx/common-hal/busio/I2C.c ports/nrf/common-hal/busio/I2C.c
13631372
msgid "SDA or SCL needs a pull up"
@@ -1979,6 +1988,10 @@ msgstr ""
19791988
msgid "can't send non-None value to a just-started generator"
19801989
msgstr ""
19811990

1991+
#: shared-module/sdcardio/SDCard.c
1992+
msgid "can't set 512 block size"
1993+
msgstr ""
1994+
19821995
#: py/objnamedtuple.c
19831996
msgid "can't set attribute"
19841997
msgstr ""
@@ -2105,6 +2118,10 @@ msgstr ""
21052118
msgid "could not invert Vandermonde matrix"
21062119
msgstr ""
21072120

2121+
#: shared-module/sdcardio/SDCard.c
2122+
msgid "couldn't determine SD card version"
2123+
msgstr ""
2124+
21082125
#: extmod/ulab/code/approx.c
21092126
msgid "data must be iterable"
21102127
msgstr ""
@@ -2662,6 +2679,10 @@ msgstr ""
26622679
msgid "negative shift count"
26632680
msgstr ""
26642681

2682+
#: shared-module/sdcardio/SDCard.c
2683+
msgid "no SD card"
2684+
msgstr ""
2685+
26652686
#: py/vm.c
26662687
msgid "no active exception to reraise"
26672688
msgstr ""
@@ -2683,6 +2704,10 @@ msgstr ""
26832704
msgid "no reset pin available"
26842705
msgstr ""
26852706

2707+
#: shared-module/sdcardio/SDCard.c
2708+
msgid "no response from SD card"
2709+
msgstr ""
2710+
26862711
#: py/runtime.c
26872712
msgid "no such attribute"
26882713
msgstr ""
@@ -3073,6 +3098,14 @@ msgstr ""
30733098
msgid "timeout must be >= 0.0"
30743099
msgstr ""
30753100

3101+
#: shared-module/sdcardio/SDCard.c
3102+
msgid "timeout waiting for v1 card"
3103+
msgstr ""
3104+
3105+
#: shared-module/sdcardio/SDCard.c
3106+
msgid "timeout waiting for v2 card"
3107+
msgstr ""
3108+
30763109
#: shared-bindings/time/__init__.c
30773110
msgid "timestamp out of range for platform time_t"
30783111
msgstr ""

ports/atmel-samd/mpconfigport.mk

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ ifndef CIRCUITPY_TOUCHIO_USE_NATIVE
3737
CIRCUITPY_TOUCHIO_USE_NATIVE = 1
3838
endif
3939

40+
CIRCUITPY_SDCARDIO ?= 0
41+
4042
# SAMD21 needs separate endpoint pairs for MSC BULK IN and BULK OUT, otherwise it's erratic.
4143
USB_MSC_EP_NUM_OUT = 1
4244

ports/litex/mpconfigport.mk

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,15 @@ CIRCUITPY_AUDIOIO = 0
1818
CIRCUITPY_BITBANGIO = 0
1919
CIRCUITPY_BOARD = 0
2020
CIRCUITPY_BUSIO = 0
21+
CIRCUITPY_COUNTIO = 0
2122
CIRCUITPY_DISPLAYIO = 0
2223
CIRCUITPY_FREQUENCYIO = 0
2324
CIRCUITPY_I2CPERIPHERAL = 0
2425
CIRCUITPY_NVM = 0
2526
CIRCUITPY_PULSEIO = 0
2627
CIRCUITPY_ROTARYIO = 0
2728
CIRCUITPY_RTC = 0
28-
CIRCUITPY_COUNTIO = 0
29+
CIRCUITPY_SDCARDIO = 0
2930
# Enable USB support
3031
CIRCUITPY_USB_HID = 1
3132
CIRCUITPY_USB_MIDI = 1

ports/nrf/boards/pca10100/mpconfigboard.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ CIRCUITPY_PIXELBUF = 0
2121
CIRCUITPY_RGBMATRIX = 0
2222
CIRCUITPY_ROTARYIO = 0
2323
CIRCUITPY_RTC = 1
24+
CIRCUITPY_SDCARDIO = 0
2425
CIRCUITPY_TOUCHIO = 0
2526
CIRCUITPY_ULAB = 0
2627

py/circuitpy_defns.mk

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,9 @@ endif
213213
ifeq ($(CIRCUITPY_SAMD),1)
214214
SRC_PATTERNS += samd/%
215215
endif
216+
ifeq ($(CIRCUITPY_SDCARDIO),1)
217+
SRC_PATTERNS += sdcardio/%
218+
endif
216219
ifeq ($(CIRCUITPY_STAGE),1)
217220
SRC_PATTERNS += _stage/%
218221
endif
@@ -384,6 +387,8 @@ SRC_SHARED_MODULE_ALL = \
384387
fontio/__init__.c \
385388
framebufferio/FramebufferDisplay.c \
386389
framebufferio/__init__.c \
390+
sdcardio/SDCard.c \
391+
sdcardio/__init__.c \
387392
gamepad/GamePad.c \
388393
gamepad/__init__.c \
389394
gamepadshift/GamePadShift.c \

py/circuitpy_mpconfig.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,13 @@ extern const struct _mp_obj_module_t samd_module;
537537
#define SAMD_MODULE
538538
#endif
539539

540+
#if CIRCUITPY_SDCARDIO
541+
extern const struct _mp_obj_module_t sdcardio_module;
542+
#define SDCARDIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_sdcardio), (mp_obj_t)&sdcardio_module },
543+
#else
544+
#define SDCARDIO_MODULE
545+
#endif
546+
540547
#if CIRCUITPY_STAGE
541548
extern const struct _mp_obj_module_t stage_module;
542549
#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;
709716
ROTARYIO_MODULE \
710717
RTC_MODULE \
711718
SAMD_MODULE \
719+
SDCARDIO_MODULE \
712720
STAGE_MODULE \
713721
STORAGE_MODULE \
714722
STRUCT_MODULE \

py/circuitpy_mpconfig.mk

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,9 @@ CFLAGS += -DCIRCUITPY_RTC=$(CIRCUITPY_RTC)
166166
CIRCUITPY_SAMD ?= 0
167167
CFLAGS += -DCIRCUITPY_SAMD=$(CIRCUITPY_SAMD)
168168

169+
CIRCUITPY_SDCARDIO ?= $(CIRCUITPY_FULL_BUILD)
170+
CFLAGS += -DCIRCUITPY_SDCARDIO=$(CIRCUITPY_SDCARDIO)
171+
169172
# Currently always off.
170173
CIRCUITPY_STAGE ?= 0
171174
CFLAGS += -DCIRCUITPY_STAGE=$(CIRCUITPY_STAGE)

shared-bindings/busio/SPI.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,3 +418,10 @@ const mp_obj_type_t busio_spi_type = {
418418
.make_new = busio_spi_make_new,
419419
.locals_dict = (mp_obj_dict_t*)&busio_spi_locals_dict,
420420
};
421+
422+
busio_spi_obj_t *validate_obj_is_spi_bus(mp_obj_t obj) {
423+
if (!MP_OBJ_IS_TYPE(obj, &busio_spi_type)) {
424+
mp_raise_TypeError_varg(translate("Expected a %q"), busio_spi_type.name);
425+
}
426+
return MP_OBJ_TO_PTR(obj);
427+
}

shared-bindings/busio/SPI.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,6 @@ uint8_t common_hal_busio_spi_get_polarity(busio_spi_obj_t* self);
7070
// This is used by the supervisor to claim SPI devices indefinitely.
7171
extern void common_hal_busio_spi_never_reset(busio_spi_obj_t *self);
7272

73+
extern busio_spi_obj_t *validate_obj_is_spi_bus(mp_obj_t obj_in);
74+
7375
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_BUSIO_SPI_H

shared-bindings/sdcardio/SDCard.c

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
/*
2+
* This file is part of the Micro Python project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2020 Jeff Epler for Adafruit Industries
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*/
26+
27+
#include "py/obj.h"
28+
#include "py/objproperty.h"
29+
#include "py/runtime.h"
30+
#include "py/objarray.h"
31+
32+
#include "shared-bindings/sdcardio/SDCard.h"
33+
#include "shared-module/sdcardio/SDCard.h"
34+
#include "common-hal/busio/SPI.h"
35+
#include "shared-bindings/busio/SPI.h"
36+
#include "shared-bindings/microcontroller/Pin.h"
37+
#include "supervisor/flash.h"
38+
39+
//| class SDCard:
40+
//| """SD Card Block Interface
41+
//|
42+
//| Controls an SD card over SPI. This built-in module has higher read
43+
//| performance than the library adafruit_sdcard, but it is only compatible with
44+
//| `busio.SPI`, not `bitbangio.SPI`. Usually an SDCard object is used
45+
//| with ``storage.VfsFat`` to allow file I/O to an SD card."""
46+
//|
47+
//| def __init__(bus:busio.SPI, cs=digitalio.DigitalInOut, baudrate=8000000):
48+
//| """Construct an SPI SD Card object with the given properties
49+
//|
50+
//| :param busio.SPI spi: The SPI bus
51+
//| :param microcontroller.Pin cs: The chip select connected to the card
52+
//| :param int baudrate: The SPI data rate to use after card setup
53+
//| :param busio.SDIO sdio: The SDIO bus. Mutually exclusive with spi and cs.
54+
//|
55+
//| Note that during detection and configuration, a hard-coded low baudrate is used.
56+
//| Data transfers use the specified baurate (rounded down to one that is supported by
57+
//| the microcontroller)
58+
//|
59+
//| Example usage:
60+
//|
61+
//| .. code-block:: python
62+
//|
63+
//| import os
64+
//|
65+
//| import board
66+
//| import sdcardio
67+
//| import storage
68+
//|
69+
//| sd = sdcardio.SDCard(board.SPI(), board.SD_CS)
70+
//| vfs = storage.VfsFat(sd)
71+
//| storage.mount(vfs, '/sd')
72+
//| os.listdir('/sd')"""
73+
74+
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) {
75+
enum { ARG_spi, ARG_cs, ARG_baudrate, ARG_sdio, NUM_ARGS };
76+
static const mp_arg_t allowed_args[] = {
77+
{ MP_QSTR_spi, MP_ARG_OBJ, {.u_obj = mp_const_none } },
78+
{ MP_QSTR_cs, MP_ARG_OBJ, {.u_obj = mp_const_none } },
79+
{ MP_QSTR_baudrate, MP_ARG_INT, {.u_int = 8000000} },
80+
{ MP_QSTR_sdio, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_int = 8000000} },
81+
};
82+
MP_STATIC_ASSERT( MP_ARRAY_SIZE(allowed_args) == NUM_ARGS );
83+
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
84+
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
85+
86+
busio_spi_obj_t *spi = validate_obj_is_spi_bus(args[ARG_spi].u_obj);
87+
mcu_pin_obj_t *cs = validate_obj_is_free_pin(args[ARG_cs].u_obj);
88+
89+
sdcardio_sdcard_obj_t *self = m_new_obj(sdcardio_sdcard_obj_t);
90+
self->base.type = &sdcardio_SDCard_type;
91+
92+
common_hal_sdcardio_sdcard_construct(self, spi, cs, args[ARG_baudrate].u_int);
93+
94+
return self;
95+
}
96+
97+
98+
//| def count() -> int:
99+
//| """Returns the total number of sectors
100+
//|
101+
//| Due to technical limitations, this is a function and not a property.
102+
//|
103+
//| :return: The number of 512-byte blocks, as a number"""
104+
//|
105+
mp_obj_t sdcardio_sdcard_count(mp_obj_t self_in) {
106+
sdcardio_sdcard_obj_t *self = (sdcardio_sdcard_obj_t*)self_in;
107+
return mp_obj_new_int_from_ull(common_hal_sdcardio_sdcard_get_blockcount(self));
108+
}
109+
MP_DEFINE_CONST_FUN_OBJ_1(sdcardio_sdcard_count_obj, sdcardio_sdcard_count);
110+
111+
//| def deinit() -> None:
112+
//| """Disable permanently.
113+
//|
114+
//| :return: None"""
115+
//|
116+
mp_obj_t sdcardio_sdcard_deinit(mp_obj_t self_in) {
117+
sdcardio_sdcard_obj_t *self = (sdcardio_sdcard_obj_t*)self_in;
118+
common_hal_sdcardio_sdcard_deinit(self);
119+
return mp_const_none;
120+
}
121+
MP_DEFINE_CONST_FUN_OBJ_1(sdcardio_sdcard_deinit_obj, sdcardio_sdcard_deinit);
122+
123+
124+
//| def readblocks(start_block: int, buf: bytearray) -> None:
125+
//|
126+
//| """Read one or more blocks from the card
127+
//|
128+
//| :param int start_block: The block to start reading from
129+
//| :param bytearray buf: The buffer to write into. Length must be multiple of 512.
130+
//|
131+
//| :return: None"""
132+
//|
133+
134+
mp_obj_t sdcardio_sdcard_readblocks(mp_obj_t self_in, mp_obj_t start_block_in, mp_obj_t buf_in) {
135+
uint32_t start_block = mp_obj_get_int(start_block_in);
136+
mp_buffer_info_t bufinfo;
137+
mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_WRITE);
138+
sdcardio_sdcard_obj_t *self = (sdcardio_sdcard_obj_t*)self_in;
139+
int result = common_hal_sdcardio_sdcard_readblocks(self, start_block, &bufinfo);
140+
if (result < 0) {
141+
mp_raise_OSError(-result);
142+
}
143+
return mp_const_none;
144+
}
145+
146+
MP_DEFINE_CONST_FUN_OBJ_3(sdcardio_sdcard_readblocks_obj, sdcardio_sdcard_readblocks);
147+
148+
//| def writeblocks(start_block: int, buf: bytearray) -> None:
149+
//|
150+
//| """Write one or more blocks to the card
151+
//|
152+
//| :param int start_block: The block to start writing from
153+
//| :param bytearray buf: The buffer to read from. Length must be multiple of 512.
154+
//|
155+
//| :return: None"""
156+
//|
157+
158+
mp_obj_t sdcardio_sdcard_writeblocks(mp_obj_t self_in, mp_obj_t start_block_in, mp_obj_t buf_in) {
159+
uint32_t start_block = mp_obj_get_int(start_block_in);
160+
mp_buffer_info_t bufinfo;
161+
mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_READ);
162+
sdcardio_sdcard_obj_t *self = (sdcardio_sdcard_obj_t*)self_in;
163+
int result = common_hal_sdcardio_sdcard_writeblocks(self, start_block, &bufinfo);
164+
if (result < 0) {
165+
mp_raise_OSError(-result);
166+
}
167+
return mp_const_none;
168+
}
169+
MP_DEFINE_CONST_FUN_OBJ_3(sdcardio_sdcard_writeblocks_obj, sdcardio_sdcard_writeblocks);
170+
171+
STATIC const mp_rom_map_elem_t sdcardio_sdcard_locals_dict_table[] = {
172+
{ MP_ROM_QSTR(MP_QSTR_count), MP_ROM_PTR(&sdcardio_sdcard_count_obj) },
173+
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&sdcardio_sdcard_deinit_obj) },
174+
{ MP_ROM_QSTR(MP_QSTR_readblocks), MP_ROM_PTR(&sdcardio_sdcard_readblocks_obj) },
175+
{ MP_ROM_QSTR(MP_QSTR_writeblocks), MP_ROM_PTR(&sdcardio_sdcard_writeblocks_obj) },
176+
};
177+
STATIC MP_DEFINE_CONST_DICT(sdcardio_sdcard_locals_dict, sdcardio_sdcard_locals_dict_table);
178+
179+
const mp_obj_type_t sdcardio_SDCard_type = {
180+
{ &mp_type_type },
181+
.name = MP_QSTR_SDCard,
182+
.make_new = sdcardio_sdcard_make_new,
183+
.locals_dict = (mp_obj_dict_t*)&sdcardio_sdcard_locals_dict,
184+
};

0 commit comments

Comments
 (0)