
Прошивка RP2040, которая превращает SDIO микронакопитель Toshiba MK4001MTD 0.85" в USB-устройство массового хранения, реализуя полный стек протоколов SDIO-ATA с нуля с ускоренными PIO чтением/записью и восстановлением битых секторов.
Прошивка для RP2040 Pico, которая подключает Toshiba MK4001MTD 0,85" SDIO микродиск как USB-накопитель.

MK4001MTD — это 4-гигабайтный микродиск, изначально использовавшийся в музыкальном телефоне Nokia N91 и других устройствах, таких как MP3-плееры или USB-флешки, в те времена, когда флеш-память была еще довольно дорогой.
Возможно, вы видели утверждения, что этот накопитель использует протокол MMC, но это на самом деле неверно. Я некоторое время изучал этот вопрос: пытался построить 8-битный кардридер MMCplus и тестировал различные SD/MMC-ридеры безрезультатно. В качестве последней меры я купил Nokia N91, чтобы захватить логические сигналы и подтвердить, какой протокол он на самом деле использует.
Вот фото, когда я пытался использовать его с моей платой 8bit-MMCPlus ридера, и оказалось, что это не MMC :(

Поэтому я в итоге приобрел N91 для сбора сигналов:

В отличие от стандартных ATA/CF микродисков, он использует интерфейс SDIO с командами ATA, туннелированными через CMD52/CMD53. Ни один существующий драйвер не поддерживает этот протокол, поэтому данная прошивка реализует полный стек с нуля.
Это меня удивило, поскольку существует стандарт SDIO-to-ATA под названием CE-ATA. Но если внимательно посмотреть на временную шкалу выпуска, CE-ATA появился позже, чем этот накопитель. В результате этот накопитель полностью полагается на команды SDIO, и CE-ATA недоступен. CE-ATA имеет две новые команды CMD60/CMD61 и использует CMD12/39, но из сигналов видно, что ни одна из них не применяется.
Второй аппаратный момент, который стоит упомянуть: еще одно распространенное заблуждение — утверждение, что это 8-битная карта MMCPlus, — не только неверно, но и распиновка не соответствует стандарту MMC. Вы можете найти сервисное руководство Nokia N91 с некоторой документацией по распиновке: хотя нумерация контактов соответствует стандарту MMCPlus, назначение контактов — нет. Это важная деталь, если вы подключаете его самостоятельно: используется тот же разъем MMC, но назначение контактов другое, подробнее в разделе Hardware.
Наконец, отмечу, что этот проект разрабатывался совместно с Claude/OpenClaw. Я вручную собирал логические сигналы и настроил замкнутый испытательный стенд для OpenClaw, чтобы он мог итеративно разрабатывать — анализировать сигналы и реализовывать функции. Документацию в основном пишет Claude; я также добавлю свои заметки прямо в текст. Я прочитал и перепроверил документацию самостоятельно, она должна быть надежной и легко читаемой.
Для анализа сигналов N91 смотрите /docs/N91_TRACE_ANALYSIS.md; я также поместил туда сервисное руководство N91 вместе с сырыми логическими сигналами.
Подробнее в блоге: https://www.willwhang.dev/Reading-MK4001MTD/
Посмотрите в работе: https://youtu.be/GC4xil3_Bbc
Полностью функциональный USB-накопитель массового хранения с ускоренным PIO чтением/записью и управлением питанием в простое.
| Метрика | Значение |
|---|---|
| Скорость чтения | ~985 кБ/с (ограничение USB full-speed) |
| Скорость записи | ~920 кБ/с (ограничение USB full-speed, рекламируется кэш записи) |
| Сырая скорость на стороне SDIO | ~2,35 МБ/с чтение / ~2,15 МБ/с запись (ограничено накопителем) |
| Емкость | 3,75 ГБ (7 862 400 секторов) |
| Файловая система | FAT32 проверена (mount/unmount/fsck чисто) |
| Целостность данных | Проверено чтение+обратное чтение; для всех 4 линий DAT используется CRC16 на блок |
| Ожидание в простое | 5 с бездействия или USB suspend → STANDBY IMMEDIATE + отключение питания |
USB Host ←→ USB MSC (TinyUSB) ←→ ATA Layer ←→ SDIO Layer (PIO) ←→ MK4001MTD
Прошивка состоит из четырех слоев:
USB MSC (msc_device.c) — TinyUSB Mass Storage Class. Преобразует SCSI READ(10)/WRITE(10) в операции с секторами ATA. Буфер EP 32 КБ, пакетирование до 64 секторов за одну передачу USB. Ввод/вывод накопителя перекрывается с USB в обоих направлениях, как в реальном мосту ATA-USB с кэширующим диском: последовательный предварительный выборщик чтения подгружает следующий фрагмент, пока предыдущий передается на хост, а запись ставится в очередь и сбрасывается, пока USB принимает следующую часть. Устройство рекламирует свой кэш записи (Caching mode page, WCE=1 — хосты сообщают «Write cache: enabled» и выдают SYNCHRONIZE CACHE при fsync/размонтировании/приостановке, что прошивка соблюдает). Неудачный фондрайн сбрасывания отображается как MEDIUM ERROR при следующей операции WRITE или SYNCHRONIZE CACHE; запись в заведомо плохие сектора идет строго синхронным путем.
ATA-поверх-SDIO (ata_sdio.c) — Реализует команды ATA (IDENTIFY, READ SECTORS, WRITE SECTORS) путем записи в регистры ATA, отображенные в адресное пространство функции 1 SDIO через CMD52, и передачи данных секторов через CMD53. 3-уровневая логика повторов на уровнях CMD, данных и ATA.
PIO SDIO (sdio_pio.c, sdio.pio) — Аппаратно-ускоренный SDIO с использованием периферии PIO RP2040 (4-битная шина на 10 МГц, 4 цикла PIO на бит с отключенными синхронизаторами входа). Три программы PIO совместно используют одну конечную машину через динамическую замену программ:
Пин/Питание (sdio_hw.c) — Инициализация GPIO и управление питанием HDD. Вся связь SDIO осуществляется через PIO.
Заметки человека: Интересно, что Claude очень не хотел реализовывать SDIO на PIO, и многие циклы разработки были потрачены на метания между PIO и бит-бэнгом.
MK4001MTD представляется как SDIO-карта с одной функцией ввода/вывода. Стандартная инициализация SDIO-карты (CMD5/CMD3/CMD7) настраивает шину, затем регистры ATA доступны через команды SDIO:
Доступ к регистрам (CMD52): Каждый регистр ATA отображается на адрес функции 1:
| Адрес | Регистр | Использование |
|---|---|---|
| 0x00 | DATA | Цель CMD53 для данных секторов |
| 0x01 | ERR/FEAT | Ошибка (чтение) / Feature (запись) |
| 0x02 | SECCOUNT | Количество секторов |
| 0x03 | LBA_LO | LBA биты 0-7 |
| 0x04 | LBA_MID | LBA биты 8-15 |
| 0x05 | LBA_HI | LBA биты 16-23 |
| 0x06 | DEV/HEAD | Device/Head + LBA биты 24-27 |
| 0x07 | CMD/STATUS | Команда (запись) / Статус (чтение) |
Передача данных (CMD53): Данные секторов передаются путем выдачи CMD53 в блочном режиме с целью регистра DATA (адрес 0x00). Для многосекторного чтения один CMD53 с block_count=N передает N × 512 байт за одну мультиблочную транзакцию SDIO.
Сигнализация прерывания: Накопитель сигнализирует о готовности сектора, устанавливая прерывание SDIO (бит INT_PENDING 1 в регистре CCCR 0x05). Чтение регистра STATUS ATA сбрасывает прерывание.
Для чтения 16 секторов: