數(shù)據(jù)存儲(chǔ)方案:SPI驅(qū)動(dòng)與可靠性設(shè)計(jì))
1. 為什么MRAM在工業(yè)嵌入式場(chǎng)景里越來越受關(guān)注做工業(yè)控制和嵌入式數(shù)據(jù)采集的朋友最近幾年應(yīng)該都有一個(gè)共同的感受傳統(tǒng)EEPROM和NOR Flash在頻繁寫入場(chǎng)景下越來越不夠用了。EEPROM的擦寫壽命通常在100萬次左右NOR Flash大概10萬次而工業(yè)現(xiàn)場(chǎng)很多設(shè)備要求每秒甚至每毫秒記錄一次關(guān)鍵狀態(tài)一年下來寫入次數(shù)輕松突破千萬級(jí)別。這時(shí)候MR25H40CDF這顆MRAM磁性隨機(jī)存儲(chǔ)器就進(jìn)入了視野。MR25H40CDF是Everspin推出的一款4Mbit SPI接口MRAM采用8引腳DFN或SOIC封裝工作電壓3.3V支持最高40MHz的SPI時(shí)鐘。它最大的特點(diǎn)就是近乎無限的擦寫壽命官方標(biāo)稱10^14次以上和無延遲寫入——不需要像Flash那樣先擦后寫也不需要等待內(nèi)部寫周期完成。這兩點(diǎn)對(duì)于工業(yè)數(shù)據(jù)記錄來說價(jià)值非常大。而PIC18F4550是一顆經(jīng)典的Microchip 8位單片機(jī)帶全速USB 2.0接口40引腳PDIP封裝擁有32KB Flash程序存儲(chǔ)器、2KB SRAM、256字節(jié)EEPROM內(nèi)置MSSP模塊可以配置為SPI主模式。它雖然不是新片子但在工業(yè)儀表、數(shù)據(jù)采集模塊、USB通信設(shè)備中存量巨大很多老項(xiàng)目還在持續(xù)維護(hù)和升級(jí)。把MR25H40CDF掛到PIC18F4550的SPI總線上做工業(yè)現(xiàn)場(chǎng)的數(shù)據(jù)存儲(chǔ)和讀取是一個(gè)非常務(wù)實(shí)的組合。這篇文章主要面向正在做工業(yè)嵌入式數(shù)據(jù)記錄、或者手頭有PIC18F4550老平臺(tái)需要擴(kuò)展非易失存儲(chǔ)的工程師。我會(huì)從硬件連接、SPI驅(qū)動(dòng)、讀寫時(shí)序、數(shù)據(jù)管理策略幾個(gè)層面把整個(gè)方案拆開講清楚包括我在實(shí)際項(xiàng)目中踩過的坑和驗(yàn)證過的參數(shù)配置。2. 方案整體設(shè)計(jì)與關(guān)鍵選型考量2.1 為什么選MRAM而不是FRAM或NVSRAM在工業(yè)數(shù)據(jù)存儲(chǔ)這個(gè)領(lǐng)域可選的非易失方案其實(shí)不少EEPROM、NOR Flash、FRAM鐵電存儲(chǔ)器、NVSRAM帶電池的SRAM、MRAM。我當(dāng)初做選型的時(shí)候把這幾種都過了一遍最終選MR25H40CDF主要基于以下幾個(gè)維度的權(quán)衡。FRAM的寫入壽命也很高10^12到10^14次寫入速度也快但FRAM的容量通常偏小常見的是256Kbit到2Mbit而且價(jià)格按位算比MRAM貴不少。NVSRAM雖然寫入無限快但內(nèi)部靠電池維持?jǐn)?shù)據(jù)工業(yè)現(xiàn)場(chǎng)高溫環(huán)境下電池壽命是個(gè)隱患而且封裝里帶電池的器件在振動(dòng)場(chǎng)景下可靠性存疑。NOR Flash容量大、便宜但擦寫壽命和寫入速度是硬傷做頻繁數(shù)據(jù)記錄需要配合磨損均衡算法軟件復(fù)雜度上去了。MR25H40CDF的4Mbit容量512KB對(duì)于工業(yè)數(shù)據(jù)記錄來說剛好夠用——可以存幾萬條帶時(shí)間戳的狀態(tài)記錄或者做環(huán)形緩沖區(qū)存最近的歷史數(shù)據(jù)。SPI接口簡(jiǎn)單PIC18F4550的MSSP模塊直接支持不需要額外的地址總線。最關(guān)鍵的是寫入不需要等待SPI發(fā)送完數(shù)據(jù)數(shù)據(jù)就已經(jīng)在MRAM單元里了沒有內(nèi)部寫周期這對(duì)實(shí)時(shí)性要求高的場(chǎng)景非常友好。2.2 PIC18F4550的SPI資源分配PIC18F4550的MSSP模塊可以工作在SPI主模式或從模式。我們這里用主模式時(shí)鐘由單片機(jī)產(chǎn)生。引腳分配如下PIC18F4550引腳功能連接到MR25H40CDFRC3 (SCK)SPI時(shí)鐘SCKRC5 (SDO)主出從入SIRC4 (SDI)主入從出SORA5 (SS)片選軟件控制CSVDD/VSS電源VDD/VSS這里有個(gè)細(xì)節(jié)需要注意PIC18F4550的MSSP模塊在SPI主模式下SS引腳RA5可以被配置為普通IO片選由軟件控制。我建議一定要用軟件片選不要用硬件片選自動(dòng)控制。原因后面講時(shí)序的時(shí)候會(huì)詳細(xì)說。另外PIC18F4550的SPI時(shí)鐘極性CKP和時(shí)鐘邊沿CKE需要根據(jù)MR25H40CDF的時(shí)序要求來配置。MR25H40CDF支持SPI模式0CPOL0CPHA0和模式3CPOL1CPHA1。我一般用模式0因?yàn)榇蠖鄶?shù)邏輯分析儀和調(diào)試工具默認(rèn)就是模式0抓波形方便。2.3 電源與去耦設(shè)計(jì)MR25H40CDF的工作電壓是2.7V到3.6V典型3.3V。PIC18F4550可以工作在5V或3.3V。如果PIC18F4550用5V供電SPI信號(hào)電平就是5V直接連到MRAM的3.3V引腳會(huì)超過絕對(duì)最大額定值。這時(shí)候需要電平轉(zhuǎn)換可以用74LVC245或者簡(jiǎn)單的電阻分壓但電阻分壓在高頻下波形會(huì)變差。我的建議是整個(gè)系統(tǒng)統(tǒng)一用3.3V供電PIC18F4550在3.3V下可以跑到20MHz需要配置PLLSPI時(shí)鐘也能滿足MRAM的40MHz上限。這樣省掉電平轉(zhuǎn)換硬件簡(jiǎn)單可靠性高。去耦電容方面MR25H40CDF的VDD引腳旁邊必須放一個(gè)0.1uF的陶瓷電容越近越好。如果SPI走線超過10cm建議再并一個(gè)1uF的電容。工業(yè)現(xiàn)場(chǎng)電磁干擾大電源上的毛刺可能導(dǎo)致MRAM寫入錯(cuò)誤這個(gè)電容不能省。3. MR25H40CDF的SPI驅(qū)動(dòng)實(shí)現(xiàn)細(xì)節(jié)3.1 指令集與操作碼解析MR25H40CDF的指令集和標(biāo)準(zhǔn)SPI Flash很像但有幾個(gè)關(guān)鍵區(qū)別。先看操作碼表指令名稱操作碼說明WREN0x06寫使能WRDI0x04寫禁止RDSR0x05讀狀態(tài)寄存器WRSR0x01寫狀態(tài)寄存器READ0x03讀數(shù)據(jù)WRITE0x02寫數(shù)據(jù)SLEEP0xB9進(jìn)入低功耗模式WAKE0xAB喚醒和Flash最大的區(qū)別是MRAM的WRITE指令不需要先擦除。Flash寫之前必須發(fā)WREN然后發(fā)扇區(qū)擦除指令等待擦除完成再發(fā)WREN再發(fā)WRITE。MRAM只需要發(fā)WREN然后直接發(fā)WRITE指令加地址和數(shù)據(jù)就行。整個(gè)流程簡(jiǎn)化了很多寫入延遲也從毫秒級(jí)降到微秒級(jí)。狀態(tài)寄存器的bit0是WEL寫使能鎖存bit1是BP0bit2是BP1bit7是SRWD。上電后WEL是0任何寫操作之前必須先發(fā)WREN把WEL置1。寫操作完成后WEL自動(dòng)清零。這個(gè)機(jī)制和Flash一樣是為了防止誤寫入。3.2 PIC18F4550的SPI初始化代碼下面是我在實(shí)際項(xiàng)目中用的SPI初始化函數(shù)基于XC8編譯器void SPI_Init(void) { // 配置SPI引腳 TRISCbits.TRISC3 0; // SCK輸出 TRISCbits.TRISC5 0; // SDO輸出 TRISCbits.TRISC4 1; // SDI輸入 TRISAbits.TRISA5 0; // CS輸出 // 片選默認(rèn)拉高 CS_MRAM 1; // MSSP配置SPI主模式時(shí)鐘Fosc/16 // SSPCON1: WCOL0, SSPOV0, SSPEN1, CKP0, SSPM0000 SSPCON1 0x20; // 使能SPI模式0時(shí)鐘Fosc/16 // SSPSTAT: SMP0(中間采樣), CKE0(空閑到激活時(shí)鐘) SSPSTAT 0x00; // 時(shí)鐘速度計(jì)算Fosc48MHz時(shí)SPI時(shí)鐘3MHz // 如果需要更快可以改SSPM為0001(Fosc/4)12MHz }這里解釋一下時(shí)鐘配置。PIC18F4550的SPI時(shí)鐘由SSPCON1的SSPM位決定SSPM0000Fosc/4SSPM0001Fosc/16SSPM0010Fosc/64SSPM0011TMR2溢出如果系統(tǒng)時(shí)鐘是48MHzUSB應(yīng)用常用Fosc/16就是3MHzFosc/4就是12MHz。MR25H40CDF支持40MHz所以12MHz完全沒問題。但實(shí)際用的時(shí)候要考慮PCB走線質(zhì)量和信號(hào)完整性我一般先用3MHz調(diào)通再逐步提高到12MHz。3.3 字節(jié)讀寫函數(shù)實(shí)現(xiàn)SPI發(fā)送和接收在PIC18F4550上是同一個(gè)寄存器SSPBUF。發(fā)送時(shí)寫SSPBUF接收時(shí)讀SSPBUF。注意讀之前要等BFBuffer Full標(biāo)志置位否則讀到的可能是舊數(shù)據(jù)。uint8_t SPI_Transfer(uint8_t data) { SSPBUF data; // 寫入發(fā)送數(shù)據(jù) while(!PIR1bits.SSPIF); // 等待傳輸完成 PIR1bits.SSPIF 0; // 清除中斷標(biāo)志 return SSPBUF; // 返回接收數(shù)據(jù) } void MRAM_WriteEnable(void) { CS_MRAM 0; SPI_Transfer(0x06); // WREN指令 CS_MRAM 1; } void MRAM_Write(uint32_t addr, uint8_t *buf, uint16_t len) { MRAM_WriteEnable(); CS_MRAM 0; SPI_Transfer(0x02); // WRITE指令 SPI_Transfer((addr 16) 0xFF); // 地址高8位 SPI_Transfer((addr 8) 0xFF); // 地址中8位 SPI_Transfer(addr 0xFF); // 地址低8位 for(uint16_t i 0; i len; i) { SPI_Transfer(buf[i]); } CS_MRAM 1; } void MRAM_Read(uint32_t addr, uint8_t *buf, uint16_t len) { CS_MRAM 0; SPI_Transfer(0x03); // READ指令 SPI_Transfer((addr 16) 0xFF); SPI_Transfer((addr 8) 0xFF); SPI_Transfer(addr 0xFF); for(uint16_t i 0; i len; i) { buf[i] SPI_Transfer(0x00); // 發(fā)送啞數(shù)據(jù)接收返回 } CS_MRAM 1; }注意MRAM_Read里發(fā)送的是0x00這是啞數(shù)據(jù)dummy byte因?yàn)镾PI是全雙工讀的時(shí)候也要有時(shí)鐘主機(jī)發(fā)送什么無所謂從機(jī)不關(guān)心。但有些SPI從機(jī)要求啞數(shù)據(jù)是特定值MR25H40CDF不挑0x00或0xFF都行。3.4 片選時(shí)序的關(guān)鍵細(xì)節(jié)前面提到一定要用軟件片選原因在這里。MR25H40CDF的CS引腳有兩個(gè)作用一是選中芯片二是界定指令邊界。每個(gè)SPI事務(wù)必須以CS拉低開始以CS拉高結(jié)束。如果在事務(wù)中間CS抖動(dòng)了一下MRAM會(huì)認(rèn)為指令結(jié)束后續(xù)數(shù)據(jù)就丟了。PIC18F4550的硬件SS功能在SPI主模式下如果SSPCON1的SSPM0000且SSEN使能SS引腳會(huì)被自動(dòng)控制。但硬件SS的時(shí)序和MRAM要求的CS時(shí)序不一定完全匹配而且硬件SS在多從機(jī)場(chǎng)景下容易出問題。所以我的做法是把SS功能關(guān)掉RA5配置為普通IO手動(dòng)控制CS。手動(dòng)控制CS的時(shí)候要注意CS拉低之后要等至少10ns再發(fā)第一個(gè)時(shí)鐘MRAM的tCSS參數(shù)。CS拉高之前要確保最后一個(gè)時(shí)鐘已經(jīng)完成tCSH至少10ns。在3MHz SPI時(shí)鐘下一個(gè)時(shí)鐘周期333ns這些納秒級(jí)的延時(shí)自然滿足。但如果SPI跑到40MHz周期25ns就需要檢查一下代碼里CS操作的順序確保沒有額外的指令延遲導(dǎo)致時(shí)序違規(guī)。4. 工業(yè)數(shù)據(jù)存儲(chǔ)策略與實(shí)操流程4.1 數(shù)據(jù)幀格式設(shè)計(jì)工業(yè)現(xiàn)場(chǎng)的數(shù)據(jù)記錄不能只存原始字節(jié)要有結(jié)構(gòu)。我一般設(shè)計(jì)一個(gè)固定長度的數(shù)據(jù)幀包含時(shí)間戳、設(shè)備ID、數(shù)據(jù)類型、數(shù)據(jù)載荷和校驗(yàn)。下面是我在一個(gè)電機(jī)監(jiān)控項(xiàng)目里用的幀格式字段長度說明幀頭2字節(jié)0xAA55用于同步時(shí)間戳4字節(jié)秒級(jí)Unix時(shí)間戳設(shè)備ID1字節(jié)區(qū)分不同設(shè)備數(shù)據(jù)類型1字節(jié)0x01溫度0x02電流0x03轉(zhuǎn)速數(shù)據(jù)載荷8字節(jié)浮點(diǎn)數(shù)或整型CRC162字節(jié)校驗(yàn)幀頭到載荷幀尾1字節(jié)0x0D一幀總共19字節(jié)。512KB的MRAM可以存大約27000幀。如果每秒記錄一幀可以存7.5小時(shí)。如果每分鐘記錄一幀可以存18天。實(shí)際項(xiàng)目中我會(huì)根據(jù)需求調(diào)整記錄頻率或者用環(huán)形緩沖區(qū)覆蓋最舊的數(shù)據(jù)。4.2 環(huán)形緩沖區(qū)實(shí)現(xiàn)環(huán)形緩沖區(qū)是工業(yè)數(shù)據(jù)記錄的經(jīng)典方案。MRAM的地址空間從0x00000到0x7FFFF512KB。我把它分成兩部分前4KB存元數(shù)據(jù)寫指針、讀指針、總記錄數(shù)、設(shè)備配置后面508KB存數(shù)據(jù)幀。元數(shù)據(jù)區(qū)的結(jié)構(gòu)typedef struct { uint32_t write_ptr; // 下一個(gè)寫入位置相對(duì)于數(shù)據(jù)區(qū)起始 uint32_t read_ptr; // 下一個(gè)讀取位置 uint32_t total_count; // 總寫入幀數(shù) uint32_t magic; // 0x4D52414D (MRAM) uint8_t reserved[240]; // 預(yù)留 } MRAM_Meta_t;每次寫入一幀之前先讀元數(shù)據(jù)計(jì)算寫入地址寫數(shù)據(jù)幀然后更新write_ptr和total_count再把元數(shù)據(jù)寫回。這里有個(gè)關(guān)鍵點(diǎn)元數(shù)據(jù)的更新必須原子化。如果寫完數(shù)據(jù)幀但元數(shù)據(jù)沒更新下次上電后write_ptr還是舊的新數(shù)據(jù)會(huì)被覆蓋。我的做法是元數(shù)據(jù)存兩份地址0x0000和0x0800各一份每份帶CRC。寫入時(shí)先寫數(shù)據(jù)幀再寫第一份元數(shù)據(jù)再寫第二份元數(shù)據(jù)。上電初始化時(shí)讀兩份元數(shù)據(jù)比較CRC和write_ptr取有效的那份。如果兩份都無效說明MRAM是全新的或者數(shù)據(jù)損壞就初始化元數(shù)據(jù)。4.3 寫入速度實(shí)測(cè)與優(yōu)化我用邏輯分析儀抓過實(shí)際波形在SPI時(shí)鐘3MHz下寫一幀19字節(jié)的時(shí)間構(gòu)成如下CS拉低到第一個(gè)時(shí)鐘約1us指令地址4字節(jié) × 8bit / 3MHz 10.7us數(shù)據(jù)載荷19字節(jié) × 8bit / 3MHz 50.7usCS拉高到下一個(gè)事務(wù)約2us元數(shù)據(jù)更新讀寫約200us總計(jì)約265us。如果SPI時(shí)鐘提到12MHz數(shù)據(jù)部分可以縮短到12.7us但元數(shù)據(jù)更新的時(shí)間主要花在SPI事務(wù)的固定開銷上提升有限。所以瓶頸不在SPI時(shí)鐘而在元數(shù)據(jù)管理策略。優(yōu)化方法不要每寫一幀就更新元數(shù)據(jù)??梢栽赟RAM里緩存write_ptr每寫10幀或者每1秒更新一次元數(shù)據(jù)。這樣元數(shù)據(jù)更新頻率降低10倍整體寫入速度提升明顯。但代價(jià)是掉電時(shí)可能丟失最近幾幀的元數(shù)據(jù)不過數(shù)據(jù)幀本身已經(jīng)寫進(jìn)MRAM了只是write_ptr沒更新下次上電后新數(shù)據(jù)會(huì)被覆蓋。如果對(duì)數(shù)據(jù)完整性要求極高可以每幀都更新元數(shù)據(jù)犧牲速度換可靠性。4.4 讀取流程與數(shù)據(jù)導(dǎo)出讀取數(shù)據(jù)有兩種模式實(shí)時(shí)讀取和歷史導(dǎo)出。實(shí)時(shí)讀取是設(shè)備正常運(yùn)行時(shí)上位機(jī)通過USB或串口發(fā)送指令單片機(jī)從MRAM里讀最新幾幀返回。歷史導(dǎo)出是設(shè)備停機(jī)維護(hù)時(shí)把MRAM里的數(shù)據(jù)全部讀出來上傳到PC。實(shí)時(shí)讀取的實(shí)現(xiàn)比較簡(jiǎn)單根據(jù)read_ptr計(jì)算地址讀一幀CRC校驗(yàn)通過USB CDC發(fā)送。PIC18F4550的USB模塊可以配置成CDC類在PC上顯示為虛擬串口波特率隨便設(shè)USB CDC不受波特率限制。歷史導(dǎo)出的數(shù)據(jù)量比較大508KB全部讀出來在3MHz SPI下需要約1.4秒508K × 8 / 3M。如果通過USB全速12Mbps傳輸理論上一秒多能傳完。實(shí)際測(cè)試中加上協(xié)議開銷和PC端處理大概3到5秒。這個(gè)速度對(duì)于維護(hù)場(chǎng)景完全可以接受。導(dǎo)出的時(shí)候要注意讀取過程中設(shè)備不能寫入否則read_ptr和write_ptr會(huì)沖突。我的做法是進(jìn)入導(dǎo)出模式前先停止數(shù)據(jù)記錄任務(wù)把write_ptr和read_ptr快照到SRAM然后按快照讀取。導(dǎo)出完成后恢復(fù)記錄任務(wù)。5. 常見問題與排查技巧實(shí)錄5.1 寫入后讀出來全是0xFF或0x00這是新手最常遇到的問題。原因通常有三個(gè)一是WREN指令沒發(fā)或者發(fā)完WREN后CS拉高了WEL位被清除二是地址發(fā)錯(cuò)了寫到了未使用的區(qū)域三是SPI模式配置不對(duì)數(shù)據(jù)采樣邊沿錯(cuò)了。排查步驟先用邏輯分析儀抓SPI波形看WREN指令0x06后面CS是否保持低電平直到WRITE指令發(fā)完。MRAM要求WREN和WRITE可以在同一個(gè)CS低電平周期內(nèi)完成也可以分開。我一般分開做WREN一個(gè)CS周期WRITE另一個(gè)CS周期。但要注意WREN之后WEL位保持直到下一個(gè)WRITE完成或者WRDI。如果波形沒問題讀一下狀態(tài)寄存器RDSR0x05看bit0WEL是否為1。如果WEL是0說明WREN沒生效。檢查CS時(shí)序WREN指令發(fā)送時(shí)CS必須拉低發(fā)送完可以拉高WEL會(huì)保持。5.2 SPI通信不穩(wěn)定偶爾讀到錯(cuò)誤數(shù)據(jù)工業(yè)現(xiàn)場(chǎng)電磁干擾大SPI走線長的時(shí)候容易出現(xiàn)位錯(cuò)誤。我遇到過的情況是MRAM和單片機(jī)在同一塊板上但SPI走線經(jīng)過了電機(jī)驅(qū)動(dòng)區(qū)域電機(jī)啟動(dòng)時(shí)SPI數(shù)據(jù)偶爾出錯(cuò)。解決方法一是降低SPI時(shí)鐘從12MHz降到3MHz給信號(hào)建立更多時(shí)間二是在SCK和MOSI上串22歐姆電阻抑制反射三是在MRAM的VDD和VSS之間加0.1uF和1uF電容靠近芯片引腳四是軟件上加CRC校驗(yàn)發(fā)現(xiàn)錯(cuò)誤就重讀。CRC校驗(yàn)我用的是一幀一校驗(yàn)讀出來先算CRC16和幀尾的CRC比較不匹配就丟棄或者重讀。重讀最多3次3次都失敗就記錄錯(cuò)誤日志。實(shí)際跑下來加了CRC之后數(shù)據(jù)可靠性提升非常明顯誤碼率從萬分之一降到百萬分之一以下。5.3 掉電后數(shù)據(jù)丟失或元數(shù)據(jù)損壞MRAM本身是非易失的掉電數(shù)據(jù)不會(huì)丟。但元數(shù)據(jù)更新過程中掉電可能導(dǎo)致write_ptr和實(shí)際數(shù)據(jù)不一致。比如數(shù)據(jù)幀寫完了元數(shù)據(jù)還沒更新掉電后write_ptr指向舊位置下次上電后新數(shù)據(jù)覆蓋了剛寫的那幀。解決方案是雙份元數(shù)據(jù)加CRC。寫入順序?qū)憯?shù)據(jù)幀 → 寫元數(shù)據(jù)A → 寫元數(shù)據(jù)B。上電時(shí)讀A和B如果A的CRC有效且write_ptr B的write_ptr用A否則用B。如果兩個(gè)都無效用默認(rèn)值初始化。還有一種情況是掉電時(shí)正在寫元數(shù)據(jù)導(dǎo)致元數(shù)據(jù)A寫了一半。這時(shí)候A的CRC肯定無效B還是舊的但有效用B恢復(fù)。最壞情況是A和B都寫了一半但概率極低因?yàn)樵獢?shù)據(jù)只有幾十字節(jié)寫入時(shí)間幾十微秒掉電恰好落在兩個(gè)元數(shù)據(jù)寫入之間的概率很小。5.4 常見問題速查表現(xiàn)象可能原因排查方法解決措施讀出來全0xFFCS時(shí)序錯(cuò)誤邏輯分析儀抓CS和SCK確保CS在事務(wù)期間保持低寫入不生效WREN未執(zhí)行讀狀態(tài)寄存器bit0寫前先發(fā)WREN數(shù)據(jù)偶爾錯(cuò)誤SPI時(shí)鐘太快降低時(shí)鐘測(cè)試降到3MHz或加CRC元數(shù)據(jù)損壞掉電時(shí)寫入讀雙份元數(shù)據(jù)雙份CRC恢復(fù)邏輯通信完全無響應(yīng)電源或接線測(cè)VDD和CS電壓檢查3.3V和地連接讀取速度慢SPI時(shí)鐘低測(cè)SCK頻率提高SSPM配置5.5 實(shí)操心得先調(diào)通再優(yōu)化我剛開始做這個(gè)方案的時(shí)候想一步到位把SPI配到12MHz結(jié)果波形慘不忍睹數(shù)據(jù)錯(cuò)誤率很高。后來退回到3MHz先把讀寫邏輯調(diào)通確認(rèn)數(shù)據(jù)正確再逐步提高時(shí)鐘。每提高一檔就用邏輯分析儀看波形用CRC統(tǒng)計(jì)誤碼率。最后在12MHz下穩(wěn)定運(yùn)行但3MHz到12MHz之間試了四五次才找到穩(wěn)定的配置。另一個(gè)心得是MRAM的寫入不需要等待但SPI事務(wù)之間的間隔不能太短。我試過連續(xù)寫CS拉高后立刻拉低發(fā)下一個(gè)指令結(jié)果偶爾出錯(cuò)。后來在CS拉高后加了一個(gè)NOP延時(shí)約1us就穩(wěn)定了。查手冊(cè)發(fā)現(xiàn)MRAM的tSHSLCS高電平最短時(shí)間是10ns理論上不需要延時(shí)但實(shí)際PCB上有寄生電容CS上升沿需要時(shí)間加個(gè)小延時(shí)更保險(xiǎn)。6. 性能優(yōu)化與擴(kuò)展思路6.1 SPI時(shí)鐘提升的邊界條件MR25H40CDF標(biāo)稱最高40MHz SPI時(shí)鐘但實(shí)際能跑多快取決于PCB走線、電源質(zhì)量和單片機(jī)SPI輸出能力。PIC18F4550在48MHz系統(tǒng)時(shí)鐘下SPI最高是Fosc/412MHz。如果想更快需要換更高級(jí)的單片機(jī)比如PIC32系列或者STM32SPI可以跑到40MHz以上。在12MHz下我實(shí)測(cè)寫一幀19字節(jié)含元數(shù)據(jù)更新約120us讀一幀約60us。如果去掉元數(shù)據(jù)更新寫一幀只要約30us。這個(gè)速度對(duì)于大多數(shù)工業(yè)數(shù)據(jù)記錄場(chǎng)景已經(jīng)足夠了。如果要求更高可以考慮用DMA或者雙緩沖但PIC18F4550沒有DMA只能靠?jī)?yōu)化代碼。6.2 數(shù)據(jù)壓縮與存儲(chǔ)效率508KB的數(shù)據(jù)區(qū)如果每幀19字節(jié)能存27000多幀。如果數(shù)據(jù)有規(guī)律可以做壓縮。比如溫度數(shù)據(jù)變化緩慢可以只存變化量delta編碼每幀從8字節(jié)載荷壓縮到2字節(jié)。這樣一幀變成13字節(jié)存儲(chǔ)量提升30%以上。但壓縮會(huì)增加CPU負(fù)擔(dān)和代碼復(fù)雜度。PIC18F4550的指令周期是48MHz/412MIPS做簡(jiǎn)單的delta編碼沒問題做復(fù)雜的壓縮算法就吃力了。我的建議是如果數(shù)據(jù)變化確實(shí)緩慢用簡(jiǎn)單的差分編碼如果數(shù)據(jù)隨機(jī)性大不要壓縮直接存原始數(shù)據(jù)可靠性優(yōu)先。6.3 多設(shè)備SPI總線擴(kuò)展一個(gè)PIC18F4550的SPI總線可以掛多個(gè)MRAM芯片用不同的CS引腳選擇。比如掛4片MR25H40CDF總?cè)萘?MB。CS引腳可以用RA5、RB0、RB1、RB2軟件控制。但要注意SPI總線的負(fù)載效應(yīng)每增加一個(gè)從機(jī)SCK和MOSI上的電容就增加高速下波形會(huì)變差。如果掛4片以上建議加SPI緩沖器或者用多路復(fù)用器。另一個(gè)擴(kuò)展方向是用MRAM做程序存儲(chǔ)器的擴(kuò)展。PIC18F4550只有32KB Flash如果程序超過32KB可以把不常用的函數(shù)或數(shù)據(jù)表放到MRAM里運(yùn)行時(shí)按需讀取。但MRAM是SPI接口不能直接執(zhí)行代碼只能做數(shù)據(jù)存儲(chǔ)。如果需要執(zhí)行代碼得用并行接口的MRAM或者帶XIP功能的Flash。6.4 低功耗設(shè)計(jì)考慮工業(yè)現(xiàn)場(chǎng)有些設(shè)備是電池供電的低功耗很重要。MR25H40CDF支持SLEEP模式0xB9指令進(jìn)入后電流降到幾微安。PIC18F4550也有多種低功耗模式比如Sleep模式電流可以降到0.1uA以下。我的做法是數(shù)據(jù)記錄間隔期間單片機(jī)進(jìn)入SleepMRAM進(jìn)入SLEEP。定時(shí)器或者外部中斷喚醒后先發(fā)WAKE指令0xAB喚醒MRAM等待tWAK約100us再開始SPI通信。這樣平均功耗可以做到毫安級(jí)以下用一節(jié)18650電池能撐幾個(gè)月。但要注意MRAM從SLEEP喚醒后需要重新發(fā)WREN才能寫入。而且SLEEP期間CS必須保持高電平否則MRAM可能被意外喚醒。這些細(xì)節(jié)在低功耗調(diào)試的時(shí)候很容易忽略我當(dāng)初就因?yàn)镃S在Sleep期間被拉低導(dǎo)致MRAM一直處于喚醒狀態(tài)功耗降不下來。7. 寫在最后的一些個(gè)人體會(huì)這個(gè)方案我從2019年第一次用在電機(jī)監(jiān)控項(xiàng)目上到現(xiàn)在陸續(xù)改了三四版中間踩過的坑基本都寫在上面了。MR25H40CDF這顆芯片給我的感覺是貴有貴的道理。單片價(jià)格比同容量的NOR Flash貴好幾倍但在頻繁寫入、高可靠性要求的場(chǎng)景下省下來的軟件復(fù)雜度和維護(hù)成本遠(yuǎn)超硬件差價(jià)。PIC18F4550雖然老但生態(tài)成熟XC8編譯器穩(wěn)定USB功能開箱即用配合MRAM做數(shù)據(jù)記錄模塊整體BOM成本可控開發(fā)周期短。如果你手頭有PIC18F4550的存量項(xiàng)目需要擴(kuò)展存儲(chǔ)或者正在選型工業(yè)數(shù)據(jù)記錄方案這個(gè)組合值得認(rèn)真考慮。最后分享一個(gè)小技巧MRAM的WRITE指令支持跨頁寫入不像Flash那樣有頁邊界限制。你可以一次寫任意長度只要不超過芯片容量。但實(shí)際用的時(shí)候我建議單次寫入不要超過256字節(jié)因?yàn)镻IC18F4550的SRAM只有2KB緩沖區(qū)太大容易和其他任務(wù)沖突。256字節(jié)的緩沖區(qū)加上元數(shù)據(jù)和??臻gSRAM占用在可控范圍內(nèi)。