Handle partitioned and unpartitioned SD cards
All checks were successful
Build RPi Pico firmware image / Build-Firmware (push) Successful in 3m28s
Check code formatting / Check-C-Format (push) Successful in 7s
Check code formatting / Check-Python-Flake8 (push) Successful in 9s
Check code formatting / Check-Bash-Shellcheck (push) Successful in 5s
Run unit tests on host / Run-Unit-Tests (push) Successful in 9s

Add utils.MBRPartition to implement basic partitioned device support.

Try to mount partition 1 of SDCard first, if that fails, try to mount
entire device as FAT file system.

Signed-off-by: Matthias Blankertz <matthias@blankertz.org>
This commit is contained in:
2025-08-05 19:20:38 +02:00
parent 34f9a44cdb
commit e9bd4f72b6
4 changed files with 92 additions and 29 deletions

View File

@@ -5,7 +5,6 @@ import aiorepl
import asyncio
import machine
import micropython
import os
import time
from machine import Pin
from math import pi, sin, pow
@@ -17,8 +16,7 @@ from mfrc522 import MFRC522
from mp3player import MP3Player
from nfc import Nfc
from rp2_neopixel import NeoPixel
from rp2_sd import SDCard
from utils import Buttons, TimerManager
from utils import Buttons, SDContext, TimerManager
micropython.alloc_emergency_exception_buf(100)
@@ -54,30 +52,6 @@ machine.mem32[0x4001c004 + 8*4] = 0x67
machine.mem32[0x40030000 + 0x00] = 0x10
class SDContext:
def __init__(self, mosi, miso, sck, ss, baudrate):
self.mosi = mosi
self.miso = miso
self.sck = sck
self.ss = ss
self.baudrate = baudrate
def __enter__(self):
self.sdcard = SDCard(self.mosi, self.miso, self.sck, self.ss, self.baudrate)
try:
os.mount(self.sdcard, '/sd')
except Exception:
self.sdcard.deinit()
raise
return self
def __exit__(self, exc_type, exc_value, traceback):
try:
os.umount('/sd')
finally:
self.sdcard.deinit()
def run():
asyncio.new_event_loop()
# Setup LEDs
@@ -105,4 +79,6 @@ def run():
if __name__ == '__main__':
run()
if machine.Pin(17, machine.Pin.IN, machine.Pin.PULL_UP).value() != 0:
time.sleep(5)
run()

View File

@@ -2,6 +2,8 @@
# Copyright (c) 2025 Matthias Blankertz <matthias@blankertz.org>
from utils.buttons import Buttons
from utils.mbrpartition import MBRPartition
from utils.sdcontext import SDContext
from utils.timer import TimerManager
__all__ = ["Buttons", "TimerManager"]
__all__ = ["Buttons", "MBRPartition", "SDContext", "TimerManager"]

View File

@@ -0,0 +1,46 @@
# SPDX-License-Identifier: MIT
# Copyright (c) 2025 Matthias Blankertz <matthias@blankertz.org>
from array import array
import struct
class MBRPartition:
def __init__(self, bdev, partno):
assert partno >= 0 and partno < 4
self.bdev = bdev
bdev_len = bdev.ioctl(4, None)
bdev_bs = bdev.ioctl(5, None)
assert bdev_bs == 512
mbr = array('B', 512*b'0')
bdev.readblocks(0, mbr)
if mbr[510] != 0x55 or mbr[511] != 0xaa:
raise ValueError("Not a valid MBR")
partofs = 0x1be + partno*16
(boot_ind, _, _, _,
parttype, _, _, _,
lba_start, lba_len) = struct.unpack_from('<BBBBBBBBLL', mbr, partofs)
print(f'Partition {partno} bi {boot_ind} type {parttype} start {lba_start} len {lba_len}')
if (boot_ind != 0x00 and boot_ind != 0x80) or parttype == 0x00:
raise ValueError("Not a valid partition")
self.offset = lba_start
self.size = lba_len
assert lba_start + lba_len <= bdev_len
def ioctl(self, op, arg):
if op == 4:
return self.size
elif op == 5:
return 512
else:
return None
def readblocks(self, block, buf):
if block >= self.size:
raise ValueError("Block out of range")
return self.bdev.readblocks(block+self.offset, buf)
def writeblocks(self, block, buf):
if block >= self.size:
raise ValueError("Block out of range")
return self.bdev.writeblocks(block+self.offset, buf)

View File

@@ -0,0 +1,39 @@
# SPDX-License-Identifier: MIT
# Copyright (c) 2025 Matthias Blankertz <matthias@blankertz.org>
import os
from . import MBRPartition
from rp2_sd import SDCard
class SDContext:
def __init__(self, mosi, miso, sck, ss, baudrate):
self.mosi = mosi
self.miso = miso
self.sck = sck
self.ss = ss
self.baudrate = baudrate
def __enter__(self):
self.sdcard = SDCard(self.mosi, self.miso, self.sck, self.ss, self.baudrate)
# Try first partition
try:
self.part = MBRPartition(self.sdcard, 0)
os.mount(self.part, '/sd')
return self
except Exception:
print("Failed to mount SDCard partition, trying whole device...")
# Try whole device
try:
os.mount(self.sdcard, '/sd')
return self
except Exception as ex:
self.sdcard.deinit()
raise RuntimeError("Could not mount SD card") from ex
def __exit__(self, exc_type, exc_value, traceback):
try:
os.umount('/sd')
finally:
self.sdcard.deinit()