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. 主機模式發送數據
主機模式下,發送數據的流程如下:
- 檢查總線狀態
- 發送起始信號
- 發送從機地址(寫)
- 發送數據
- 發送停止信號
寄存器操作
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. 主機模式接收數據
- 發送起始信號
- 發送從機地址(寫)
- 發送寄存器地址
- 發送重復起始信號
- 發送從機地址(讀)
- 讀取數據
- 發送 NACK,結束傳輸
- 發送停止信號
寄存器操作
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 主機發送數據(寄存器變化)
步驟
- 發送 起始信號
- 發送 從機地址 + 寫(bit 0 = 0)
- 發送 數據字節
- 發送 停止信號
2.1 發送起始信號
寄存器變化
操作 | I2C_CR1 | I2C_SR1 | 說明 |
---|---|---|---|
`I2C1->CR1 | = I2C_CR1_START;` | START = 1 | |
等待 SB=1 | SB=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
| |
| 讀取 SR2
清 ADDR
| | ADDR=0
| 地址發送完成 |
代碼
I2C1->DR = (devAddr << 1) | 0; // 發送從機地址 + 寫
while (!(I2C1->SR1 & I2C_SR1_ADDR)); // 等待地址匹配
(void)I2C1->SR2; // 讀取 SR2 清除 ADDR 標志
2.3 發送數據
寄存器變化
操作 | I2C_DR | I2C_SR1 | 說明 |
---|---|---|---|
I2C1->DR = data; | 發送數據 | TXE=0 | 數據正在發送 |
等待 TXE=1 | TXE=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 主機接收數據(寄存器變化)
步驟
- 發送 起始信號
- 發送 從機地址 + 讀(bit 0 = 1)
- 讀取 數據
- 發送 NACK
- 發送 停止信號
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
| |
| 讀取 SR2
清 ADDR
| | ADDR=0
| 地址發送完成 |
代碼
I2C1->DR = (devAddr << 1) | 1;
while (!(I2C1->SR1 & I2C_SR1_ADDR));
(void)I2C1->SR2;
3.3 讀取數據
寄存器變化
操作 | I2C_SR1 | I2C_DR | 說明 |
---|---|---|---|
等待 RXNE=1 | RXNE=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 傳輸狀態(SB
、ADDR
、TXE
、RXNE
)。I2C_DR
數據寄存器:用于收發數據。I2C_CR1
控制寄存器:用于產生START
、STOP
、ACK
。
寄存器級的 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_CR1 | START=1 | 產生起始信號 |
發送設備地址(寫) | I2C_DR | ADDR=1 | 發送地址 |
發送寄存器地址 | I2C_DR | TXE=1 | 發送數據 |
發送重復起始信號 | I2C_CR1 | START=1 | 重新開始 |
發送設備地址(讀) | I2C_DR | ADDR=1 | 發送地址 |
讀取數據 | I2C_DR | RXNE=1 | 接收數據 |
發送 NACK | I2C_CR1 | ACK=0 | 結束讀取 |
發送停止信號 | I2C_CR1 | STOP=1 | 終止傳輸 |
5. 結論
I2C_CR1
控制起始、停止、ACK/NACK 發送。I2C_SR1
監視數據傳輸狀態(SB
、ADDR
、TXE
、RXNE
)。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) 初始化配置
- 配置 GPIO:將 SCL/SDA 引腳設為復用開漏模式(需外部上拉電阻)。
- 配置 I2C 時鐘:
- CR2 的
FREQ[5:0]
位:設置 APB 時鐘頻率(單位 MHz)。
- CR2 的
- 配置時鐘分頻:
- CCR 的
CCR[11:0]
位:設置 SCL 時鐘分頻。 - 標準模式(100 kHz)或快速模式(400 kHz)。
- CCR 的
- 配置上升時間:
- TRISE:根據模式設置(標準模式:1000ns →
TRISE = F_APB1(MHz) + 1
)。
- TRISE:根據模式設置(標準模式:1000ns →
- 使能 I2C:
- CR1 的
PE
位置 1,使能外設。
- CR1 的
(2) 發送起始條件
- 生成 START 信號:
- CR1 的
START
位置 1。
- CR1 的
- 等待起始條件完成:
- 輪詢 SR1 的
SB
位(Start Bit),當SB=1
時,起始條件生成成功。 - 必須讀取 SR1 后寫 DR 寄存器(硬件自動清除
SB
)。
- 輪詢 SR1 的
(3) 發送從機地址
- 寫入從機地址 + 方向位:
- DR 寫入
7-bit地址<<1 | R/W位(0 表示寫)
。
- DR 寫入
- 等待地址應答:
- 輪詢 SR1 的
ADDR
位(地址發送完成)。 - 必須讀取 SR1 和 SR2 以清除
ADDR
標志。
- 輪詢 SR1 的
(4) 發送數據
- 寫入數據到 DR:
- DR 寫入待發送的數據字節。
- 等待數據發送完成:
- 輪詢 SR1 的
TXE
位(Transmit Data Register Empty)。 - 當
TXE=1
,表示數據已轉移到移位寄存器,可寫入下一字節。
- 輪詢 SR1 的
- 重復步驟 4.1-4.2 發送所有數據。
(5) 發送停止條件
- 生成 STOP 信號:
- CR1 的
STOP
位置 1。
- CR1 的
- 等待停止完成:
- 根據時序要求等待(無需輪詢特定標志)。
3. 主機接收模式(Master Receiver)流程
流程與發送模式類似,但需注意:
- 發送從機地址時,
R/W
位置 1。 - 配置 CR1 的
ACK
位以控制是否發送應答:- 在接收最后一個字節前,
ACK
位需清零(發送 NACK)。
- 在接收最后一個字節前,
- 從 DR 讀取數據前,需檢查 SR1 的
RXNE
位(Receive Data Register Not Empty)。
4. 關鍵狀態標志與錯誤處理
- BUSY (SR2[1]):總線忙狀態。
- BTF (SR1[2]):字節傳輸完成。
- AF (SR1[10]):應答失敗(NACK)。
- ARLO (SR1[9]):仲裁丟失。
- BERR (SR1[8]):總線錯誤。
錯誤處理:
- 檢測到錯誤標志后,清除錯誤(通過寫 CR1 的
SWRST
位或重新初始化)。 - 重新發起通信。
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. 注意事項
- 時鐘配置:
CCR
和TRISE
需嚴格匹配時序要求。 - 標志清除順序:某些標志(如
ADDR
)需通過讀 SR1 + SR2 清除。 - 中斷/DMA:可通過 CR2 配置中斷或 DMA 傳輸,減少 CPU 輪詢開銷。
通過直接操作寄存器,開發者可以更精細地控制 I2C 時序,但需嚴格遵循 STM32 參考手冊的流程以避免硬件錯誤。
好的!我將更詳細地分析 I2C 外設在數據傳輸過程中寄存器的具體變化,結合 STM32 的硬件行為,逐步拆解每個階段的寄存器操作和狀態標志的響應。以下是深入細節的流程說明:
1. 初始化階段的寄存器配置
(1) GPIO 模式配置
- GPIOx_CRL/CRH:配置 SCL/SDA 引腳為 復用開漏模式(
GPIO_Mode_AF_OD
)。 - GPIOx_ODR:無需手動設置,但硬件要求外部上拉電阻。
(2) I2C 時鐘與模式配置
- CR2 的
FREQ[5:0]
:設置 APB1 總線時鐘頻率(例如,36 MHz →FREQ=36
)。 - CCR 的
CCR[11:0]
:- 標準模式(100 kHz):
CCR = APB1_CLK / (2 * 100000)
。 - 快速模式(400 kHz):
CCR = APB1_CLK / (2 * 400000)
。 - 快速模式+(1 MHz):需使能
F/S
位(CCR[15])。
- 標準模式(100 kHz):
- TRISE:設置 SCL 上升時間(例如,標準模式:
TRISE = APB1_CLK(MHz) + 1
)。 - CR1 的
PE
位:置 1 使能 I2C 外設。
2. 主機發送模式(Master Transmitter)詳細流程
(1) 生成起始條件(START)
- 操作寄存器:
- CR1 的
START
位置 1。
- CR1 的
- 硬件行為:
- I2C 硬件檢測總線空閑(
SR2.BUSY=0
)后,生成 START 條件。
- I2C 硬件檢測總線空閑(
- 狀態標志變化:
- SR1.SB 置 1:表示 START 條件已生成。
- 關鍵操作:
- 必須 讀取 SR1 寄存器(清除
SB
位),然后立即寫入從機地址到 DR 寄存器。
- 必須 讀取 SR1 寄存器(清除
(2) 發送從機地址
- 寫入地址到 DR:
- DR 寫入
(SlaveAddr << 1) | 0
(0 表示寫操作)。
- DR 寫入
- 硬件行為:
- 硬件自動發送地址 + R/W 位,并等待從機的 ACK。
- 狀態標志變化:
- SR1.ADDR 置 1:地址已發送且收到 ACK。
- SR2.TRA 置 1:表示當前處于發送模式。
- 關鍵操作:
- 必須 讀取 SR1 和 SR2 寄存器以清除
ADDR
標志。 - 示例代碼:
volatile uint32_t dummy = I2C1->SR1; // 讀取 SR1 清除 ADDR dummy = I2C1->SR2; // 讀取 SR2 清除 BUSY 狀態
- 必須 讀取 SR1 和 SR2 寄存器以清除
(3) 發送數據字節
- 寫入數據到 DR:
- DR 寫入待發送的數據(例如
0x55
)。
- DR 寫入待發送的數據(例如
- 硬件行為:
- 硬件將 DR 中的數據移到移位寄存器,并逐位發送到 SDA 線。
- 狀態標志變化:
- SR1.TXE 置 1:表示 DR 已空,可以寫入下一個字節。
- SR1.BTF 置 1:表示當前字節已完全發送(包括 ACK 周期)。
- 關鍵操作:
- 若
TXE=1
,寫入新數據到 DR,硬件自動清除TXE
。 - 若
BTF=1
,表示數據已發送完成,但需結合TXE
判斷。
- 若
(4) 發送停止條件(STOP)
- 操作寄存器:
- CR1 的
STOP
位置 1。
- CR1 的
- 硬件行為:
- 生成 STOP 條件,釋放總線。
- 狀態標志變化:
- SR2.BUSY 置 0:總線空閑。
3. 主機接收模式(Master Receiver)詳細流程
(1) 生成 START 并發送讀地址
- 發送 START(同發送模式)。
- 寫入讀地址到 DR:
- DR 寫入
(SlaveAddr << 1) | 1
(1 表示讀操作)。
- DR 寫入
- 狀態標志變化:
- SR1.ADDR 置 1:地址發送成功。
- SR2.TRA 置 0:表示當前處于接收模式。
(2) 接收數據流程
- 配置 ACK/NACK:
- CR1.ACK 置 1:使能 ACK(接收每個字節后發送 ACK)。
- 在接收最后一個字節前,需 清零 ACK 位(發送 NACK)。
- 讀取數據:
- 等待 SR1.RXNE=1:表示 DR 中有新數據。
- 讀取 DR 寄存器,硬件自動清除
RXNE
。
- 狀態標志變化:
- SR1.RXNE 置 1:數據已接收完畢。
- SR1.BTF 置 1:字節傳輸完成(包括 ACK/NACK 周期)。
(3) 生成 STOP 條件
- 同發送模式,需在接收最后一個字節后立即生成 STOP。
4. 寄存器狀態變化時序圖(示例)
以主機發送模式為例,展示寄存器關鍵位的變化時序:
操作 | CR1.START | SR1.SB | SR1.ADDR | SR1.TXE | SR1.BTF | SR2.BUSY |
---|---|---|---|---|---|---|
初始狀態 | 0 | 0 | 0 | 0 | 0 | 0 |
設置 START=1 | 1 | 0 | 0 | 0 | 0 | 1 |
START 生成完成 | 1 | 1 | 0 | 0 | 0 | 1 |
寫入地址到 DR | 1 | 0 | 0 | 0 | 0 | 1 |
地址發送完成 | 1 | 0 | 1 | 0 | 0 | 1 |
清除 ADDR(讀 SR1/SR2) | 1 | 0 | 0 | 0 | 0 | 1 |
寫入數據到 DR | 1 | 0 | 0 | 0 | 0 | 1 |
數據開始發送 | 1 | 0 | 0 | 0 | 0 | 1 |
數據發送完成 | 1 | 0 | 0 | 1 | 1 | 1 |
設置 STOP=1 | 0 | 0 | 0 | 1 | 1 | 0 |
5. 錯誤處理與寄存器恢復
(1) 應答失敗(NACK)
- 觸發條件:從機未應答地址或數據。
- 狀態標志:SR1.AF=1。
- 恢復操作:
- 清除
AF
標志:寫 SR1 的AF
位為 0。 - 生成 STOP 或重復 START:
I2C1->CR1 |= I2C_CR1_STOP; // 強制 STOP I2C1->SR1 &= ~I2C_SR1_AF; // 清除 AF 標志
- 清除
(2) 總線仲裁丟失(Arbitration Lost)
- 觸發條件:多主機競爭時,STM32 失去總線控制權。
- 狀態標志:SR1.ARLO=1。
- 恢復操作:
- 清除
ARLO
:寫 SR1 的ARLO
位為 0。 - 重新初始化 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 來清除。AF
和ARLO
標志需通過寫 0 清除。
8. 總結
通過直接操作寄存器,可以精確控制 I2C 的每一個總線動作,但需要嚴格遵循以下原則:
- 狀態標志的清除順序:如
ADDR
必須讀 SR1 + SR2。 - 時序匹配:
CCR
和TRISE
需根據 APB1 時鐘頻率計算。 - 錯誤恢復:檢測到錯誤標志后,必須清除并重新初始化外設。
實際開發中,建議結合 STM32 參考手冊的 I2C 時序圖 和 寄存器描述,通過邏輯分析儀抓取 SCL/SDA 波形,驗證寄存器操作是否符合預期。