
برنامج ثابت لـ RP2040 يربط مشغل Toshiba MK4001MTD 0.85" SDIO microdrive كجهاز تخزين كبير عبر USB، مع تنفيذ كامل لمكدس بروتوكول SDIO-ATA من الصفر باستخدام عمليات قراءة/كتابة مسرّعة بـ PIO واسترداد القطاعات التالفة.
برنامج ثابت لـ RP2040 Pico يوصل محرك Toshiba MK4001MTD 0.85" SDIO كجهاز تخزين كتلي USB.

محرك MK4001MTD هو محرك صغير سعة 4 جيجابايت كان يُستخدم أصلاً في هاتف Nokia N91 الموسيقي وبعض الأجهزة الأخرى، مثل مشغلات MP3 أو محركات USB، في وقت كانت فيه ذاكرة الفلاش لا تزال باهظة الثمن.
ربما تكون قد رأيت مقدمات تدّعي أن هذا المحرك يستخدم بروتوكول MMC، لكن هذا غير صحيح في الواقع. لقد كنت أبحث في هذا الأمر لبعض الوقت: حاولت بناء قارئ بطاقة MMCplus ذات 8 بت واختبرت عدة قُرّاء 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، لكن يمكنك رؤية من التتبعات أنها لا تستخدم أيًا منها.
النقطة الثانية المتعلقة بالأجهزة هي أن معلومة خاطئة أخرى منتشرة – تدّعي أنها بطاقة MMCPlus ذات 8 بت – ليست غير صحيحة فحسب، بل إن ترتيب الأطراف لا يتبع معيار MMC أيضًا. يمكنك العثور على دليل خدمة Nokia N91 مع بعض التوثيق لترتيب الأطراف: بينما يتبع ترقيم الأطراف معيار MMCPlus، فإن تعيين الأطراف لا يتبعه. هذه تفاصيل مهمة إذا كنت تقوم بالتوصيل بنفسك: فهو يستخدم نفس موصل MMC، لكن تعيين الأطراف مختلف، مزيد من التفاصيل في قسم الأجهزة.
أخيرًا، لاحظ أن هذا تم تطويره بالاشتراك مع Claude/OpenClaw. قمت أنا بجمع التتبعات المنطقية يدويًا وإعداد محطة اختبار مغلقة الحلقة لـ OpenClaw لتكرار التطوير – تحليل التتبعات وتنفيذ الميزات. سيتم كتابة التوثيق بشكل أساسي بواسطة Claude؛ وسأضيف ملاحظاتي الخاصة في نفس السياق. لقد قرأت أيضًا التوثيق بنفسي وتأكدت منه، ويجب أن يكون موثوقًا وسهل المتابعة.
للحصول على رؤى حول التحليلات على تتبع N91، هي موجودة في /docs/N91_TRACE_ANALYSIS.md، وقد وضعت أيضًا دليل خدمة N91 هناك مع التتبعات المنطقية الأولية.
شاهد المزيد في منشور المدونة هنا: https://www.willwhang.dev/Reading-MK4001MTD/
شاهده وهو يعمل هنا: https://youtu.be/GC4xil3_Bbc
تخزين كتلي USB يعمل بكامل وظائفه مع قراءة/كتابة مُسرّعة عبر PIO وإدارة استهلاك الطاقة في وضع الخمول.
مضيف USB ←→ USB MSC (TinyUSB) ←→ طبقة ATA ←→ طبقة SDIO (PIO) ←→ MK4001MTD
يتكون البرنامج الثابت من أربع طبقات:
USB MSC (msc_device.c) — فئة التخزين الكتلي TinyUSB. يترجم أوامر SCSI READ(10)/WRITE(10) إلى عمليات قطاع ATA. مخزن مؤقت لنقطة النهاية 32 كيلوبايت، تجميع يصل إلى 64 قطاعًا لكل نقل USB. يتم تداخل إدخال/إخراج المحرك مع USB في كلا الاتجاهين، مثل جسر ATA-USB حقيقي مع قرص تخزين مؤقت: جالب مسبق للقراءة المتسلسلة يجلب الجزء التالي بينما يتم دفق الجزء السابق إلى المضيف، ويتم تخزين الكتابات مؤقتًا وتفريغها أثناء استقبال USB للجزء التالي. يعلن الجهاز عن ذاكرة التخزين المؤقت للكتابة (صفحة وضع التخزين المؤقت، WCE=1 — تقارير المضيفين "ذاكرة تخزين مؤقت للكتابة: ممكّنة" ويصدرون SYNCHRONIZE CACHE عند fsync/فك التركيب/التعليق، وهو ما يلتزم به البرنامج الثابت). يؤدي فشل التفريغ الخلفي إلى ظهور MEDIUM ERROR في عملية WRITE أو SYNCHRONIZE CACHE التالية؛ تأخذ الكتابات إلى القطاعات المعروفة بأنها تالفة مسارًا تزامنيًا صارمًا.
ATA-over-SDIO (ata_sdio.c) — ينفذ أوامر ATA (IDENTIFY، READ SECTORS، WRITE SECTORS) عن طريق الكتابة إلى سجلات ATA المُخطط لها في مساحة عنوان وظيفة SDIO 1 عبر CMD52، ونقل بيانات القطاع عبر CMD53. منطق إعادة محاولة من 3 مستويات على مستويات CMD والبيانات وATA.
PIO SDIO (sdio_pio.c, sdio.pio) — SDIO مُسرّع بالأجهزة باستخدام الطرفية PIO لـ RP2040 (ناقل 4 بت بسرعة 10 ميجاهرتز، 4 دورات PIO لكل بت مع تخطي مزامنات الإدخال). ثلاثة برامج PIO تشترك في آلة حالة واحدة عبر التبديل الديناميكي للبرنامج:
الطرفية/الطاقة (sdio_hw.c) — تهيئة GPIO والتحكم في طاقة القرص الصلب. جميع اتصالات SDIO تستخدم PIO.
ملاحظات بشرية: من المثير للاهتمام، أن Claude كان مترددًا حقًا في تنفيذ SDIO في PIO، وتم إهدار الكثير من دورات التطوير في التبديل ذهابًا وإيابًا بين PIO والتلاعب بالبتات.
يقدم MK4001MTD نفسه كبطاقة SDIO مع وظيفة إدخال/إخراج واحدة. تهيئة بطاقة SDIO القياسية (CMD5/CMD3/CMD7) تقوم بإعداد الناقل، ثم يتم الوصول إلى سجلات ATA من خلال أوامر SDIO:
الوصول إلى السجل (CMD52): يتم تعيين كل سجل ATA إلى عنوان وظيفة 1:
نقل البيانات (CMD53): يتم نقل بيانات القطاع عن طريق إصدار CMD53 في وضع الكتلة مستهدفًا سجل DATA (العنوان 0x00). للقراءات متعددة القطاعات، يقوم CMD53 واحد مع block_count=N بنقل N × 512 بايت في معاملة واحدة متعددة الكتل SDIO.
إشارات المقاطعة: يشير المحرك إلى جاهزية القطاع عن طريق تأكيد مقاطعة SDIO (بت INT_PENDING 1 في سجل CCCR 0x05). قراءة سجل STATUS ATA تمسح المقاطعة.
لقراءة 16 قطاعًا:
1. كتابة سجلات ATA عبر PIO CMD52:
SECCOUNT=16, LBA_LO/MID/HI, DEV/HEAD=0xE0, CMD=0x20
2. استقصاء STATUS عبر CMD52 حتى يتم تعيين DRQ (بت 3)
3. تبديل PIO إلى برنامج قراءة DAT
4. إرسال CMD53: block_mode=1, fn=1, addr=0x0000, block_count=16
5. PIO قراءة DAT: لكل من الكتل الـ 16:
أ. انتظار بت البداية (جميع خطوط DAT منخفضة)
ب. DMA 1024 nibble (512 بايت) من PIO RX FIFO إلى المخزن المؤقت
ج. انتظار انتهاء SM من ساعة nibbles CRC+النهاية (استقصاء SM PC)
د. إعادة تجميع nibbles → بايتات في المكان
6. تبديل PIO مرة أخرى إلى برنامج CMD
لكتابة 16 قطاعًا:
1. كتابة سجلات ATA عبر PIO CMD52:
SECCOUNT=16, LBA, DEV/HEAD=0xE0, CMD=0x30
2. استقصاء STATUS عبر CMD52 حتى يتم تعيين DRQ (بت 3)
(STATUS 0xD8 = BSY+DRQ يُعامل كجاهز DRQ، حسب تتبع N91)
3. تبديل PIO إلى برنامج كتابة DAT
4. إرسال CMD53: block_mode=1, fn=1, addr=0x0000, block_count=16
5. PIO كتابة DAT: لكل من الكتل الـ 16:
أ. حساب مسبق CRC16-CCITT لكل خط DAT (4 CRC مستقلة)
ب. بناء تيار nibble: start(0x0) + data(1024 nibble) + CRC(16) + end(0xF)
ج. DMA تيار nibble إلى PIO TX FIFO
د. PIO يخرج جميع nibbles، ثم:
- يبدل DAT إلى الإدخال
- يولد 16 دورة لحالة CRC من البطاقة
- يستقصي DAT0 حتى تطلق البطاقة الانشغال
- يطلق IRQ 0 للإشارة إلى اكتمال الكتلة
6. تبديل PIO مرة أخرى إلى برنامج CMD
لدى RP2040 PIO 32 فتحة تعليمات لكل كتلة. برامجنا الثلاثة تحتوي على 55 تعليمة إجمالاً، لذا لا يمكنها التواجد معًا. بدلاً من ذلك، يتم استخدام SM0 واحد على PIO0، ويتم تبديل البرامج عن طريق الكتابة مباشرة إلى ذاكرة تعليمات PIO:
static void load_program_raw(const pio_program_t *program) {
for (uint i = 0; i < program->length; i++)
pio->instr_mem[FIXED_OFFSET + i] = program->instructions[i];
}
هذا يتجاوز مُخصص SDK pio_add_program/pio_remove_program. يستغرق تبديل البرنامج حوالي 1 µs. يتبع كل تبديل إعادة تهيئة خاصة بالبرنامج تضبط تعيينات الطرفية واتجاه الإزاحة ومقسّم الساعة.
يكشف تحليل تتبعات Nokia N91 المنطقية عن إدارة طاقة عدوانية:
يكرر البرنامج الثابت هذا السلوك مع مهلة خمول قابلة للتكوين:
#define IDLE_STANDBY_MS 5000 // في main.c
مساران يؤديان إلى قطع طاقة القرص الصلب:
كلا المسارين يرسلان ATA STANDBY IMMEDIATE (0xE0) لتفريغ ذاكرة التخزين المؤقت للكتابة وإيقاف الرؤوس، ثم قطع الطاقة عبر GP9.
تسلسل الإيقاظ (يتم تشغيله بواسطة أول READ/WRITE بعد القطع):
عندما يواجه نقل متعدد القطاعات قطاعًا تالفًا:
STATUS/ERROR النهائية بدلاً من تحويل الفشل إلى "مهلة DRQ" عامةMEDIUM ERROR (قراءة: 03/11/00، كتابة: 03/0C/00)النقطة 6 ليست أكاديمية: كان لهذا المحرك قطاع غير قابل للقراءة طويل الأمد عند LBA 1952 (قراءة: ST=0x51 ERR ERR=0x40 UNC). بمجرد أن سمح الجسر بوصول كتابة إليه فعليًا، أعاد المحرك كتابة القطاع ومنذ ذلك الحين تمت قراءته بشكل نظيف:
[ATA] FAST-RD: ST=0x51 ERR ERR=0x40 UNC LBA=1952
[MSC] BAD SECTOR read LBA=1952
[MSC] Bad sector LBA=1952 repaired by write
arm-none-eabi-gcc)إصدار SDK مُثبت: إذا تم تعيين PICO_SDK_PATH (متغير بيئة أو
متغير CMake) يتم استخدامه ويتم التحقق من إصداراته مقابل التثبيت —
يؤدي عدم التطابق إلى فشل التكوين مع تعليمات (تجاوز باستخدام
-DMK4001_ALLOW_SDK_MISMATCH=ON). بدون أي PICO_SDK_PATH على الإطلاق، يتم
جلب إصدار SDK المثبت من GitHub تلقائيًا في وقت التكوين،
لذا فإن git clone && cmake && make قابل للتكرار بالكامل.
يحتاج البرنامج الثابت إلى مشغل فئة TinyUSB MSC مُصحح (بيانات الإحساس
التطبيقية محفوظة في أخطاء القراءة/الكتابة + صفحة وضع تخزين مؤقت مع WCE=1). هذا
الملف مضمن في هذا المستودع في lib/tinyusb_patched/msc_device.c —
يقوم البناء تلقائيًا بتجميعه بدلاً من نسخة SDK، لذا لا حاجة لأي
تعديل على SDK مطلقًا. الفرق مقابل TinyUSB الرئيسي (0.18.0، كما
مضمن مع pico-sdk 2.2.0) موجود في lib/tinyusb_patched/؛ تثبيت SDK
موجود تحديدًا لأن هذا الملف المضمن يجب أن يتتبع TinyUSB الخاص بـ SDK.
cd /home/pi/mk4001_bridge/build
cmake ..
make -j4
sudo openocd -f interface/cmsis-dap.cfg -f target/rp2040.cfg \
-c "adapter speed 1000" -c "init" -c "reset halt" -c "sleep 200" \
-c "program /home/pi/mk4001_bridge/build/mk4001_bridge.elf verify" \
-c "reset run" -c "exit"
ملاحظة: GP0 و GP1 معطّلان في وحدة Pico هذه تحديدًا. جميع تعيينات طرفية SDIO مُزاحة بمقدار +2.
ملاحظات بشرية: كان Claude مخطئًا هنا لأنه لم يدرك أن GP0 و GP1 كانا يُستخدمان لطرفية UART في تكوين البناء الخاص به. استمر في نسيان هذا، لدرجة أنني قمت ببساطة بنقل GPIOs SDIO بعيدًا عن UART تلك.
HDD_PWR ليس ضروريًا. ليس عليك إعادة تشغيل الطاقة للمحرك لاستخدامه؛ إنها بالأحرى وسيلة راحة تطويرية لإعادة تعيين القرص الصلب عندما تكون الكثير من الأشياء مشفرة بشكل ثابت. ومع ذلك، إذا كنت تريد توفير الطاقة، يمكنك استخدام تلك الإشارة، لكنها تستطيع التعامل مع إعادة التعيين الدافئ دون أي مشكلة.
سترى رسائل التصحيح عبر UART. إنها لا تمر عبر USB-CDC لأنه كان من الأسهل لـ Claude إعداد رابط تسجيل UART-to-USB منفصل لا ينقطع أو يصبح غير مستقر أثناء التطوير المبكر.
يسجل سجل UART أيضًا درجة حرارة المحرك كل 30 ثانية أثناء نشاط المحرك ([TEMP] drive temperature: 29 C). تم اكتشاف المستشعر عن طريق الهندسة العكسية لأمر البائع Toshiba 0xC2 — يقرأه N91 في بداية كل جلسة محرك لفرض حدود درجة حرارة تشغيل القرص الصلب الخاصة به. التفاصيل في docs/N91_TRACE_ANALYSIS.md §4.
فيما يلي مثال على السجل:
========================================
MK4001MTD USB Bridge v0.11
SDIO-ATA → USB Mass Storage (PIO)
========================================
[MAIN] Pre-delay 5000ms...
[PIO] Init OK: clkdiv=3.12 (~10.0 MHz), CMD@0
[MAIN] Power cycling HDD...
[SDIO] HDD power OFF
[SDIO] HDD power ON
[MAIN] SDIO init (PIO)...
[SDIO] CMD5 ready (OCR=0x901F8000)
[SDIO] RCA=0x0001
[SDIO] fn1 ready (attempt 0)
[MAIN] ATA IDENTIFY...
[ATA] IDENTIFY complete
Model: [TOSHIBA MK4001MTD]
Serial: [ 763B004HA]
Firmware: [VH173A]
Sectors: 7862400 (3839 MB)
SMART: not supported (supported=0, enabled=0)
IDENTIFY: W0=0040 W47=0000 W49=0000 W59=0000
ATA W80=0000 Cmd W82=0000 W83=0000 W84=0000
En W85=0000 W86=0000 W87=0000 W89=0008 W128=0001
[DIAG] === Drive Diagnostics ===
[DIAG] Standard SMART: not supported (IDENTIFY W82 bit0 = 0)
[DIAG] Toshiba vendor CMD 0xC2:
FEAT=0x01 unknown_01 → SC=00 LBA=02/00/00 ST=50
FEAT=0x02 unknown_02 â SC=00 LBA=02/00/00 ST=50
FEAT=0x03 unknown_03 → SC=00 LBA=02/00/00 ST=50
FEAT=0x04 unknown_04 → SC=00 LBA=02/00/00 ST=50
FEAT=0x10 diag_10 (LBA_LO varies) → SC=00 LBA=00/00/00 ST=50
FEAT=0x11 diag_11 → SC=00 LBA=00/00/00 ST=50
FEAT=0x12 diag_12 (LBA_LO varies) → SC=00 LBA=01/00/00 ST=50
FEAT=0x20 query_20 (N91: SC=0xFF always) → SC=FE LBA=00/FF/00 ST=50
FEAT=0x21 query_21 (N91: SC varies per boot) → SC=1B LBA=00/FF/00 ST=50
[MAIN] MBR: valid 0x55AA
[MAIN] Warming up...
[MAIN] PIO OK, STATUS=0x50
[MAIN] Drive: 7862400 sectors (3839 MB)
[MAIN] Ready.
[PWR] Idle 5000ms → STANDBY + power gate
[PWR] STANDBY IMMEDIATE → power gate
[SDIO] HDD power OFF
أخيرًا، إليك التوصيل الفعلي بالمحرك.
هذه لقطة من مخطط N91، يمكنك تعيين رقم الطرف أيضًا.

ملاحظة جانبية: هذا محرك 3 فولت لكن أعتقد أن 3.3 فولت جيدة، بشكل أساسي لتوفير بعض أعمال تغيير مستوى الجهد.
تم تصميم الأجهزة خصيصًا لهذا المحرك تحت /hardware!

# التحقق من ظهور الجهاز
lsblk -dno NAME,MODEL | grep MK4001
# اختبار نظام الملفات — تركيب، نسخ ملفات، تحقق
sudo mount /dev/sdX1 /mnt/mk4001
cp /tmp/testfile /mnt/mk4001/
sync
md5sum /tmp/testfile /mnt/mk4001/testfile # يجب أن تتطابق
sudo umount /mnt/mk4001
# معايير السرعة (جهاز خام، لا تقم بالتركيب أولاً — سيتلف نظام الملفات)
# استخدم إزاحة آمنة بعد نظام الملفات أو محرك غير مقسم
sudo dd if=/dev/sdX of=/dev/null bs=64k count=128 iflag=direct # قراءة
sudo dd if=/dev/zero of=/dev/sdX bs=64k count=64 oflag=direct seek=1024 # كتابة (إزاحة بعد نظام الملفات)
ملاحظات بشرية هنا، حقيقة مضحكة: عندما بدأ اختبار السرعة لأول مرة، قام بالفعل بعمل dd مباشرة على المحرك وأتلف أنظمة الملفات..... لحسن الحظ، لا يهم كثيرًا هنا أثناء التطوير، لكن ضع في اعتبارك دائمًا عند التعامل مع OpenClaw إعدادك.
لا يهمني.
| المقياس | القيمة |
|---|
| سرعة القراءة | ~985 كيلوبايت/ثانية (محدودة بسرعة USB الكاملة) |
| سرعة الكتابة | ~920 كيلوبايت/ثانية (محدودة بسرعة USB الكاملة، ذاكرة تخزين مؤقت للكتابة مُعلن عنها) |
| سرعة جانب SDIO الخام | ~2.35 ميجابايت/ثانية قراءة / ~2.15 ميجابايت/ثانية كتابة (محدودة بالمحرك) |
| السعة | 3.75 جيجابايت (7,862,400 قطاع) |
| نظام الملفات | FAT32 مُتحقق منه (تركيب/فك/fsck نظيف) |
| سلامة البيانات | تم التحقق من الكتابة + إعادة القراءة؛ CRC16 لكل كتلة على جميع خطوط DAT الأربعة |
| وضع الاستعداد الخامل | 5 ثوانٍ خامل أو تعليق USB → STANDBY IMMEDIATE + قطع الطاقة |
| العنوان | السجل | الاستخدام |
|---|
| 0x00 | DATA | هدف CMD53 لبيانات القطاع |
| 0x01 | ERR/FEAT | خطأ (قراءة) / ميزة (كتابة) |
| 0x02 | SECCOUNT | عدد القطاعات |
| 0x03 | LBA_LO | LBA بت 0-7 |
| 0x04 | LBA_MID | LBA بت 8-15 |
| 0x05 | LBA_HI | LBA بت 16-23 |
| 0x06 | DEV/HEAD | الجهاز/الرأس + LBA بت 24-27 |
| 0x07 | CMD/STATUS | أمر (كتابة) / حالة (قراءة) |
| Pico GPIO | الوظيفة | ملاحظات |
|---|
| GP2 | SDIO_CLK | إخراج ساعة المضيف |
| GP3 | SDIO_CMD | خط أوامر ثنائي الاتجاه |
| GP4 | SDIO_DAT0 | بت البيانات 0 |
| GP5 | SDIO_DAT1 | بت البيانات 1 |
| GP6 | SDIO_DAT2 | بت البيانات 2 |
| GP7 | SDIO_DAT3 | بت البيانات 3 |
| GP9 | HDD_EN | تمكين طاقة المحرك (HIGH=تشغيل) |
| GP12 | UART TX | إخراج التصحيح @ 115200 |
| GP13 | UART RX | إدخال التصحيح |
| GP16 | LED: طاقة القرص الصلب | نشط منخفض |
| GP17 | LED: قرص صلب سليم | نشط منخفض |
| GP18 | LED: قراءة | نشط منخفض |
| GP19 | LED: كتابة | نشط منخفض |
| الملف | الأسطر | الغرض |
|---|
main.c | 210 | التهيئة، الاستعداد الخامل، تعليق/استئناف USB |
msc_device.c | 400 | استدعاءات USB MSC، إيقاظ قطع الطاقة، ذاكرة تخزين مؤقت للقطاعات التالفة |
ata_sdio.c | 390 | أوامر ATA، استعادة الأخطاء، تشخيصات البائع |
sdio_pio.c | 635 | PIO SDIO: CMD52، CMD53 قراءة/كتابة، تبديل البرنامج، CRC16 |
sdio_hw.c | 45 | تهيئة الطرفية + التحكم في طاقة القرص الصلب |
sdio.pio | 200 | تجميع PIO + أدوات مساعدة لتهيئة C SDK |
led.h | 37 | أدوات مساعدة لـ LED (GP16–GP19، نشط منخفض) |
usb_descriptors.c | 77 | أوصاف جهاز USB/التكوين/السلسلة |
tusb_config.h | 20 | تكوين TinyUSB (MSC، مخزن مؤقت لنقطة النهاية 32 كيلوبايت) |
| الإصدار | القراءة | الكتابة | التغيير الرئيسي |
|---|
| v0.1–v0.3 | 105 كيلوبايت/ثانية | 93 كيلوبايت/ثانية | SDIO بالتلاعب بالبتات، CRC16، منطق إعادة المحاولة |
| v0.5 | 374 كيلوبايت/ثانية | — | PIO ذو SM واحد، تبديل ذاكرة التعليمات المباشر |
| v0.6 | 583 كيلوبايت/ثانية | 93 كيلوبايت/ثانية | قراءات CMD53 متعددة الكتل، إصلاح استنزاف ساعة CRC |
| v0.8 | 588 كيلوبايت/ثانية | 274 كيلوبايت/ثانية | كتابات PIO، إصلاح تفريغ OSR |
| v0.9 | 475 كيلوبايت/ثانية | 371 كيلوبايت/ثانية | دفعات من 64 قطاعًا، التحقق من قراءة CRC16 |
| v0.10 | 453 كيلوبايت/ثانية | 329 كيلوبايت/ثانية | إعادة تعيين LED، طرفية تمكين القرص الصلب، UART على GP12/GP13 |
| v0.11 | ~450 كيلوبايت/ثانية | ~340 كيلوبايت/ثانية | قطع طاقة القرص الصلب، إيقاظ PIO، استشعار القطاع التالف، تعليق USB |
| v0.12 | ~985 كيلوبايت/ثانية | ~920 كيلوبايت/ثانية | تداخل المحرك/USB (جلب مسبق للقراءة + ذاكرة تخزين مؤقت للكتابة معلنة مع كتابة خلفية)، كتل PIO خط أنابيب، DMA تبادل البايت، حلقات PIO ذات 4 دورات، دلالات القطاع التالف على غرار SBC (إصلاح بالكتابة)، مشغل TinyUSB MSC مضمن |