I2C協議詳解及STM32 HAL庫硬件I2C卡死問題分析

一、I2C協議詳解

1. I2C協議概述

Inter-Integrated Circuit (I2C) 是由 Philips 半導體(現 NXP 半導體)于 1980 年代設計的一種同步串行通信總線協議。該協議采用半雙工通信模式,支持多主從架構,專為短距離、低速率的芯片間通信優化。

1.1 I2C協議特點
  • 兩線制:僅需兩根信號線(SDA-數據線,SCL-時鐘線)

  • 多主從結構:支持多個主設備和多個從設備

  • 地址尋址:每個從設備有唯一地址

  • 速度標準

    • 標準模式:100kbps

    • 快速模式:400kbps

    • 高速模式:3.4Mbps

    • 超快速模式:5Mbps

  • 半雙工通信:同一時間只能發送或接收數據

  • 總線仲裁:支持多主設備沖突檢測和仲裁

2. I2C物理層

2.1 硬件連接

I2C總線由兩根線組成:

  • SCL(Serial Clock):時鐘線,由主設備產生

  • SDA(Serial Data):數據線,用于雙向數據傳輸

所有設備都并聯在這兩條線上,采用開漏輸出結構,需要外接上拉電阻(通常4.7kΩ)。

2.2 電氣特性
  • 邏輯"1":高電平(上拉電阻拉高)

  • 邏輯"0":低電平(設備主動拉低)

  • 開漏輸出結構允許不同電源電壓的設備共存于同一總線

3. I2C協議層

3.1 數據有效性
  • 數據在SCL高電平時必須保持穩定,變化只能在SCL低電平時發生

  • 起始和停止條件例外,它們在SCL高電平時改變SDA狀態

3.2 起始和停止條件
  • 起始條件(START):SCL高電平時,SDA由高變低

  • 停止條件(STOP):SCL高電平時,SDA由低變高

  • 重復起始條件(Repeated START):在不發送停止條件的情況下,主設備再次發送起始條件

3.3 數據傳輸格式

每個字節傳輸包含:

  1. 起始條件

  2. 7位/10位從設備地址 + 1位讀寫標志(0-寫,1-讀)

  3. 從設備應答(ACK)

  4. 數據字節(8位)

  5. 接收方應答(ACK/NACK)

  6. 停止條件

3.4 應答機制
  • ACK:接收方在第9個時鐘周期拉低SDA

  • NACK:接收方在第9個時鐘周期保持SDA高電平

3.5 7位和10位地址模式
  • 7位地址:可尋址128個設備(實際112個,部分地址保留)

  • 10位地址:可擴展至1024個設備

4. I2C通信流程

4.1 主設備發送數據到從設備
  1. 主設備發送起始條件

  2. 主設備發送從設備地址(7位/10位)+ 寫標志(0)

  3. 從設備應答(ACK)

  4. 主設備發送數據字節

  5. 從設備應答(ACK)

  6. 重復4-5直到數據傳輸完成

  7. 主設備發送停止條件

4.2 主設備從從設備讀取數據
  1. 主設備發送起始條件

  2. 主設備發送從設備地址(7位/10位)+ 讀標志(1)

  3. 從設備應答(ACK)

  4. 從設備發送數據字節

  5. 主設備應答(ACK/NACK)

  6. 重復4-5直到數據傳輸完成

  7. 主設備發送停止條件

4.3 復合格式(寫后讀)
  1. 主設備發送起始條件

  2. 主設備發送從設備地址 + 寫標志

  3. 從設備應答

  4. 主設備發送寄存器地址/命令

  5. 從設備應答

  6. 主設備發送重復起始條件

  7. 主設備發送從設備地址 + 讀標志

  8. 從設備應答

  9. 從設備發送數據

  10. 主設備應答/NACK

  11. 主設備發送停止條件

5. I2C時鐘同步與仲裁

5.1 時鐘同步

當多個主設備同時傳輸時,SCL線通過"線與"機制實現時鐘同步:

  • 只有所有主設備都釋放SCL(輸出高電平)時,SCL才變高

  • 任一主設備拉低SCL將使SCL保持低電平

5.2 總線仲裁
  • 基于SDA線上的數據仲裁

  • 主設備在發送每位數據時檢測SDA狀態

  • 如果檢測到與自己發送的數據不符,則失去仲裁權

  • 仲裁不會破壞數據,失敗的設備自動轉為從設備

6. I2C擴展特性

6.1 時鐘拉伸

從設備可以通過保持SCL低電平來暫停通信(時鐘拉伸),直到準備好繼續傳輸。

6.2 總線超時

防止設備長時間占用總線:

  • SCL低超時(最大允許SCL低電平時間)

  • 總線空閑超時(起始條件后無活動的最長時間)

二、STM32 HAL庫硬件I2C卡死問題分析

1. STM32硬件I2C架構

STM32的I2C外設實現了標準I2C協議,支持:

  • 多主模式

  • 7位/10位地址

  • 標準模式(100kHz)和快速模式(400kHz)

  • 時鐘拉伸

  • DMA支持

  • 錯誤檢測

2. 常見卡死原因及解決方案

2.1 總線沖突或仲裁失敗

現象:I2C總線卡死,SCL或SDA線被拉低無法恢復

原因

  • 多主設備競爭導致仲裁失敗

  • 從設備異常導致總線占用

解決方案

  1. 實現超時機制,超時后重新初始化I2C

  2. 檢查總線狀態,必要時發送停止條件

  3. 增加總線仲裁處理邏輯

// 超時處理示例
#define I2C_TIMEOUT 100 // 100msHAL_StatusTypeDef I2C_WriteWithTimeout(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size)
{uint32_t tickstart = HAL_GetTick();HAL_StatusTypeDef status;while((status = HAL_I2C_Master_Transmit(hi2c, DevAddress, pData, Size, 10)) != HAL_OK){if((HAL_GetTick() - tickstart) > I2C_TIMEOUT){// 超時處理I2C_Recovery(hi2c);return HAL_TIMEOUT;}}return status;
}
2.2 從設備無響應或異常

現象:I2C通信卡在等待ACK階段

原因

  • 從設備掉電或故障

  • 從設備地址錯誤

  • 從設備忙(如EEPROM正在寫入)

解決方案

  1. 檢查從設備電源和連接

  2. 確認從設備地址正確

  3. 實現ACK polling(對于EEPROM等設備)

  4. 增加重試機制

// ACK polling示例(針對EEPROM)
void EEPROM_WriteByte(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint8_t Data)
{uint8_t buffer[3];buffer[0] = MemAddress >> 8;   // 高地址字節buffer[1] = MemAddress & 0xFF; // 低地址字節buffer[2] = Data;// 嘗試寫入,直到收到ACKwhile(HAL_I2C_Master_Transmit(hi2c, DevAddress, buffer, 3, 10) != HAL_OK){// 可選:添加延遲和超時處理HAL_Delay(5);}
}
2.3 時鐘拉伸處理不當

現象:SCL線被從設備長時間拉低,通信停滯

原因

  • 從設備使用時鐘拉伸但主設備未正確處理

  • 從設備處理時間過長

解決方案

  1. 啟用I2C時鐘拉伸功能(如果支持)

  2. 增加SCL低超時檢測

  3. 調整從設備配置減少拉伸時間

// STM32CubeMX中啟用時鐘拉伸
// 在I2C配置中設置Clock No Stretch Mode為Disabled
 
2.4 中斷或DMA配置問題

現象:I2C中斷或DMA未正確觸發,導致狀態機卡死

原因

  • 中斷優先級配置不當

  • DMA通道配置錯誤

  • 中斷未正確清除

解決方案

  1. 檢查中斷優先級設置

  2. 驗證DMA配置

  3. 確保所有中斷標志被正確清除

// 中斷處理示例
void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *hi2c)
{// 處理錯誤,如總線錯誤、ACK錯誤等if(__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_AF)) // ACK失敗{__HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_AF);// 重試或恢復處理}// 其他錯誤處理...
}
 
2.5 總線噪聲或信號完整性問題

現象:隨機通信失敗或卡死

原因

  • 長距離傳輸導致信號衰減

  • 電磁干擾

  • 上拉電阻值不合適

解決方案

  1. 縮短總線長度

  2. 增加適當的濾波電容

  3. 調整上拉電阻值(通常4.7kΩ-10kΩ)

  4. 使用屏蔽電纜

3. STM32 HAL庫硬件I2C常見問題

3.1 狀態機卡死

STM32硬件I2C是狀態機驅動的,異常情況下可能進入錯誤狀態無法恢復。

解決方案

void I2C_Recovery(I2C_HandleTypeDef *hi2c)
{// 1. 禁用I2C__HAL_I2C_DISABLE(hi2c);// 2. 手動切換SCL線直到釋放總線GPIO_InitTypeDef GPIO_InitStruct = {0};// 配置SCL為通用開漏輸出GPIO_InitStruct.Pin = hi2c->Instance == I2C1 ? GPIO_PIN_6 : (hi2c->Instance == I2C2 ? GPIO_PIN_10 : GPIO_PIN_7);GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);// 發送時鐘脈沖直到SDA變高uint32_t timeout = 1000;while((HAL_GPIO_ReadPin(GPIOB, hi2c->Instance == I2C1 ? GPIO_PIN_7 : (hi2c->Instance == I2C2 ? GPIO_PIN_11 : GPIO_PIN_8)) == GPIO_PIN_RESET && timeout--){HAL_GPIO_WritePin(GPIOB, hi2c->Instance == I2C1 ? GPIO_PIN_6 : (hi2c->Instance == I2C2 ? GPIO_PIN_10 : GPIO_PIN_7), GPIO_PIN_SET);HAL_Delay(1);HAL_GPIO_WritePin(GPIOB, hi2c->Instance == I2C1 ? GPIO_PIN_6 : (hi2c->Instance == I2C2 ? GPIO_PIN_10 : GPIO_PIN_7), GPIO_PIN_RESET);}// 3. 發送停止條件HAL_GPIO_WritePin(GPIOB, hi2c->Instance == I2C1 ? GPIO_PIN_6 : (hi2c->Instance == I2C2 ? GPIO_PIN_10 : GPIO_PIN_7), GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOB, hi2c->Instance == I2C1 ? GPIO_PIN_7 : (hi2c->Instance == I2C2 ? GPIO_PIN_11 : GPIO_PIN_8), GPIO_PIN_SET);HAL_Delay(1);// 4. 重新初始化I2CHAL_I2C_Init(hi2c);
}
 
3.2 HAL庫函數阻塞問題

某些HAL_I2C函數可能因等待標志位而長時間阻塞。

解決方案

  1. 使用非阻塞模式(中斷或DMA)

  2. 實現超時機制

  3. 使用較低層API(LL庫)獲得更直接控制

4. 最佳實踐建議

  1. 始終實現超時機制:所有I2C操作都應包含超時處理

  2. 添加總線恢復函數:準備總線恢復函數以備異常情況

  3. 合理配置時鐘:確保I2C時鐘頻率適合所有設備

  4. 使用適當的上拉電阻:根據總線長度和速度選擇合適阻值

  5. 避免頻繁初始化:減少I2C外設的重復初始化

  6. 監控總線狀態:定期檢查總線健康狀況

  7. 考慮使用軟件I2C:對于可靠性要求高的場合,可考慮軟件實現

c

// 綜合示例:帶錯誤處理的I2C讀取
HAL_StatusTypeDef Safe_I2C_Read(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint8_t *pData, uint16_t Size)
{HAL_StatusTypeDef status;uint32_t tickstart = HAL_GetTick();// 第一步:寫入要讀取的內存地址uint8_t memAddr[2] = {MemAddress >> 8, MemAddress & 0xFF};do {status = HAL_I2C_Master_Transmit(hi2c, DevAddress, memAddr, 2, 10);if((HAL_GetTick() - tickstart) > I2C_TIMEOUT) {I2C_Recovery(hi2c);return HAL_TIMEOUT;}} while(status != HAL_OK);// 第二步:讀取數據tickstart = HAL_GetTick();do {status = HAL_I2C_Master_Receive(hi2c, DevAddress, pData, Size, 10);if((HAL_GetTick() - tickstart) > I2C_TIMEOUT) {I2C_Recovery(hi2c);return HAL_TIMEOUT;}} while(status != HAL_OK);return HAL_OK;
}
 

三、總結

I2C協議作為一種簡單高效的串行通信協議,在嵌入式系統中廣泛應用。STM32的硬件I2C外設雖然功能完善,但在實際應用中可能因各種原因導致卡死。理解I2C協議的工作原理和STM32 HAL庫的實現機制,能夠幫助開發者快速定位和解決這些問題。通過實現超時機制、總線恢復函數和合理的錯誤處理邏輯,可以顯著提高I2C通信的可靠性。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/86920.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/86920.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/86920.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

HTTP協議-后端接收請求

起因就是不知道post這個請求體中這些格式有什么區別,后端又怎么去接收這些不同格式的內容 Get請求 get請求是比較簡單的一類 正常的直接用參數接收(不寫的話名字要匹配)或者RequestParam都可以接收,用對象綁定也可以 resultful…

HTML5 實現的圣誕主題網站源碼,使用了 HTML5 和 CSS3 技術,界面美觀、節日氛圍濃厚。

以下是一個 HTML5 實現的圣誕主題網站源碼,使用了 HTML5 和 CSS3 技術,界面美觀、節日氛圍濃厚。它包括: 圣誕樹動畫 🎄雪花飄落特效 ??圣誕祝福語 🎁響應式布局,適配移動端 你可以將代碼保存為 index.…

Spring Cloud Bus 和 Spring Cloud Stream

Spring Cloud Bus 和 Spring Cloud Stream 都是 Spring Cloud 生態中的消息通信組件,但它們的定位和使用場景有顯著區別: 1. Spring Cloud Bus 核心定位:分布式系統的消息廣播(配置刷新、事件傳播)。 典型場景&#x…

磁懸浮軸承位移信號的高精度估計:卡爾曼濾波算法深度解析

無需位移傳感器,濾波算法如何實現微米級精度? 磁懸浮軸承作為革命性的非接觸式支承技術,憑借無磨損、無需潤滑、高轉速等優勢,在飛輪儲能、高速電機、人工心臟泵和航空航天領域獲得了廣泛應用。其核心控制依賴于對轉子位移信號的高精度實時檢測,傳統電渦流傳感器雖能提供位…

DAY 43 預訓練模型

目錄 一、預訓練的概念 二、 經典的預訓練模型 2.1 CNN架構預訓練模型 2.2 Transformer類預訓練模型 2.3 自監督預訓練模型 三、常見的分類預訓練模型介紹 3.1 預訓練模型的發展史 3.2 預訓練模型的訓練策略 知識點回顧: 預訓練的概念常見的分類預訓練模型圖像…

Redis:事物

🌈 個人主頁:Zfox_ 🔥 系列專欄:Redis 🔥 什么是事務 Redis的事務和MySQL的事務概念上是類似的.都是把?系列操作綁定成?組.讓這?組能夠批量執?. 但是注意體會Redis的事務和MySQL事務的區別: 弱化的原?性:redi…

CppCon 2018 學習:An allocator is a handle to a heap Lessons learned from std::pmr

“An allocator is a handle to a heap — Lessons learned from std::pmr” 翻譯過來就是:“分配器(allocator)是對堆(heap)的一種句柄(handle)——從 std::pmr 中學到的經驗”。 基礎概念 分…

設備健康實時監測方法演進:從傳感網絡到AI決策樹的工業智能實踐

引言:當設備運維遇上AIoT革命 在工業4.0進程中,?毫秒級設備狀態捕獲能力正成為智能工廠的核心競爭力。傳統監測方法因數據滯后、診斷粗放被詬病,本文將深入探討三大前沿實時監測技術路徑,并揭秘中訊燭龍系統如何通過深度強化學習…

劍指offer53_二叉樹的深度

二叉樹的深度 輸入一棵二叉樹的根結點,求該樹的深度。 從根結點到葉結點依次經過的結點(含根、葉結點)形成樹的一條路徑,最長路徑的長度為樹的深度。 數據范圍 樹中節點數量 [ 0 , 500 ] [0,500] [0,500]。 樣例 輸入&#…

探秘AI的秘密:leaked-system-prompts

揭秘:揭秘系統提示合集背后的秘密 在當今這個人工智能技術迅速發展的時代,了解和使用大型語言模型(LLM)已成為技術愛好者、開發者和研究人員的共同目標。而作為核心組成部分,系統提示(system prompts)的設計和應用直接影響了LLM的表現和功能。今天, 我們將為大家揭示一…

Gaming Mode四大功能(VRR、QMS、QFT、ALLM)

HDMI 2.1定義的Gaming Mode四大功能(VRR、QMS、QFT、ALLM)通過協同優化幀傳輸、刷新率同步與延遲控制,顯著提升了游戲和影音的流暢性與響應速度。以下是這些功能的詳細解析及其應用價值: 🔄 1. 可變刷新率(…

數據庫總結(關系代數-函數依賴-范式)

以下是關系代數中基本操作的詳細說明: 并(Union) 關系R和S的并操作表示為R ∪ S,要求R和S具有相同的屬性集(并相容性)。結果包含所有屬于R或S的元組,自動去除重復項。 示例: R …

react經驗:在nextjs中使用motion組件

什么是motion組件? 一種動畫組件 motion組件文檔 在nextjs中的應用步驟 1.安裝motion npm i framer-motion2.在next.config.js中配置轉義 export default {transpilePackages: [framer-motion] }3.開始應用 **注意要點:**在服務端渲染不可直接用&am…

怎樣大語言模型 遵守規則

如何讓應用中的提示工程更能適應未來變化 目錄 如何讓應用中的提示工程更能適應未來變化怎樣大語言模型 遵守規則提示詞 很有效:Memorize these rules提示可分為穩定組件和易變組件怎樣大語言模型 遵守規則 實驗背景:讓大語言模型可靠地遵守規則很難,尤其是規則數量增多時。…

如何通過SSL證書配置防止源站IP泄露 - 全面防護指南

問題背景:SSL證書如何導致源站IP泄露 近期多位站長反饋,即使已部署高防CDN并做好源站IP保密工作,服務器仍頻繁遭受DDoS攻擊。經深入排查,發現問題根源在于SSL證書。當前網絡環境中存在大量爬蟲工具24小時不間斷掃描全網IP地址&am…

醫院信息化發展要經過哪幾個階段

目前,幾乎所有的醫院都離不開信息技術的建設和支持。沒有信息技術,醫院的業務可能無法繼續。醫院信息化的發展主要經歷三個階段,即醫院管理信息化階段、臨床管理信息化階段和醫療智能化階段。從基礎設施的角度來看,每個階段都有不…

【Vscode】Vscode切換成中文語言

安裝中文語言包 啟動 VSCode。按下Ctrl Shift X(或者點擊左側邊欄的擴展圖標),打開擴展面板。在搜索框中輸入Chinese (Simplified),在搜索結果里找到Chinese (Simplified) Language Pack for Visual Studio Code并點擊安裝按鈕…

【百日精通JAVA | 數據結構篇】 一文了解泛型體系

一、初識泛型 在推出泛型以前,程序員可以創建一個元素類型Object的集合,該集合能夠存儲任意的數據類型對象,而在使用該集合的過程中,需要明確知道存儲每個元素的類型,否則容易引發ClassCastException異常。 泛型是JD…

賦能 Java 工程,飛算科技重新定義智能開發

在數字經濟蓬勃發展的當下,軟件開發行業正經歷著前所未有的變革。飛算科技作為一家自主創新型的數字科技公司,始終以互聯網科技、大數據、人工智能等前沿技術為根基。憑借團隊在相關領域多年積累的深厚實踐經驗,公司深度融合技術與應用&#…

【藍牙】Linux Qt4藍牙設備列表刷新加載采用什么策略,使用什么對應的Linux命令或dbus接口

在 Linux 系統中,使用 Qt4 開發藍牙設備列表刷新功能時,通常會結合 BlueZ 藍牙協議棧 和 D-Bus 通信機制 實現對藍牙設備的發現與管理。以下是常見的實現策略和對應的命令或接口。 🧩 一、藍牙設備列表刷新策略 1. 主動掃描(Scan…