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