數(shù)據(jù)采集方案:SPI驅(qū)動、掉電保護(hù)與調(diào)試實戰(zhàn))
1. 項目緣起與方案選型為什么是 MRAM 加 PIC18F45251.1 一個真實的需求場景前陣子接了個工業(yè)數(shù)據(jù)采集終端的活需求說起來不復(fù)雜設(shè)備裝在配電柜里每隔幾百毫秒采集一次電流、溫度、開關(guān)狀態(tài)本地要能存最近若干條記錄斷電不能丟上電后主控要能立刻把數(shù)據(jù)讀出來做趨勢判斷?,F(xiàn)場環(huán)境比較惡劣夏天柜內(nèi)溫度能到七十多度冬天戶外柜可能零下二三十度還有電機(jī)啟停帶來的電源波動和電磁干擾。一開始我按慣性想用 EEPROM比如 24C 系列便宜、熟悉、驅(qū)動簡單。但仔細(xì)一算就發(fā)現(xiàn)問題了采集頻率高如果每條都寫 EEPROM按 100 萬次擦寫壽命算一天寫幾萬次幾個月就把壽命耗光了。換成 SPI Flash 呢容量大、便宜但 Flash 有個繞不開的毛病——寫之前必須先擦除整個扇區(qū)而且擦寫壽命也就十萬次量級頻繁小數(shù)據(jù)寫入同樣扛不住更麻煩的是掉電瞬間如果正在擦寫整個扇區(qū)數(shù)據(jù)可能就廢了。這時候 MRAM 就進(jìn)入了視野。MR25H40CDF 是 Everspin 出的 4Mbit 磁性隨機(jī)存儲器SPI 接口最關(guān)鍵的特性是寫入不需要擦除、字節(jié)級可寫、擦寫壽命幾乎無限官方標(biāo)稱 10^14 次以上、掉電數(shù)據(jù)不丟、讀寫速度接近 SRAM。這幾點幾乎完美命中工業(yè)場景的痛點。主控這邊選 PIC18F4525理由也很實在8 位機(jī)里外設(shè)夠用、SPI 模塊成熟、工業(yè)溫度范圍版本好買、開發(fā)工具鏈穩(wěn)定而且這個項目對算力要求不高用 32 位屬于殺雞用牛刀成本還上去了。所以這套組合的核心邏輯就是用 MRAM 解決頻繁寫、斷電不丟、壽命長的存儲難題用 PIC18F4525 解決穩(wěn)定采集、可靠通信、成本可控的控制難題。下面我把整個設(shè)計、調(diào)試、踩坑的過程完整拆一遍如果你也在做類似的數(shù)據(jù)記錄、參數(shù)存儲、掉電保護(hù)類項目這套思路可以直接參考。1.2 MR25H40CDF 到底強在哪先把這顆芯片的關(guān)鍵參數(shù)擺出來方便你判斷是否適合自己的項目。參數(shù)項數(shù)值說明容量4 Mbit512 KB按字節(jié)尋址夠存大量記錄接口SPI最高 40 MHz支持 Mode 0 和 Mode 3供電2.7V ~ 3.6V注意是 3.3V 器件不能直接 5V擦寫壽命10^14 次以上基本可以認(rèn)為寫不壞數(shù)據(jù)保持20 年以上斷電不丟無需備份電池寫入方式字節(jié)/頁均可無需擦除這是和 Flash 最大的區(qū)別工作溫度-40℃ ~ 85℃工業(yè)級部分型號到 105℃封裝8 引腳 SOIC / DFN引腳少布線簡單我第一次用 MRAM 的時候最直觀的感受就是像操作 SRAM 一樣操作它。傳統(tǒng) Flash 你要先發(fā)寫使能、再發(fā)擦除命令、等擦除完成、再發(fā)寫使能、再寫數(shù)據(jù)、再等寫完成一套流程下來幾十毫秒就沒了。MRAM 呢發(fā)個寫命令把地址和數(shù)據(jù)懟進(jìn)去等幾微秒完事。這個差異在高速采集場景下是決定性的。1.3 PIC18F4525 的角色定位PIC18F4525 是 Microchip 的 8 位單片機(jī)44 引腳32KB Flash 程序空間3936 字節(jié) RAM自帶 MSSP 模塊可以配置成 SPI 主模式。選它主要看中幾點SPI 硬件模塊穩(wěn)定不用軟件模擬時序有足夠的 GPIO 做采集和指示工業(yè)級版本供貨穩(wěn)定用 MPLAB X XC8 編譯器開發(fā)工具鏈成熟出問題好查。這里要提醒一句PIC18F4525 的供電范圍是 2.0V ~ 5.5V但 MR25H40CDF 是 3.3V 器件所以整個系統(tǒng)要么統(tǒng)一跑 3.3V要么做電平轉(zhuǎn)換。我這次是統(tǒng)一 3.3V 供電省掉了電平轉(zhuǎn)換芯片布線也簡單。如果你手上是 5V 系統(tǒng)SPI 的 MOSI、SCK、CS 這幾根線從 5V 主控打到 3.3V 從機(jī)必須加電平轉(zhuǎn)換或者至少串電阻分壓否則長期跑會損傷 MRAM 的輸入級。2. 硬件設(shè)計細(xì)節(jié)與 SPI 時序要點2.1 硬件連接與引腳分配MR25H40CDF 是 8 引腳封裝引腳定義很清晰CS片選、SO數(shù)據(jù)輸出即 MISO、SI數(shù)據(jù)輸入即 MOSI、SCK時鐘、VDD、VSS、WP寫保護(hù)、HOLD保持。實際用的時候 WP 和 HOLD 如果不用直接拉到 VDD 即可但要注意這兩個引腳是低有效懸空會誤觸發(fā)。我這次給 PIC18F4525 的引腳分配是這樣的RC3 / SCKSPI 時鐘RC5 / SDO主出從入接 MRAM 的 SIRC4 / SDI主入從出接 MRAM 的 SORA5片選 CS用普通 GPIO 控制RA4寫保護(hù) WP 控制可選調(diào)試時方便RB0狀態(tài)指示燈這里有個細(xì)節(jié)值得說片選為什么用普通 GPIO 而不是硬件 SS 引腳。PIC18F4525 的 MSSP 模塊在 SPI 主模式下SS 引腳其實可以配置成普通 IO片選完全由軟件控制。這樣做的好處是靈活你可以在一條總線上掛多個 SPI 從機(jī)用不同 GPIO 分別片選。工業(yè)項目里經(jīng)常要掛 MRAM、Flash、顯示屏好幾個 SPI 設(shè)備軟件片選是標(biāo)配做法。硬件片選雖然省一個 IO但多從機(jī)場景下反而麻煩。2.2 SPI 模式選擇與時序分析MR25H40CDF 支持 SPI Mode 0CPOL0, CPHA0和 Mode 3CPOL1, CPHA1。我選的是 Mode 0原因是 PIC 的 MSSP 模塊配置 Mode 0 最直接而且大多數(shù)調(diào)試工具默認(rèn)也是 Mode 0抓波形方便。Mode 0 的時序特征時鐘空閑時為低電平數(shù)據(jù)在時鐘上升沿采樣下降沿輸出。這意味著主控在 SCK 下降沿把數(shù)據(jù)放到 MOSI 上從機(jī)在上升沿讀取從機(jī)在下降沿把數(shù)據(jù)放到 MISO 上主控在上升沿讀取。理解這個時序?qū)ε挪橥ㄐ艈栴}非常關(guān)鍵后面講排查的時候會用到。關(guān)于時鐘頻率MRAM 最高支持 40MHz但 PIC18F4525 在 3.3V 下跑 SPI 主模式實際能穩(wěn)定跑的頻率受限于系統(tǒng)時鐘和分頻系數(shù)。我系統(tǒng)時鐘用的是 32MHz外部晶振 8MHz 四倍頻SPI 分頻選 4得到 8MHz 的 SCK。實測 8MHz 下讀寫都很穩(wěn)波形干凈。如果你追求更高速度可以試試分頻 2 得到 16MHz但要注意 PCB 走線質(zhì)量線長了或者有干擾高速下容易出錯。提示SPI 時鐘頻率不是越高越好。工業(yè)現(xiàn)場干擾大我一般會留足余量8MHz 夠用就不硬上 16MHz。穩(wěn)定性永遠(yuǎn)優(yōu)先于速度。2.3 PCB 布局的幾個經(jīng)驗SPI 雖然是有同步時鐘的通信理論上比 UART、I2C 抗干擾強但在工業(yè)環(huán)境里布局不好照樣出問題。我踩過的坑總結(jié)幾條第一SCK 走線盡量短且遠(yuǎn)離大電流回路。SCK 是時鐘線頻率高最容易輻射也最容易被干擾。我第一版板子把 SCK 和繼電器驅(qū)動線并排走了幾厘米結(jié)果繼電器一動作SPI 就讀寫錯誤。后來把 SCK 改短、加地線隔離問題消失。第二CS 線要單獨走不要和 SCK 平行長距離。CS 是控制信號如果和 SCK 耦合可能在時鐘跳變時誤觸發(fā)片選導(dǎo)致通信錯亂。第三MRAM 的電源引腳旁邊必須放 0.1uF 去耦電容越近越好。MRAM 寫入瞬間電流會有波動去耦不好會導(dǎo)致寫入失敗或者數(shù)據(jù)錯誤。我一般還會再并一個 1uF 的鉭電容做低頻濾波。第四WP 和 HOLD 不要懸空。這兩個引腳內(nèi)部沒有強上拉懸空時電平不確定可能導(dǎo)致寫保護(hù)隨機(jī)生效或者器件進(jìn)入保持狀態(tài)。直接拉到 VDD 最省心。3. 軟件實現(xiàn)從底層驅(qū)動到數(shù)據(jù)管理3.1 SPI 底層驅(qū)動編寫PIC18F4525 的 MSSP 模塊配置成 SPI 主模式核心寄存器就幾個SSPCON1、SSPSTAT、SSPADD主模式下不用。初始化代碼如下我用 XC8 編譯器寫的void SPI_Init(void) { TRISC3 0; // SCK 輸出 TRISC5 0; // SDO 輸出 TRISC4 1; // SDI 輸入 TRISA5 0; // CS 輸出 CS 1; // 片選默認(rèn)拉高 SSPCON1 0x20; // SSPEN1, CKP0, SPI Master, Fosc/4 SSPSTAT 0x40; // CKE0, SMP0, Mode 0 } unsigned char SPI_Transfer(unsigned char data) { SSPBUF data; while(!SSPIF); // 等待傳輸完成 SSPIF 0; return SSPBUF; }這段代碼看著簡單但有幾個點新手容易錯。SSPSTAT的 CKE 和 SMP 位決定了 SPI 模式Mode 0 對應(yīng) CKE0、SMP0配錯了時序就完全對不上。還有SSPIF標(biāo)志位發(fā)送完必須手動清零否則下一次等待會直接跳過。我見過有人忘了清標(biāo)志結(jié)果讀回來的數(shù)據(jù)全是錯的查了半天。3.2 MRAM 讀寫命令解析MR25H40CDF 的命令集很精簡常用的就幾條命令十六進(jìn)制功能WREN0x06寫使能WRDI0x04寫禁止RDSR0x05讀狀態(tài)寄存器WRSR0x01寫狀態(tài)寄存器READ0x03讀數(shù)據(jù)WRITE0x02寫數(shù)據(jù)讀操作的流程拉低 CS發(fā) 0x03發(fā) 24 位地址4Mbit 需要 19 位地址但按 24 位發(fā)高位補零然后連續(xù)讀數(shù)據(jù)讀完拉高 CS。寫操作的流程拉低 CS發(fā) 0x06 寫使能拉高 CS再拉低 CS發(fā) 0x02發(fā) 24 位地址發(fā)數(shù)據(jù)拉高 CS。這里有個和 Flash 不一樣的地方MRAM 的寫使能是每次寫之前都要發(fā)但不需要等待寫完成。Flash 寫完要輪詢狀態(tài)寄存器等 WIP 位清零MRAM 寫完數(shù)據(jù)就進(jìn)去了最多等幾微秒。所以驅(qū)動里不需要寫輪詢邏輯簡單很多。void MRAM_WriteByte(unsigned long addr, unsigned char data) { CS 0; SPI_Transfer(0x06); // WREN CS 1; CS 0; SPI_Transfer(0x02); // WRITE SPI_Transfer((addr 16) 0xFF); SPI_Transfer((addr 8) 0xFF); SPI_Transfer(addr 0xFF); SPI_Transfer(data); CS 1; } unsigned char MRAM_ReadByte(unsigned long addr) { unsigned char dat; CS 0; SPI_Transfer(0x03); // READ SPI_Transfer((addr 16) 0xFF); SPI_Transfer((addr 8) 0xFF); SPI_Transfer(addr 0xFF); dat SPI_Transfer(0x00); CS 1; return dat; }3.3 地址空間規(guī)劃與數(shù)據(jù)組織512KB 的空間看著大但如果不規(guī)劃好用起來一樣亂。我的做法是把空間分成幾個區(qū)0x00000 ~ 0x000FF系統(tǒng)配置區(qū)存設(shè)備參數(shù)、校準(zhǔn)系數(shù)、版本號0x00100 ~ 0x00FFF運行日志區(qū)存關(guān)鍵事件記錄0x01000 ~ 0x7FFFF采集數(shù)據(jù)區(qū)環(huán)形緩沖區(qū)采集數(shù)據(jù)區(qū)用環(huán)形緩沖的方式管理維護(hù)一個寫指針和一個讀指針。寫指針每次寫完一條記錄就往后移到末尾繞回開頭。讀指針用于上位機(jī)來取數(shù)據(jù)時定位。這樣既不會寫滿溢出又能保留最近的歷史數(shù)據(jù)。每條記錄我定義成 16 字節(jié)的結(jié)構(gòu)4 字節(jié)時間戳、4 字節(jié)電流值、4 字節(jié)溫度值、2 字節(jié)開關(guān)狀態(tài)、2 字節(jié)校驗。校驗用簡單的累加和雖然不如 CRC 強但對這種短記錄夠用計算也快。注意環(huán)形緩沖區(qū)的讀寫指針本身也要存到 MRAM 里而且要定期更新。如果只在掉電時保存掉電瞬間可能來不及寫。我的做法是每寫 100 條記錄更新一次指針這樣即使掉電丟失最多 100 條對趨勢判斷影響不大。3.4 掉電保護(hù)與數(shù)據(jù)完整性工業(yè)項目最怕的就是掉電丟數(shù)據(jù)或者數(shù)據(jù)錯亂。MRAM 本身掉電不丟但主控在掉電瞬間可能正在寫寫到一半斷電這條記錄就不完整了。解決辦法是加一個記錄有效標(biāo)志每條記錄寫完數(shù)據(jù)后最后寫一個特定的標(biāo)志字節(jié)比如 0xA5。讀的時候先檢查標(biāo)志標(biāo)志不對就認(rèn)為這條記錄無效跳過。另外電源部分我加了一個大電容和電壓檢測電路。電壓檢測用 PIC 的 ADC 監(jiān)測供電當(dāng)電壓低于閾值時立即停止采集把當(dāng)前狀態(tài)和指針保存到 MRAM然后進(jìn)入低功耗等待。這樣能保證關(guān)鍵數(shù)據(jù)不丟。實測從檢測到掉電到完成保存大概 2 毫秒電容容量選夠就行。4. 調(diào)試實錄那些讓我熬夜的問題4.1 讀出來全是 0xFF 或 0x00這是最常見的 SPI 通信問題。第一次調(diào)的時候讀 MRAM 返回全是 0xFF我第一反應(yīng)是片選沒拉低或者時序不對。用邏輯分析儀抓波形發(fā)現(xiàn) SCK 有、MOSI 有數(shù)據(jù)但 MISO 一直是高。查了半天發(fā)現(xiàn)是 MRAM 的 SO 引腳和 PIC 的 SDI 接反了——我把 SO 接到了 PIC 的 SDO 上。SPI 的命名容易混主機(jī)的 SDO 接從機(jī)的 SI主機(jī)的 SDI 接從機(jī)的 SO記住輸出接輸入就不會錯。還有一種情況是讀出來全是 0x00。這個通常是片選一直有效或者時鐘極性配錯。如果 CPOL 配反了從機(jī)在錯誤的邊沿采樣讀到的就是無效數(shù)據(jù)。這時候把 SSPSTAT 的 CKE 位翻轉(zhuǎn)一下試試往往就對了。4.2 寫入后讀出來不對寫入看似成功讀出來卻是舊數(shù)據(jù)或者亂碼這個問題我遇到過兩次。第一次是寫使能命令沒發(fā)對WREN 必須在每次寫之前單獨發(fā)一次而且發(fā)完 WREN 后 CS 要先拉高再拉低才能發(fā) WRITE。我一開始把 WREN 和 WRITE 放在同一個 CS 有效周期里結(jié)果寫不進(jìn)去。第二次是 WP 引腳懸空導(dǎo)致寫保護(hù)隨機(jī)生效有時候能寫有時候不能寫。把 WP 拉到 VDD 后徹底解決。4.3 高速下偶發(fā)錯誤8MHz 跑得好好的我試著提到 16MHz結(jié)果發(fā)現(xiàn)偶爾讀寫出錯概率大概千分之一。這種偶發(fā)問題最頭疼。用示波器看波形發(fā)現(xiàn) SCK 上升沿有點過沖而且 MISO 的數(shù)據(jù)建立時間在高速下余量不足。解決辦法有兩個一是降低頻率回到 8MHz二是優(yōu)化 PCB 走線縮短 MISO 長度并加匹配電阻。我選了前者因為工業(yè)項目穩(wěn)定第一8MHz 完全夠用。4.4 常見問題速查表現(xiàn)象可能原因排查方法解決讀全 0xFFMISO 未連接或接反查 SO-SDI 連線正確交叉連接讀全 0x00片選常有效或 CPOL 錯抓 CS 和 SCK 波形修正片選邏輯或 SPI 模式寫不進(jìn)WREN 時序錯或 WP 懸空查寫使能流程和 WP 電平分開 CS 周期WP 拉高偶發(fā)錯誤頻率過高或干擾降頻測試看波形質(zhì)量降頻或優(yōu)化布局?jǐn)?shù)據(jù)錯亂地址計算錯核對地址字節(jié)順序統(tǒng)一大端或小端掉電丟數(shù)據(jù)保存時機(jī)太晚測掉電到保存的時間加電壓檢測提前保存5. 性能實測與優(yōu)化建議5.1 讀寫速度實測在 8MHz SPI 時鐘下我實測了單字節(jié)讀寫和連續(xù)讀寫的速度。單字節(jié)寫含 WREN大概 5 微秒單字節(jié)讀大概 3 微秒。連續(xù)讀的時候效率高很多因為地址只發(fā)一次后面可以連續(xù)讀實測連續(xù)讀 1KB 數(shù)據(jù)大概 1.1 毫秒算下來接近 1MB/s 的吞吐。對于采集頻率幾百赫茲的應(yīng)用這個速度綽綽有余。如果你需要更快可以試試這幾個方向提高 SPI 時鐘到 16MHz 甚至 20MHz前提是布局過關(guān)用 DMA 搬運數(shù)據(jù)不過 PIC18F4525 沒有 DMA得換更高端的型號減少 WREN 的開銷連續(xù)寫多個字節(jié)時 WREN 只需發(fā)一次。5.2 功耗考量MRAM 的功耗比 Flash 略高但比 SRAM 加備份電池的方案低得多。待機(jī)電流大概幾百微安讀寫時幾毫安。對于電池供電的設(shè)備可以在不讀寫的時候把 CS 拉高M(jìn)RAM 會自動進(jìn)入低功耗狀態(tài)。如果對功耗要求極致還可以考慮給 MRAM 單獨供電不用的時候切斷電源——反正數(shù)據(jù)不丟。5.3 長期可靠性驗證工業(yè)項目最看重長期可靠性。我做了幾個測試連續(xù)寫入 100 萬次后讀取校驗數(shù)據(jù)完全正確高溫 85℃ 下連續(xù)運行 72 小時讀寫正常反復(fù)上下電 1000 次配置數(shù)據(jù)無丟失。這些測試雖然不能完全代表現(xiàn)場情況但至少說明這套方案在常規(guī)工業(yè)環(huán)境下是可靠的。心得MRAM 雖然壽命標(biāo)稱 10^14 次但實際項目中沒必要去挑戰(zhàn)極限。合理的數(shù)據(jù)管理策略比如環(huán)形緩沖、批量寫入比單純依賴器件壽命更靠譜。6. 這套方案還能怎么擴(kuò)展如果你已經(jīng)跑通了 MR25H40CDF 加 PIC18F4525 的基本讀寫可以考慮幾個擴(kuò)展方向。一是加文件系統(tǒng)把 MRAM 當(dāng)成一個小磁盤用 FatFs 之類的輕量文件系統(tǒng)管理這樣上位機(jī)可以直接按文件讀取數(shù)據(jù)不用自己解析裸數(shù)據(jù)。二是加無線模塊把采集數(shù)據(jù)通過 LoRa 或者 4G 傳出去MRAM 作為本地緩存網(wǎng)絡(luò)斷了也不丟數(shù)據(jù)。三是多片 MRAM 級聯(lián)用不同片選掛在同一條 SPI 總線上容量翻倍適合數(shù)據(jù)量更大的場景。我個人在實際操作中的體會是MRAM 這類器件的價值不在于它有多快多大而在于它把頻繁寫、斷電不丟、長壽命這三件事同時做到了這在以前是要用 SRAM 加電池或者鐵電存儲器才能實現(xiàn)的而 MRAM 在成本和可靠性上更有優(yōu)勢。選型的時候別只盯著價格算一算整個生命周期的維護(hù)成本和數(shù)據(jù)丟失的風(fēng)險往往就明白貴的那點錢花得值。