之前使用 Raspberry Pi Pico 控制 LCD熒幕,本來想將讓 LCD 顯示一些連續圖片
但由於 Raspberry Pi Pico 內置儲存空間只有 1MiB,無法儲存太多資料
因此在下需要 Raspberry Pi Pico 能存取一些外置儲存媒體
令能夠 Raspberry Pi Pico 執行更多操作或讀取資料
但由於 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卡
由於 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 或 SDHC 制式的 SD卡 能夠即時使用
而預設使用 exFAT 的 SDXC 及 SDUC 則不能即時使用
必須將 SD卡 格式化為 FAT16 或 FAT32 才能使用 SPI 存取
掛載點
很多網上教學都指定 /sd 或 /SD 這個掛載點名稱
但實際並沒有限制掛載點名稱,只要符合檔案命名規則,及 SD卡 沒有重覆的路徑,便可以掛載
但實際並沒有限制掛載點名稱,只要符合檔案命名規則,及 SD卡 沒有重覆的路徑,便可以掛載
檔案名稱
由於必須使用 FAT16 或 FAT32 檔案系統,而這兩個檔案系統 不會區分英文大小寫
因此 掛載點 及 檔案名稱 時,不需要考慮英文大小寫
因此 掛載點 及 檔案名稱 時,不需要考慮英文大小寫
讀取 SD卡 資料
使用 Raspberry Pi Pico 讀取資料前,先將資料寫入到 SD卡
由於在下想確保沒有多餘的控制字元
例如 Carriage Return 及 End Of File 的 Line Feed ,所以使用指令製作測試文字檔案
如果閣下沒有特別需要,使用一般文字編輯器製作文字檔案亦可以
例如 Carriage Return 及 End Of File 的 Line Feed ,所以使用指令製作測試文字檔案
如果閣下沒有特別需要,使用一般文字編輯器製作文字檔案亦可以
簡單的文字檔案內容
在下還想測試 Raspberry Pi Pico 讀取 SD卡 的內容時,能否支援 Unicode 等字元
因此製作含有中文字的內容
在下還想測試 Raspberry Pi Pico 讀取 SD卡 的內容時,能否支援 Unicode 等字元
因此製作含有中文字的內容
由於在下使用 CircuitPython , 因此使用由 Adafruit 提供的 CircuitPython 函式庫
方便存取 SD卡 的資料
方便存取 SD卡 的資料
或到 Adafruit 的 GitHub 頁面 下載亦可
將 adafruit_sdcard.py 或 adafruit_sdcard.mpy 存放到 Raspberry Pi Pico 的 /lib 目錄
(py 為純文字檔案,可以了解原始碼;mpy 編譯為二進制檔案的 py ,檔案較小)
(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 為例子)
SPI模式 只支援 FAT16 及 FAT32 的檔案系統
因此使用 SPI模式 讀寫 SD卡 前,必須先將 SD卡 格式化為 FAT16 或 FAT32 的檔案系統
(在下以 FAT32 為例子)
SD卡 的 SPI模 使用 模式0
- SS引腳 為 高電壓 時為 閒置狀態
- SCK引腳 為 低電壓 時採樣
- 採樣次序 以 最高有效位 開始
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卡 回復到 閒置狀態
正式操作前,需要將 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.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位元組指令
- 第7位元固定低電壓
- 第6位元固定高電壓
- 第5至0位元指令訊號
- 4位元組參數
- 由最高位元組開始
- 例如參數是 142857 = 0x00022E09
參數1 = 0x00, 參數2 = 0x02, 參數3 = 0x2E, 參數4 = 0x09
- 例如參數是 142857 = 0x00022E09
- 由最高位元組開始
- 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 |
波紋時序圖
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 |
波紋時序圖
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 |
波紋時序圖
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 |
傳送指令的完整波紋時序圖
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訊號 ,直至傳回狀態
因此在傳回狀態前,需要傳送一定數量的 0xFF訊號 ,直至傳回狀態
SPI狀態 為 1位元組
- 第0至第n位元組
- 等待傳回狀態
- 第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 | * | * | * | * | * | * | * |
波紋時序圖
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 | * |
傳回狀態的完整波紋時序圖
SPI 回應
SD卡 傳回狀態後,部分指令還會傳回回應
SPI回應 為 n位元組
- 第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 | * | * | * | * | * | * | * | * |
波紋時序圖
def getResponses(length): responses = [] for i in range(length): responses.append(transfer(0xFF)) return responses
SPI 讀取資料
SD卡 讀取資料的指令
- 第0至第n位元組
- 等待
- 第n+1位元組
- 傳回令牌
- 傳回0xFE 為 資料令牌,適合 0x11, 0x12, 0x18 指令使用 (示範)
- 傳回0xFC 為 資料令牌,適合 0x19 指令使用
- 傳回0xFD 為 停止令牌,適合 0x19 指令使用
- 傳回令牌
- 第n+2至第m位元組
- 讀取資料
- 第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 |
波紋時序圖
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 | * | * |
讀取資料的完整波紋時序圖
SPI 寫入資料
除了讀取資料,亦可以寫入資料
- 第0位元組
- 提交有效令牌(0xFE)
- 第1至第n位元組
- 寫入資料
- 第n+1至第n+2位元組
- 傳回CRC
- 第n+3至第m位元組
- 等待回應
- 第m+1位元組
- 傳回回應
- 傳回0x05 為 資料有效
- 傳回0x0B 為 資料CRC錯誤,資料無效
- 傳回0x0D 為 資料寫入錯誤,資料無效
- 傳回回應
- 第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 |
波紋時序圖
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 |
波紋時序圖
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 |
波紋時序圖
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 |
波紋時序圖
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 |
寫入資料的完整波紋時序圖
起動步驟
SD卡 起動步驟大概為
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卡 狀態步驟大概為
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卡 讀取步驟大概為
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卡 寫入步驟大概為
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卡 ,即是:
來掛載格式化為 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卡 引腳
編號 | 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卡 引腳
編號 | 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卡 引腳
編號 | 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個磁碟分割表
FAT起動磁區 (FAT Boot Sector)
保存磁區訊息、掛載方法
檔案系統資訊磁區 (File System Information Sector)
保存叢集總數資訊、下個叢集的位置
檔案目錄表 (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)
保存下個鏈式叢集的位置、叢集的狀態
資料區域 (Data Area)
保存檔案資料的空間
如果要讓 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
每分割位置並不相同,因此真實位置可能會稍有偏移
以在下的情況,FAT32起動磁區 的位置為 0x100000 ,即是偏移位置為 0x100000
由於這個專案令在下重新學習檔案系統的知識及技術
令在下回憶經常有一些 SD卡 掛載後無法使用
因此想利用這個技術強行重建檔案系統,希望能幫功資料恢復用途
令在下回憶經常有一些 SD卡 掛載後無法使用
因此想利用這個技術強行重建檔案系統,希望能幫功資料恢復用途
在下使用 虛擬機 將 虛擬硬碟 以 MBR方式低階格式化,將資料完全刪除
第1分割區格式化為 FAT32
(裝置顯示 /dev/sda1 表示 第1硬碟的第1分割區)
(裝置顯示 /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硬碟不分割)
(裝置顯示 /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 必定不同)
(格式化後 UUID 必定不同)
在下嘗試使用 Raspberry Pi Pico 實戰測試
將 SD卡 低階格式化
(將實體 SD卡 低階格式化需要很長時間,32GB 需要大約 1小時)
(將實體 SD卡 低階格式化需要很長時間,32GB 需要大約 1小時)
將 mbr.img, fat32-bs.img, fsinfo-sector.img, fat32-table.img 複製到 Raspberry Pi Pico
再使用 open 功能,將檔案的二進制碼寫入到 SD卡
再使用 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個檔案
改成 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 的程式碼加到的 函式庫 中
即是這種強行建立 FAT32 檔案系統的方法,可以達到在下期望的效果
下在將 SD卡 格式化為 FAT32 的程式碼加到的 函式庫 中
但由於在下的 SD卡 ,因此在下複製的磁區資料未必與其他 SD卡 相容
所以閣下使用前仍然需要自行修改成相符合的資料才使用,否則可能會破壞閣下的 SD卡 的資料
所以閣下使用前仍然需要自行修改成相符合的資料才使用,否則可能會破壞閣下的 SD卡 的資料
如果不使用磁碟分割,可以直接將 FAT32起動磁區 、 檔案系統資訊磁區 、 檔案分配表
順移至 0, 1, 32 的磁區位置,便可以使用整個分割
順移至 0, 1, 32 的磁區位置,便可以使用整個分割
由於使用 主開機紀錄 的方法需要規劃磁碟分割的位置
由於會佔用部分儲存空間,而且並非所有分割的位置及數量都相同,備份及修復反而會困難
因此如果想 SD卡 用盡整個儲存空間,便不要使用 主開機紀錄 ,而且更方便修復資料
由於會佔用部分儲存空間,而且並非所有分割的位置及數量都相同,備份及修復反而會困難
因此如果想 SD卡 用盡整個儲存空間,便不要使用 主開機紀錄 ,而且更方便修復資料
總結
接駁線路時不慎將 VCC 及 GND 倒轉接駁,導致 Raspberry Pi Pico 及 電壓穩定器 異常灼熱
幸好早發現,否則可能會燒毀 Raspberry Pi Pico 或 電壓穩定器
還有將 微控制器 及 Micro SD卡 模組 的 MOSI 及 MISO 倒轉接駁
幸好早發現,否則可能會燒毀 Raspberry Pi Pico 或 電壓穩定器
還有將 微控制器 及 Micro SD卡 模組 的 MOSI 及 MISO 倒轉接駁
在下發現當使用預設函式庫的 busio.SPI ,不使指定 MOSI 及 MISO 引腳時
必須使用 Raspberry Pi Pico 的 MOSI 及 MISO 引腳,否則會發生 ValueError: Invalid pins 錯誤
雖然線路連接上會受到一定限制,但因為使用硬件提供的 SPI 因此速度會比較快
必須使用 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卡資料 的方法
在下尋找資料 及 嘗試理解 CircuitPython 的 adafruit_sdcard.py 及 MicroPython 的 sdcard.py
以 adafruit_sdcard.py 為基礎,自行製作 讀寫SD卡資料 的類別
執行主程式前,在 adafruit_sdcard.py 需要執行的功能中,加上大量 print 以顯示執行後的結果
雖然花了很多時間,但最終能夠製作自己 讀寫SD卡 的類別
而且在製作途中還學習到 FAT檔案系統 的操作知識及修復技巧
而且在製作途中還學習到 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.............. ................ ................ ................ ................ ................ ................ ................ ................ ................ ................ ................ ................ ................ ................ ................ ................ ................ ................ ................ ................ ................ ................ ................ ................ ................ ................ ................ ................ |
雖然這些知識可能已經有文件資料,但靠自己學習及發現,比單純查資料印象更深刻
沒有留言 :
張貼留言