動器高頻掉電數(shù)據(jù)記錄方案詳解)
前陣子做一臺伺服驅(qū)動器的數(shù)據(jù)記錄改造原來的方案在板上放了顆EEPROM存運行參數(shù)調(diào)試時發(fā)現(xiàn)三個問題同時冒出來寫入一次要幾個毫秒電機(jī)關(guān)斷時序窗口根本等不及頻繁記錄的累計運行時間把壽命打到極限中間一次意外掉電恢復(fù)后參數(shù)還是壞了一塊。后來我把存儲介質(zhì)換成了Everspin的MR25H40CDF一顆4Mbit的SPI接口MRAMMCU側(cè)用的NXP Kinetis MKV44F256VLH16從接線到驅(qū)動全部重寫問題才算根治。這篇文章把這套組合從選型邏輯、電路細(xì)節(jié)到讀寫代碼完整拆一遍給正在做工業(yè)控制、嵌入式數(shù)據(jù)采集尤其是電機(jī)控制里需要掉電保存高頻數(shù)據(jù)的同學(xué)一個參考。為什么是這兩顆芯片MR25H40CDF解決的是非易失存儲寫入壽命和速度的痛點MKV44F256VLH16提供168MHz主頻的電機(jī)控制外設(shè)和足夠強(qiáng)壯的SPI控制器。你不需要用電池備份SRAM也不需要忍受EEPROM的毫秒級字節(jié)寫入和十萬到百萬次壽命上限MRAM這個掉電不丟的內(nèi)存在工業(yè)場景里非常對胃口。1. 項目背景與需求拆解1.1 項目到底要解決什么問題工業(yè)嵌入式設(shè)備和消費電子不太一樣很多現(xiàn)場工況是寫多讀少且寫入時機(jī)不可控的。伺服驅(qū)動器就是一個典型每次運行動作結(jié)束都可能要記錄當(dāng)前位置、累計運行時長、故障碼、電流環(huán)參數(shù)標(biāo)定值。這些數(shù)據(jù)的特點是更新頻率高、單次數(shù)據(jù)量不大、掉電后必須還在。用傳統(tǒng)方案會出現(xiàn)以下幾種難受的情況。EEPROM雖然按字節(jié)寫很簡單但單字節(jié)寫入等待時間通常在3到5毫秒在電機(jī)控制那種微秒級執(zhí)行周期里根本插不進(jìn)去。更致命的是耐久次數(shù)常見EEPROM標(biāo)稱10萬到100萬次擦寫聽著夠用但一個24小時不間斷記錄累計運行時間的設(shè)備每秒寫一次一年就是3153萬次一個月不到就把EEPROM壽命寫完。Flash也好不到哪里去NOR Flash頁編程要等擦除要等還有塊結(jié)構(gòu)限制為了改一個字節(jié)要先把整個扇區(qū)搬出來改完再擦回這在工業(yè)現(xiàn)場隨時掉電的環(huán)境下非常容易出數(shù)據(jù)損壞。BBSRAM也就是帶電池的靜態(tài)RAM讀寫得倒快但電池在高溫環(huán)境下的壽命、維護(hù)周期、防拆要求都是麻煩。有些設(shè)備在85℃環(huán)境跑三年電池先掛數(shù)據(jù)照樣丟。這個項目真正需要的存儲器長這樣寫入速度接近RAM沒有擦除過程寫入次數(shù)高到可以忽略壽命問題掉電不丟數(shù)據(jù)溫度范圍覆蓋工業(yè)級。MRAM就是這個思路下的產(chǎn)物而MR25H40CDF恰好是SPI接口、小封裝、容量4Mbit的主流型號非常適合掛在通用MCU的SPI總線后面。1.2 為什么選中MR25H40CDF加MKV44F256VLH16這套組合MR25H40CDF是Everspin的第四代SPI MRAM容量4Mbit也就是512KB8引腳DFN封裝SPI時鐘最高可以跑到40MHz讀寫速度基本是SRAM級別寫入壽命超過10的14次方次相當(dāng)于可以無限次寫。它的行為非常樸素地址、命令、數(shù)據(jù)都是標(biāo)準(zhǔn)SPI操作協(xié)議簡單到像一顆帶掉電保持的SRAM。MCU側(cè)選MKV44F256VLH16也有自己的考慮。它屬于NXP Kinetis V系列ARM Cortex-M4F內(nèi)核主頻168MHzLQFP64封裝Flash 256KBSRAM 32KB溫度范圍是工業(yè)級的-40到105℃。KV系列本來就是定位電機(jī)控制板載的FlexTimer、高速ADC、PGA等外設(shè)對這個項目后續(xù)擴(kuò)展很有用。更重要的是它有一個成熟穩(wěn)定的DSPI模塊可以作為SPI主機(jī)直接掛MRAMDMA配置也方便不會成為性能瓶頸。這套組合還有一個隱性優(yōu)勢整板只有一顆MCU加一顆存儲供電3.3V統(tǒng)一走線簡單MRAM和MCU都支持在工業(yè)溫度范圍工作沒有外部電池要維護(hù)。對于需要過環(huán)境認(rèn)證的工控設(shè)備物料清單越短可靠性越高。2. 兩顆芯片的關(guān)鍵細(xì)節(jié)2.1 MR25H40CDFMRAM不是普通存儲器MRAM的原理值得簡單說清楚因為理解了原理你才知道它為什么能同時做到快和不丟。它內(nèi)部用磁性隧道結(jié)存儲數(shù)據(jù)核心結(jié)構(gòu)是兩層鐵磁層中間夾著一層厚度只有納米級的絕緣勢壘。當(dāng)兩層鐵磁層的磁化方向一致隧穿電阻小表示一個邏輯狀態(tài)方向相反時隧穿電阻大表示另一個邏輯狀態(tài)。寫入數(shù)據(jù)時通過電流產(chǎn)生的磁場或者自旋轉(zhuǎn)移力矩把自由層磁化方向翻轉(zhuǎn)不需要先擦除再寫。這就是MRAM和Flash最本質(zhì)的區(qū)別Flash寫入前必須把整個區(qū)塊擦成1再往里面寫0擦除和寫入是兩個獨立步驟所以慢而且傷壽命而MRAM可以直接把目標(biāo)位寫入目標(biāo)值如同SRAM一樣。MR25H40CDF的主要參數(shù)我列一下項目參數(shù)容量4Mbit512K x 8接口SPI支持Mode 0和Mode 3最高時鐘40MHz供電電壓2.7V到3.6V寫入耐久大于10的14次方次數(shù)據(jù)保持85℃下超過20年工作溫度-40℃到85℃工業(yè)級封裝8引腳DFN待機(jī)電流典型約5mA寫操作按字節(jié)寫無需頁編程或擦除需要特別說明的是MR25H40CDF的寫入耐久指標(biāo)并不是估計值而是Everspin通過實際循環(huán)測試保證的。它沒有Flash那種寫壞塊的概念至少在你的產(chǎn)品生命周期內(nèi)可以認(rèn)為不存在磨損問題。這對量產(chǎn)設(shè)備的意義很大不需要做磨損均衡不需要做壞塊管理存儲驅(qū)動可以簡化很多。2.2 MKV44F256VLH16Kinetis KV44在工業(yè)端的底氣MKV44F256VLH16的定位是電機(jī)控制專用MCU但把它當(dāng)通用工業(yè)控制器用也完全沒問題。Cortex-M4F帶FPU跑浮點PID運算很輕松168MHz主頻足夠處理電機(jī)控制環(huán)路再加上數(shù)據(jù)記錄任務(wù)。外設(shè)方面FlexTimer可以做多路PWM輸出PGA和高速比較器可以直接處理模擬量12位ADC的采樣時序可以跟PWM同步這一套組合在伺服和變頻器領(lǐng)域非常成熟。我這里主要關(guān)心DSPI模塊。Kinetis V系列的DSPI支持SPI、DSPI和Queued SPI模式可以配置為PCS自動片選也可以手動控制。對MRAM這種連續(xù)讀寫命令我建議選擇手動片選理由后面代碼部分詳細(xì)說。DSPI的發(fā)送FIFO和接收FIFO各16個條目在40MHz時鐘下連續(xù)讀寫512字節(jié)數(shù)據(jù)時基本不需要CPU干預(yù)配合DMA甚至可以在CPU不介入的情況下把整塊MRAM內(nèi)容搬到內(nèi)存。本次項目數(shù)據(jù)量不大我直接用了阻塞式讀寫簡單穩(wěn)妥代碼也好維護(hù)。選MKV44F256VLH16還有一個原因是庫存和供貨。256KB Flash的Kinetis V系列在市場上流通量大LQFP64封裝也方便手工焊接和返修不像BGA那樣只能靠回流焊。對很多工控小批量產(chǎn)品來說可維護(hù)性本身就是硬指標(biāo)。3. 硬件設(shè)計與連接要點3.1 引腳映射與電路接法MR25H40CDF的8個引腳功能固定除電源地之外核心信號就是CS、SCK、MOSI、MISO、WP、HOLD。MKV44F256VLH16這側(cè)的DSPI引腳具體編號跟實際原理圖走線有關(guān)不同開發(fā)板和最小系統(tǒng)板的引腳定義不一定相同這里我按功能對應(yīng)關(guān)系給出連接方式。MR25H40CDF引腳作用連接目標(biāo)說明CS片選低有效MCU某GPIO手動控制平時拉高SCKSPI時鐘DSPI_SCK最高40MHz建議17度上升斜率MOSI命令和數(shù)據(jù)輸入DSPI_SOUTMCU向MRAM寫入數(shù)據(jù)MISO數(shù)據(jù)輸出DSPI_SINMRAM向MCU返回數(shù)據(jù)WP寫保護(hù)低有效直接接VDD上拉為高允許寫狀態(tài)寄存器HOLD暫停通信低有效直接接VDD必須上拉懸空會出詭異問題VDD電源3.3V并聯(lián)0.1uF加4.7uF去耦電容GND地系統(tǒng)地單點接地走線短粗需要強(qiáng)調(diào)幾點。WP和HOLD引腳不能懸空。HOLD拉低時MRAM會忽略SCK邊沿如果在高速通信過程中HOLD上出現(xiàn)毛刺就會發(fā)生幀錯位出現(xiàn)數(shù)據(jù)寫進(jìn)去但讀出來是亂的這種問題。WP雖然只鎖狀態(tài)寄存器寫入但同樣建議直接接高避免意外拉低后WRSR命令無效排查起來很隱蔽。CS用GPIO控制還是用DSPI硬件PCS控制這是電路設(shè)計階段就要定的事。我強(qiáng)烈建議用GPIO控制片選原因后面代碼部分展開硬件上只需把CS引腳接到任意空閑GPIO不需要DSPI的PCS功能映射到同一個引腳這樣在設(shè)計上就規(guī)避了引腳沖突。3.2 DFN8封裝與PCB設(shè)計經(jīng)驗MR25H40CDF的DFN封裝沒有裸露焊盤但引腳間距小焊接時還是要注意。我這邊是加熱臺配合鋼網(wǎng)或者手工風(fēng)槍加焊膏260到280℃溫度檔位別超過300℃吹到焊膏完全熔化后再稍微給點時間讓錫珠聚攏。焊完后可以用萬用表測一下VDD和GND之間有沒有短路再測CS、SCK、MOSI對地電阻確保沒連錫。有條件的話拿放大鏡或顯微鏡看一眼每個引腳側(cè)面有沒有爬錫這比單純量阻值更可靠。PCB走線方面40MHz的SPI信號不算高頻但也不能太隨意。SCK、MOSI、MISO三條信號線長度盡量控制在3厘米以內(nèi)如果超過5厘米建議在SCK上串一個22歐到33歐的電阻用于抑制振鈴并在終端加10到20pF電容濾掉高頻噪聲。工業(yè)現(xiàn)場最常見的干擾源是變頻器的IGBT開關(guān)噪聲它們會通過地線耦合進(jìn)SPI總線。所以MRAM的地腳到MCU地腳之間要走單獨的粗地線不要和功率部分的回流地共用一段細(xì)走線。電源去耦是另一個容易被忽略的點。MRAM在40MHz翻轉(zhuǎn)時瞬態(tài)電流不低如果去耦電容離VDD引腳太遠(yuǎn)VDD在讀寫瞬間會產(chǎn)生壓降導(dǎo)致偶發(fā)寫失敗。我的做法是0.1uF盡量貼著VDD引腳放置同時在3.3V電源入口再加4.7uF鉭電容或陶瓷電容確保高頻和低頻去耦都有覆蓋。4. 軟件讀寫實現(xiàn)4.1 DSPI初始化和時鐘選擇MKV44F256VLH16的DSPI配置在NXP的MCUXpresso SDK里有現(xiàn)成的驅(qū)動直接調(diào)用DSPI_MasterInit即可。關(guān)鍵參數(shù)是模式選擇、時鐘分頻和片選策略。MR25H40CDF支持SPI Mode 0和Mode 3區(qū)別只在SCK空閑電平。Mode 0是空閑低電平數(shù)據(jù)在SCK上升沿采樣Mode 3是空閑高電平數(shù)據(jù)同樣在上升沿采樣。兩個模式都可以用我習(xí)慣用Mode 0因為初始化時SCK默認(rèn)低電平能讓總線處于更安靜的狀態(tài)。初始化代碼如下#include fsl_dspi.h #define MRAM_DSPI_BASE DSPI0 #define MRAM_DSPI_CLK_FREQ CLOCK_GetFreq(kCLOCK_Dspi0) void mram_dspi_init(void) { dspi_master_config_t cfg; DSPI_MasterGetDefaultConfig(cfg); cfg.baudRate 40000000U; // 40MHz cfg.whichPcs kDSPI_Pcs0; // 僅用PCS編號實際CS由GPIO接管 cfg.clockMode kDSPI_ClockMode0; cfg.phasePolarity kDSPI_ClockPhaseFirstEdge; DSSI_MasterInit(MRAM_DSPI_BASE, cfg, MRAM_DSPI_CLK_FREQ); }時鐘頻率選40MHz這是MR25H40CDF的上限。如果你的PCB走線質(zhì)量一般或者經(jīng)過連接器轉(zhuǎn)接可以先把頻率降到20MHz驗證功能再逐步往上提。實測中40MHz穩(wěn)定工作沒問題但前提是走線短、地回流干凈。另外要注意DSPI的時鐘分頻是二分頻疊加實際波特率可能和配置值有微小偏差只要低于40MHz上限即可不必糾結(jié)精確值。CS引腳配置為GPIO輸出默認(rèn)拉高執(zhí)行命令時手動拉低拉高。GPIO的速度性能在這里不是瓶頸因為一條SPI命令短則幾微秒GPIO翻轉(zhuǎn)時間都在納秒級完全跟得上市面常見的存儲讀寫節(jié)奏。4.2 命令集與狀態(tài)寄存器MR25H40CDF的命令集非常精簡實際用到的就這么幾條命令操作碼功能后續(xù)內(nèi)容WREN0x06寫使能置位WEL無WRDI0x04寫禁用清除WEL無RDSR0x05讀狀態(tài)寄存器返回狀態(tài)字節(jié)WRSR0x01寫狀態(tài)寄存器狀態(tài)字節(jié)需要先WRENREAD0x03讀數(shù)據(jù)3字節(jié)地址然后輸出數(shù)據(jù)WRITE0x02寫數(shù)據(jù)3字節(jié)地址然后輸入數(shù)據(jù)SLEEP0xB9進(jìn)入睡眠模式無WAKE0xAB喚醒無狀態(tài)寄存器里最常用的是Bit7的WPEN和Bit1的WEL。WEL是寫使能鎖存位發(fā)送WREN命令后置位執(zhí)行一次WRITE或WRDI后會清除。也就是說寫操作之前必須發(fā)WREN否則寫命令會被忽略。這個機(jī)制和Flash保持一致目的就是防止總線上的意外毛刺把數(shù)據(jù)改寫。有一個很多新手會混淆的點MRAM不像Flash那樣有忙標(biāo)志寫完數(shù)據(jù)不需要輪詢WIP位也不會出現(xiàn)頁編程等待。WRITE命令的最后一個字節(jié)時鐘邊沿結(jié)束數(shù)據(jù)已經(jīng)物理寫入MRAM單元了。這對工業(yè)時序設(shè)計特別友好你可以在關(guān)斷供電前的最后幾十微秒內(nèi)把關(guān)鍵狀態(tài)寫進(jìn)去完全來得及。4.3 完整驅(qū)動代碼與講解我給出一個精簡但可直接用的MRAM驅(qū)動框架包含初始化、寫使能、讀、寫、讀狀態(tài)寄存器這些基本操作。因為MKV44F256VLH16的SDK封裝了DSPI底層傳輸代碼可讀性會好很多你直接替換引腳定義和DSPI編號就能用。#include fsl_dspi.h #include fsl_gpio.h #include fsl_port.h #define MRAM_CS_GPIO_PORT PORTA #define MRAM_CS_GPIO_PIN 10U static void mram_cs_low(void) { GPIO_PinWrite(MRAM_CS_GPIO_PORT, MRAM_CS_GPIO_PIN, 0); } static void mram_cs_high(void) { GPIO_PinWrite(MRAM_CS_GPIO_PORT, MRAM_CS_GPIO_PIN, 1); } void mram_gpio_init(void) { gpio_pin_config_t pin_cfg {kGPIO_DigitalOutput, 1}; PORT_SetPinMux(MRAM_CS_GPIO_PORT, MRAM_CS_GPIO_PIN, kPORT_MuxAsGpio); GPIO_PinInit(MRAM_CS_GPIO_PORT, MRAM_CS_GPIO_PIN, pin_cfg); } void mram_write_enable(void) { uint8_t cmd 0x06; uint8_t rx 0x00; mram_cs_low(); DSPI_MasterWriteBlocking(MRAM_DSPI_BASE, cmd, rx, 1); mram_cs_high(); } uint8_t mram_read_status(void) { uint8_t cmd 0x05; uint8_t status 0x00; mram_cs_low(); DSPI_MasterWriteBlocking(MRAM_DSPI_BASE, cmd, status, 1); DSPI_MasterWriteBlocking(MRAM_DSPI_BASE, cmd, status, 1); mram_cs_high(); return status; } void mram_write_burst(uint32_t addr, const uint8_t *data, uint32_t len) { uint8_t buf[4]; mram_write_enable(); buf[0] 0x02; buf[1] (uint8_t)((addr 16) 0xFF); buf[2] (uint8_t)((addr 8) 0xFF); buf[3] (uint8_t)(addr 0xFF); mram_cs_low(); DSPI_MasterWriteBlocking(MRAM_DSPI_BASE, buf, buf, 4); DSPI_MasterWriteBlocking(MRAM_DSPI_BASE, (uint8_t *)data, (uint8_t *)data, len); mram_cs_high(); } void mram_read_burst(uint32_t addr, uint8_t *data, uint32_t len) { uint8_t buf[4]; buf[0] 0x03; buf[1] (uint8_t)((addr 16) 0xFF); buf[2] (uint8_t)((addr 8) 0xFF); buf[3] (uint8_t)(addr 0xFF); mram_cs_low(); DSPI_MasterWriteBlocking(MRAM_DSPI_BASE, buf, buf, 4); DSPI_MasterWriteBlocking(MRAM_DSPI_BASE, (uint8_t *)data, (uint8_t *)data, len); mram_cs_high(); }讀狀態(tài)寄存器那里我故意用了一次多余的發(fā)送原因在于SPI是全雙工總線每一次時鐘邊沿同時發(fā)送和接收。讀狀態(tài)寄存器時必須先發(fā)出0x05命令讓MRAM把狀態(tài)字節(jié)放到輸出緩沖區(qū)緊接著再產(chǎn)生一個時鐘才能把狀態(tài)字節(jié)采回來。所以在mram_cs_low()之后第一次DSPI_MasterWriteBlocking把命令發(fā)出去第二次發(fā)送產(chǎn)生接收時鐘把狀態(tài)拿到。當(dāng)然SDK的DSPI_MasterReadBlocking也會有類似邏輯封裝方式不同而已但你要理解底層的數(shù)據(jù)流否則容易把命令位移錯。mram_write_burst的時序要特別說明。整條WRITE命令是連續(xù)不中斷的先拉低CS發(fā)送0x02緊接著發(fā)三個地址字節(jié)再連續(xù)發(fā)送數(shù)據(jù)字節(jié)全部發(fā)完才把CS拉高。MRAM內(nèi)部會自動把地址遞增所以你可以在一次CS低電平窗口期寫入任意長度的連續(xù)數(shù)據(jù)沒有頁邊界限制。讀操作同樣如此一次READ命令可以連續(xù)讀出多個字節(jié)。這里有個細(xì)節(jié)如果讀操作超過了整個512KB地址空間末尾地址會回卷到0x00000。工業(yè)應(yīng)用幾乎不會把512KB一次讀完但你要知道這個行為存在別在寫環(huán)形緩沖的時候讓它靜默回卷覆蓋了配置區(qū)。4.4 為什么CS用GPIO控制而不是硬件PCS這是我覺得最值得展開的一個設(shè)計點。NXP的DSPI硬件PCS引腳可以在配置里設(shè)置為自動片選每一幀傳輸開始時自動拉低傳輸結(jié)束后自動拉高。聽起來很方便但問題在于MRAM的一條命令往往由多個字節(jié)組成如果是命令加地址加數(shù)據(jù)的完整傳輸必須要求CS在整個過程中保持低電平。而DSPI的PCS在非持續(xù)模式下每個8位幀結(jié)束就會釋放CS。要做硬件持續(xù)模式也可以需要配置DSPI的持續(xù)模式并配合PCS控制寄存器把CS在整個傳輸過程中鎖住傳輸結(jié)束后再由軟件釋放。這套機(jī)制能工作但多了一層狀態(tài)管理在中途發(fā)生異常時CS可能保持低電平卡死總線排查起來比較麻煩。GPIO控制CS的優(yōu)點是絕對直觀命令字節(jié)發(fā)完了嗎地址字節(jié)發(fā)完了嗎數(shù)據(jù)字節(jié)發(fā)完了嗎好拉高CS。任何一步出錯都只是當(dāng)次傳輸失敗下一次從拉低CS重新開始。而且GPIO控制CS天然支持命令和命令之間的任意等待比如你先發(fā)WRENCS拉高隔任意時間再發(fā)WRITE不需要給外設(shè)寄存器設(shè)置狀態(tài)。所以我統(tǒng)一用GPIO做片選把whichPcs僅當(dāng)一個編號占位符引腳復(fù)用為GPIO方式避免硬件PCS和GPIO同時驅(qū)動同一個物理引腳造成沖突。5. 實際調(diào)試中的問題與排查5.1 常見故障速查表調(diào)試過程中我積累了一張故障對照表方向感很強(qiáng)分享出來現(xiàn)象可能原因處理方式讀回全是0xFFCS沒有拉低或SCK無時鐘WP/HOLD懸空檢查GPIO輸出是否正確把WP/HOLD接VDD數(shù)據(jù)錯位一個字節(jié)CS在命令傳輸中途被拉高幀被切斷確認(rèn)整條命令期間CS保持低用GPIO控制寫使能后寫命令無效WREN后WEL被意外清除檢查WREN到WRITE之間是否執(zhí)行了其他命令確認(rèn)時序偶爾讀出某個位翻轉(zhuǎn)SPI時鐘過高信號完整性差降到20MHzSCK串22歐電阻檢查地線回流掉電后上次數(shù)據(jù)丟失供電在寫數(shù)據(jù)過程中跌落加大去耦電容確保掉電檢測后預(yù)留2ms穩(wěn)定供電連續(xù)寫入后數(shù)據(jù)重疊覆蓋地址回卷或環(huán)形緩沖越界在驅(qū)動里對地址強(qiáng)制掩碼并增加邊界檢查高溫老化后偶發(fā)失敗溫度超過芯片工作范圍確認(rèn)環(huán)境溫度不超85℃貼片芯片測溫要測本體而非環(huán)境5.2 兩個讓我印象深刻的bug第一個bug是CS被硬件PCS和GPIO雙重驅(qū)動。最初版本硬件上把CS接在DSPI的PCS0引腳上代碼里又開了一個GPIO控制同一條線結(jié)果CS信號被兩個輸出源疊加邊緣變緩整個通信時好時壞。后來我把引腳Mux改為純GPIODSPI只用SCK和SOUT、SIN三根線把所有片選邏輯全部交給GPIO問題消失。如果你在調(diào)試中遇到時好時壞的通信故障第一個要查的就是有沒有兩個外設(shè)模塊在驅(qū)動同一個引腳。第二個bug是HOLD引腳懸空惹的禍。原型板上HOLD沒接任何東西導(dǎo)致在連續(xù)讀寫時偶爾出現(xiàn)一幀錯位寫128字節(jié)的數(shù)據(jù)讀回來前面正確中間某一段亂了。起初懷疑MRAM壞道換了芯片還是同樣問題。用示波器看HOLD引腳發(fā)現(xiàn)上面有幾十毫伏的隨機(jī)噪聲正好在SCK邊沿附近觸發(fā)了一次HOLD。把HOLD上拉到VDD后跑了48小時老化測試一幀都沒有錯過。這個教訓(xùn)很簡單但很貴MRAM的所有控制引腳只要數(shù)據(jù)手冊說低有效并且內(nèi)部沒有默認(rèn)上拉的統(tǒng)統(tǒng)外部上拉不要省。6. 在工業(yè)項目中擴(kuò)展使用6.1 日志分區(qū)與環(huán)形緩沖設(shè)計MR25H40CDF的512KB空間在工控數(shù)據(jù)記錄場景里非常充裕但設(shè)計上仍然建議分區(qū)使用不要所有數(shù)據(jù)都堆在一個地址區(qū)間。我推薦三個分區(qū)配置區(qū)、日志區(qū)、掉電保護(hù)區(qū)。配置區(qū)放在起始地址0x00000到0x0FFFF共64KB存設(shè)備序列號、標(biāo)定參數(shù)、通訊地址這類不經(jīng)常變的數(shù)據(jù)。每次寫配置時先寫一個版本號再做一次讀回校驗并且用兩個備份區(qū)交替寫入。如果現(xiàn)場掉電中斷了配置更新下次上電還能從第二個備份恢復(fù)避免設(shè)備變成磚頭。日志區(qū)放在0x10000到0x6FFFF共384KB做成環(huán)形緩沖。每一條日志固定長度比如32字節(jié)包含時間戳、狀態(tài)字、故障碼。寫指針指向當(dāng)前寫入位置每寫一條日志就移動一次。因為MRAM不怕寫環(huán)形緩沖不需要做擦除操作指針繞圈寫就行比Flash方案簡單太多。掉電保護(hù)區(qū)放在0x70000到0x7FFFF共64KB保存掉電瞬間需要立即記錄的狀態(tài)量。平時不頻繁寫只在檢測到掉電信號后利用MCU掉電中斷和儲能電容提供的最后幾毫秒把電機(jī)位置、控制狀態(tài)、累計脈沖數(shù)寫進(jìn)去。上電后先讀這個區(qū)域判斷上次是否正常斷電再決定是否恢復(fù)到掉電前狀態(tài)。6.2 幾個實際經(jīng)驗MRAM在邏輯上接近SRAM但驅(qū)動代碼里還是建議加一層封裝不要讓業(yè)務(wù)代碼直接面對SPI命令。哪怕只是簡單封裝成mram_write(addr, buf, len)和mram_read(addr, buf, len)兩個函數(shù)后續(xù)做日志模塊、配置模塊時也會清爽很多。我甚至在驅(qū)動里主動屏蔽了地址越界凡是地址超過0x7FFFF的請求直接返回錯誤碼避免環(huán)形緩沖寫飛后把配置區(qū)覆蓋掉。工業(yè)現(xiàn)場如果有多個任務(wù)同時訪問MRAM比如一個任務(wù)寫日志另一個任務(wù)讀配置SPI總線會被并發(fā)訪問。這時需要在驅(qū)動層加一個簡單的互斥鎖無論是裸機(jī)的臨界區(qū)還是RTOS的互斥信號量保證每一條MRAM命令都是原子的。不然兩個任務(wù)交錯訪問CS信號會被打斷讀回的數(shù)據(jù)就是亂的。還有一點是關(guān)于掉電檢測的優(yōu)先級。MKV44F256VLH16正常供電在3.3V當(dāng)電源管理單元檢測到主電源跌落時要立即關(guān)掉外部設(shè)備的SPI訪問把總線資源讓給掉電保存任務(wù)。我自己用了一個比較器做掉電檢測當(dāng)VDD低于3.0V時觸發(fā)中斷中斷里禁用DSPI的DMA傳輸然后只執(zhí)行一次MRAM寫操作。這樣即使掉電過程再快最后幾個字節(jié)也能穩(wěn)定寫進(jìn)去。成本方面MR25H40CDF的單價比同容量EEPROM貴一些但考慮到省去的磨損均衡邏輯、調(diào)試時間和售后返修綜合成本反而更劃算。我現(xiàn)在做電機(jī)控制相關(guān)的數(shù)據(jù)保存項目凡是寫入頻率高于每分鐘一次的選擇MRAM寫入頻率很低的才用EEPROM。最后再分享一個小技巧。調(diào)試MRAM驅(qū)動時不要一上來就寫業(yè)務(wù)邏輯先用最簡單的命令流驗證讀寫上電讀狀態(tài)寄存器發(fā)WREN寫一個0x00到0xFFFF的遞增序列再讀回比對。這一步通過了再上自己的業(yè)務(wù)代碼。很多看似玄學(xué)的存儲問題最后定位下來都是SPI時序或者電源噪聲先用最小用例把物理鏈路確定后面的效率會高很多。