SD卡 寫入及讀取資料

之前使用 Raspberry Pi Pico 控制 LCD熒幕,本來想將讓 LCD 顯示一些連續圖片
但由於 Raspberry Pi Pico 內置儲存空間只有 1MiB,無法儲存太多資料
因此在下需要 Raspberry Pi Pico 能存取一些外置儲存媒體
令能夠 Raspberry Pi Pico 執行更多操作或讀取資料

Micro SD卡 模組外觀

見下文
Micro SD卡 模組 正面

見下文
Micro SD卡 模組 背面

Micro SD卡 模組引腳

編號引腳方向功能
1CS輸入晶片選擇,低電壓時啟用
2SCK輸入序列時脈,高電壓時為閒置狀態
3MOSI輸入由 微控制器 傳送資料到 從機
4MISO輸出由 從機 傳送資料到 微控制器
5VCC電源,接受 4V 至 12V
6GND接地

Micro SD卡 模組其他元件

見下文
AMS1117 3.3V 電壓穩定器
由於 SD卡 使用 3.3V ,使用超過 3.3V 會增加損壞 SD卡 的風險
Micro SD卡 模組,提供 3.3V 電壓穩定器,支援沒有 3.3V 的 微控制器 都能夠安全接駁到 Micro SD卡

見下文
LVC125A 三態輸出四路緩衝晶片

線路原型

見下文
Raspberry Pi Pico 接駁到 Micro SD卡 模組

接駁線路

見下文
見下文
見下文
Micro SD卡 模組能夠使用 SPI模式 ,因此需要使用 4支 引腳連接

存取 SD卡 限制

檔案系統
一般市面上購買的 SD卡 會分為:
制式(代號)類別(類別)預設檔案系統
SD標準FAT16
SDHC高容量FAT32
SDXC額外容量exFAT
SDUC超高容量exFAT
由於使用 SPI 存取 SD卡 ,必須使用 FAT16 FAT32 檔案系統
因此使用 SD SDHC 制式的 SD卡 能夠即時使用
而預設使用 exFAT SDXC SDUC 則不能即時使用
必須將 SD卡 格式化為 FAT16 或 FAT32 才能使用 SPI 存取

掛載點
很多網上教學都指定 /sd /SD 這個掛載點名稱
但實際並沒有限制掛載點名稱,只要符合檔案命名規則,及 SD卡 沒有重覆的路徑,便可以掛載

檔案名稱
由於必須使用 FAT16 或 FAT32 檔案系統,而這兩個檔案系統 不會區分英文大小寫
因此 掛載點 及 檔案名稱 時,不需要考慮英文大小寫

讀取 SD卡 資料

見下文
使用 Raspberry Pi Pico 讀取資料前,先將資料寫入到 SD卡

見下文
見下文
由於在下想確保沒有多餘的控制字元
例如 Carriage Return 及 End Of File 的 Line Feed ,所以使用指令製作測試文字檔案
如果閣下沒有特別需要,使用一般文字編輯器製作文字檔案亦可以

見下文
見下文
簡單的文字檔案內容
在下還想測試 Raspberry Pi Pico 讀取 SD卡 的內容時,能否支援 Unicode 等字元
因此製作含有中文字的內容

見下文
由於在下使用 CircuitPython , 因此使用由 Adafruit 提供的 CircuitPython 函式庫
方便存取 SD卡 的資料

見下文
或到 Adafruit 的 GitHub 頁面 下載亦可

見下文
adafruit_sdcard.py adafruit_sdcard.mpy 存放到 Raspberry Pi Pico 的 /lib 目錄
(py 為純文字檔案,可以了解原始碼;mpy 編譯為二進制檔案的 py ,檔案較小)

操作 SD卡

掛載檔案系統
見下文
需要先掛載 SD卡 才能存取當中的資料
import busio, board, digitalio, adafruit_sdcard, storage, os

mount_point = "/sdcard"
spi = busio.SPI(board.GP14, MOSI = board.GP15, MISO = board.GP12)
cs = digitalio.DigitalInOut(board.GP13)
sdcard = adafruit_sdcard.SDCard(spi, cs)
vfs = storage.VfsFat(sdcard)
storage.mount(vfs, mount_point)

print(os.listdir(mount_point))

讀取檔案
見下文
讀取檔案的資料
import busio, board, digitalio, adafruit_sdcard, storage, os

mount_point = "/sdcard"
spi = busio.SPI(board.GP14, MOSI = board.GP15, MISO = board.GP12)
cs = digitalio.DigitalInOut(board.GP13)
sdcard = adafruit_sdcard.SDCard(spi, cs)
vfs = storage.VfsFat(sdcard)
storage.mount(vfs, mount_point)

for file in os.listdir(mount_point):
	with open(mount_point + "/" + file, "r") as handle:
		print("----- " + file + " -----")
		print(handle.read())

寫入檔案
見下文
將資料寫入到檔案
import busio, board, digitalio, adafruit_sdcard, storage, os

mount_point = "/sdcard"
spi = busio.SPI(board.GP14, MOSI = board.GP15, MISO = board.GP12)
cs = digitalio.DigitalInOut(board.GP13)
sdcard = adafruit_sdcard.SDCard(spi, cs)
vfs = storage.VfsFat(sdcard)
storage.mount(vfs, mount_point)

file = mount_point + "/file3.txt"
with open(file, "w") as handle:
	handle.write("hello")
	handle.flush()
with open(file, "r") as handle:
	print(handle.read())

見下文
見下文
已存在的檔案使用寫入模式會 取代原本檔案

見下文
如果要避免取代原本檔案,需要使用加入模式
import busio, board, digitalio, adafruit_sdcard, storage, os

mount_point = "/sdcard"
spi = busio.SPI(board.GP14, MOSI = board.GP15, MISO = board.GP12)
cs = digitalio.DigitalInOut(board.GP13)
sdcard = adafruit_sdcard.SDCard(spi, cs)
vfs = storage.VfsFat(sdcard)
storage.mount(vfs, mount_point)

file = mount_point + "/file3.txt"
with open(file, "w") as handle:
	handle.write("hello")
	handle.flush()
with open(file, "a") as handle:
	handle.write(", world")
	handle.flush()
with open(file, "r") as handle:
	print(handle.read())

建立目錄
見下文
建立目錄與 Bash 的 mkdir 指令相同
import busio, board, digitalio, adafruit_sdcard, storage, os

mount_point = "/sdcard"
spi = busio.SPI(board.GP14, MOSI = board.GP15, MISO = board.GP12)
cs = digitalio.DigitalInOut(board.GP13)
sdcard = adafruit_sdcard.SDCard(spi, cs)
vfs = storage.VfsFat(sdcard)
storage.mount(vfs, mount_point)

print(os.listdir(mount_point))
os.mkdir(mount_point + "/new-dir")
print(os.listdir(mount_point))

移動檔案
見下文
os 函式庫沒有 move 功能,但可以使用 rename 以重新命名的方式改動檔案的位置
import busio, board, digitalio, adafruit_sdcard, storage, os

mount_point = "/sdcard"
spi = busio.SPI(board.GP14, MOSI = board.GP15, MISO = board.GP12)
cs = digitalio.DigitalInOut(board.GP13)
sdcard = adafruit_sdcard.SDCard(spi, cs)
vfs = storage.VfsFat(sdcard)
storage.mount(vfs, mount_point)

dir = mount_point + "/new-dir"
file = "/file3.txt"
print(os.listdir(dir))
os.mkdir(mount_point + file, die + file)
print(os.listdir(dir))

刪除目錄及檔案
見下文
Python 同樣使用類似 Bash 的指令刪除檔案及目錄
但刪除檔目錄前必須將該目錄中的檔案全部刪除,否則會出現錯誤
import busio, board, digitalio, adafruit_sdcard, storage, os

mount_point = "/sdcard"
spi = busio.SPI(board.GP14, MOSI = board.GP15, MISO = board.GP12)
cs = digitalio.DigitalInOut(board.GP13)
sdcard = adafruit_sdcard.SDCard(spi, cs)
vfs = storage.VfsFat(sdcard)
storage.mount(vfs, mount_point)

dir = mount_point + "/new-dir"
file = dir + "/file3.txt"
print(os.listdir(dir))
os.remove(file)
print(os.listdir(dir))
print(os.listdir(mount_point))
os.rmdir(dir)
print(os.listdir(mount_point))

基本上,只要將 SD卡 掛載到 Raspberry Pi Pico
其餘的操作與一般檔案系統相同

SPI 模式

SD卡 具備 SD模式 及 SPI模式 ,但 Micro SD卡 模組只提供 SPI模式
SPI模式 只支援 FAT16 及 FAT32 的檔案系統
因此使用 SPI模式 讀寫 SD卡 前,必須先將 SD卡 格式化為 FAT16 或 FAT32 的檔案系統
(在下以 FAT32 為例子)

SD卡 的 SPI模 使用 模式0
  • SS引腳 為 高電壓 時為 閒置狀態
  • SCK引腳 為 低電壓 時採樣
  • 採樣次序 以 最高有效位 開始
便可以自行製作 SD卡的SPI訊號

def transfer(data):
	response = 0
	for i in range(7, -1, -1):
		mosi.value = (data & (1 << i)) > 0
		sck.value = False
		response |= (miso.value << i)
		sck.value = True

將 Micro SD卡 按裝到 Micro SD卡 模組,再將引腳連接到正確的線路
正式操作前,需要將 SS引腳 及 MOSI引腳 設定為 高電壓
再讓 SCK引腳 傳送最少16週期 的 時脈訊號,讓 SD卡 回復到 閒置狀態
引腳開始重覆最少16次結束
76543210
SS1
SCK101010101010101011
SDI010
SSSCKSDI
波紋時序圖
ss.value = True
sck.value = True
mosi.value = True
for i in range(128):
	sck.value = False
	sck.value = True
亦可以使用 SPI訊號
ss.value = True
sck.value = True
for i in range(16):
	transfer(0xFF)
SPI 指令
SD卡 提供一些 指令,讓 SD卡 透過 SPI模式 傳送指令到 SD卡

  1. 1位元組指令
    • 第7位元固定低電壓
    • 第6位元固定高電壓
    • 第5至0位元指令訊號
  2. 4位元組參數
    • 由最高位元組開始
      • 例如參數是 142857 = 0x00022E09
        參數1 = 0x00, 參數2 = 0x02, 參數3 = 0x2E, 參數4 = 0x09
  3. 1位元組CRC
    • 第7至1位元CRC訊號
    • 第0位元固定高電壓
引腳開始固定指令結束
543210
SS101
SCK101010101010101011
SDI01******
SDO11111111
SSSDISDO
波紋時序圖
def setCommand(command):
	transfer(0x40 | command)
引腳開始參數1至4結束
76543210
SS101
SCK101010101010101011
SDI********
SDO11111111
SDI
波紋時序圖
def setParameters(param1, param2, param3, param4):
	transfer(param1)
	transfer(param2)
	transfer(param3)
	transfer(param4)
引腳開始循環冗餘校驗(CRC)固定結束
6543210
SS101
SCK101010101010101011
SDI*******1
SDO11111111
SDI
波紋時序圖
def setCRC(crc):
	transfer((crc << 1) | 0x01)
引腳位元
474645444342414039:876543210
第0位元組第1至4位元組第5位元組
01指令參數1至4CRC1
SDI01***1
SDO0xFF0xFF0xFF
SSSCKSDISDO
傳送指令的完整波紋時序圖

SPI指令 使用 6位元,在下使用的其中 9個指令來讀寫 SD卡資料
6位元指令DECHEX功能需要令牌回應
位元組長度
543210
00000000x00重置設定0
00100080x08檢查電壓範圍4
00100190x09讀取 CSD 暫存器16
010000160x10修改讀取區塊大小0
010001170x11從區塊讀取資料1
011000240x18寫入資料到區塊0
101001410x29初始化0
110111550x37使用進階指令(ACMD)0
111010580x3A讀取 OCR 暫存器4
SPI 狀態
SD卡 接收到指令後,會傳回狀態,但 SD卡 可能需要一些時間才能傳回狀態
因此在傳回狀態前,需要傳送一定數量的 0xFF訊號 ,直至傳回狀態

SPI狀態 為 1位元組
  1. 第0至第n位元組
    • 等待傳回狀態
  2. 第n+1位元組
    • 傳回狀態
引腳重覆n次
開始等待結束
76543210
SS101
SCK101010101010101011
SDI11111111
SDO11111111
波紋時序圖
引腳開始狀態結束
76543210
SS101
SCK101010101010101011
SDI11111111
SDO0*******
SDO
波紋時序圖
def getStatus():
	for i in range(8):
		response = transfer(0xFF)
		if (response & 0x80) == 0:
			return response
	raise
01111111-> 8位元狀態資料
 ^^^^^^^
 |||||||
 ||||||+-> 閒置狀態
 |||||+--> 清除重置
 ||||+---> 不合法指令
 |||+----> 指令CRC錯誤
 ||+-----> 清除序列錯誤
 |+------> 地址錯誤
 +-------> 參數錯誤
正常情況下,主要傳回 閒置(0x01) 或 運作(0x00) 的狀態
引腳第0至n位元組第n+1位元組
等待狀態
SDI0xFF0xFF
SDO0xFF0*
SSSCKSDISDO
傳回狀態的完整波紋時序圖

SPI 回應

SD卡 傳回狀態後,部分指令還會傳回回應

SPI回應 為 n位元組
  1. 第0至第n位元組
    • 回應內容
引腳重覆n次
開始回應結束
76543210
SS101
SCK101010101010101011
SDI11111111
SDO********
SDO
波紋時序圖
def getResponses(length):
	responses = []
	for i in range(length):
		responses.append(transfer(0xFF))
	return responses

SPI 讀取資料

SD卡 讀取資料的指令
  1. 第0至第n位元組
    • 等待
  2. 第n+1位元組
    • 傳回令牌
      • 傳回0xFE 為 資料令牌,適合 0x11, 0x12, 0x18 指令使用 (示範)
      • 傳回0xFC 為 資料令牌,適合 0x19 指令使用
      • 傳回0xFD 為 停止令牌,適合 0x19 指令使用
  3. 第n+2至第m位元組
    • 讀取資料
  4. 第m+1至第m+2位元組
    • 傳回CRC
引腳重覆n次
開始等待結束
76543210
SS101
SCK101010101010101011
SDI11111111
SDO11111111
引腳開始令牌結束
76543210
SS101
SCK101010101010101011
SDI11111111
SDO11111110
SDO
波紋時序圖
def isToken():
	for i in range(8):
		response = transfer(0xFF)
		if response == 0xFE:
			return True
	return False
引腳根據數量重覆
開始資料結束
76543210
SS101
SCK101010101010101011
SDI11111111
SDO********
波紋時序圖
def getData(length):
	return getResponses(length)
引腳重覆2次
開始CRC結束
76543210
SS101
SCK101010101010101011
SDI11111111
SDO********
波紋時序圖
def getCRC():
	return getResponses(2)
引腳第0至n位元組第n+1位元組第n+2至m位元組第m+1至m+2位元組
等待令牌資料CRC
SDI0xFF0xFF0xFF0xFF
SDO0xFF0xFE**
SSSCKSDISDO
讀取資料的完整波紋時序圖

SPI 寫入資料

除了讀取資料,亦可以寫入資料
  1. 第0位元組
    • 提交有效令牌(0xFE)
  2. 第1至第n位元組
    • 寫入資料
  3. 第n+1至第n+2位元組
    • 傳回CRC
  4. 第n+3至第m位元組
    • 等待回應
  5. 第m+1位元組
    • 傳回回應
      • 傳回0x05 為 資料有效
      • 傳回0x0B 為 資料CRC錯誤,資料無效
      • 傳回0x0D 為 資料寫入錯誤,資料無效
  6. 第m+2至z位元組
    • 等待完成
引腳開始令牌結束
76543210
SS101
SCK101010101010101011
SDI11111110
SDO11111111
SDI
波紋時序圖
def setToken():
	transfer(0xFE)
引腳根據數量重覆
開始資料結束
76543210
SS101
SCK101010101010101011
SDI********
SDO11111111
SDI
波紋時序圖
def setData(data):
	for d in data:
		transfer(d)
引腳重覆2次
開始CRC結束
76543210
SS101
SCK101010101010101011
SDI11111111
SDO********
波紋時序圖
引腳重覆n次
開始等待結束
76543210
SS101
SCK101010101010101011
SDI11111111
SDO11111111
波紋時序圖
getCRC()
引腳開始回應結束
76543210
SS101
SCK101010101010101011
SDI11111111
SDO0000***1
SDO
波紋時序圖
def isDataAccept():
	for i in range(8):
		response = transfer(0xFF)
		if response == 0x05:
			return True
	return False
引腳重覆n次
開始狀態結束
76543210
SS101
SCK101010101010101011
SDI11111111
SDO00000000
SDO
波紋時序圖
def isFinish():
	response = 0
	while response == 0:
		response = transfer(0xFF)
引腳第0位元組第1至n位元組第n+1至n+2位元組第n+3至m位元組第m+1位元組第m+2至z位元組
令牌資料CRC等待回應回應等待完成
SDI0xFE*0xFF0xFF0xFF0xFF
SDO0xFF0xFF*0xFF0x050x00
SSSCKSDISDO
寫入資料的完整波紋時序圖

起動步驟

SD卡 起動步驟大概為
SD CardPower OnCMD0Idle?SD CardNot FoundCMD8Idle?SD Card VersionNot RecongizedTry?SD CardNot InitializedCMD55ACMD41Idle?CMD58Idle?SD CardCannot Read OCRCMD9Idle?SD CardNo ResponseCMD16Idle?SD Card CannotSet Block SizeTo 512 BytesSD CardInitializedAnd RunningNoYesNoYesNoYesYesNoYesNoYesNoYesNo
if (CMD0 is not idle) {
	stderr("SD Card Not Found");
	exit();
}
if (CMD8 is not idle) {
	stderr("SD Card Not Recognized");
	exit();
}
isIdle = True;
loop (n times) {
	CMD55;
	if (ACMD41 is not idle) {
		isIdle = False
		break;
	}
}
if (isIdle) {
	stderr("SD Card Not Initialized");
	exit();
}
if (CMD58 is idle) {
	stderr("SD Card Cannot Read OCR");
	exit();
}
if (CMD9 is idle) {
	stderr("SD Card No Response");
	exit();
}
if (CMD16 is idle) {
	stderr("SD Card Cannot Set Block Size To 512 Bytes");
	exit();
}
stdout("SD Card Initialized And Running");

狀態步驟

SD卡 狀態步驟大概為
StartTry?SD CardNot ReadyStatusNot 0x80?Status & 0x01IdleStatus & 0x02Erase ResetStatus & 0x04Illegel CommandStatus & 0x08Command CRC ErrorStatus & 0x10Erase Sequense ErrorStatus & 0x20Address ErrorStatus & 0x40Parameter ErrorNoYesYesNo
isValid = False;
loop (n times) {
	if (Status != 0x80) {
		if (Status & 0x01) {
			isValid = True;
			stderr("Idle");
		}
		if (Status & 0x02) {
			stderr("Erase Reset");
			exit();
		}
		if (Status & 0x04) {
			stderr("Illegel Command");
			exit();
		}
		if (Status & 0x08) {
			stderr("Command CRC Error");
			exit();
		}
		if (Status & 0x10) {
			stderr("Erase Sequense Error");
			exit();
		}
		if (Status & 0x20) {
			stderr("Address Error");
			exit();
		}
		if (Status & 0x40) {
			stderr("Parameter Error");
			exit();
		}
	}
}
if (!isValid) {
	stderr("SD Card Not Ready");
	exit();
}

讀取步驟

SD卡 讀取步驟大概為
StartCMD17Idle?SD CardCannot Read BlockToken0xFE?Read BlockFrom SectorCRCError TokenB000XXXXXError & 0x01Unknown ErrorError & 0x02CC ErrorError & 0x04Card ECC FailError & 0x08Out Of RangeError & 0x10Card Is LockedYesNoYesNo
if (CMD17 is idle) {
	stderr("SD Card Cannot Read Block");
	exit();
}
if (token != 0xFE) {
	if (error & 0x01) {
		stderr("Unknown Error");
	}
	if (error & 0x02) {
		stderr("CC Error");
	}
	if (error & 0x04) {
		stderr("Card ECC Fail");
	}
	if (error & 0x08) {
		stderr("Out Of Range");
	}
	if (error & 0x10) {
		stderr("Card Is Locked");
	}
	exit();
}
data = readblock();
read 2 bytes of CRC;

寫入步驟

SD卡 寫入步驟大概為
StartCMD24Idle?SD CardCannot Write BlockSend 0xFE TokenWrite BlockTo SectorCRCResponse0x05?Writing0x00?EndResponse0x0B?CRC ErrorResponse0x0C?Write ErrorUnknown ErrorYesNoYesYesNoNoYesNoYesNo
if (CMD24 is idle) {
	stderr("SD Card Cannot Write Block");
	exit();
}
send 0xFE token;
writeblock(data);
read 2 bytes of CRC;
isValid = True;
loop (n times) {
	if (response == 0x05) {
		isValid = False
		break;
	} else if (response == 0x0B) {
		stderr("CRC Error");
		exit();
	} else if (response == 0x0C) {
		stderr("Write Error");
		exit();
	}
}
if (!isValid) {
	stderr("Unknown Error");
	exit();
}
Writing = 0x00;
while (Writing == 0x00) {
	Writing = stillWriting();
}

擴展類別

Python 能夠使用 storage 類別的 VfsFat 功能
來掛載格式化為 FAT16 或 FAT32 的 SD卡 的 檔案系統
但類別還需要實現 count(), readblocks(start, buffer), writeblocks(start, buffer)
才能讓 os 類別 或 IO 功能來存取 SD卡 ,即是:
class MySDCard:
	def __init(self):
#		constructor of this class
	def count(self):
#		implement this method
	def readblocks(self, start, buffer):
#		implement this method
	def writeblocks(self, start, buffer):
#		implement this method
在下將程式碼上載到 https://bitbucket.org/hkgoldenmra/asdcard
閣下有興趣可以自行下載測試及使用

補充資料

SD卡 類型
常見SD卡主要有3種,但引腳用途相同
SD卡 引腳
123456789
編號SD模式引腳SPI模式引腳
1DAT3SS
2CMDSDI
3GND
4VCC
5SCK
6GND
7DAT0SDO
8DAT1NC
9DAT2NC

Mini SD卡 引腳
9123101145678
編號SD模式引腳SPI模式引腳
1DAT3SS
2CMDSDI
3GND
4VCC
5SCK
6GND
7DAT0SDO
8DAT1NC
9DAT2NC
10NCNC
11NCNC

Micro SD卡 引腳
12345678
編號SD模式SPI模式
1DAT2NC
2DAT3SS
3CMDSDI
4VCC
5SCK
6GND
7DAT0SDO
8DAT1NC

起動及檔案讀寫流程
使用 微控制器 控制 SD卡需要模疑掛載程序
主開機紀錄 (Master Boot Record)
主開機紀錄用來起動磁區及保存最多4個磁碟分割表
列號欄號
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F0123456789ABCDEF
00000000
00000010
00000020
00000030
00000040
00000050
00000060
00000070
00000080
00000090
000000A0
000000B0
000000C0
000000D0
000000E0
000000F0
00000100
00000110
00000120
00000130
00000140
00000150
00000160
00000170
00000180
00000190
000001A0
000001B0
000001C0
000001D0
000001E0
000001F0
FA B8 00 10 8E D0 BC 00 B0 B8 00 00 8E D8 8E C0
FB BE 00 7C BF 00 06 B9 00 02 F3 A4 EA 21 06 00
00 BE BE 07 38 04 75 0B 83 C6 10 81 FE FE 07 75
F3 EB 16 B4 02 B0 01 BB 00 7C B2 80 8A 74 01 8B
4C 02 CD 13 EA 00 7C 00 00 EB FE 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
1E FC D2 11 00 00 00 04
01 04 0C FE C2 FF 00 08 00 00 00 28 B1 03 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00
55 AA
................
...|.........!..
....8.u........u
.........|...t..
L.....|.........
................
................
................
................
................
................
................
................
..........
.. . . . .
................
................
................
................
................
................
................
................
................
................
................
................
................
........
.... .. ..
...........(..
..
..............
..
..............
..
..............
U.
FAT起動磁區 (FAT Boot Sector)
保存磁區訊息、掛載方法
列號欄號
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F0123456789ABCDEF
00100000
00100010
00100020
00100030
00100040
00100050
00100060
00100070
00100080
00100090
001000A0
001000B0
001000C0
001000D0
001000E0
001000F0
00100100
00100110
00100120
00100130
00100140
00100150
00100160
00100170
00100180
00100190
001001A0
001001B0
001001C0
001001D0
001001E0
001001F0
EB 58 90 6D 6B 66 73 2E 66 61 74 00 02 20 20 00
02 00 00 00 00 F8 00 00 20 00 40 00 00 08 00 00
00 28 B1 03 20 3B 00 00 00 00 00 00 02 00 00 00
01 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00
80 00 29 7F EE B3 05 5A 45 52 4F 46 49 4C 4C 45
44 20
46 41 54 33 32 20 20 20 0E 1F BE 77 7C AC
22 C0 74 0B 56 B4 0E BB 07 00 CD 10 5E EB F0 32
E4 CD 16 CD 19 EB FE 54 68 69 73 20 69 73 20 6E
6F 74 20 61 20 62 6F 6F 74 61 62 6C 65 20 64 69
73 6B 2E 20 20 50 6C 65 61 73 65 20 69 6E 73 65
72 74 20 61 20 62 6F 6F 74 61 62 6C 65 20 66 6C
6F 70 70 79 20 61 6E 64 0D 0A 70 72 65 73 73 20
61 6E 79 20 6B 65 79 20 74 6F 20 74 72 79 20 61
67 61 69 6E 20 2E 2E 2E 20 0D 0A 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00
55 AA
.X. mkfs.fat .. .
. .. .. . .. . @. ....
.(;. ;.. .. .. ....
.. .. ............
. . ) ..q. ZEROFILLE
D
FAT32 ...w|.
".t.V.......^..2
.......This is n
ot a bootable di
sk.  Please inse
rt a bootable fl
oppy and..press
any key to try a
gain ... .......
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
..............
U.
檔案系統資訊磁區 (File System Information Sector)
保存叢集總數資訊、下個叢集的位置
列號欄號
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F0123456789ABCDEF
00100200
00100210
00100220
00100230
00100240
00100250
00100260
00100270
00100280
00100290
001002A0
001002B0
001002C0
001002D0
001002E0
001002F0
00100300
00100310
00100320
00100330
00100340
00100350
00100360
00100370
00100380
00100390
001003A0
001003B0
001003C0
001003D0
001003E0
001003F0
52 52 61 41 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00
72 72 41 61 8C 85 1D 00 02 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 AA
RRaA ............
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
....
rrAa .... ....
.............. U.
檔案目錄表 (File Directory Table)
保存根目錄中的項目資料,包括檔案及子目錄
列號欄號
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F0123456789ABCDEF
00FCC000
00FCC010
00FCC020
00FCC030
00FCC040
00FCC050
00FCC060
00FCC070
00FCC080
00FCC090
00FCC0A0
00FCC0B0
00FCC0C0
00FCC0D0
00FCC0E0
00FCC0F0
00FCC100
00FCC110
00FCC120
00FCC130
00FCC140
00FCC150
00FCC160
00FCC170
00FCC180
00FCC190
00FCC1A0
00FCC1B0
00FCC1C0
00FCC1D0
00FCC1E0
00FCC1F0
5A 45 52 4F 46 49 4C 4C 45 44 20 08 00 00 FB 84
2C 55 2C 55 00 00 FB 84 2C 55 00 00 00 00 00 00
46 49 4C 45 20 20 20 20 54 58 54 20 18 00 07 A3
2C 55 00 00 00 00 09 A3 2C 55 03 00 11 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
ZEROFILLED . . . ..
,U ,U .. .. ,U .. ....
FILE    TXT ....
,U..............
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
檔案分配表 (File Allocation Table)
保存下個鏈式叢集的位置、叢集的狀態
列號欄號
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F0123456789ABCDEF
00104000
00104010
00104020
00104030
00104040
00104050
00104060
00104070
00104080
00104090
001040A0
001040B0
001040C0
001040D0
001040E0
001040F0
00104100
00104110
00104120
00104130
00104140
00104150
00104160
00104170
00104180
00104190
001041A0
001041B0
001041C0
001041D0
001041E0
001041F0
F8 FF FF 0F FF FF FF 0F F8 FF FF 0F FF FF FF 0F
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
.... .... .... ....
.... ............
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
資料區域 (Data Area)
保存檔案資料的空間
列號欄號
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F0123456789ABCDEF
00FE0000
00FE0010
00FE0020
00FE0030
00FE0040
00FE0050
00FE0060
00FE0070
00FE0080
00FE0090
00FE00A0
00FE00B0
00FE00C0
00FE00D0
00FE00E0
00FE00F0
00FE0100
00FE0110
00FE0120
00FE0130
00FE0140
00FE0150
00FE0160
00FE0170
00FE0180
00FE0190
00FE01A0
00FE01B0
00FE01C0
00FE01D0
00FE01E0
00FE01F0
49 20 61 6D 20 48 4B 47 6F 6C 64 65 6E 4D 72 2E
41
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
I am HKGoldenMr.
A
...............
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
如果要讓 SD卡 能讓其他宿主裝置辨識,必須建構:
磁區磁區位置磁區位元組位置
DECHEX
主開機紀錄000x000000
FAT起動磁區0+offset0+offset0x000000+offset
檔案系統資訊磁區1+offset512+offset0x000200+offset
檔案分配表32+offset16384+offset0x004000+offset

offset 是指位置偏移
每分割位置並不相同,因此真實位置可能會稍有偏移
以在下的情況,FAT32起動磁區 的位置為 0x100000 ,即是偏移位置為 0x100000

由於這個專案令在下重新學習檔案系統的知識及技術
令在下回憶經常有一些 SD卡 掛載後無法使用
因此想利用這個技術強行重建檔案系統,希望能幫功資料恢復用途

見下文
見下文
見下文
見下文
在下使用 虛擬機 將 虛擬硬碟 以 MBR方式低階格式化,將資料完全刪除

見下文
見下文
見下文
第1分割區格式化為 FAT32
(裝置顯示 /dev/sda1 表示 第1硬碟的第1分割區)

見下文
並將 0 , 2048 , 2049 , 2080 的磁區複製
drive="/dev/sdb"
block_size="512"
dd if="${drive}" bs="${block_size}" count="1" skip="0" of="mbr.img"
dd if="${drive}" bs="${block_size}" count="1" skip="2080" of="fat32-table.img"
dd if="${drive}" bs="${block_size}" count="1" skip="2049" of="fsinfo-sector.img"
dd if="${drive}" bs="${block_size}" count="1" skip="2048" of="fat32-bs.img"

由於 /dev/sdb1 第0磁區 等於 /dev/sdb 第2048磁區,因此可以改寫成
drive="/dev/sdb"
block_size="512"
dd if="${drive}" bs="${block_size}" count="1" skip="0" of="mbr.img"
dd if="${drive}1" bs="${block_size}" count="1" skip="32" of="fat32-table.img"
dd if="${drive}1" bs="${block_size}" count="1" skip="1" of="fsinfo-sector.img"
dd if="${drive}1" bs="${block_size}" count="1" skip="0" of="fat32-bs.img"
見下文
以 無磁碟分割方法低階格式化 將資料完全刪除
(裝置顯示 /dev/sda 表示 第1硬碟不分割)

見下文
將複製的磁區取代原本的磁區
drive="/dev/sdb"
block_size="512"
dd of="${drive}" bs="${block_size}" count="1" seek="0" if="mbr.img"
dd of="${drive}" bs="${block_size}" count="1" seek="2080" if="fat32-table.img"
dd of="${drive}" bs="${block_size}" count="1" seek="2049" if="fsinfo-sector.img"
dd of="${drive}" bs="${block_size}" count="1" seek="2048" if="fat32-bs.img"
drive="/dev/sdb"
block_size="512"
dd of="${drive}" bs="${block_size}" count="1" seek="0" if="mbr.img"
dd of="${drive}1" bs="${block_size}" count="1" seek="32" if="fat32-table.img"
dd of="${drive}1" bs="${block_size}" count="1" seek="1" if="fsinfo-sector.img"
dd of="${drive}1" bs="${block_size}" count="1" seek="0" if="fat32-bs.img"

見下文
結果能將 虛擬硬碟 強行建立 FAT32 檔案系統
(格式化後 UUID 必定不同)

見下文
在下嘗試使用 Raspberry Pi Pico 實戰測試

見下文
將 SD卡 低階格式化
(將實體 SD卡 低階格式化需要很長時間,32GB 需要大約 1小時)

mbr.img , fat32-bs.img , fsinfo-sector.img , fat32-table.img 複製到 Raspberry Pi Pico
再使用 open 功能,將檔案的二進制碼寫入到 SD卡
block_files = {
	0: "/mbr.img",
	2080: "/fat32-table.img",
	2049: "/fsinfo-bs.img",
	2048: "/fat32-bs.img"
}
for address in block_files:
	handle = open(block_files[address], "rb")
	sdcard.writeblocks(address, handle.read())
	handle.close()

在下直接使用 writeblocks 功能將資料寫入

見下文
或將 主開機紀錄 FAT起動磁區 檔案系統資訊磁區 檔案分配表 的十六進制碼
改成 python二進制碼 寫法亦可,即是 b"\x00" ,便可以減省附帶4個檔案
blocks = {
	0: b"\xFA\xB8\x00\x10\x8E\xD0\xBC\x00\xB0\xB8\x00\x00\x8E\xD8\x8E\xC0\xFB\xBE\x00\x7C\xBF\x00\x06\xB9\x00\x02\xF3\xA4\xEA\x21\x06\x00\x00\xBE\xBE\x07\x38\x04\x75\x0B\x83\xC6\x10\x81\xFE\xFE\x07\x75\xF3\xEB\x16\xB4\x02\xB0\x01\xBB\x00\x7C\xB2\x80\x8A\x74\x01\x8B\x4C\x02\xCD\x13\xEA\x00\x7C\x00\x00\xEB\xFE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1E\xFC\xD2\x11\x00\x00\x00\x04\x01\x04\x0C\xFE\xC2\xFF\x00\x08\x00\x00\x00\x28\xB1\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x55\xAA",
	2080: b"\xF8\xFF\xFF\x0F\xFF\xFF\xFF\x0F\xF8\xFF\xFF\x0F\xFF\xFF\xFF\x0F\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
	2049: b"\x52\x52\x61\x41\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x72\x72\x41\x61\x8C\x85\x1D\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x55\xAA",
	2048: b"\xEB\x58\x90\x6D\x6B\x66\x73\x2E\x66\x61\x74\x00\x02\x20\x20\x00\x02\x00\x00\x00\x00\xF8\x00\x00\x20\x00\x40\x00\x00\x08\x00\x00\x00\x28\xB1\x03\x20\x3B\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x00\x29\x7F\xEE\xB3\x05\x5A\x45\x52\x4F\x46\x49\x4C\x4C\x45\x44\x20\x46\x41\x54\x33\x32\x20\x20\x20\x0E\x1F\xBE\x77\x7C\xAC\x22\xC0\x74\x0B\x56\xB4\x0E\xBB\x07\x00\xCD\x10\x5E\xEB\xF0\x32\xE4\xCD\x16\xCD\x19\xEB\xFE\x54\x68\x69\x73\x20\x69\x73\x20\x6E\x6F\x74\x20\x61\x20\x62\x6F\x6F\x74\x61\x62\x6C\x65\x20\x64\x69\x73\x6B\x2E\x20\x20\x50\x6C\x65\x61\x73\x65\x20\x69\x6E\x73\x65\x72\x74\x20\x61\x20\x62\x6F\x6F\x74\x61\x62\x6C\x65\x20\x66\x6C\x6F\x70\x70\x79\x20\x61\x6E\x64\x0D\x0A\x70\x72\x65\x73\x73\x20\x61\x6E\x79\x20\x6B\x65\x79\x20\x74\x6F\x20\x74\x72\x79\x20\x61\x67\x61\x69\x6E\x20\x2E\x2E\x2E\x20\x0D\x0A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x55\xAA"
}
for address in blocks:
	sdcard.writeblocks(address, blocks[address])
見下文
寫入資料後,讓電腦掛載,結果同樣成功
即是這種強行建立 FAT32 檔案系統的方法,可以達到在下期望的效果
下在將 SD卡 格式化為 FAT32 的程式碼加到的 函式庫 中

但由於在下的 SD卡 ,因此在下複製的磁區資料未必與其他 SD卡 相容
所以閣下使用前仍然需要自行修改成相符合的資料才使用,否則可能會破壞閣下的 SD卡 的資料

見下文
如果不使用磁碟分割,可以直接將 FAT32起動磁區 、 檔案系統資訊磁區 、 檔案分配表
順移至 0, 1, 32 的磁區位置,便可以使用整個分割

由於使用 主開機紀錄 的方法需要規劃磁碟分割的位置
由於會佔用部分儲存空間,而且並非所有分割的位置及數量都相同,備份及修復反而會困難
因此如果想 SD卡 用盡整個儲存空間,便不要使用 主開機紀錄 ,而且更方便修復資料

總結

接駁線路時不慎將 VCC 及 GND 倒轉接駁, 導致 Raspberry Pi Pico 及 電壓穩定器 異常灼熱
幸好早發現,否則可能會燒毀 Raspberry Pi Pico 或 電壓穩定器
還有將 微控制器 及 Micro SD卡 模組 的 MOSI 及 MISO 倒轉接駁

在下發現當使用預設函式庫的 busio.SPI , 不使指定 MOSI 及 MISO 引腳時
必須使用 Raspberry Pi Pico 的 MOSI 及 MISO 引腳,否則會發生 ValueError: Invalid pins 錯誤
雖然線路連接上會受到一定限制,但因為使用硬件提供的 SPI 因此速度會比較快

不過由於 CircuitPython 的 busio.SPI 必須使用指定引腳,在下對這種限制感到不滿
因此在下如過往相同,花時間學習 讀寫SD卡資料 的方法
在下尋找資料 及 嘗試理解 CircuitPython 的 adafruit_sdcard.py 及 MicroPython 的 sdcard.py
以 adafruit_sdcard.py 為基礎,自行製作 讀寫SD卡資料 的類別
執行主程式前,在 adafruit_sdcard.py 需要執行的功能中,加上大量 print 以顯示執行後的結果

雖然花了很多時間,但最終能夠製作自己 讀寫SD卡 的類別
而且在製作途中還學習到 FAT檔案系統 的操作知識及修復技巧

另外在下發現 FAT32 將檔案名的第一個字元修改為 0xE5 便當作刪除檔案
亦即是將檔案名改回原本的字元便能修復檔案
列號欄號
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F0123456789ABCDEF
00FCC000
00FCC010
00FCC020
00FCC030
00FCC040
00FCC050
00FCC060
00FCC070
00FCC080
00FCC090
00FCC0A0
00FCC0B0
00FCC0C0
00FCC0D0
00FCC0E0
00FCC0F0
00FCC100
00FCC110
00FCC120
00FCC130
00FCC140
00FCC150
00FCC160
00FCC170
00FCC180
00FCC190
00FCC1A0
00FCC1B0
00FCC1C0
00FCC1D0
00FCC1E0
00FCC1F0
5A 45 52 4F 46 49 4C 4C 45 44 20 08 00 00 FB 84
2C 55 2C 55 00 00 FB 84 2C 55 00 00 00 00 00 00
E5 49 4C 45 20 20 20 20 54 58 54 20 18 00 07 A3
2C 55 00 00 00 00 09 A3 2C 55 03 00 11 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
ZEROFILLED .....
,U,U....,U......
. ILE    TXT ....
,U..............
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................

雖然這些知識可能已經有文件資料,但靠自己學習及發現,比單純查資料印象更深刻

參考資料