2022-09-22

SD卡 寫入及讀取資料

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

Micro SD卡 模組外觀

見下文
Micro SD卡 模組 正面

見下文
Micro SD卡 模組 背面

Micro SD卡 模組引腳

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

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卡 ,必須使用 FAT16FAT32 檔案系統
因此使用 SDSDHC 制式的 SD卡 能夠即時使用
而預設使用 exFATSDXCSDUC 則不能即時使用
必須將 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.pyadafruit_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次 結束
7 6 5 4 3 2 1 0
SS 1
SCK 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 1
SDI 0 1 0
SS SCK SDI
波紋時序圖
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位元固定高電壓
引腳 開始 固定 指令 結束
5 4 3 2 1 0
SS 1 0 1
SCK 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 1
SDI 0 1 * * * * * *
SDO 1 1 1 1 1 1 1 1
SS SDI SDO
波紋時序圖
def setCommand(command):
	transfer(0x40 | command)
引腳 開始 參數1至4 結束
7 6 5 4 3 2 1 0
SS 1 0 1
SCK 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 1
SDI * * * * * * * *
SDO 1 1 1 1 1 1 1 1
SDI
波紋時序圖
def setParameters(param1, param2, param3, param4):
	transfer(param1)
	transfer(param2)
	transfer(param3)
	transfer(param4)
引腳 開始 循環冗餘校驗(CRC) 固定 結束
6 5 4 3 2 1 0
SS 1 0 1
SCK 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 1
SDI * * * * * * * 1
SDO 1 1 1 1 1 1 1 1
SDI
波紋時序圖
def setCRC(crc):
	transfer((crc << 1) | 0x01)
引腳 位元
47 46 45 44 43 42 41 40 39:8 7 6 5 4 3 2 1 0
第0位元組 第1至4位元組 第5位元組
0 1 指令 參數1至4 CRC 1
SDI 0 1 * * * 1
SDO 0xFF 0xFF 0xFF
SS SCK SDI SDO
傳送指令的完整波紋時序圖

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

SPI狀態 為 1位元組
  1. 第0至第n位元組
    • 等待傳回狀態
  2. 第n+1位元組
    • 傳回狀態
引腳 重覆n次
開始 等待 結束
7 6 5 4 3 2 1 0
SS 1 0 1
SCK 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 1
SDI 1 1 1 1 1 1 1 1
SDO 1 1 1 1 1 1 1 1
波紋時序圖
引腳 開始 狀態 結束
7 6 5 4 3 2 1 0
SS 1 0 1
SCK 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 1
SDI 1 1 1 1 1 1 1 1
SDO 0 * * * * * * *
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位元組
等待 狀態
SDI 0xFF 0xFF
SDO 0xFF 0 *
SS SCK SDI SDO
傳回狀態的完整波紋時序圖

SPI 回應

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

SPI回應 為 n位元組
  1. 第0至第n位元組
    • 回應內容
引腳 重覆n次
開始 回應 結束
7 6 5 4 3 2 1 0
SS 1 0 1
SCK 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 1
SDI 1 1 1 1 1 1 1 1
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次
開始 等待 結束
7 6 5 4 3 2 1 0
SS 1 0 1
SCK 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 1
SDI 1 1 1 1 1 1 1 1
SDO 1 1 1 1 1 1 1 1
引腳 開始 令牌 結束
7 6 5 4 3 2 1 0
SS 1 0 1
SCK 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 1
SDI 1 1 1 1 1 1 1 1
SDO 1 1 1 1 1 1 1 0
SDO
波紋時序圖
def isToken():
	for i in range(8):
		response = transfer(0xFF)
		if response == 0xFE:
			return True
	return False
引腳 根據數量重覆
開始 資料 結束
7 6 5 4 3 2 1 0
SS 1 0 1
SCK 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 1
SDI 1 1 1 1 1 1 1 1
SDO * * * * * * * *
波紋時序圖
def getData(length):
	return getResponses(length)
引腳 重覆2次
開始 CRC 結束
7 6 5 4 3 2 1 0
SS 1 0 1
SCK 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 1
SDI 1 1 1 1 1 1 1 1
SDO * * * * * * * *
波紋時序圖
def getCRC():
	return getResponses(2)
引腳 第0至n位元組 第n+1位元組 第n+2至m位元組 第m+1至m+2位元組
等待 令牌 資料 CRC
SDI 0xFF 0xFF 0xFF 0xFF
SDO 0xFF 0xFE * *
SS SCK SDI SDO
讀取資料的完整波紋時序圖

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位元組
    • 等待完成
引腳 開始 令牌 結束
7 6 5 4 3 2 1 0
SS 1 0 1
SCK 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 1
SDI 1 1 1 1 1 1 1 0
SDO 1 1 1 1 1 1 1 1
SDI
波紋時序圖
def setToken():
	transfer(0xFE)
引腳 根據數量重覆
開始 資料 結束
7 6 5 4 3 2 1 0
SS 1 0 1
SCK 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 1
SDI * * * * * * * *
SDO 1 1 1 1 1 1 1 1
SDI
波紋時序圖
def setData(data):
	for d in data:
		transfer(d)
引腳 重覆2次
開始 CRC 結束
7 6 5 4 3 2 1 0
SS 1 0 1
SCK 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 1
SDI 1 1 1 1 1 1 1 1
SDO * * * * * * * *
波紋時序圖
引腳 重覆n次
開始 等待 結束
7 6 5 4 3 2 1 0
SS 1 0 1
SCK 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 1
SDI 1 1 1 1 1 1 1 1
SDO 1 1 1 1 1 1 1 1
波紋時序圖
getCRC()
引腳 開始 回應 結束
7 6 5 4 3 2 1 0
SS 1 0 1
SCK 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 1
SDI 1 1 1 1 1 1 1 1
SDO 0 0 0 0 * * * 1
SDO
波紋時序圖
def isDataAccept():
	for i in range(8):
		response = transfer(0xFF)
		if response == 0x05:
			return True
	return False
引腳 重覆n次
開始 狀態 結束
7 6 5 4 3 2 1 0
SS 1 0 1
SCK 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 1
SDI 1 1 1 1 1 1 1 1
SDO 0 0 0 0 0 0 0 0
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 等待回應 回應 等待完成
SDI 0xFE * 0xFF 0xFF 0xFF 0xFF
SDO 0xFF 0xFF * 0xFF 0x05 0x00
SS SCK SDI SDO
寫入資料的完整波紋時序圖

起動步驟

SD卡 起動步驟大概為
SD Card Power On CMD0 Idle? SD Card Not Found CMD8 Idle? SD Card Version Not Recongized Try? SD Card Not Initialized CMD55 ACMD41 Idle? CMD58 Idle? SD Card Cannot Read OCR CMD9 Idle? SD Card No Response CMD16 Idle? SD Card Cannot Set Block Size To 512 Bytes SD Card Initialized And Running No Yes No Yes No Yes Yes No Yes No Yes No Yes No
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卡 狀態步驟大概為
Start Try? SD Card Not Ready Status Not 0x80? Status & 0x01 Idle Status & 0x02 Erase Reset Status & 0x04 Illegel Command Status & 0x08 Command CRC Error Status & 0x10 Erase Sequense Error Status & 0x20 Address Error Status & 0x40 Parameter Error No Yes Yes No
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卡 讀取步驟大概為
Start CMD17 Idle? SD Card Cannot Read Block Token 0xFE? Read Block From Sector CRC Error Token B000XXXXX Error & 0x01 Unknown Error Error & 0x02 CC Error Error & 0x04 Card ECC Fail Error & 0x08 Out Of Range Error & 0x10 Card Is Locked Yes No Yes No
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卡 寫入步驟大概為
Start CMD24 Idle? SD Card Cannot Write Block Send 0xFE Token Write Block To Sector CRC Response 0x05? Writing 0x00? End Response 0x0B? CRC Error Response 0x0C? Write Error Unknown Error Yes No Yes Yes No No Yes No Yes No
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卡 引腳
1 2 3 4 5 6 7 8 9
編號 SD模式引腳 SPI模式引腳
1 DAT3 SS
2 CMD SDI
3 GND
4 VCC
5 SCK
6 GND
7 DAT0 SDO
8 DAT1 NC
9 DAT2 NC

Mini SD卡 引腳
9 1 2 3 10 11 4 5 6 7 8
編號 SD模式引腳 SPI模式引腳
1 DAT3 SS
2 CMD SDI
3 GND
4 VCC
5 SCK
6 GND
7 DAT0 SDO
8 DAT1 NC
9 DAT2 NC
10 NC NC
11 NC NC

Micro SD卡 引腳
1 2 3 4 5 6 7 8
編號 SD模式 SPI模式
1 DAT2 NC
2 DAT3 SS
3 CMD SDI
4 VCC
5 SCK
6 GND
7 DAT0 SDO
8 DAT1 NC

起動及檔案讀寫流程
使用 微控制器 控制 SD卡需要模疑掛載程序
主開機紀錄 (Master Boot Record)
主開機紀錄用來起動磁區及保存最多4個磁碟分割表
列號 欄號
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 0123456789ABCDEF
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 0F 0123456789ABCDEF
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 0F 0123456789ABCDEF
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 0F 0123456789ABCDEF
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 0F 0123456789ABCDEF
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 0F 0123456789ABCDEF
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卡 能讓其他宿主裝置辨識,必須建構:
磁區 磁區位置 磁區位元組位置
DEC HEX
主開機紀錄 0 0 0x000000
FAT起動磁區 0+offset 0+offset 0x000000+offset
檔案系統資訊磁區 1+offset 512+offset 0x000200+offset
檔案分配表 32+offset 16384+offset 0x004000+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 0F 0123456789ABCDEF
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..............
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................

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

參考資料

沒有留言 :

張貼留言