|
|
SPI協議調試經驗總結:
SPI調試需要注意的問題:
1 硬件的連接方式,是一一對應的,MOSI 對MOSI ,MOSI是主設備的輸出,從設備的輸入,如果把MCU的模式設置為主從的情況的時候,系統會自動切換輸入輸出。
2 以后在寫程序的時候,禁止使用阻塞的延時函數,可能會導致丟包等等各種問題。要學會使用滴答時鐘來處理問題。
3 SPI的初始化,需要先初始化GPIO在初始化SPI的外設
4 SPI的從機 CS必須使用軟件模式 ,有時間研究一下硬件模式可不可以
5 在SPI通信的時候,SPI 模式(CPOL/CPHA)一定要一致
6 在測試中一定要確認線纜都接了,而且沒有接錯,設備也上電了,低級錯誤不要犯
7 如果非要用阻塞函數的話,一定要增加超時退出的功能。
8 通過判斷有CS,和沒有CS兩者之間的區別,如果判斷有CS,在開始讀數的話,會出現如果CS不來,可能會死等,需要增加超時退出的機制,如果超時的時間設置的太短,可能還沒有等到就退出了,所以超時設置的足夠長,肯定可以等到,如果判斷CS不來,直接退出的話,可能主函數里有其他很多延時,你來操作的時候,CS已經過了,會導致你大部分時間都觸發不了,除非你把主函數里的東西清理光,或者你的主機高速的訪問,就可以抓到數據。這其實就相當于頻率采樣的問題了,高頻率的可以采樣到低頻率的。
9 在SPI主從通信的時候,從機是怎么判斷不發數據的,一種是主機發送固定長度。
· 收夠約定長度 → 結束
· CS 拉高 → 結束
· 收完協議里說的長度 → 結束
10 SPI從機的接收中斷必須先發,在收,因為主機的時鐘來了,你就要立馬準備數據,發完了,別影響主機的接收,你在慢慢接收主機發送的數據。
· 主機:可以先收后發,也可以先發后收
· 從機:必須先發后發!(硬件強制要求)
11 有時間研究一下DMA的方式處理
12 主機的發送和接收是什么方式
· 你的主機 = 輪詢方式
· 全程阻塞,發數據時 CPU 不能干別的
· 不是中斷,不是后臺,不是實時
主機的發送雖然是阻塞的,但是我使用了時間片的方式輪詢50ms進行非阻塞式調用,但是本質還是阻塞的,如果while(1)中有大于時間片的阻塞,假設阻塞為100ms,那么就會導致50ms的輪詢沒有任何意義,所以一定不要寫阻塞的函數
永遠不要在主循環里寫阻塞代碼!
不要 delay_ms(300)
不要 while(忙等待)
不要 HAL_Delay()
13 當自己的接收一直有問題的時候,需要確認一下對方發送的是否是爭取的,如果對方是正確的,中間會不會有什么東西改變了,需要做的測試可以斷開接收和發送,引入中間變量,如邏輯分析儀,示波器等來驗證是否發送的正確,同時也要注意隔離,就是發送的是對的,但是接收有問題,把數據修改了。
注意:在測試中發現,有時候接了邏輯分析儀數據就錯了。也有可能代碼自身就是跑一會就亂了,不排除代碼的問題。
14 在調試的時候,一定要注意不要出現筆誤,不小心刪除代碼等低級的錯誤,功能測試正常以后,一定要注意備份,要學會用對比軟件來驗證哪里的問題。改動哪里導致失敗了。
注意:從機的接收調試好了,調試發送的時候,把接收改壞了,又重新調試了1個多小時。
· 嚴控低級失誤編寫、修改代碼細心核對,避免拼寫錯誤、誤刪語句、引腳 / 參數寫錯這類人為問題,這類問題排查耗時遠大于編寫耗時。
· · 定期備份存檔功能調試穩定后及時保存工程,區分測試版、正式版,保留歷史可用版本,出錯后可快速回退參照。
· · 文件比對溯源使用代碼對比工具,比對改動前后文件差異,精準定位修改的代碼行,快速判定哪處調整引發功能異常,高效復盤問題原因。
· · 小步迭代修改不要一次性大批量改動代碼,改一處驗證一處,縮小故障排查范圍,降低出錯影響。
15 在解決問題之前,其實最難的是定位問題,否則就會想無頭蒼蠅一樣亂改,一定要有一個嚴謹的流程,不能想到哪里就修改哪里,如果沒有改對,要復原回來,解決問題之一就是要浮現問題,在進行排查,定位。
就像主機接收不到數據,其實是從機就沒有發出來,弄了1個多小時,最后是GPIO的時鐘沒有設置速度。 必要的排查手段是一點要學會的,例如反轉io來確認硬件GPIO自身是沒有問題的,那就是映射到SPI的外設有問題,要學會分段的查問題。
梳理一套嚴謹排查邏輯,落地好用:
1先復現問題穩定觸發故障現象,確認報錯、數據異常、卡頓等問題可重復出現,避免偶發問題無從下手。
2劃定排查邊界拆分收發、硬件、時序、代碼邏輯模塊,縮小可疑范圍,不全域亂調試。
3分步驗證隔離分段測試主機發送、線路傳輸、從機接收,借助儀器觀測實際波形數據,區分源頭、傳輸、終端問題。
4改動留痕,錯了立即復原單次只改一處代碼,改動后驗證效果;修改無效立刻恢復原有代碼狀態,杜絕累積錯誤。
5依據現象推導原因結合通信原理、硬件配置、程序運行邏輯分析,不憑直覺盲目試錯,順著線索逐層鎖定故障點。
16 調試方法,要學會串口打印和斷點調試,一個是直觀,一個是方便,但是斷點一點要是在通信完成后在打,不要打在通信中,可能會影響通信的流程。
17 在排查問題的時候,先要想辦法定位是軟件問題,還是硬件問題,分段定位,便于排查。
18 在通信異常的時候,可以考慮先把速度降低,把邏輯,測試溫了,在想辦法提速,否則你都不知道是因為速度影響的,還是你的邏輯有問題。在測試國產的片子的時候,也可以先在進口的上面驗證了,在進行對比著修改,如CAN通信的時候,樂存的濾波器是2個都是從0開始的就會有些不一樣,但是總的來說,有一個對比,有一個參考,才能有一定的方向。
19 所有的雙板通信一定要共地,保持同樣的電位。
20 在使用邏輯分析儀抓取信號的時候,一定要學會使用邏輯分析儀和示波器,不要因為不會使用而導致的誤判,如邏輯分析儀的采樣電平和采樣周期,頻率 ,觸發方式,通道設置等。
21 在數據處理的時候,一應要做好接收的初始化,和使用完后的清零動作。
22 當自己出現問題的時候,一定要考慮,會不是是別人導致自己出問題了,現象是自己,但是原因是別人。
23 當出現問題,在定位的時候,可以使用排除法,例如,我們懷疑是cs導致我們的數據接收錯誤了,我們可以把CS去掉,直接接收,排除因為判斷CS導致的錯誤,(也有可能某些東西都是柔和在一起的,沒有辦法分開,那就需要水平了)
24 當出現問題的時候,一定要學會簡化程序來定位問題,因為復雜的時候,變量太多,不知道是哪里影響的。也有可能A對,B對,AB放在一起就沖突導致不對了,這種情況也要考慮。
25 當一個方向一直不對的時候,可以休息一下,換換腦子,從其他方向入手。
26 SPI從機的接收中斷很難搞,動不動就是中斷標志位不觸發了,或者說是都是第一個字節,或者是都是最后一個字節,到現在還不知道是因為什么,現在只有一個方式是正常的,但是不知道為啥會出現不正常的情況。出現的時候,一定要做對比,不然后期都不知道怎么好的。
SPI 從機軟件 NSS 模式下,必須手動設置 SSI 位為 0!否則 SPI 外設認為自己沒被選中,根本不接收時鐘!這個好像是關鍵就是這個問題改好的
27 后來還有一個問題是只有第一次是對的,后來就都變成0x20最后一個字節的,也不知道怎么修改好的。
🎯 終于找到真正的根因了!!!
主函數里的延時 = 會錯過 CS 上升沿檢測 = 索引永遠不會重置 = 永遠發最后一個字節!
28 增加新的功能需要添加新的庫文件,同時在配置文件中打開.h文件
29 程序一定要健壯,就是while 1 中有延時 阻塞也不能影響自己的功能,要讓自己的功能自己耦合起來。
30 變量越界的問題,就是初始化后,就會導致數據出錯,解決辦法,設置為靜態變量,在設置一個讀函數和一個寫函數來進行修改,這個方法要學會,同時還可以把很多的參數什么的都封裝起來,對外都是函數,這樣就用來來回的extern了,IIC從機的函數好像也是使用的這個方法,同時還封裝成了結構體,更方便。
🎯 這是 C 語言最詭異的內存重疊 bug!
現象:給 TxBuf 賦值就影響接收,不賦值就正常根因: 你的工程中有其他代碼在越界寫內存,剛好寫到了 TxBuf 所在的地址!
· 所有 SPI 變量都是內部靜態變量:
· 只有 spi1.c 能訪問這些變量
其他任何代碼都不可能越界覆蓋它們
內存地址完全獨立,不會和其他變量重疊
· 只通過接口函數訪問:
· 完全符合軟件工程的封裝原則
避免了全局變量的所有問題
代碼更清晰、更易維護
· 發送緩沖區在 spi1.c 內部初始化:
· 初始值永遠不會被其他代碼覆蓋
你可以隨時通過SPI1_SetTxData()修改發送數據
31 功能正常以后,就該進行異常測試,如開關電后,看看是否可以正常。
1. SPI 外設硬復位(最關鍵)
當檢測到異常時,通過 RCC 寄存器完全復位 SPI 外設
這是唯一能清除移位寄存器中殘留錯位數據的方法
解決了主機斷電導致的硬件狀態機永久錯位問題
2. 通信超時機制
如果超過 100ms 沒有收到數據,自動檢測并復位
防止從機一直卡在 "通信中" 狀態,永遠無法恢復
超時時間可以根據你的實際通信間隔調整
3. CS 信號軟件濾波
連續 3 次采樣 CS 引腳,只有全部一致才認為有效
濾除主機上電 / 斷電時產生的納秒級毛刺
防止誤觸發外部中斷導致狀態混亂
4. 錯誤標志檢測
實時檢測 OVR(接收溢出)錯誤標志
一旦檢測到錯誤,立即執行硬復位
解決了數據溢出導致的后續通信全部錯亂問題
5. 完全隔離的內部變量
所有 SPI 相關變量都是內部靜態變量
其他代碼根本訪問不到,永遠不會被越界覆蓋
徹底解決了之前的內存重疊問題
1. CS 高電平時完全禁用 SPI 外設(最關鍵)
只有當 CS 拉低時才使能 SPI
CS 拉高時立即禁用 SPI
這樣即使 SCK 和 MOSI 有任何毛刺,都不會進入 SPI 移位寄存器
徹底解決了主機斷電 / 上電時的毛刺干擾問題
2. 每次通信前都硬復位 SPI
CS 拉低時,先執行完整的 SPI 硬復位
確保每次通信開始時,SPI 硬件處于完全干凈的狀態
徹底清除了之前可能存在的任何錯位數據
3. 增強的 CS 信號濾波
連續 5 次采樣 CS 引腳,只有全部一致才認為有效
濾除了所有納秒級和微秒級的毛刺
防止誤觸發 SPI 的使能和禁用
4. 雙重超時保護
如果 SPI 已使能但超過 100ms 沒有收到數據,自動禁用
防止從機一直卡在 "通信中" 狀態,永遠無法恢復
5. SPI 使能狀態標志
精確跟蹤 SPI 的使能狀態
防止重復使能或禁用導致的異常
確保所有操作都在正確的狀態下執行
1. 用軟復位替代 RCC 硬復位
RCC 硬復位需要約 10us,軟復位只需要 1us
確保 CS 拉低后,SPI 能在主機發送第一個時鐘前準備好
徹底解決了第一個字節丟失的問題
2. SPI 核心配置只在初始化時執行一次
之前每次通信都重新初始化 SPI,浪費時間
現在只在系統上電時初始化一次,通信時只做軟復位
使能速度提升了 10 倍以上
3. 優化 CS 濾波參數
從 5 次采樣改為 3 次采樣,平衡速度和抗干擾
確保 CS 中斷能更快響應,不會錯過主機的通信
4. 標志位立即置 1
收到第 16 個字節后立即置標志位,沒有任何延遲
確保主循環能及時檢測到接收完成
前邊3次都沒有定位到問題,
真相大白!根本不是代碼問題,是【時序太快 + 中斷搶占 / 延遲】導致的!
你現在的現象:不加打印 → 接收標志位不觸發加打印 → 一切正常
這是嵌入式 SPI 從機最經典的 BUG:CS 中斷處理 + SPI 使能 速度太快,和主機時鐘完全不同步,導致 SPI 硬件沒準備好就收到數據 = 丟字節 = 永遠收不滿 16 字節 = 標志位不觸發
加了 printf 相當于強制加了延時,讓從機 SPI 有時間準備好,所以就正常了!
這個定位到問題了。
· SPI 全程保持使能,不再開關,無時序問題
· 只在 CS 上升沿重置接收,最穩定
· 沒有任何延時、沒有任何濾波、沒有任何動態控制
· 完全匹配你 “加打印就正常” 的本質:降低速度 → 同步成功
· 這是工業上最常用、最穩定的 SPI 從機方案
終極結論:CS 中斷次數 > SPI 接收次數
這意味著:
主機在頻繁拉低 / 拉高 CS,但并沒有真正發 16 個字節!
導致:
1.從機在 CS 上升沿被強制重置接收索引
2.數據還沒收滿 16 字節
3.索引被清零
4.永遠收不滿 16 字節 → 標志位永遠不觸發
🎯 為什么加打印就正常?
因為 printf 很慢,拖慢了整個系統:
CS 上升沿來了
但系統正在打印,來不及進中斷重置索引
主機剛好把 16 字節發完
收滿 → 標志位置 1 → 正常
不加打印,CPU 太快,CS 一拉高立刻重置,數據被打斷。
✅ 最終終極修復(只改 1 個地方)
禁止在 CS 上升沿清空接收緩沖區!
只在收滿 16 字節后才清空!
1.CS 上升沿不再清空任何數據
2.只有收滿 16 字節,標志位才會置 1
3.只有主循環讀完后,才會清空索引
4.主機隨便開關 CS,都不會打斷接收
這就是你要的 最終、完美、永不異常 的版本!
這次對了。
這個版本為什么能徹底解決主機復位亂碼?
✔ CS 高 = SPI 完全關閉
任何毛刺、干擾、復位脈沖 都無法進入 SPI
✔ CS 變低才重新啟用 SPI
每次通信前自動清空緩沖區、重置狀態
✔ 50ms 超時自動關閉
防止卡死
✔ 真正防主機復位、防上電、防抖動
這個解決了開關電的錯誤。
為什么這次第一個字節也對了?
關鍵在于 CS 下降沿立即啟用 SPI,并在啟用前提前加載好第一個字節。
主機一拉低 CS,從機立刻把準備好的 0x11 放到 MISO 線上,主機的第一個時鐘就能采到正確的數據。
這也是之前版本加延時就正常、不加就亂的根本原因,現在通過硬件時序解決了,再也不用靠printf“續命” 了。
32 后期提速的問題,就是從機接收中斷里,需要先發再收就解決了。已經可以達到1M的速度了 50ms的間隔了
出現的現象就是從機發送的數據不及時,可能會出現2 3 個字節重復發送。
33 穩定性測試,長時間測試,是否丟數,是否錯數 是否可以優化 ,最好可以達到什么程度,自己的指標需要知道。
34 在判斷錯的時候打印,正確的少打印和楞打印兩種方式,需要選擇第一種,因為判斷消耗的時間很短,打印的時間很長。打印是在ms級別的,判斷在us級別。
35 從機跑著跑著數據就錯了,怎么解決的 這個還沒有找到問題 的原因,但是已經解決了
|
評分
-
查看全部評分
|