|
| 1 | +# SDCardManager |
| 2 | + |
| 3 | +SDCardManager provides a unified API for SD card initialization, mounting, and management in both SPI and SDIO modes. It follows the singleton + class-method-delegation pattern used by all MPOS frameworks. |
| 4 | + |
| 5 | +## Overview |
| 6 | + |
| 7 | +SDCardManager centralizes all SD card operations in a single class: |
| 8 | + |
| 9 | +- **Dual-Mode Support** - Auto-detects SPI or SDIO based on provided pins |
| 10 | +- **Unified API** - Single `from mpos import SDCardManager` for all SD card needs |
| 11 | +- **Safe Mounting** - Idempotent mount (treats already-mounted as success) |
| 12 | +- **Format Support** - Format uninitialized cards as FAT32 |
| 13 | +- **Singleton Pattern** - Class methods delegate to a single instance initialized at boot |
| 14 | + |
| 15 | +## Architecture |
| 16 | + |
| 17 | +SDCardManager is initialized once at boot by the board definition, then accessed via class methods: |
| 18 | + |
| 19 | +```python |
| 20 | +class SDCardManager: |
| 21 | + _instance = None |
| 22 | + |
| 23 | + @classmethod |
| 24 | + def init(cls, mode=None, spi_bus=None, cs_pin=None, cmd_pin=None, clk_pin=None, |
| 25 | + d0_pin=None, d1_pin=None, d2_pin=None, d3_pin=None, slot=1, width=None, freq=20000000): |
| 26 | + ... |
| 27 | + |
| 28 | + @classmethod |
| 29 | + def mount(cls, format=False): |
| 30 | + if not cls._instance: |
| 31 | + return False |
| 32 | + ... |
| 33 | +``` |
| 34 | + |
| 35 | +The board definition calls `SDCardManager.init(...)` at boot with the hardware-specific pin configuration. Apps then call `mount()`, `is_mounted()`, `get_mount_point()`, etc. without needing to know about the underlying hardware. |
| 36 | + |
| 37 | +## Usage |
| 38 | + |
| 39 | +### Basic Mount |
| 40 | + |
| 41 | +```python |
| 42 | +from mpos import SDCardManager |
| 43 | + |
| 44 | +SDCardManager.mount() |
| 45 | +mountpoint = SDCardManager.get_mount_point() |
| 46 | +if mountpoint: |
| 47 | + import os |
| 48 | + os.listdir(mountpoint) |
| 49 | +``` |
| 50 | + |
| 51 | +### Checking Mount Status |
| 52 | + |
| 53 | +```python |
| 54 | +from mpos import SDCardManager |
| 55 | + |
| 56 | +if SDCardManager.is_mounted(): |
| 57 | + mp = SDCardManager.get_mount_point() |
| 58 | + mode = SDCardManager.get_mode() |
| 59 | + print("SD card mounted at %s (%s mode)" % (mp, mode)) |
| 60 | +``` |
| 61 | + |
| 62 | +### Format and Mount |
| 63 | + |
| 64 | +```python |
| 65 | +from mpos import SDCardManager |
| 66 | + |
| 67 | +SDCardManager.mount(format=True) |
| 68 | +``` |
| 69 | + |
| 70 | +### Listing Contents |
| 71 | + |
| 72 | +```python |
| 73 | +from mpos import SDCardManager |
| 74 | + |
| 75 | +if SDCardManager.is_mounted(): |
| 76 | + contents = SDCardManager.get_raw().list(SDCardManager.get_mount_point()) |
| 77 | + for item in contents: |
| 78 | + print(item) |
| 79 | +``` |
| 80 | + |
| 81 | +## API Reference |
| 82 | + |
| 83 | +### Class Methods |
| 84 | + |
| 85 | +#### `init(mode=None, spi_bus=None, cs_pin=None, cmd_pin=None, clk_pin=None, d0_pin=None, d1_pin=None, d2_pin=None, d3_pin=None, slot=1, width=None, freq=20000000)` |
| 86 | + |
| 87 | +Initialize the SD card hardware. Called once at boot by the board definition. Mode is auto-detected from which pins are provided (SDIO pins → SDIO mode, SPI pins → SPI mode). Can be overridden with the `mode` parameter. |
| 88 | + |
| 89 | +**Parameters:** |
| 90 | +- `mode` (str, optional): `'spi'` or `'sdio'`. Auto-detected if not provided. |
| 91 | +- `spi_bus` (machine.SPI, optional): SPI bus instance for SPI mode. |
| 92 | +- `cs_pin` (int, optional): Chip-select pin number for SPI mode. |
| 93 | +- `cmd_pin` (int, optional): CMD pin for SDIO mode. |
| 94 | +- `clk_pin` (int, optional): CLK pin for SDIO mode. |
| 95 | +- `d0_pin` (int, optional): Data 0 pin for SDIO mode (required). |
| 96 | +- `d1_pin` (int, optional): Data 1 pin for 4-bit SDIO mode. |
| 97 | +- `d2_pin` (int, optional): Data 2 pin for 4-bit SDIO mode. |
| 98 | +- `d3_pin` (int, optional): Data 3 pin for 4-bit SDIO mode. |
| 99 | +- `slot` (int): SDIO slot number (0 or 1). Default: `1`. |
| 100 | +- `width` (int, optional): SDIO bus width (`1` or `4`). Auto-detected from data pins if not provided. |
| 101 | +- `freq` (int): SDIO clock frequency in Hz. Default: `20000000`. |
| 102 | + |
| 103 | +**Example:** |
| 104 | +```python |
| 105 | +import machine |
| 106 | +from mpos import SDCardManager |
| 107 | + |
| 108 | +# SPI mode |
| 109 | +spi = machine.SPI(2, baudrate=20000000, sck=machine.Pin(40), mosi=machine.Pin(41), miso=machine.Pin(38)) |
| 110 | +SDCardManager.init(spi_bus=spi, cs_pin=21) |
| 111 | + |
| 112 | +# SDIO 1-bit mode |
| 113 | +SDCardManager.init(cmd_pin=39, clk_pin=40, d0_pin=38, width=1) |
| 114 | +``` |
| 115 | + |
| 116 | +--- |
| 117 | + |
| 118 | +#### `mount(format=False)` |
| 119 | + |
| 120 | +Mount the SD card at `/sdcard`. Returns `True` on success, `False` on failure. Treats already-mounted as success. If `format=True` and the mount fails, the card is formatted as FAT32 and re-mounted. |
| 121 | + |
| 122 | +**Parameters:** |
| 123 | +- `format` (bool): Whether to format the card if mount fails. Default: `False`. |
| 124 | + |
| 125 | +**Returns:** bool - `True` if mounted, `False` otherwise. |
| 126 | + |
| 127 | +**Example:** |
| 128 | +```python |
| 129 | +SDCardManager.mount() |
| 130 | +SDCardManager.mount(format=True) |
| 131 | +``` |
| 132 | + |
| 133 | +--- |
| 134 | + |
| 135 | +#### `is_mounted()` |
| 136 | + |
| 137 | +Check whether the SD card is currently mounted at `/sdcard`. Verifies by listing the root directory and testing with a temporary directory. |
| 138 | + |
| 139 | +**Returns:** bool - `True` if mounted, `False` otherwise. |
| 140 | + |
| 141 | +**Example:** |
| 142 | +```python |
| 143 | +if SDCardManager.is_mounted(): |
| 144 | + print("SD card is ready") |
| 145 | +``` |
| 146 | + |
| 147 | +--- |
| 148 | + |
| 149 | +#### `get_mount_point()` |
| 150 | + |
| 151 | +Get the mount point path if the card is mounted, or `None` if not. |
| 152 | + |
| 153 | +**Returns:** str or None - `"/sdcard"` if mounted, `None` otherwise. |
| 154 | + |
| 155 | +**Example:** |
| 156 | +```python |
| 157 | +mp = SDCardManager.get_mount_point() |
| 158 | +if mp: |
| 159 | + print("Mounted at %s" % mp) |
| 160 | +``` |
| 161 | + |
| 162 | +--- |
| 163 | + |
| 164 | +#### `get_mode()` |
| 165 | + |
| 166 | +Get the current mode (`'spi'` or `'sdio'`), or `None` if not initialized. |
| 167 | + |
| 168 | +**Returns:** str or None |
| 169 | + |
| 170 | +**Example:** |
| 171 | +```python |
| 172 | +mode = SDCardManager.get_mode() |
| 173 | +print("SD card mode: %s" % mode) |
| 174 | +``` |
| 175 | + |
| 176 | +--- |
| 177 | + |
| 178 | +#### `format()` |
| 179 | + |
| 180 | +Format the SD card as FAT32. Unmounts first if already mounted. |
| 181 | + |
| 182 | +**Returns:** bool - `True` on success, `False` on failure. |
| 183 | + |
| 184 | +**Example:** |
| 185 | +```python |
| 186 | +SDCardManager.format() |
| 187 | +``` |
| 188 | + |
| 189 | +--- |
| 190 | + |
| 191 | +#### `get_raw()` |
| 192 | + |
| 193 | +Get the underlying `SDCardManager` instance. Useful for calling instance methods like `list()`. |
| 194 | + |
| 195 | +**Returns:** SDCardManager instance or None |
| 196 | + |
| 197 | +**Example:** |
| 198 | +```python |
| 199 | +instance = SDCardManager.get_raw() |
| 200 | +if instance: |
| 201 | + files = instance.list(SDCardManager.get_mount_point()) |
| 202 | +``` |
| 203 | + |
| 204 | +## Practical Examples |
| 205 | + |
| 206 | +### App Using SD Card for File Storage |
| 207 | + |
| 208 | +Real-world example from the Retro-Go launcher app: |
| 209 | + |
| 210 | +```python |
| 211 | +from mpos import SDCardManager |
| 212 | + |
| 213 | +class RetroLauncher(Activity): |
| 214 | + def onResume(self, screen): |
| 215 | + self.bootfile_prefix = "" |
| 216 | + SDCardManager.mount() |
| 217 | + prefix = SDCardManager.get_mount_point() |
| 218 | + if prefix: |
| 219 | + self.bootfile_prefix = prefix + "/" |
| 220 | +``` |
| 221 | + |
| 222 | +([View on GitHub](https://github.com/MicroPythonOS/MicroPythonOS/blob/main/internal_filesystem/apps/com.micropythonos.doom_launcher/retrogo_launcher.py#L122)) |
| 223 | + |
| 224 | +### SPI Board Configuration |
| 225 | + |
| 226 | +```python |
| 227 | +import machine |
| 228 | +from mpos import SDCardManager |
| 229 | + |
| 230 | +spi = machine.SPI(2, baudrate=20000000, sck=machine.Pin(40), mosi=machine.Pin(41), miso=machine.Pin(38)) |
| 231 | +SDCardManager.init(spi_bus=spi, cs_pin=21) |
| 232 | +``` |
| 233 | + |
| 234 | +### SDIO 4-Bit Board Configuration |
| 235 | + |
| 236 | +```python |
| 237 | +from mpos import SDCardManager |
| 238 | + |
| 239 | +SDCardManager.init( |
| 240 | + cmd_pin=39, clk_pin=40, |
| 241 | + d0_pin=38, d1_pin=37, d2_pin=36, d3_pin=35, |
| 242 | + width=4, freq=20000000 |
| 243 | +) |
| 244 | +``` |
| 245 | + |
| 246 | +## Implementation Details |
| 247 | + |
| 248 | +### File Structure |
| 249 | + |
| 250 | +``` |
| 251 | +mpos/ |
| 252 | +├── sdcard.py # SDCardManager implementation |
| 253 | +└── __init__.py # Exports SDCardManager |
| 254 | +``` |
| 255 | + |
| 256 | +### Initialization Flow |
| 257 | + |
| 258 | +1. **Boot** - Board definition calls `SDCardManager.init(...)` with hardware-specific pins |
| 259 | +2. **On Demand** - Apps call `SDCardManager.mount()` when they need SD access |
| 260 | +3. **Safe** - Mount is idempotent; already-mounted returns `True` |
| 261 | +4. **Fallback** - If `format=True`, failed mount triggers format-and-retry |
| 262 | + |
| 263 | +## Design Patterns |
| 264 | + |
| 265 | +### Singleton with Class Method Delegation |
| 266 | + |
| 267 | +```python |
| 268 | +class SDCardManager: |
| 269 | + _instance = None # Set once at boot |
| 270 | + |
| 271 | + @classmethod |
| 272 | + def mount(cls, format=False): |
| 273 | + if not cls._instance: |
| 274 | + return False |
| 275 | + return cls._instance._try_mount(_MOUNT_POINT) |
| 276 | +``` |
| 277 | + |
| 278 | +All public methods are class methods that forward to the single instance. Apps never create instances directly. |
| 279 | + |
| 280 | +## Related Frameworks |
| 281 | + |
| 282 | +- **[FileExplorerActivity](file-explorer-activity.md)** - File browser that uses SDCardManager for SD card access |
| 283 | +- **[BuildInfo](build-info.md)** - Build and board configuration metadata |
| 284 | + |
| 285 | +## See Also |
| 286 | + |
| 287 | +- [Architecture Overview](../architecture/overview.md) |
| 288 | +- [Frameworks](../architecture/frameworks.md) |
| 289 | +- [Creating Apps](../apps/creating-apps.md) |
0 commit comments