STM32_IIC外設工作流程

STM32 I2C 外設工作流程(基于寄存器)

在 STM32 中,I2C 通信主要通過一系列寄存器控制。理解這些寄存器的作用,能夠幫助我們掌握 I2C 硬件的運行機制,實現高效的數據傳輸。本文以 STM32F1(如 STM32F103)為例,詳細講解 I2C 外設的寄存器級操作。


1. STM32 I2C 相關寄存器

STM32 的 I2C 主要涉及以下寄存器:

  • 控制寄存器 1(I2C_CR1):控制 I2C 外設的啟用、應答、時鐘伸展等功能。
  • 控制寄存器 2(I2C_CR2):配置 I2C 的時鐘、DMA 使能、中斷使能等。
  • 時鐘控制寄存器(I2C_CCR):設置 I2C 通信速率(時鐘分頻)。
  • 濾波寄存器(I2C_TRISE):配置最大上升時間,用于同步時鐘。
  • 狀態寄存器 1(I2C_SR1):存儲 I2C 通信的狀態標志,如起始位、地址匹配、數據發送完成等。
  • 狀態寄存器 2(I2C_SR2):存儲總線狀態、模式、從機地址等信息。
  • 數據寄存器(I2C_DR):用于收發數據。

2. I2C 外設初始化

在使用 I2C 之前,需要進行初始化,主要包括:

  • 使能 I2C 時鐘
  • 配置 GPIO(SCL、SDA)
  • 配置 I2C 速率
  • 使能 I2C 外設

寄存器配置

void I2C_Init(void) {// 1. 使能 I2C1 時鐘(I2C1 掛載在 APB1 總線上)RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;// 2. 使能 GPIOB 時鐘(I2C1_SCL=PB6, I2C1_SDA=PB7)RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;// 3. 配置 GPIO 為復用開漏模式GPIOB->CRL &= ~((0xF << (6 * 4)) | (0xF << (7 * 4)));  // 清除原配置GPIOB->CRL |= (0xB << (6 * 4)) | (0xB << (7 * 4)); // 復用開漏// 4. 配置 I2C 時鐘I2C1->CR2 = 36; // PCLK1 = 36MHzI2C1->CCR = 180; // 標準模式(100kHz):T_high = T_low = 10us, CCR = 180I2C1->TRISE = 37; // TRISE = (1000ns / (1/36MHz)) + 1// 5. 使能 I2C 外設I2C1->CR1 |= I2C_CR1_PE;
}

3. 主機模式發送數據

主機模式下,發送數據的流程如下:

  1. 檢查總線狀態
  2. 發送起始信號
  3. 發送從機地址(寫)
  4. 發送數據
  5. 發送停止信號

寄存器操作

void I2C_WriteByte(uint8_t devAddr, uint8_t regAddr, uint8_t data) {while (I2C1->SR2 & I2C_SR2_BUSY);  // 等待總線空閑I2C1->CR1 |= I2C_CR1_START;  // 發送起始信號while (!(I2C1->SR1 & I2C_SR1_SB)); // 等待起始信號發送完成I2C1->DR = (devAddr << 1) | 0;  // 發送從機地址 + 寫while (!(I2C1->SR1 & I2C_SR1_ADDR)); // 等待地址發送完成(void)I2C1->SR2;  // 讀取 SR2 以清除 ADDR 標志I2C1->DR = regAddr;  // 發送寄存器地址while (!(I2C1->SR1 & I2C_SR1_TXE)); // 等待數據寄存器空I2C1->DR = data;  // 發送數據while (!(I2C1->SR1 & I2C_SR1_BTF)); // 等待數據傳輸完成I2C1->CR1 |= I2C_CR1_STOP;  // 發送停止信號
}

4. 主機模式接收數據

  1. 發送起始信號
  2. 發送從機地址(寫)
  3. 發送寄存器地址
  4. 發送重復起始信號
  5. 發送從機地址(讀)
  6. 讀取數據
  7. 發送 NACK,結束傳輸
  8. 發送停止信號

寄存器操作

uint8_t I2C_ReadByte(uint8_t devAddr, uint8_t regAddr) {uint8_t data;while (I2C1->SR2 & I2C_SR2_BUSY);  // 檢查總線狀態I2C1->CR1 |= I2C_CR1_START;  // 發送起始信號while (!(I2C1->SR1 & I2C_SR1_SB));I2C1->DR = (devAddr << 1) | 0;  // 發送從機地址(寫)while (!(I2C1->SR1 & I2C_SR1_ADDR));(void)I2C1->SR2;I2C1->DR = regAddr;  // 發送寄存器地址while (!(I2C1->SR1 & I2C_SR1_TXE));I2C1->CR1 |= I2C_CR1_START;  // 發送重復起始信號while (!(I2C1->SR1 & I2C_SR1_SB));I2C1->DR = (devAddr << 1) | 1;  // 發送從機地址(讀)while (!(I2C1->SR1 & I2C_SR1_ADDR));(void)I2C1->SR2;I2C1->CR1 &= ~I2C_CR1_ACK;  // 發送 NACKI2C1->CR1 |= I2C_CR1_STOP;  // 發送停止信號while (!(I2C1->SR1 & I2C_SR1_RXNE));data = I2C1->DR;  // 讀取數據return data;
}

5. 復位 I2C 總線

當 I2C 總線鎖死時,可通過軟件復位:

I2C1->CR1 &= ~I2C_CR1_PE;  // 關閉 I2C
I2C1->CR1 |= I2C_CR1_PE;   // 重新使能 I2C

或手動拉高 SCL 并發送 9 個時鐘脈沖。


6. 總結

  • I2C 初始化 需要配置 GPIO、I2C 時鐘、CCR 寄存器
  • 發送數據 依賴 I2C_CR1(START, STOP)I2C_SR1(SB, TXE, BTF)I2C_DR
  • 接收數據 依賴 ACK、NACK、重復起始
  • 通過 軟件復位 處理總線鎖死

掌握這些寄存器,可以更深入地理解 STM32 I2C 外設的運行機制,優化通信效率和穩定性。


STM32 I2C 數據收發過程(寄存器級詳細解析)

STM32 的 I2C 外設工作過程中,多個寄存器的值會發生變化。我們將逐步拆解 主機發送數據主機接收數據 的流程,并詳細說明寄存器狀態的變化,幫助你深入理解 STM32 I2C 硬件的底層機制。


1. STM32 I2C 主要寄存器

在 I2C 傳輸過程中,涉及以下主要寄存器:

1.1 控制寄存器

寄存器作用
I2C_CR1控制 I2C 外設(啟動、停止、應答、軟件復位等)
I2C_CR2配置 I2C 時鐘、DMA、中斷

1.2 狀態寄存器

寄存器作用
I2C_SR1反映當前 I2C 事件,如 SB(起始位)、ADDR(地址匹配)、TXE(數據寄存器空)等
I2C_SR2反映 I2C 總線的狀態,如 BUSY(總線忙)、MSL(主機模式)等

1.3 數據寄存器

寄存器作用
I2C_DR讀寫數據

2. I2C 主機發送數據(寄存器變化)

步驟

  1. 發送 起始信號
  2. 發送 從機地址 + 寫(bit 0 = 0)
  3. 發送 數據字節
  4. 發送 停止信號

2.1 發送起始信號

寄存器變化
操作I2C_CR1I2C_SR1說明
`I2C1->CR1= I2C_CR1_START;`START = 1
等待 SB=1SB=1起始條件已發送
代碼
I2C1->CR1 |= I2C_CR1_START;  // 發送起始信號
while (!(I2C1->SR1 & I2C_SR1_SB));  // 等待起始位(SB=1)

2.2 發送從機地址(寫)

寄存器變化

| 操作 | I2C_DR | I2C_SR1 | I2C_SR2 | 說明 |
|---------|--------|----------|----------|
| I2C1->DR = (devAddr << 1) | 0; | 發送地址 | ADDR=1 | |
| 讀取 SR2ADDR | | ADDR=0 | 地址發送完成 |

代碼
I2C1->DR = (devAddr << 1) | 0;  // 發送從機地址 + 寫
while (!(I2C1->SR1 & I2C_SR1_ADDR));  // 等待地址匹配
(void)I2C1->SR2;  // 讀取 SR2 清除 ADDR 標志

2.3 發送數據

寄存器變化
操作I2C_DRI2C_SR1說明
I2C1->DR = data;發送數據TXE=0數據正在發送
等待 TXE=1TXE=1數據發送完成
代碼
I2C1->DR = data;  // 發送數據
while (!(I2C1->SR1 & I2C_SR1_TXE));  // 等待數據傳輸完成

2.4 發送停止信號

寄存器變化
操作I2C_CR1說明
`I2C1->CR1= I2C_CR1_STOP;`STOP=1
代碼
I2C1->CR1 |= I2C_CR1_STOP;  // 發送停止信號

3. I2C 主機接收數據(寄存器變化)

步驟

  1. 發送 起始信號
  2. 發送 從機地址 + 讀(bit 0 = 1)
  3. 讀取 數據
  4. 發送 NACK
  5. 發送 停止信號

3.1 發送起始信號

與主機發送數據相同:

I2C1->CR1 |= I2C_CR1_START;
while (!(I2C1->SR1 & I2C_SR1_SB));

3.2 發送從機地址(讀)

寄存器變化

| 操作 | I2C_DR | I2C_SR1 | I2C_SR2 | 說明 |
|---------|--------|----------|----------|
| I2C1->DR = (devAddr << 1) | 1; | 發送地址 | ADDR=1 | |
| 讀取 SR2ADDR | | ADDR=0 | 地址發送完成 |

代碼
I2C1->DR = (devAddr << 1) | 1;
while (!(I2C1->SR1 & I2C_SR1_ADDR));
(void)I2C1->SR2;

3.3 讀取數據

寄存器變化
操作I2C_SR1I2C_DR說明
等待 RXNE=1RXNE=1數據可讀
data = I2C1->DR;RXNE=0讀取數據數據被取走
代碼
while (!(I2C1->SR1 & I2C_SR1_RXNE));  // 等待數據準備好
data = I2C1->DR;  // 讀取數據

3.4 發送 NACK

寄存器變化
操作I2C_CR1說明
I2C1->CR1 &= ~I2C_CR1_ACK;關閉 ACK發送 NACK
代碼
I2C1->CR1 &= ~I2C_CR1_ACK;  // 發送 NACK

3.5 發送停止信號

與主機發送數據相同:

I2C1->CR1 |= I2C_CR1_STOP;  // 發送停止信號

4. 復位 I2C 總線

若 I2C 總線鎖死,可執行軟件復位:

I2C1->CR1 &= ~I2C_CR1_PE;  // 關閉 I2C
I2C1->CR1 |= I2C_CR1_PE;   // 重新啟用 I2C

5. 總結

  • I2C_SR1 標志寄存器:指示 I2C 傳輸狀態(SBADDRTXERXNE)。
  • I2C_DR 數據寄存器:用于收發數據。
  • I2C_CR1 控制寄存器:用于產生 STARTSTOPACK

寄存器級的 I2C 操作能提供更高的靈活性,適用于驅動底層 I2C 設備,如攝像頭、EEPROM、傳感器等。


STM32 I2C 完整收發流程(寄存器級詳細解析)

為了更清楚地理解 STM32 I2C 外設的寄存器級操作,我們使用一個 完整的示例
假設 STM32 作為 主機,從 I2C 設備(如 EEPROM、傳感器)讀取一個寄存器 的值,
然后 修改該值并寫回


1. 示例任務

目標

  • 讀取 從機(設備地址 0x50)的寄存器 0x10 的值。
  • 修改該值(加 1)。
  • 寫回 該值到 0x10

I2C 設備信息

  • 設備地址0x50
  • 寄存器地址0x10
  • I2C 速率:100kHz(標準模式)

2. I2C 數據收發完整流程

完整步驟

1. 發送起始信號
  • I2C_CR1 |= I2C_CR1_START
  • I2C_SR1 置位 SB=1
2. 發送設備地址(寫)
  • I2C_DR = 0x50 << 1 | 0
  • I2C_SR1 置位 ADDR=1
  • 讀取 I2C_SR2 清除 ADDR
3. 發送寄存器地址
  • I2C_DR = 0x10
  • I2C_SR1 置位 TXE=1
4. 發送重復起始信號
  • I2C_CR1 |= I2C_CR1_START
  • I2C_SR1 置位 SB=1
5. 發送設備地址(讀)
  • I2C_DR = 0x50 << 1 | 1
  • I2C_SR1 置位 ADDR=1
  • 讀取 I2C_SR2 清除 ADDR
6. 讀取數據
  • I2C_SR1 置位 RXNE=1
  • data = I2C_DR
7. 發送 NACK
  • I2C_CR1 &= ~I2C_CR1_ACK
8. 發送停止信號
  • I2C_CR1 |= I2C_CR1_STOP
9. 修改數據
  • data++
10. 發送起始信號
  • I2C_CR1 |= I2C_CR1_START
11. 發送設備地址(寫)
  • I2C_DR = 0x50 << 1 | 0
12. 發送寄存器地址
  • I2C_DR = 0x10
13. 發送數據
  • I2C_DR = data
14. 發送停止信號
  • I2C_CR1 |= I2C_CR1_STOP

3. 代碼實現

#include "stm32f10x.h"#define I2C_ADDRESS  0x50  // 從設備地址
#define REG_ADDRESS  0x10  // 目標寄存器地址void I2C_WriteByte(uint8_t devAddr, uint8_t regAddr, uint8_t data);
uint8_t I2C_ReadByte(uint8_t devAddr, uint8_t regAddr);int main(void) {uint8_t data;// 1. 讀取寄存器值data = I2C_ReadByte(I2C_ADDRESS, REG_ADDRESS);// 2. 修改數據data++;// 3. 寫回數據I2C_WriteByte(I2C_ADDRESS, REG_ADDRESS, data);while (1);
}// 讀取 I2C 設備寄存器值
uint8_t I2C_ReadByte(uint8_t devAddr, uint8_t regAddr) {uint8_t data;// 1. 發送起始信號I2C1->CR1 |= I2C_CR1_START;while (!(I2C1->SR1 & I2C_SR1_SB));// 2. 發送設備地址(寫)I2C1->DR = (devAddr << 1) | 0;while (!(I2C1->SR1 & I2C_SR1_ADDR));(void)I2C1->SR2;// 3. 發送寄存器地址I2C1->DR = regAddr;while (!(I2C1->SR1 & I2C_SR1_TXE));// 4. 發送重復起始信號I2C1->CR1 |= I2C_CR1_START;while (!(I2C1->SR1 & I2C_SR1_SB));// 5. 發送設備地址(讀)I2C1->DR = (devAddr << 1) | 1;while (!(I2C1->SR1 & I2C_SR1_ADDR));(void)I2C1->SR2;// 6. 讀取數據while (!(I2C1->SR1 & I2C_SR1_RXNE));data = I2C1->DR;// 7. 發送 NACKI2C1->CR1 &= ~I2C_CR1_ACK;// 8. 發送停止信號I2C1->CR1 |= I2C_CR1_STOP;return data;
}// 向 I2C 設備寄存器寫入數據
void I2C_WriteByte(uint8_t devAddr, uint8_t regAddr, uint8_t data) {// 1. 發送起始信號I2C1->CR1 |= I2C_CR1_START;while (!(I2C1->SR1 & I2C_SR1_SB));// 2. 發送設備地址(寫)I2C1->DR = (devAddr << 1) | 0;while (!(I2C1->SR1 & I2C_SR1_ADDR));(void)I2C1->SR2;// 3. 發送寄存器地址I2C1->DR = regAddr;while (!(I2C1->SR1 & I2C_SR1_TXE));// 4. 發送數據I2C1->DR = data;while (!(I2C1->SR1 & I2C_SR1_TXE));// 5. 發送停止信號I2C1->CR1 |= I2C_CR1_STOP;
}

4. 關鍵寄存器變化總結

步驟寄存器變化作用
發送起始信號I2C_CR1START=1產生起始信號
發送設備地址(寫)I2C_DRADDR=1發送地址
發送寄存器地址I2C_DRTXE=1發送數據
發送重復起始信號I2C_CR1START=1重新開始
發送設備地址(讀)I2C_DRADDR=1發送地址
讀取數據I2C_DRRXNE=1接收數據
發送 NACKI2C_CR1ACK=0結束讀取
發送停止信號I2C_CR1STOP=1終止傳輸

5. 結論

  • I2C_CR1 控制起始、停止、ACK/NACK 發送。
  • I2C_SR1 監視數據傳輸狀態(SBADDRTXERXNE)。
  • I2C_DR 用于收發數據。

這個完整的流程展示了 I2C 數據收發寄存器級操作,適用于傳感器、EEPROM、攝像頭等 I2C 設備的底層驅動開發。


STM32 的 I2C 外設(通常標記為 I2Cx,如 I2C1、I2C2)在寄存器層面的工作流程涉及多個關鍵寄存器的配置和狀態監測。以下是基于寄存器操作的工作流程詳解:


1. I2C 外設寄存器概覽

STM32 I2C 外設的核心寄存器包括:

  • CR1 (Control Register 1):配置 I2C 使能、ACK、時鐘等。
  • CR2 (Control Register 2):設置時鐘頻率、中斷/DMA 使能。
  • OAR1/OAR2 (Own Address Register):配置自身地址。
  • DR (Data Register):發送/接收數據。
  • SR1/SR2 (Status Registers):標志位(如起始條件、地址匹配、數據收發完成等)。
  • CCR (Clock Control Register):設置時鐘分頻和模式(標準/快速)。
  • TRISE (TRise Register):配置 SCL 上升時間。

2. 主機發送模式(Master Transmitter)流程

(1) 初始化配置
  1. 配置 GPIO:將 SCL/SDA 引腳設為復用開漏模式(需外部上拉電阻)。
  2. 配置 I2C 時鐘
    • CR2FREQ[5:0] 位:設置 APB 時鐘頻率(單位 MHz)。
  3. 配置時鐘分頻
    • CCRCCR[11:0] 位:設置 SCL 時鐘分頻。
    • 標準模式(100 kHz)或快速模式(400 kHz)。
  4. 配置上升時間
    • TRISE:根據模式設置(標準模式:1000ns → TRISE = F_APB1(MHz) + 1)。
  5. 使能 I2C
    • CR1PE 位置 1,使能外設。
(2) 發送起始條件
  1. 生成 START 信號
    • CR1START 位置 1。
  2. 等待起始條件完成
    • 輪詢 SR1SB 位(Start Bit),當 SB=1 時,起始條件生成成功。
    • 必須讀取 SR1 后寫 DR 寄存器(硬件自動清除 SB)。
(3) 發送從機地址
  1. 寫入從機地址 + 方向位
    • DR 寫入 7-bit地址<<1 | R/W位(0 表示寫)
  2. 等待地址應答
    • 輪詢 SR1ADDR 位(地址發送完成)。
    • 必須讀取 SR1 和 SR2 以清除 ADDR 標志。
(4) 發送數據
  1. 寫入數據到 DR
    • DR 寫入待發送的數據字節。
  2. 等待數據發送完成
    • 輪詢 SR1TXE 位(Transmit Data Register Empty)。
    • TXE=1,表示數據已轉移到移位寄存器,可寫入下一字節。
  3. 重復步驟 4.1-4.2 發送所有數據。
(5) 發送停止條件
  1. 生成 STOP 信號
    • CR1STOP 位置 1。
  2. 等待停止完成
    • 根據時序要求等待(無需輪詢特定標志)。

3. 主機接收模式(Master Receiver)流程

流程與發送模式類似,但需注意:

  1. 發送從機地址時,R/W 位置 1。
  2. 配置 CR1ACK 位以控制是否發送應答:
    • 在接收最后一個字節前,ACK 位需清零(發送 NACK)。
  3. DR 讀取數據前,需檢查 SR1RXNE 位(Receive Data Register Not Empty)。

4. 關鍵狀態標志與錯誤處理

  • BUSY (SR2[1]):總線忙狀態。
  • BTF (SR1[2]):字節傳輸完成。
  • AF (SR1[10]):應答失敗(NACK)。
  • ARLO (SR1[9]):仲裁丟失。
  • BERR (SR1[8]):總線錯誤。

錯誤處理

  1. 檢測到錯誤標志后,清除錯誤(通過寫 CR1 的 SWRST 位或重新初始化)。
  2. 重新發起通信。

5. 典型寄存器操作示例(主機發送)

// 等待 I2C 空閑
while (I2C1->SR2 & I2C_SR2_BUSY);// 發送 START
I2C1->CR1 |= I2C_CR1_START;// 等待 SB=1
while (!(I2C1->SR1 & I2C_SR1_SB));// 發送地址(寫模式)
I2C1->DR = (SlaveAddr << 1) | 0;// 等待 ADDR=1
while (!(I2C1->SR1 & I2C_SR1_ADDR));
volatile uint32_t temp = I2C1->SR1 | (I2C1->SR2 << 16); // 清除 ADDR// 發送數據
I2C1->DR = DataByte;
while (!(I2C1->SR1 & I2C_SR1_TXE));// 發送 STOP
I2C1->CR1 |= I2C_CR1_STOP;

6. 注意事項

  1. 時鐘配置CCRTRISE 需嚴格匹配時序要求。
  2. 標志清除順序:某些標志(如 ADDR)需通過讀 SR1 + SR2 清除。
  3. 中斷/DMA:可通過 CR2 配置中斷或 DMA 傳輸,減少 CPU 輪詢開銷。

通過直接操作寄存器,開發者可以更精細地控制 I2C 時序,但需嚴格遵循 STM32 參考手冊的流程以避免硬件錯誤。


好的!我將更詳細地分析 I2C 外設在數據傳輸過程中寄存器的具體變化,結合 STM32 的硬件行為,逐步拆解每個階段的寄存器操作和狀態標志的響應。以下是深入細節的流程說明:


1. 初始化階段的寄存器配置

(1) GPIO 模式配置
  • GPIOx_CRL/CRH:配置 SCL/SDA 引腳為 復用開漏模式GPIO_Mode_AF_OD)。
  • GPIOx_ODR:無需手動設置,但硬件要求外部上拉電阻。
(2) I2C 時鐘與模式配置
  • CR2FREQ[5:0]:設置 APB1 總線時鐘頻率(例如,36 MHz → FREQ=36)。
  • CCRCCR[11:0]
    • 標準模式(100 kHz):CCR = APB1_CLK / (2 * 100000)
    • 快速模式(400 kHz):CCR = APB1_CLK / (2 * 400000)
    • 快速模式+(1 MHz):需使能 F/S 位(CCR[15])。
  • TRISE:設置 SCL 上升時間(例如,標準模式:TRISE = APB1_CLK(MHz) + 1)。
  • CR1PE 位:置 1 使能 I2C 外設。

2. 主機發送模式(Master Transmitter)詳細流程

(1) 生成起始條件(START)
  1. 操作寄存器
    • CR1START 位置 1。
  2. 硬件行為
    • I2C 硬件檢測總線空閑(SR2.BUSY=0)后,生成 START 條件。
  3. 狀態標志變化
    • SR1.SB 置 1:表示 START 條件已生成。
  4. 關鍵操作
    • 必須 讀取 SR1 寄存器(清除 SB 位),然后立即寫入從機地址到 DR 寄存器。
(2) 發送從機地址
  1. 寫入地址到 DR
    • DR 寫入 (SlaveAddr << 1) | 0(0 表示寫操作)。
  2. 硬件行為
    • 硬件自動發送地址 + R/W 位,并等待從機的 ACK。
  3. 狀態標志變化
    • SR1.ADDR 置 1:地址已發送且收到 ACK。
    • SR2.TRA 置 1:表示當前處于發送模式。
  4. 關鍵操作
    • 必須 讀取 SR1SR2 寄存器以清除 ADDR 標志。
    • 示例代碼:
      volatile uint32_t dummy = I2C1->SR1; // 讀取 SR1 清除 ADDR
      dummy = I2C1->SR2;                   // 讀取 SR2 清除 BUSY 狀態
      
(3) 發送數據字節
  1. 寫入數據到 DR
    • DR 寫入待發送的數據(例如 0x55)。
  2. 硬件行為
    • 硬件將 DR 中的數據移到移位寄存器,并逐位發送到 SDA 線。
  3. 狀態標志變化
    • SR1.TXE 置 1:表示 DR 已空,可以寫入下一個字節。
    • SR1.BTF 置 1:表示當前字節已完全發送(包括 ACK 周期)。
  4. 關鍵操作
    • TXE=1,寫入新數據到 DR,硬件自動清除 TXE
    • BTF=1,表示數據已發送完成,但需結合 TXE 判斷。
(4) 發送停止條件(STOP)
  1. 操作寄存器
    • CR1STOP 位置 1。
  2. 硬件行為
    • 生成 STOP 條件,釋放總線。
  3. 狀態標志變化
    • SR2.BUSY 置 0:總線空閑。

3. 主機接收模式(Master Receiver)詳細流程

(1) 生成 START 并發送讀地址
  1. 發送 START(同發送模式)。
  2. 寫入讀地址到 DR
    • DR 寫入 (SlaveAddr << 1) | 1(1 表示讀操作)。
  3. 狀態標志變化
    • SR1.ADDR 置 1:地址發送成功。
    • SR2.TRA 置 0:表示當前處于接收模式。
(2) 接收數據流程
  1. 配置 ACK/NACK
    • CR1.ACK 置 1:使能 ACK(接收每個字節后發送 ACK)。
    • 在接收最后一個字節前,需 清零 ACK 位(發送 NACK)。
  2. 讀取數據
    • 等待 SR1.RXNE=1:表示 DR 中有新數據。
    • 讀取 DR 寄存器,硬件自動清除 RXNE
  3. 狀態標志變化
    • SR1.RXNE 置 1:數據已接收完畢。
    • SR1.BTF 置 1:字節傳輸完成(包括 ACK/NACK 周期)。
(3) 生成 STOP 條件
  • 同發送模式,需在接收最后一個字節后立即生成 STOP。

4. 寄存器狀態變化時序圖(示例)

以主機發送模式為例,展示寄存器關鍵位的變化時序:

操作CR1.STARTSR1.SBSR1.ADDRSR1.TXESR1.BTFSR2.BUSY
初始狀態000000
設置 START=1100001
START 生成完成110001
寫入地址到 DR100001
地址發送完成101001
清除 ADDR(讀 SR1/SR2)100001
寫入數據到 DR100001
數據開始發送100001
數據發送完成100111
設置 STOP=1000110

5. 錯誤處理與寄存器恢復

(1) 應答失敗(NACK)
  • 觸發條件:從機未應答地址或數據。
  • 狀態標志SR1.AF=1
  • 恢復操作
    1. 清除 AF 標志:寫 SR1AF 位為 0。
    2. 生成 STOP 或重復 START:
      I2C1->CR1 |= I2C_CR1_STOP;  // 強制 STOP
      I2C1->SR1 &= ~I2C_SR1_AF;   // 清除 AF 標志
      
(2) 總線仲裁丟失(Arbitration Lost)
  • 觸發條件:多主機競爭時,STM32 失去總線控制權。
  • 狀態標志SR1.ARLO=1
  • 恢復操作
    1. 清除 ARLO:寫 SR1ARLO 位為 0。
    2. 重新初始化 I2C 外設(PE=0 → 重新配置 → PE=1)。

6. 關鍵代碼示例(寄存器級操作)

主機發送單字節數據
// 等待總線空閑
while (I2C1->SR2 & I2C_SR2_BUSY);// 1. 發送 START
I2C1->CR1 |= I2C_CR1_START;// 2. 等待 SB=1
while (!(I2C1->SR1 & I2C_SR1_SB));// 3. 發送從機地址(寫模式)
I2C1->DR = (0x50 << 1); // 假設從機地址 0x50// 4. 等待 ADDR=1
while (!(I2C1->SR1 & I2C_SR1_ADDR));
volatile uint32_t temp = I2C1->SR1; // 清除 SR1.ADDR
temp = I2C1->SR2;                  // 清除 SR2 狀態// 5. 發送數據
I2C1->DR = 0xAA; // 發送數據 0xAA
while (!(I2C1->SR1 & I2C_SR1_TXE)); // 等待 TXE=1// 6. 發送 STOP
I2C1->CR1 |= I2C_CR1_STOP;

7. 深入理解寄存器交互

  • DR 寄存器的雙緩沖機制
    • TXE=1 時,表示 DR 已空,可以寫入下一個字節,但硬件可能仍在發送前一個字節。
    • 寫入 DR 會觸發硬件開始發送,同時清除 TXE 位。
  • 狀態標志的清除順序
    • ADDR 標志必須通過 先讀 SR1,再讀 SR2 來清除。
    • AFARLO 標志需通過寫 0 清除。

8. 總結

通過直接操作寄存器,可以精確控制 I2C 的每一個總線動作,但需要嚴格遵循以下原則:

  1. 狀態標志的清除順序:如 ADDR 必須讀 SR1 + SR2。
  2. 時序匹配CCRTRISE 需根據 APB1 時鐘頻率計算。
  3. 錯誤恢復:檢測到錯誤標志后,必須清除并重新初始化外設。

實際開發中,建議結合 STM32 參考手冊的 I2C 時序圖寄存器描述,通過邏輯分析儀抓取 SCL/SDA 波形,驗證寄存器操作是否符合預期。

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

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

相關文章

集合遍歷的多種方式

目錄 1.增強for 2.迭代器&#xff08;在遍歷的過程中需要刪除元素&#xff0c;請使用迭代器&#xff09; 3.雙列集合 4.Lambda表達式(forEach方法) 1.單列集合&#xff1a; 2.雙列集合&#xff1a; 4.Stream 流 5.普通for循環 6.列表迭代器 7.總結 1.增強for 注&…

DeepSeek在MATLAB上的部署與應用

在科技飛速發展的當下&#xff0c;人工智能與編程語言的融合不斷拓展著創新邊界。DeepSeek作為一款備受矚目的大語言模型&#xff0c;其在自然語言處理領域展現出強大的能力。而MATLAB&#xff0c;作為科學計算和工程領域廣泛應用的專業軟件&#xff0c;擁有豐富的工具包和高效…

value_counts()和unique()

我今天發現一個很有意思的問題哈 import scanpy as sc import numpy as npX np.random.randn(10,3) adata1 sc.AnnData(X) adata1.obs["sample"] "H1" print(adata1)X np.random.randn(20,3) adata2 sc.AnnData(X) adata2.obs["sample"] &…

每日OJ_牛客_游游的字母串_枚舉_C++_Java

目錄 牛客_游游的字母串_枚舉 題目解析 C代碼 Java代碼 牛客_游游的字母串_枚舉 游游的字母串 描述&#xff1a; 對于一個小寫字母而言&#xff0c;游游可以通過一次操作把這個字母變成相鄰的字母。a和b相鄰&#xff0c;b和c相鄰&#xff0c;以此類推。特殊的&#xff0…

【AI深度學習基礎】Pandas完全指南入門篇:數據處理的瑞士軍刀 (含完整代碼)

&#x1f4da; Pandas 系列文章導航 入門篇 &#x1f331;進階篇 &#x1f680;終極篇 &#x1f30c; &#x1f4cc; 一、引言 在大數據與 AI 驅動的時代&#xff0c;數據預處理和分析是深度學習與機器學習的基石。Pandas 作為 Python 生態中最強大的數據處理庫&#xff0c;以…

數字萬用表的使用教程

福祿克經濟型數字萬用表前面板按鍵功能介紹示意圖 1. 萬用表簡單介紹 萬用表是一種帶有整流器的、可以測量交、直流電流、電壓及電阻等多種電學參量的磁電式儀表。分為數字萬用表&#xff0c;鉗形萬用表&#xff0c; &#xff08;1&#xff09;表筆分為紅、黑二只。使用時黑色…

C# IComparable<T> 使用詳解

總目錄 前言 在C#編程中&#xff0c;IComparable<T> 是一個非常重要的接口&#xff0c;它允許我們為自定義類型提供默認的比較邏輯。這對于實現排序、搜索和其他需要基于特定規則進行比較的操作特別有用。本文將詳細介紹 IComparable<T> 的使用方法、應用場景及其…

DeepSeek使用手冊分享-附PDF下載連接

本次主要分享DeepSeek從技術原理到使用技巧內容&#xff0c;這里展示一些基本內容&#xff0c;后面附上詳細PDF下載鏈接。 DeepSeek基本介紹 DeepSeek公司和模型的基本簡介&#xff0c;以及DeepSeek高性能低成本獲得業界的高度認可的原因。 DeepSeek技術路線解析 DeepSeek V3…

Hugging Face 推出 FastRTC:實時語音視頻應用開發變得得心應手

估值超過 40 億美元的 AI 初創公司 Hugging Face 推出了 FastRTC&#xff0c;這是一個開源 Python 庫&#xff0c;旨在消除開發者在構建實時音頻和視頻 AI 應用時的主要障礙。 "在 Python 中正確構建實時 WebRTC 和 Websocket 應用一直都很困難&#xff0c;"FastRTC…

for循環相關(循環的過程中對數據進行刪除會踩坑)

# 錯誤方式&#xff0c; 有坑&#xff0c;結果不是你想要的。 user_list ["劉的話", "范德彪", "劉華強", 劉尼古拉斯趙四, "宋小寶", "劉能"] for item in user_list: if item.startswith("劉"): …

Qt顯示一個hello world

一、顯示思路 思路一&#xff1a;通過圖形化方式&#xff0c;界面上創建出一個控件顯示。 思路二&#xff1a;通過編寫C代碼在界面上創建控件顯示。 二、思路一實現 點開 Froms 的 widget.ui&#xff0c;拖拽 label 控件&#xff0c;顯示 hello world 即可。 qmake 基于 .…

復合機器人為 CNC 毛坯件上下料注入 “智能強心針”

在競爭日益激烈的 CNC 加工行業&#xff0c;如何提升生產效率、保證產品質量、實現智能化生產成為眾多企業亟待解決的問題。富唯智能憑借其先進的復合機器人技術&#xff0c;成功為多家 CNC 加工企業提供了毛坯件上下料的優質解決方案&#xff0c;有效提升了生產效能&#xff0…

電商業務數據測試用例參考

1. 數據采集層測試 用例編號測試目標測試場景預期結果TC-001驗證用戶行為日志采集完整性模擬用戶瀏覽、點擊、加購行為Kafka Topic中日志記錄數與模擬量一致TC-002驗證無效數據過濾規則發送爬蟲請求&#xff08;高頻IP&#xff09;清洗后數據中無該IP的日志記錄 2. 數據處理層…

Spring Cloud Gateway 網關的使用

在之前的學習中&#xff0c;所有的微服務接口都是對外開放的&#xff0c;這就意味著用戶可以直接訪問&#xff0c;為了保證對外服務的安全性&#xff0c;服務端實現的微服務接口都帶有一定的權限校驗機制&#xff0c;但是由于使用了微服務&#xff0c;就需要每一個服務都進行一…

webstorm的Live Edit插件配合chrome擴展程序JetBrains IDE Support實現實時預覽html效果

前言 我們平時在前端網頁修改好代碼要點擊刷新再去看修改的效果&#xff0c;這樣比較麻煩&#xff0c;那么很多軟件都提供了實時預覽的功能&#xff0c;我們一邊編輯代碼一邊可以看到效果。下面說的是webstorm。 1 Live Edit 首先我們需要在webstorm的settings里安裝插件Live …

map的operator[]的實現

map的operator[]的實現 operator[]里包含插入操作&#xff0c;所以我們先看一下首先看一下map的insert函數 返回值是一個pair類型。正常的常見的insert&#xff0c;插入成功返回true&#xff0c;失敗返回false 這里設計的insert不單單返回布爾值&#xff0c;而是返回一個pair…

定時器的編碼器接口模式

選擇編碼器接口模式的方法是&#xff1a;如果計數器只在TI2的邊沿計數&#xff0c;則置TIMx_SMCR寄存器中的SMS001&#xff0c;如果只在TI1邊沿計數&#xff0c;則置SMS010&#xff0c;如果計數器同時在TI1和TI2邊沿計數&#xff0c;則置SMS 011 明確一點&#xff0c;計數器…

Openshift配置默認調度

配置默認調度選擇角色為worker的機器運行pod。 編輯scheduler oc edit schedulers.config.openshift.iospec:defaultNodeSelector: node-role.kubernetes.io/worker ## 添加這一段如果pod需要運行在非worker主機&#xff0c;需要配置pod所在的項目添加注解 openshift.io/node…

突破光學成像局限:全視野光學血管造影技術新進展

全視野光學血管造影&#xff08;FFOA&#xff09;作為一種實時、無創的成像技術&#xff0c;能夠提取生物血液微循環信息&#xff0c;為深入探究生物組織的功能和病理變化提供關鍵數據。然而&#xff0c;傳統FFOA成像方法受到光學鏡頭景深&#xff08;DOF&#xff09;的限制&am…

OpenHarmony 進階——HDF 驅動框架的原理小結

文章大綱 引言一、HDF的驅動加載&#xff08;驅動安裝&#xff09;方式1、動態加載&#xff08;主要是uhdf&#xff09;2、靜態加載(主要是khdf)2.1、驅動入口實現2.1.1、Bind接口2.1.2、Init接口2.1.3、Release接口 2.2、HDF_INIT 驅動入口符號2.3、獲取驅動列表2.4、獲取設備…