打印
[应用相关]

STM32_IIC外设工作流程

[复制链接]
434|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
STM32 I²C 外设工作流程(基于寄存器)
在 STM32 中,I²C 通信主要通过一系列寄存器控制。理解这些寄存器的作用,能够帮助我们掌握 I²C 硬件的运行机制,实现高效的数据传输。本文以 STM32F1(如 STM32F103)为例,详细讲解 I²C 外设的寄存器级操作。

1. STM32 I²C 相关寄存器
STM32 的 I²C 主要涉及以下寄存器:

控制寄存器 1(I2C_CR1):控制 I²C 外设的启用、应答、时钟伸展等功能。
控制寄存器 2(I2C_CR2):配置 I²C 的时钟、DMA 使能、中断使能等。
时钟控制寄存器(I2C_CCR):设置 I²C 通信速率(时钟分频)。
滤波寄存器(I2C_TRISE):配置最大上升时间,用于同步时钟。
状态寄存器 1(I2C_SR1):存储 I²C 通信的状态标志,如起始位、地址匹配、数据发送完成等。
状态寄存器 2(I2C_SR2):存储总线状态、模式、从机地址等信息。
数据寄存器(I2C_DR):用于收发数据。
2. I²C 外设初始化
在使用 I²C 之前,需要进行初始化,主要包括:

使能 I²C 时钟
配置 GPIO(SCL、SDA)
配置 I²C 速率
使能 I²C 外设
寄存器配置
void I2C_Init(void) {
    // 1. 使能 I²C1 时钟(I²C1 挂载在 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. 配置 I²C 时钟
    I2C1->CR2 = 36; // PCLK1 = 36MHz
    I2C1->CCR = 180; // 标准模式(100kHz):T_high = T_low = 10us, CCR = 180
    I2C1->TRISE = 37; // TRISE = (1000ns / (1/36MHz)) + 1

    // 5. 使能 I²C 外设
    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;  // 发送 NACK
    I2C1->CR1 |= I2C_CR1_STOP;  // 发送停止信号
    while (!(I2C1->SR1 & I2C_SR1_RXNE));

    data = I2C1->DR;  // 读取数据
    return data;
}



5. 复位 I²C 总线
当 I²C 总线锁死时,可通过软件复位:

I2C1->CR1 &= ~I2C_CR1_PE;  // 关闭 I²C
I2C1->CR1 |= I2C_CR1_PE;   // 重新使能 I²C


或手动拉高 SCL 并发送 9 个时钟脉冲。

6. 总结
I²C 初始化 需要配置 GPIO、I²C 时钟、CCR 寄存器
发送数据 依赖 I2C_CR1(START, STOP)、I2C_SR1(SB, TXE, BTF)、I2C_DR
接收数据 依赖 ACK、NACK、重复起始
通过 软件复位 处理总线锁死
掌握这些寄存器,可以更深入地理解 STM32 I²C 外设的运行机制,优化通信效率和稳定性。

STM32 I²C 数据收发过程(寄存器级详细解析)
STM32 的 I²C 外设工作过程中,多个寄存器的值会发生变化。我们将逐步拆解 主机发送数据 和 主机接收数据 的流程,并详细说明寄存器状态的变化,帮助你深入理解 STM32 I²C 硬件的底层机制。

1. STM32 I²C 主要寄存器
在 I²C 传输过程中,涉及以下主要寄存器:

1.1 控制寄存器



1.2 状态寄存器



1.3 数据寄存器



2. I²C 主机发送数据(寄存器变化)
步骤

发送 起始信号
发送 从机地址 + 写(bit 0 = 0)
发送 数据字节
发送 停止信号
2.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 发送数据
寄存器变化



代码
I2C1->DR = data;  // 发送数据
while (!(I2C1->SR1 & I2C_SR1_TXE));  // 等待数据传输完成


2.4 发送停止信号
寄存器变化



代码
I2C1->CR1 |= I2C_CR1_STOP;  // 发送停止信号


3. I²C 主机接收数据(寄存器变化)
步骤

发送 起始信号
发送 从机地址 + 读(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 读取数据
寄存器变化



代码
while (!(I2C1->SR1 & I2C_SR1_RXNE));  // 等待数据准备好
data = I2C1->DR;  // 读取数据


3.4 发送 NACK
寄存器变化



代码
I2C1->CR1 &= ~I2C_CR1_ACK;  // 发送 NACK


3.5 发送停止信号
与主机发送数据相同:

I2C1->CR1 |= I2C_CR1_STOP;  // 发送停止信号


4. 复位 I²C 总线
若 I²C 总线锁死,可执行软件复位:

I2C1->CR1 &= ~I2C_CR1_PE;  // 关闭 I²C
I2C1->CR1 |= I2C_CR1_PE;   // 重新启用 I²C


5. 总结
I2C_SR1 标志寄存器:指示 I²C 传输状态(SB、ADDR、TXE、RXNE)。
I2C_DR 数据寄存器:用于收发数据。
I2C_CR1 控制寄存器:用于产生 START、STOP、ACK。
寄存器级的 I²C 操作能提供更高的灵活性,适用于驱动底层 I²C 设备,如摄像头、EEPROM、传感器等。

STM32 I²C 完整收发流程(寄存器级详细解析)
为了更清楚地理解 STM32 I²C 外设的寄存器级操作,我们使用一个 完整的示例:
假设 STM32 作为 主机,从 I²C 设备(如 EEPROM、传感器)读取一个寄存器 的值,
然后 修改该值并写回。

1. 示例任务
目标
读取 从机(设备地址 0x50)的寄存器 0x10 的值。
修改该值(加 1)。
写回 该值到 0x10。
I²C 设备信息
设备地址:0x50
寄存器地址:0x10
I²C 速率:100kHz(标准模式)
2. I²C 数据收发完整流程
完整步骤
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. 发送 NACK
    I2C1->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. 关键寄存器变化总结



5. 结论
I2C_CR1 控制起始、停止、ACK/NACK 发送。
I2C_SR1 监视数据传输状态(SB、ADDR、TXE、RXNE)。
I2C_DR 用于收发数据。
这个完整的流程展示了 I²C 数据收发 的 寄存器级操作,适用于传感器、EEPROM、摄像头等 I²C 设备的底层驱动开发。

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)。
配置时钟分频:
CCR 的 CCR[11:0] 位:设置 SCL 时钟分频。
标准模式(100 kHz)或快速模式(400 kHz)。
配置上升时间:
TRISE:根据模式设置(标准模式:1000ns → TRISE = F_APB1(MHz) + 1)。
使能 I2C:
CR1 的 PE 位置 1,使能外设。
(2) 发送起始条件
生成 START 信号:
CR1 的 START 位置 1。
等待起始条件完成:
轮询 SR1 的 SB 位(Start Bit),当 SB=1 时,起始条件生成成功。
必须读取 SR1 后写 DR 寄存器(硬件自动清除 SB)。
(3) 发送从机地址
写入从机地址 + 方向位:
DR 写入 7-bit地址<<1 | R/W位(0 表示写)。
等待地址应答:
轮询 SR1 的 ADDR 位(地址发送完成)。
必须读取 SR1 和 SR2 以清除 ADDR 标志。
(4) 发送数据
写入数据到 DR:
DR 写入待发送的数据字节。
等待数据发送完成:
轮询 SR1 的 TXE 位(Transmit Data Register Empty)。
当 TXE=1,表示数据已转移到移位寄存器,可写入下一字节。
重复步骤 4.1-4.2 发送所有数据。
(5) 发送停止条件
生成 STOP 信号:
CR1 的 STOP 位置 1。
等待停止完成:
根据时序要求等待(无需轮询特定标志)。
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])。
TRISE:设置 SCL 上升时间(例如,标准模式:TRISE = APB1_CLK(MHz) + 1)。
CR1 的 PE 位:置 1 使能 I2C 外设。
2. 主机发送模式(Master Transmitter)详细流程
(1) 生成起始条件(START)
操作寄存器:
CR1 的 START 位置 1。
硬件行为:
I2C 硬件检测总线空闲(SR2.BUSY=0)后,生成 START 条件。
状态标志变化:
SR1.SB 置 1:表示 START 条件已生成。
关键操作:
必须 读取 SR1 寄存器(清除 SB 位),然后立即写入从机地址到 DR 寄存器。
(2) 发送从机地址
写入地址到 DR:
DR 写入 (SlaveAddr << 1) | 0(0 表示写操作)。
硬件行为:
硬件自动发送地址 + 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 状态


(3) 发送数据字节
写入数据到 DR:
DR 写入待发送的数据(例如 0x55)。
硬件行为:
硬件将 DR 中的数据移到移位寄存器,并逐位发送到 SDA 线。
状态标志变化:
SR1.TXE 置 1:表示 DR 已空,可以写入下一个字节。
SR1.BTF 置 1:表示当前字节已完全发送(包括 ACK 周期)。
关键操作:
若 TXE=1,写入新数据到 DR,硬件自动清除 TXE。
若 BTF=1,表示数据已发送完成,但需结合 TXE 判断。
(4) 发送停止条件(STOP)
操作寄存器:
CR1 的 STOP 位置 1。
硬件行为:
生成 STOP 条件,释放总线。
状态标志变化:
SR2.BUSY 置 0:总线空闲。
3. 主机接收模式(Master Receiver)详细流程
(1) 生成 START 并发送读地址
发送 START(同发送模式)。
写入读地址到 DR:
DR 写入 (SlaveAddr << 1) | 1(1 表示读操作)。
状态标志变化:
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. 寄存器状态变化时序图(示例)
以主机发送模式为例,展示寄存器关键位的变化时序:



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 波形,验证寄存器操作是否符合预期。
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/m0_57249200/article/details/146025789

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

41

主题

92

帖子

0

粉丝