戰(zhàn):從時(shí)鐘極性與寄存器到代碼)
簡介面向DSP2812初學(xué)者的SPI通信完整工程資源以“EX04_SPI_LED”實(shí)例為主線講解SPI協(xié)議、硬件資源、寄存器配置和驅(qū)動(dòng)LED亮度控制的實(shí)現(xiàn)方法。資源包含88個(gè)文件以C源碼、頭文件和工程配置文件為主另有obj/out/map等編譯產(chǎn)物涵蓋DSP28_Spi.c、main.c、SPI_LED.pjt等關(guān)鍵文件壓縮包僅358KB便于快速下載和導(dǎo)入CCS工程對(duì)照學(xué)習(xí)。內(nèi)容覆蓋SPI四種模式選擇、時(shí)鐘分頻、片選管理、收發(fā)寄存器及中斷處理等核心環(huán)節(jié)并配有可運(yùn)行的LED驅(qū)動(dòng)示例。通過查看源碼、構(gòu)建日志和調(diào)試輸出可以直觀理解DSP2812 SPI模塊的初始化流程與通信時(shí)序適合正在學(xué)習(xí)DSP外設(shè)開發(fā)或準(zhǔn)備課程設(shè)計(jì)、綜合實(shí)驗(yàn)的學(xué)生參考。已有261人學(xué)習(xí)資源結(jié)構(gòu)清晰是一份輕量實(shí)用、能直接上手調(diào)試的入門資料。 很多年前我第一次調(diào)DSP2812的SPI時(shí)還挺自信的結(jié)果一上電數(shù)據(jù)就亂飛查了半天發(fā)現(xiàn)是時(shí)鐘極性和相位搞錯(cuò)了。那會(huì)兒網(wǎng)上資料少全靠啃英文手冊(cè)和一遍遍試寄存器?,F(xiàn)在回過頭看DSP2812的SPI模塊其實(shí)相當(dāng)規(guī)整只要你把幾條核心配置鏈路搞清楚基本不會(huì)出大問題。這篇就把我實(shí)際用下來的經(jīng)驗(yàn)和踩過的坑完整盤一遍從協(xié)議細(xì)節(jié)到寄存器配置再到完整的可運(yùn)行代碼和排查思路希望能幫你少走彎路。1. 內(nèi)容整體設(shè)計(jì)與思路拆解1.1 先搞懂SPI這四種模式配置才不會(huì)懵SPI本質(zhì)上就是兩個(gè)移位寄存器之間互相倒數(shù)據(jù)主設(shè)備提供時(shí)鐘SCLK數(shù)據(jù)在MOSI和MISO兩條線上按位傳輸。DSP2812的SPI模塊支持四種工作模式區(qū)別就在于時(shí)鐘極性CPOL和時(shí)鐘相位CPHA的組合。所謂CPOL指的是空閑時(shí)SCLK的電平是高還是低CPHA則決定數(shù)據(jù)是在時(shí)鐘的上升沿還是下降沿被采樣。組合起來就是模式0到模式3這是整個(gè)SPI配置里最容易出錯(cuò)的地方。很多初學(xué)者會(huì)直接照抄別人的初始化代碼但如果你外接的從設(shè)備手冊(cè)里寫的是“數(shù)據(jù)在SCLK上升沿采樣空閑時(shí)SCLK為低”那對(duì)應(yīng)的就是模式0。一旦配成模式1數(shù)據(jù)就會(huì)整體偏移半個(gè)周期表現(xiàn)為主機(jī)能發(fā)但收到的數(shù)據(jù)是錯(cuò)位的甚至完全亂碼。DSP2812里控制這個(gè)的是SPICCR寄存器的CLKPOLARITY位和SPICTL寄存器的CLK_PHASE位兩個(gè)位配合起來決定你要的模式。我個(gè)人的習(xí)慣是拿到任何一個(gè)從設(shè)備第一件事就是翻它的數(shù)據(jù)手冊(cè)找到時(shí)序圖確認(rèn)它到底屬于哪一種模式然后再去配置DSP2812。千萬不要想當(dāng)然因?yàn)橥活w芯片的不同型號(hào)默認(rèn)推薦模式可能都不一樣。比如常見的W25Q64 SPI Flash它支持模式0和模式3但很多SD卡模塊在SPI模式下又默認(rèn)是模式0所以“先看手冊(cè)再接線配置”這個(gè)順序千萬不能省。1.2 為什么選SPI而不是I2C或UARTDSP2812同時(shí)有SCI也就是UART、SPI和McBSP等接口很多人會(huì)糾結(jié)用哪個(gè)。SPI的優(yōu)勢在于全雙工、速率高、時(shí)序簡單、沒有地址幀的概念主從之間就是純粹的“你給我一個(gè)字節(jié)我還你一個(gè)字節(jié)”。I2C雖然省引腳但協(xié)議復(fù)雜還要處理應(yīng)答信號(hào)UART雖然簡單但一般只能點(diǎn)對(duì)點(diǎn)而且速率受限。在DSP2812這種老資格的C2000芯片上SPI的時(shí)鐘源來自LSPCLK默認(rèn)情況下LSPCLK是系統(tǒng)時(shí)鐘的一半。比如系統(tǒng)時(shí)鐘跑在150MHz那LSPCLK就是75MHzSPI再經(jīng)過分頻最高可以做到37.5MHz。這個(gè)速率對(duì)于驅(qū)動(dòng)顯示屏、讀寫外部Flash、和ADC芯片通信完全是足夠了。而且SPI只要你線拉得夠短10cm以內(nèi)穩(wěn)定性非??煽俊.?dāng)然SPI也有它的短板比如沒有應(yīng)答機(jī)制主設(shè)備發(fā)出數(shù)據(jù)后無法確認(rèn)從設(shè)備是不是真的收到了通信距離一長就容易受到干擾熱詞里提到的“spi通信方式通信距離”就是這個(gè)痛點(diǎn)。常規(guī)經(jīng)驗(yàn)是SPI適合板內(nèi)通信不要超過20cm線材要盡量短、盡量等長地線要確保共地。如果你是要做板間長距離通信更穩(wěn)妥的選擇是RS485或者CAN而不是硬把SPI線拉長。1.3 硬件片選與軟件片選DSP2812上怎么選SPI的從設(shè)備選擇信號(hào)CS/片選在DSP2812里比較特殊它有硬件片選和軟件片選兩種思路。硬件片選就是把SPISTE引腳復(fù)用成自動(dòng)片選主設(shè)備在每次傳輸前會(huì)自動(dòng)拉低這個(gè)引腳傳輸結(jié)束后再釋放。軟件片選則是你自己用任意一個(gè)GPIO在發(fā)送前手動(dòng)拉低發(fā)送完再手動(dòng)拉高。從我實(shí)際使用的體驗(yàn)來看軟件片選在DSP2812上更靈活也更好用。原因很簡單硬件片選SPISTE引腳的時(shí)序在某些場景下不夠可控比如你要連續(xù)讀一塊Flash數(shù)據(jù)中間不能釋放片選但硬件自動(dòng)片選可能在每字節(jié)之間都會(huì)有個(gè)拉高再拉低的動(dòng)作直接導(dǎo)致通信失敗。軟件片選就沒有這個(gè)問題你想什么時(shí)候拉低、什么時(shí)候釋放完全由你的代碼控制。另外如果你的板子上有多片SPI從設(shè)備軟件片選是唯一合理的選擇——每片分配一個(gè)GPIO即可互不干擾。DSP2812的GPIO本身有限但SPI從設(shè)備通常也就是兩三個(gè)每個(gè)片選占用一個(gè)普通IO還是很劃算的。接線方式就是用GPIO去拉CS和真正復(fù)用SPISTE并沒有什么性能差異。2. 核心細(xì)節(jié)解析與實(shí)操要點(diǎn)2.1 模塊寄存器速查照著配不會(huì)漏DSP2812的SPI寄存器數(shù)量不算多但在配置的時(shí)候一定要按順序來。SPICCR是控制寄存器主要負(fù)責(zé)軟件復(fù)位、時(shí)鐘極性、數(shù)據(jù)字長這些SPICTL是控制寄存器2主要管使能、時(shí)鐘相位、中斷方式等SPIBRR是波特率寄存器SPISTS是狀態(tài)寄存器里面有RXINT_FLAG、TX_INT_FLAG等標(biāo)志位SPIRXBUF和SPITXBUF是接收和發(fā)送緩沖區(qū)。很多人在初始化時(shí)犯的錯(cuò)誤是改了SPICCR但忘了把SPI拉出復(fù)位狀態(tài)。SPICCR的SPI_SWRESET位默認(rèn)是0也就是處于復(fù)位狀態(tài)所有配置都寫不進(jìn)去。正確的順序是先配置其他位最后把SPI_SWRESET置1讓模塊退出復(fù)位開始工作。如果你先置1再改別的寄存器輕則配置不生效重則產(chǎn)生不可預(yù)期的時(shí)序。還有一點(diǎn)值得注意SPICCR的SPI_CHAR位決定字符長度支持1到16位但不建議配成奇奇怪怪的非8位值除非你非常清楚從設(shè)備的協(xié)議要求。DSP2812的SPI硬件會(huì)把接收數(shù)據(jù)放在SPIRXBUF里發(fā)送時(shí)你往SPITXBUF寫數(shù)據(jù)硬件會(huì)自動(dòng)啟動(dòng)傳輸傳輸完成后會(huì)置位相關(guān)標(biāo)志。有些人的代碼死循環(huán)等標(biāo)志等不到多半就是忘了把數(shù)據(jù)先寫進(jìn)緩沖區(qū)或者SPI沒退出復(fù)位。建議配置完寄存器之后先回讀一遍確認(rèn)這一步能省掉后面很多排查時(shí)間。2.2 時(shí)鐘頻率計(jì)算別把分頻系數(shù)算錯(cuò)了DSP2812的SPI波特率不是直接填一個(gè)頻率值而是通過SPIBRR寄存器設(shè)定分頻系數(shù)。SPI時(shí)鐘源是LSPCLK默認(rèn)等于系統(tǒng)時(shí)鐘除以2。如果你想讓SPI工作在比較低的速率可以直接用LSPCLK除以(SPIBRR1)如果希望高速則可以繞過SPIBRR直接在SPICCR里設(shè)置一種特殊的高速模式此時(shí)SPI時(shí)鐘直接等于LSPCLK。舉個(gè)例子系統(tǒng)時(shí)鐘跑150MHzLSPCLK為75MHz。你想得到一個(gè)約1MHz的SPI時(shí)鐘那SPIBRR的值就應(yīng)該是75-174波特率等于75MHz/(741)1MHz。這里有個(gè)容易踩坑的點(diǎn)SPIBRR寄存器只有8位最大只能表示255所以最低波特率也就被限制在75MHz/256大概293kHz。如果你的從設(shè)備要求更低比如有些SD卡初始化時(shí)要求不超過400kHz那你就得考慮把LSPCLK降下來或者在SPI配置里選擇更低的輸入時(shí)鐘。如果想驗(yàn)證配置對(duì)不對(duì)最直接的辦法是把SPICLK引腳接示波器看波形或者用兩個(gè)GPIO模擬一個(gè)簡單的延時(shí)翻轉(zhuǎn)程序來確認(rèn)LSPCLK的實(shí)際值??傊逊诸l公式寫在注釋里后續(xù)改時(shí)鐘頻率的時(shí)候一定要同步修改SPIBRR否則就會(huì)出現(xiàn)通信超時(shí)或數(shù)據(jù)錯(cuò)亂。2.3 數(shù)據(jù)幀長度和中斷標(biāo)志的配合DSP2812的SPI支持1到16位的數(shù)據(jù)長度這在實(shí)際項(xiàng)目中其實(shí)很有用。很多傳感器芯片一次要讀12位或14位數(shù)據(jù)如果你只能用8位模式那就得拆成兩個(gè)字節(jié)來處理比較麻煩。DSP2812允許你把SPI_CHAR設(shè)成你想要的位數(shù)接收數(shù)據(jù)會(huì)存放在SPIRXBUF的低位高位自動(dòng)補(bǔ)零。但在16位模式下有一個(gè)細(xì)節(jié)數(shù)據(jù)是右對(duì)齊的也就是說你發(fā)送的時(shí)候要手動(dòng)把數(shù)據(jù)挪到低8位或低12位從設(shè)備返回的數(shù)據(jù)也一樣需要你在軟件里做掩碼處理。另外中斷標(biāo)志在不同數(shù)據(jù)長度下行為稍有不同在8位模式下每次接收一個(gè)字節(jié)就會(huì)置一次RXINT_FLAG在16位模式下要接收完16位才會(huì)置位。這個(gè)差別會(huì)直接影響你的狀態(tài)機(jī)設(shè)計(jì)如果你的代碼里用的是8位假設(shè)切換到16位模式后整個(gè)流程都會(huì)亂。我一般建議如果不是特別需要盡量保持8位模式把多字節(jié)協(xié)議放在軟件層組合這樣代碼可讀性好也不容易出錯(cuò)。如果確實(shí)需要16位務(wù)必在注釋里寫清楚當(dāng)前SPI被配置成了幾位避免后面維護(hù)時(shí)踩坑。3. 實(shí)操過程與核心環(huán)節(jié)實(shí)現(xiàn)3.1 完整初始化代碼可直接抄作業(yè)下面是一段我在工程中實(shí)際用過的DSP2812 SPI初始化代碼配置為SPI主模式、模式0、8位數(shù)據(jù)波特率約1MHz使用GPIO軟件片選。整個(gè)初始化流程包含了GPIO復(fù)用配置、SPI寄存器初始化、退出復(fù)位三步void SPI_Init(void) { // 1. GPIO復(fù)用配置SPICLK、SPISIMO、SPISOMI GpioMuxRegs.GPFMUX.all | 0x000F; // SPICLK - GPIOF0, SPISIMO - GPIOF1, SPISOMI - GPIOF2 // 注意GPIOF3 是 SPISTE我們不用硬件片選所以不要把它配置成SPI功能 // 2. 軟件片選引腳配置例如使用GPIOF4作為CS GpioMuxRegs.GPFMUX.all 0xFFEF; // GPIOF4作為普通GPIO GpioMuxRegs.GPFDIR.all | 0x0010; // 配置為輸出 // 3. 先拉高片選表示當(dāng)前沒有選中任何從設(shè)備 GpioDataRegs.GPFSET.all 0x0010; // 4. SPI初始化寄存器 SpiaRegs.SPICCR.all 0x0007; // 8位字符時(shí)鐘極性為0軟件復(fù)位狀態(tài) SpiaRegs.SPICTL.all 0x0006; // 主模式時(shí)鐘相位為0禁止中斷 SpiaRegs.SPIBRR 74; // 波特率 LSPCLK / (741)LSPCLK75MHz時(shí)約1MHz // 5. 退出復(fù)位使能SPI SpiaRegs.SPICCR.all 0x0087; // SPI_SWRESET1其他設(shè)置保留 // 6. 清除標(biāo)志位 SpiaRegs.SPISTS.all 0x0000; }這段代碼的核心就是先配置GPIO復(fù)用再配SPI寄存器最后把SPI_SWREST置1。不要小看這個(gè)順序如果GPIO沒有正確復(fù)用為SPI功能后面無論怎么配置SPI寄存器都沒有信號(hào)輸出。另一個(gè)容易漏的是SPICTL里的MASTER_SLAVE位如果不置1芯片會(huì)默認(rèn)工作在從模式結(jié)果就是時(shí)鐘永遠(yuǎn)出不來。3.2 主模式收發(fā)一個(gè)字節(jié)的實(shí)現(xiàn)SPI是全雙工的所以發(fā)送一個(gè)字節(jié)的同時(shí)一定會(huì)收到一個(gè)字節(jié)。即使你只關(guān)心發(fā)送也要把接收寄存器讀走否則接收緩沖區(qū)滿了之后后續(xù)的數(shù)據(jù)會(huì)丟失。下面是一個(gè)簡單的單字節(jié)收發(fā)函數(shù)Uint16 SPI_SendByte(Uint16 data) { // 等待發(fā)送緩沖區(qū)為空 while (SpiaRegs.SPISTS.bit.TX_INT_FLAG 0) {} // 發(fā)送數(shù)據(jù) SpiaRegs.SPITXBUF data; // 等待接收完成 while (SpiaRegs.SPISTS.bit.RX_INT_FLAG 0) {} // 返回接收到的數(shù)據(jù) return SpiaRegs.SPIRXBUF; }這段代碼邏輯上沒問題但實(shí)際項(xiàng)目中我遇到過一個(gè)問題如果SPI時(shí)鐘速率很高發(fā)送完成后RX_INT_FLAG可能還沒及時(shí)置位導(dǎo)致循環(huán)多等了一會(huì)兒影響實(shí)時(shí)性。另一種情況是某些從設(shè)備在CS拉低后需要一點(diǎn)時(shí)間準(zhǔn)備數(shù)據(jù)所以發(fā)送完第一個(gè)字節(jié)后收到的可能是無效數(shù)據(jù)。此時(shí)你先拉低CS再發(fā)送一個(gè)空字節(jié)比如0x00來觸發(fā)從設(shè)備返回?cái)?shù)據(jù)然后把讀到的值丟棄再從第二個(gè)字節(jié)開始取有效數(shù)據(jù)這樣更穩(wěn)定。如果你需要批量收發(fā)數(shù)據(jù)就用狀態(tài)機(jī)來管理先拉低片選、然后循環(huán)調(diào)用SPI_SendByte、最后拉高片選。記住每次通信結(jié)束后要延時(shí)一小段時(shí)間具體值看從設(shè)備手冊(cè)讓從設(shè)備完成內(nèi)部處理再開始下一次操作。對(duì)于W25Q64這類Flash寫命令之后尤其要等內(nèi)部寫周期完成這一段經(jīng)驗(yàn)也是我踩過坑之后才總結(jié)下來的。3.3 讀外部Flash的實(shí)際案例把流程完整走一遍以W25Q64為例讀數(shù)據(jù)的流程是拉低CS發(fā)送讀命令0x03再發(fā)送24位地址高字節(jié)在前然后持續(xù)發(fā)送空字節(jié)讀取數(shù)據(jù)讀完最后一個(gè)字節(jié)后拉高CS。用DSP2812的代碼大致是這樣void SPI_ReadFlash(Uint32 addr, Uint8 *buf, Uint16 len) { Uint16 i; // 選中Flash GpioDataRegs.GPFCLEAR.all 0x0010; // 發(fā)送讀命令 SPI_SendByte(0x03); // 發(fā)送24位地址 SPI_SendByte((addr 16) 0xFF); SPI_SendByte((addr 8) 0xFF); SPI_SendByte(addr 0xFF); // 連續(xù)讀取數(shù)據(jù) for (i 0; i len; i) { buf[i] (Uint8)SPI_SendByte(0xFF); } // 釋放片選 GpioDataRegs.GPFSET.all 0x0010; }這段代碼里最關(guān)鍵的就是SPI_SendByte(0xFF)這一步因?yàn)镾PI是雙向的主設(shè)備要產(chǎn)生時(shí)鐘就必須持續(xù)往發(fā)送寄存器里寫數(shù)據(jù)哪怕你只關(guān)心接收內(nèi)容。很多人一開始不明白“讀Flash為什么要發(fā)數(shù)據(jù)”其實(shí)就是這個(gè)原因。還有一點(diǎn)是地址發(fā)送順序W25Q64的命令格式是高字節(jié)在前不能搞反否則讀到的數(shù)據(jù)永遠(yuǎn)是錯(cuò)的。這個(gè)流程同樣適用于讀寫其他SPI從設(shè)備比如讀取ADC采樣結(jié)果、配置傳感器寄存器等無非是把命令和地址改成對(duì)應(yīng)芯片手冊(cè)里的格式。多加幾次實(shí)踐之后你就會(huì)發(fā)現(xiàn)SPI調(diào)試的核心就一句話把時(shí)鐘搞對(duì)把數(shù)據(jù)對(duì)齊片選時(shí)序按手冊(cè)走。4. 常見問題與排查技巧實(shí)錄4.1 只能發(fā)送不能接收多半是引腳配置錯(cuò)了我剛接觸DSP2812的SPI時(shí)遇到的最詭異問題就是示波器上能看到MOSI有波形但MISO上永遠(yuǎn)是高電平收到的數(shù)據(jù)全是0xFF。排查了很久最后發(fā)現(xiàn)是GPIO復(fù)用沒配好SPISOMI引腳還在普通GPIO輸入模式信號(hào)根本進(jìn)不了SPI模塊。DSP2812的GPIO復(fù)用寄存器比較隱蔽很多時(shí)候你只配了SPICLK和SPISIMO忘了SPISOMI結(jié)果發(fā)送正常、接收異常。所以初始化時(shí)務(wù)必把三個(gè)引腳全部配置為SPI功能并且確認(rèn)GPIO方向寄存器不會(huì)沖突。如果你用了軟件片選還要額外確認(rèn)片選引腳沒有和SPI功能引腳重疊。除此之外還有一個(gè)是硬件層面的問題兩個(gè)設(shè)備之間的地線沒接好。SPI不像RS485那樣有共模抑制能力如果主從設(shè)備不共地MISO線上的信號(hào)參考電壓就不一致大概率收不到正確的數(shù)據(jù)。所以出現(xiàn)通信問題的時(shí)候先檢查物理連接再查代碼順序不要反了。4.2 數(shù)據(jù)錯(cuò)位或亂碼排查時(shí)鐘極性和相位的順序數(shù)據(jù)錯(cuò)位是最典型的SPI故障表現(xiàn)為你發(fā)出正確命令后從設(shè)備返回的數(shù)據(jù)雖然“有點(diǎn)像”但字節(jié)順序?qū)Σ簧匣蛘呙總€(gè)字節(jié)都差一位。這種問題基本可以鎖定在時(shí)鐘極性和相位的配置上。調(diào)試手法很簡單把示波器探頭放在SCLK和MOSI上觀察空閑時(shí)段SCLK的電平以及數(shù)據(jù)變化點(diǎn)是在時(shí)鐘上升沿還是下降沿然后和從設(shè)備手冊(cè)的時(shí)序圖對(duì)比。比如你的CS拉低后SCLK一直是低說明CPOL0數(shù)據(jù)在SCLK上升沿被采樣說明CPHA0那正好是模式0。如果手冊(cè)里寫的是從模式1你卻在初始化里配成模式0那通信必然錯(cuò)。有時(shí)候錯(cuò)位還跟數(shù)據(jù)位長度相關(guān)。比如你把SPI_CHAR配置成了16位但代碼里卻按字節(jié)往里寫那數(shù)據(jù)就會(huì)兩個(gè)字節(jié)黏在一起看起來就是亂碼。遇到這種情況先回讀SPICCR確認(rèn)配置再考慮是不是代碼邏輯問題。4.3 波特率太高導(dǎo)致的不穩(wěn)定降速就正常了SPI速率調(diào)高之后偶爾會(huì)出現(xiàn)“十次通信里失敗一兩次”的隨機(jī)故障。這種隨機(jī)問題最煩人因?yàn)椴皇潜噩F(xiàn)但一旦出現(xiàn)就會(huì)打亂整個(gè)系統(tǒng)。我遇到過的情況是SPI速率跑到20MHz以上后線材稍微長一點(diǎn)就會(huì)出現(xiàn)偶發(fā)錯(cuò)誤降速到10MHz以下就完全穩(wěn)定。排查方法是用示波器測量SCLK的波形看是否存在明顯的過沖和振鈴。如果信號(hào)邊沿不夠陡峭或者出現(xiàn)臺(tái)階那就說明信號(hào)完整性已經(jīng)出問題了。解決辦法有三個(gè)方向一是降低SPI時(shí)鐘頻率二是縮短連接線長度盡量用杜邦線的話不要超過10cm三是在MISO、MOSI、SCLK上串33歐姆左右的電阻來抑制過沖。另外如果你的主控DSP2812和從設(shè)備之間的電平不匹配比如DSP是3.3V從設(shè)備是5V一定要加電平轉(zhuǎn)換芯片或者用開漏加外部上拉的方式絕對(duì)不能直接用5V信號(hào)去灌DSP引腳。這是硬件設(shè)計(jì)層面的紅線一旦燒了引腳就不好玩了。4.4 DMA和SPI配合的注意事項(xiàng)DSP2812的SPI模塊本身不帶FIFO更別說DMA了和STM32那種帶DMA的SPI體驗(yàn)完全不同。那如果要做高速連續(xù)傳輸怎么辦我的經(jīng)驗(yàn)是用SPI中斷每收到一個(gè)字節(jié)就觸發(fā)一次中斷在中斷里讀走數(shù)據(jù)和發(fā)送下一個(gè)字節(jié)。但DSP2812的中斷響應(yīng)是有延遲的SPI速率如果太高字節(jié)之間的間隔不夠中斷響應(yīng)時(shí)間就可能丟數(shù)據(jù)。所以如果你要在DSP2812上用SPI做大容量數(shù)據(jù)傳輸建議把SPI速率控制在2MHz以下或者采用“查詢方式定時(shí)啟動(dòng)”的模式來降低中斷頻率。如果一定要高速可以考慮用McBSP模塊的DMA能力但那樣的話代碼復(fù)雜度會(huì)高很多一般不推薦新手去碰。4.5 常見問題速查表現(xiàn)象可能原因解決辦法SCLK無波形SPI未退出復(fù)位、GPIO未復(fù)用、主從模式配錯(cuò)檢查SPICCR的SWRESET位、GPF復(fù)用、MASTER_SLAVE位發(fā)送正常接收全FFSPISOMI引腳未復(fù)用、MISO接線錯(cuò)誤、未共地配置SPISOMI引腳復(fù)用檢查硬件連接數(shù)據(jù)錯(cuò)位/亂碼CPOL/CPHA配錯(cuò)、數(shù)據(jù)長度不對(duì)對(duì)照手冊(cè)確認(rèn)模式回讀SPICCR確認(rèn)配置偶發(fā)通信失敗速率太快、線太長、信號(hào)過沖降速、縮短線長、串阻尼電阻讀Flash讀到全FF片選時(shí)序不對(duì)、命令格式錯(cuò)誤、Flash內(nèi)部忙核對(duì)片選拉低/拉高時(shí)序檢查命令順序等待寫周期結(jié)束每次排查的時(shí)候先看物理層再看寄存器配置最后才懷疑代碼邏輯。SPI這東西最大的特點(diǎn)就是邏輯簡單、物理敏感多數(shù)問題其實(shí)都出在引腳復(fù)用和片選時(shí)序上。我在實(shí)際項(xiàng)目里最后養(yǎng)成的一個(gè)習(xí)慣是每次初始化完SPI之后先從設(shè)備里讀一個(gè)固定ID寄存器比如Flash的JEDEC ID確認(rèn)能讀到正確的ID再繼續(xù)后續(xù)業(yè)務(wù)邏輯。這相當(dāng)于一個(gè)硬件的握手自檢能在問題發(fā)生的瞬間就把故障范圍鎖定在SPI通信環(huán)節(jié)省下來的調(diào)試時(shí)間絕對(duì)不是一點(diǎn)半點(diǎn)。如果你手里的從設(shè)備沒有ID寄存器那就用一個(gè)固定值的寄存器做同樣的驗(yàn)證辦法總比問題多。本文還有配套的精品資源點(diǎn)擊獲取