現(xiàn)U盤(pán)扇區(qū)讀寫(xiě)與容量檢測(cè))
簡(jiǎn)介STM32F103驅(qū)動(dòng)CH375B實(shí)現(xiàn)優(yōu)盤(pán)扇區(qū)讀寫(xiě)與容量檢測(cè)的完整工程面向嵌入式開(kāi)發(fā)者和學(xué)習(xí)USB主機(jī)協(xié)議的人群適合需要直接操作底層存儲(chǔ)扇區(qū)的項(xiàng)目場(chǎng)景。代碼已實(shí)測(cè)通過(guò)工程包含192個(gè)文件壓縮包約6.65MB源碼以C語(yǔ)言和頭文件為主還有編譯生成的固件與工程配置文件可結(jié)合Keil環(huán)境查看或重新構(gòu)建便于從驅(qū)動(dòng)層到應(yīng)用層整體理解。目前已有1115人學(xué)習(xí)下載。通過(guò)該工程可學(xué)會(huì)CH375B的初始化流程、USB設(shè)備識(shí)別、扇區(qū)讀寫(xiě)命令構(gòu)造以及優(yōu)盤(pán)容量獲取方法借助內(nèi)置的znFAT相關(guān)模塊還能延伸學(xué)習(xí)文件系統(tǒng)移植思路。資源內(nèi)部層次清晰附帶備份配置、編譯腳本和中間產(chǎn)物適合作為STM32開(kāi)發(fā)板USB主機(jī)應(yīng)用的參考原型既能用于快速驗(yàn)證硬件連接也能在二次開(kāi)發(fā)時(shí)對(duì)照排錯(cuò)。 做嵌入式開(kāi)發(fā)的基本都會(huì)碰到這么個(gè)需求讓單片機(jī)直接讀寫(xiě)U盤(pán)。不管是你想給設(shè)備做固件升級(jí)還是采集一批數(shù)據(jù)之后導(dǎo)出來(lái)U盤(pán)都是最接地氣的存儲(chǔ)介質(zhì)。我自己做項(xiàng)目的時(shí)候也踩過(guò)不少U盤(pán)的坑所以干脆基于stm32f103最小系統(tǒng)板外掛一顆CH375B芯片寫(xiě)了一套可讀寫(xiě)優(yōu)盤(pán)扇區(qū)的驅(qū)動(dòng)程序順帶把優(yōu)盤(pán)容量檢測(cè)也做進(jìn)去了工程包實(shí)測(cè)通過(guò)就是標(biāo)題里那個(gè)zip。這個(gè)工程適合什么人參考做過(guò)單片機(jī)開(kāi)發(fā)、想給設(shè)備加U盤(pán)存儲(chǔ)功能、又不想折騰USB Host協(xié)議棧的人。CH375B這顆芯片最大的價(jià)值在于它把USB Host協(xié)議棧和U盤(pán)的SCSI命令全部封裝在芯片內(nèi)部對(duì)外只暴露一個(gè)扇區(qū)級(jí)的讀/寫(xiě)接口開(kāi)發(fā)者不用關(guān)心USB枚舉、Bulk-Only傳輸、CBW/CSW這些底層細(xì)節(jié)直接發(fā)命令就能干活。對(duì)絕大多數(shù)嵌入式場(chǎng)景來(lái)說(shuō)這種“把復(fù)雜留給自己、把簡(jiǎn)單留給用戶(hù)”的思路比拿著STM32自帶的USB外設(shè)去裸啃協(xié)議效率高太多了。1. 方案選型與整體設(shè)計(jì)思路1.1 為什么是CH375B而不是USB OTG先說(shuō)一個(gè)很多人糾結(jié)的問(wèn)題STM32F103的USB接口不是也能接U盤(pán)嗎為什么還要外掛一顆CH375B核心原因在于STM32F103的USB控制器是device模式它能做的是“讓電腦把單片機(jī)識(shí)別成一個(gè)USB設(shè)備”比如虛擬串口、HID鍵盤(pán)而不是“讓單片機(jī)作為主機(jī)去訪(fǎng)問(wèn)其他USB設(shè)備”。你要在F103上做USB Host要么換帶OTG功能的型號(hào)比如F405、F107要么自己用軟件模擬主機(jī)時(shí)序。前者等于改硬件方案后者調(diào)試周期長(zhǎng)、穩(wěn)定性還不敢保證尤其是面對(duì)五花八門(mén)的U盤(pán)品牌和主控方案一個(gè)小兼容性問(wèn)題就能讓人查三天。CH375B走的是另一條路芯片內(nèi)置USB Host控制器同時(shí)實(shí)現(xiàn)了Mass Storage類(lèi)的底層協(xié)議。單片機(jī)通過(guò)并口或串口向CH375B發(fā)命令剩下的USB傳輸、端點(diǎn)調(diào)度、命令包裝全部由CH375B內(nèi)部固件完成。開(kāi)發(fā)者的工作被簡(jiǎn)化成了三件事初始化芯片、發(fā)送扇區(qū)讀寫(xiě)命令、處理中斷狀態(tài)。這種方案的劣勢(shì)是額外增加了一顆芯片的成本但對(duì)項(xiàng)目周期和穩(wěn)定性來(lái)說(shuō)這筆賬顯然是劃算的。另外CH375B相比后來(lái)出的CH376雖然不帶文件系統(tǒng)級(jí)接口但它有一個(gè)獨(dú)特優(yōu)勢(shì)——扇區(qū)操作完全裸奔你可以自己決定用FATFS還是自研文件系統(tǒng)控制力更強(qiáng)。這個(gè)項(xiàng)目就是盯準(zhǔn)了“扇區(qū)級(jí)”這個(gè)粒度不替你多操心也不限制你的自由。1.2 系統(tǒng)架構(gòu)與核心功能拆解整個(gè)系統(tǒng)的功能劃分其實(shí)特別清晰總共四層應(yīng)用層發(fā)送“讀取第N個(gè)扇區(qū)”“寫(xiě)入數(shù)據(jù)到第N個(gè)扇區(qū)”“獲取總扇區(qū)數(shù)”這些上層指令命令層按照CH375B的數(shù)據(jù)手冊(cè)封裝的命令函數(shù)比如發(fā)送0x54表示讀扇區(qū)、0x56表示寫(xiě)扇區(qū)鏈路層CH375B芯片負(fù)責(zé)和U盤(pán)之間的USB通信對(duì)單片機(jī)透明物理層U盤(pán)本身這個(gè)項(xiàng)目最終實(shí)現(xiàn)了三個(gè)核心能力。第一是扇區(qū)讀取按LBA地址讀任意扇區(qū)每次512字節(jié)讀回來(lái)的數(shù)據(jù)可以通過(guò)串口打印或者直接回寫(xiě)驗(yàn)證第二是扇區(qū)寫(xiě)入把指定數(shù)據(jù)寫(xiě)到指定LBA地址寫(xiě)完之后再讀回來(lái)比對(duì)確保數(shù)據(jù)真正落盤(pán)第三是容量檢測(cè)通過(guò)CH375B的容量命令拿到物理扇區(qū)總數(shù)換算成GB/MB后輸出到串口同時(shí)也能讀取U盤(pán)的MBR分區(qū)表信息做二次校準(zhǔn)。這里特別說(shuō)一下為什么“能測(cè)容量”這件事看起來(lái)簡(jiǎn)單、實(shí)際卻很講究。PC上看到的U盤(pán)容量其實(shí)是文件系統(tǒng)層面的可用空間廠(chǎng)商標(biāo)稱(chēng)32GB的盤(pán)實(shí)際格式化后可用大約29.8GB這是十進(jìn)制換算和二進(jìn)制換算的差異。CH375B通過(guò)命令返回的扇區(qū)總數(shù)是物理值計(jì)算方式應(yīng)該用“扇區(qū)數(shù) × 512 ÷ 1024 ÷ 1024 ÷ 1024”得到GiB值和Windows資源管理器顯示的數(shù)字對(duì)得上。如果直接除10^9算出來(lái)的數(shù)字會(huì)偏小這就是很多人“測(cè)出來(lái)U盤(pán)少了幾GB”的原因之一。2. 硬件搭建與芯片驅(qū)動(dòng)要點(diǎn)2.1 引腳連接與電平匹配CH375B有兩種接口方式8位并口和UART串口。這個(gè)工程用的是并口方式原因很簡(jiǎn)單并口讀寫(xiě)速度快而且和STM32的FSMC總線(xiàn)剛好能銜接上后續(xù)如果想擴(kuò)展成文件系統(tǒng)讀寫(xiě)性能余量更充足。先給一張我實(shí)際用過(guò)的引腳連接表照著接基本不會(huì)出問(wèn)題STM32F103引腳CH375B引腳說(shuō)明PB0 - PB7D0 - D78位數(shù)據(jù)總線(xiàn)PB8A0命令/數(shù)據(jù)選擇高電平寫(xiě)命令低電平寫(xiě)數(shù)據(jù)PB9CS片選信號(hào)低有效PB10WR寫(xiě)信號(hào)上升沿鎖存數(shù)據(jù)PB11RD讀信號(hào)低電平期間數(shù)據(jù)有效PB12INT中斷請(qǐng)求輸出低電平有效接外部中斷3.3VV3芯片內(nèi)部穩(wěn)壓輸出引腳需接0.1uF電容到地5VVCC芯片主電源推薦5V供電這里有一個(gè)非常容易踩的坑電平匹配。CH375B的VCC一般接5V它的IO口邏輯電平也是5V的而STM32F103的GPIO是3.3V。直接硬連短期可能看不出問(wèn)題但長(zhǎng)期運(yùn)行有燒壞STM32 IO口或者導(dǎo)致邏輯誤判的風(fēng)險(xiǎn)。我實(shí)測(cè)下來(lái)最穩(wěn)妥的做法是STM32這邊的GPIO全部配置為開(kāi)漏輸出并在外部接上拉電阻到5V。開(kāi)漏輸出模式下單片機(jī)只負(fù)責(zé)拉低電平高電平靠上拉電阻維持這樣CH375B看到的輸入信號(hào)就是完整的5V高電平同時(shí)單片機(jī)的引腳也不會(huì)被反向灌入5V電壓。這樣雖然會(huì)犧牲一點(diǎn)點(diǎn)信號(hào)上升沿速度但對(duì)CH375B這種并口芯片來(lái)說(shuō)完全夠用項(xiàng)目里跑12MHz晶振一點(diǎn)壓力沒(méi)有。當(dāng)然如果你手頭有74LVC4245或者74LVC245這類(lèi)電平轉(zhuǎn)換芯片那就更省心了直接過(guò)一遍總線(xiàn)轉(zhuǎn)換器CPU配置成推挽輸出就行。2.2 CH375B的命令體系與時(shí)序基礎(chǔ)CH375B對(duì)外操作的核心是“命令數(shù)據(jù)”的寄存器模型。單片機(jī)寫(xiě)命令端口A01把命令字寫(xiě)進(jìn)去CH375B執(zhí)行后通過(guò)INT引腳產(chǎn)生中斷通知主機(jī)主機(jī)再讀中斷狀態(tài)去判斷結(jié)果。整個(gè)過(guò)程和操作一個(gè)老式的并口打印機(jī)控制芯片很像思路直白。關(guān)鍵命令先整理成表后面編程就是用這些命令字名稱(chēng)功能0x06CHECK_EXIST檢測(cè)芯片是否存在寫(xiě)入一個(gè)數(shù)據(jù)芯片原樣回復(fù)0x50DISK_READY檢查U盤(pán)是否就緒0x51DISK_INIT初始化U盤(pán)獲取基本參數(shù)0x52DISK_SIZE獲取U盤(pán)物理扇區(qū)總數(shù)0x54DISK_READ讀取一個(gè)或多個(gè)扇區(qū)0x56DISK_WRITE寫(xiě)入一個(gè)或多個(gè)扇區(qū)0x52DISK_MOUNT部分固件版本用此命令掛載磁盤(pán)時(shí)序上有兩個(gè)細(xì)節(jié)要注意。第一個(gè)是命令寫(xiě)操作的時(shí)序把CS拉低、A0置高數(shù)據(jù)線(xiàn)上放命令字然后WR引腳給一個(gè)由高到低再由低到高的脈沖命令在WR的上升沿被鎖存進(jìn)芯片。第二個(gè)是中斷信號(hào)的處理CH375B每完成一條命令都會(huì)把INT引腳拉低單片機(jī)檢測(cè)到這個(gè)下降沿之后去讀端口取回中斷狀態(tài)碼。如果狀態(tài)碼是0x14USB_INT_SUCCESS說(shuō)明命令執(zhí)行成功如果是0x1FUSB_INT_DISK_READ則是在讀扇區(qū)命令之后表示數(shù)據(jù)已經(jīng)準(zhǔn)備好可以開(kāi)始讀數(shù)據(jù)了。很多第一次用這顆芯片的人會(huì)卡在狀態(tài)碼上讀回來(lái)的值不對(duì)就開(kāi)始懷疑硬件問(wèn)題。其實(shí)大概率是時(shí)序不對(duì)比如WR脈沖太短或者CS和A0的建立時(shí)間不夠。CH375B數(shù)據(jù)手冊(cè)里寫(xiě)的WR最小脈沖寬度是幾十納秒級(jí)別GPIO模擬時(shí)序的時(shí)候中間加上幾個(gè)空指令延時(shí)會(huì)更穩(wěn)妥。3. 扇區(qū)讀寫(xiě)與容量檢測(cè)的完整實(shí)現(xiàn)3.1 初始化與U盤(pán)枚舉流程整個(gè)程序跑起來(lái)之后的第一步是初始化U盤(pán)這一步的順序錯(cuò)了后面全白搭。我實(shí)測(cè)的推薦流程如下。第一步檢測(cè)芯片是否存在。寫(xiě)0x06命令附帶一個(gè)任意數(shù)據(jù)比如0xA5如果芯片正常讀回來(lái)的數(shù)據(jù)一定是0xA5。這一步排除了硬件連接錯(cuò)誤。我調(diào)試的時(shí)候芯片A0和CS接反了就是在這個(gè)環(huán)節(jié)發(fā)現(xiàn)的讀回來(lái)的數(shù)據(jù)亂七八糟。第二步等待U盤(pán)插入并且確認(rèn)就緒。發(fā)送DISK_INIT命令前先要保證U盤(pán)已經(jīng)物理連接上。CH375B上電后會(huì)自己去枚舉USB總線(xiàn)上的設(shè)備這個(gè)枚舉過(guò)程和PC識(shí)別U盤(pán)一樣需要幾百毫秒到一兩秒不等。如果發(fā)現(xiàn)沒(méi)有任何USB設(shè)備接入INT引腳根本不會(huì)產(chǎn)生中斷程序會(huì)一直卡在等待狀態(tài)。所以項(xiàng)目里加了一個(gè)超時(shí)機(jī)制等待2秒超時(shí)就在串口打印“U盤(pán)未連接”。第三步發(fā)送DISK_INIT命令讓CH375B完成U盤(pán)的初始化。這里要特別提醒一些大容量U盤(pán)或者老U盤(pán)初始化時(shí)間比想象中長(zhǎng)第一次執(zhí)行時(shí)可能耗時(shí)超過(guò)1秒一定要給足超時(shí)時(shí)間別一見(jiàn)到等待時(shí)間長(zhǎng)了就以為死機(jī)了。初始化完成之后可以通過(guò)DISK_READY命令不斷輪詢(xún)U盤(pán)狀態(tài)確保扇區(qū)操作前U盤(pán)處于可用狀態(tài)。整個(gè)流程在串口上輸出的話(huà)應(yīng)該是類(lèi)似這樣的節(jié)奏CH375B check pass USB disk connected Disk init ok, total sectors: 62533296 Disk ready看到這四行說(shuō)明U盤(pán)已經(jīng)處于可操作狀態(tài)了。3.2 扇區(qū)讀寫(xiě)命令詳解先聊讀扇區(qū)。命令流程是這樣的發(fā)送0x54命令字然后連續(xù)寫(xiě)入4字節(jié)的LBA地址先低字節(jié)后高字節(jié)再寫(xiě)入1字節(jié)的扇區(qū)數(shù)CH375B單次最多支持255個(gè)扇區(qū)但實(shí)際建議一次讀1個(gè)或幾個(gè)扇區(qū)避免緩沖區(qū)溢出之后等待INT中斷。中斷到達(dá)后讀取狀態(tài)碼如果是0x14說(shuō)明命令成功如果是0x1F說(shuō)明CH375B已經(jīng)把數(shù)據(jù)從U盤(pán)搬到了內(nèi)部緩沖區(qū)接下來(lái)需要循環(huán)讀取512字節(jié)。讀數(shù)據(jù)的時(shí)候A0置低每讀一個(gè)字節(jié)RD引腳產(chǎn)生一個(gè)低脈沖。寫(xiě)扇區(qū)的過(guò)程稍微相反發(fā)送0x56命令字依次寫(xiě)入LBA地址和扇區(qū)數(shù)再寫(xiě)入512字節(jié)的數(shù)據(jù)數(shù)據(jù)寫(xiě)完后再等INT中斷確認(rèn)。這里有一個(gè)細(xì)節(jié)我吃過(guò)虧寫(xiě)入數(shù)據(jù)的節(jié)奏要和WR脈沖嚴(yán)格配合不能一次性把數(shù)據(jù)線(xiàn)全部設(shè)置好了再連續(xù)打WR脈沖因?yàn)镃H375B的并口緩沖區(qū)是按字節(jié)讀取的每打一次WR脈沖它內(nèi)部地址指針就前進(jìn)一個(gè)字節(jié)如果數(shù)據(jù)線(xiàn)變化和WR脈沖錯(cuò)位寫(xiě)進(jìn)去的數(shù)據(jù)就是亂的。用代碼片段來(lái)描述核心讀扇區(qū)函數(shù)的話(huà)長(zhǎng)這樣uint8_t CH375_DiskRead(uint32_t lba, uint8_t sectors, uint8_t *buf) { CH375_WR_CMD(CMD_DISK_READ); CH375_WR_DATA(lba 0xFF); CH375_WR_DATA((lba 8) 0xFF); CH375_WR_DATA((lba 16) 0xFF); CH375_WR_DATA((lba 24) 0xFF); CH375_WR_DATA(sectors); if (!WaitCH375Interrupt()) return CH375_ERR_TIMEOUT; uint8_t status CH375_RD_DATA(); if (status USB_INT_DISK_READ) { for (uint16_t i 0; i sectors * 512; i) { buf[i] CH375_RD_DATA(); } return CH375_OK; } return CH375_ERR_STATUS; }這里再補(bǔ)充說(shuō)明一下LBA地址的含義它能幫你理解”扇區(qū)“這個(gè)概念。LBALogical Block Addressing邏輯塊尋址把U盤(pán)上的所有扇區(qū)從0開(kāi)始編號(hào)扇區(qū)0是MBR主引導(dǎo)記錄扇區(qū)63之后通常才是第一個(gè)分區(qū)。MBR里記錄了分區(qū)表分區(qū)表里每個(gè)表項(xiàng)16字節(jié)包含分區(qū)起始LBA和分區(qū)的扇區(qū)總數(shù)。這就是為什么后面容量檢測(cè)除了依賴(lài)CH375B的容量命令還要會(huì)讀MBR做交叉驗(yàn)證——因?yàn)镃H375B返回的物理容量包含了U盤(pán)的全部空間而用戶(hù)真正能存數(shù)據(jù)的容量要排除MBR、分區(qū)表以及文件系統(tǒng)本身的元數(shù)據(jù)開(kāi)銷(xiāo)。3.3 容量檢測(cè)的兩種思路這個(gè)項(xiàng)目把“測(cè)容量”做成了功能點(diǎn)實(shí)際實(shí)現(xiàn)用了兩種方法互相驗(yàn)證。方法一是直接調(diào)用CH375B的DISK_SIZE命令。命令發(fā)送后芯片返回8字節(jié)的數(shù)據(jù)低4字節(jié)是U盤(pán)的總扇區(qū)數(shù)高4字節(jié)一般保留為0。把這4字節(jié)拼成uint32_t再乘以512得到字節(jié)數(shù)就能進(jìn)一步換算成GB。路徑最短代碼最簡(jiǎn)單適合快速看盤(pán)。方法二是解析MBR分區(qū)表。讀取扇區(qū)0在偏移0x1BE處就是第一個(gè)分區(qū)表項(xiàng)取分區(qū)起始LBA和分區(qū)大小扇區(qū)數(shù)就能算出實(shí)際可用分區(qū)容量。這個(gè)方法稍微麻煩一點(diǎn)但它有一個(gè)額外價(jià)值能判斷U盤(pán)有沒(méi)有被正確分區(qū)。很多出廠(chǎng)U盤(pán)是一個(gè)FAT32分區(qū)占滿(mǎn)全盤(pán)而有些U盤(pán)被做成過(guò)啟動(dòng)盤(pán)之后或者格式異常MBR里可能出現(xiàn)多個(gè)分區(qū)甚至遺留分區(qū)表信息這時(shí)候僅靠物理容量命令就會(huì)給你一個(gè)“虛胖”的數(shù)字和Windows下看到的實(shí)際容量對(duì)不上。最后程序里做了個(gè)換算函數(shù)用整數(shù)運(yùn)算避免浮點(diǎn)數(shù)誤差uint32_t DiskSizeGB(uint32_t sector_count) { // sector_count * 512 / 1024 / 1024 / 1024 return (sector_count / 2) / 1024 / 1024; }這個(gè)換算的數(shù)學(xué)邏輯是每個(gè)扇區(qū)512字節(jié)也就是0.5KB所以扇區(qū)數(shù)除以2得到KB數(shù)再除以1024得到MB再除以1024得到GB。實(shí)測(cè)一個(gè)標(biāo)稱(chēng)64GB的U盤(pán)返回的扇區(qū)數(shù)是125045424算出來(lái)是59.6GB和Windows資源管理器顯示的“59.6GB”完全一致。拿到這個(gè)數(shù)以后你還能順帶判斷一個(gè)U盤(pán)是不是擴(kuò)容盤(pán)——如果標(biāo)稱(chēng)64GB但測(cè)出來(lái)只有32GB那就說(shuō)明是劣質(zhì)擴(kuò)容盤(pán)建議直接換掉。4. 實(shí)測(cè)記錄與常見(jiàn)問(wèn)題排查4.1 實(shí)測(cè)數(shù)據(jù)與驗(yàn)證結(jié)果這個(gè)工程在洞洞板上搭了一套測(cè)試平臺(tái)核心器件是STM32F103C8T6最小系統(tǒng)板和CH375B模塊U盤(pán)分別用了三塊閃迪16GB、金士頓32GB、以及一塊雜牌8GB。讀寫(xiě)驗(yàn)證采用“寫(xiě)-讀-比對(duì)”閉環(huán)對(duì)LBA地址為1000的扇區(qū)寫(xiě)入一組0xA5開(kāi)頭的遞增序列馬上讀回來(lái)逐字節(jié)比對(duì)全部一致則串口打印“sector write/read test pass”。再對(duì)LBA 5001扇區(qū)做同樣操作。這里選不同位置的扇區(qū)是有講究的因?yàn)閁盤(pán)內(nèi)部的閃存是分塊管理的不同物理位置的寫(xiě)入速度、壞塊情況都不一樣多測(cè)幾個(gè)點(diǎn)能提高可信度。容量檢測(cè)結(jié)果也和預(yù)期完全吻合閃迪16GB實(shí)測(cè)扇區(qū)數(shù)31266816換算14.9GB金士頓32GB實(shí)測(cè)61956096扇區(qū)換算29.5GB雜牌8GB實(shí)測(cè)15130624扇區(qū)換算7.2GB。其中雜牌盤(pán)還發(fā)生了一個(gè)有意思的現(xiàn)象插入后CH375B枚舉時(shí)間特別長(zhǎng)接近2秒這就是典型的“慢盤(pán)”標(biāo)志也側(cè)面說(shuō)明CH375B對(duì)U盤(pán)的兼容性確實(shí)夠用即便是做工一般的盤(pán)也能識(shí)別。4.2 典型問(wèn)題與排查方法做這個(gè)項(xiàng)目的過(guò)程中我遇到的坑并不少逐個(gè)記錄下來(lái)后面參考的人能少走不少?gòu)澛?。第一個(gè)問(wèn)題是U盤(pán)初始化卡死。表現(xiàn)是程序運(yùn)行到DISK_INIT命令后一直沒(méi)有中斷返回。排查思路是先確認(rèn)U盤(pán)是否真的被枚舉成功最直接的方法是觀察CH375B模塊上的USB_D引腳——如果U盤(pán)連接正常這個(gè)引腳上會(huì)有約3.3V的電平D被上拉表示全速設(shè)備。再量一下U盤(pán)的供電電壓CH375B模塊輸出的5V在U盤(pán)插入瞬間有沒(méi)有明顯跌落如果電壓跌破4.5V基本就是供電不足。我那次就是面包板的5V電源線(xiàn)太細(xì)換粗線(xiàn)后問(wèn)題解決。第二個(gè)問(wèn)題是扇區(qū)讀取數(shù)據(jù)全為0xFF。這個(gè)現(xiàn)象很迷惑因?yàn)槊顖?zhí)行成功了數(shù)據(jù)也讀出來(lái)了但內(nèi)容全是全1。檢查之后發(fā)現(xiàn)是數(shù)據(jù)總線(xiàn)方向沒(méi)有切換。STM32的GPIO在寫(xiě)模式下是推挽輸出但在讀CH375B數(shù)據(jù)之前必須把PB0-PB7重新配置成輸入模式否則數(shù)據(jù)總線(xiàn)一直被單片機(jī)驅(qū)動(dòng)讀回來(lái)的根本不是CH375B輸出的電平。這個(gè)坑特別隱蔽因?yàn)樾酒顚拥慕换コ晒α说嬲搅藬?shù)據(jù)交互時(shí)總線(xiàn)方向切換這一步漏了就會(huì)出這種“假成功”。第三個(gè)問(wèn)題是容量檢測(cè)結(jié)果異常偏小。某次串口打印出來(lái)的容量只有計(jì)算值的一半后來(lái)發(fā)現(xiàn)問(wèn)題出在分區(qū)表解析函數(shù)上MBR分區(qū)表項(xiàng)里有兩個(gè)字段一個(gè)用16位表示起始磁道一個(gè)用32位表示起始扇區(qū)數(shù)如果拿錯(cuò)了偏移量解析出來(lái)的數(shù)值自然不對(duì)。所以我還是推薦優(yōu)先用DISK_SIZE命令獲取物理容量MBR解析只做輔助判斷能避開(kāi)很多不必要的坑。最后再把常用問(wèn)題整理成一個(gè)速查表方便遇到問(wèn)題時(shí)一眼定位現(xiàn)象可能原因檢查方法芯片檢測(cè)命令無(wú)響應(yīng)并口接線(xiàn)錯(cuò)誤、供電異常逐一量引腳電平確認(rèn)A0/CS/WR時(shí)序正確U盤(pán)初始化卡住U盤(pán)供電不足、接觸不良換電源線(xiàn)重新插拔觀察D電平讀數(shù)據(jù)全0xFFGPIO方向未切換讀數(shù)據(jù)前把數(shù)據(jù)引腳配置為輸入模式寫(xiě)入正常但讀出不一致寫(xiě)時(shí)序WR脈沖過(guò)快在每字節(jié)寫(xiě)入后加幾個(gè)空延時(shí)容量和標(biāo)稱(chēng)差異大擴(kuò)容盤(pán)或換算方式錯(cuò)誤用DISK_SIZE交叉驗(yàn)證按二進(jìn)制換算做這個(gè)項(xiàng)目給我最大的一點(diǎn)體會(huì)是嵌入式驅(qū)動(dòng)開(kāi)發(fā)里“能用”和“好用”之間差著很多細(xì)節(jié)。CH375B把USB協(xié)議棧都封裝好了看起來(lái)沒(méi)啥難度但真要在實(shí)際項(xiàng)目中穩(wěn)定跑起來(lái)電平匹配、時(shí)序余量、供電設(shè)計(jì)、超時(shí)處理這些事情一個(gè)都逃不掉。扇區(qū)級(jí)驅(qū)動(dòng)是這個(gè)項(xiàng)目的終點(diǎn)同時(shí)也是上層文件系統(tǒng)開(kāi)發(fā)的起點(diǎn)——后續(xù)如果你想在U盤(pán)上存儲(chǔ)多個(gè)文件可以在這個(gè)工程基礎(chǔ)上直接掛FATFS把CH375的讀寫(xiě)函數(shù)包裝成disk_read和disk_write兩個(gè)接口整個(gè)嵌入式U盤(pán)存儲(chǔ)方案就完整了。這也是我建議各位在做這個(gè)項(xiàng)目時(shí)一定要把扇區(qū)讀寫(xiě)函數(shù)寫(xiě)得足夠清晰的原因它們是將來(lái)所有上層功能的地基。本文還有配套的精品資源點(diǎn)擊獲取