第19章 I2C介绍及应用

第十九章 I2C介绍及应用

1. I2C简介

I₂C(读作“I-squared-C”或“I-two-C”)是一种由飞利浦公司(现为NXP Semiconductors)在1980年代初开发的串行通信总线协议,用于连接低速外围设备与主控制器,广泛应用于嵌入式系统、传感器、EEPROM、实时时钟(RTC)、LCD驱动器等设备之间的通信。

基本特点:

  1. 双线制通信
  • SDA(Serial Data Line):串行数据线,用于传输数据。
  • SCL(Serial Clock Line):串行时钟线,由主设备控制,同步数据传输。
  1. 半双工通信
  • 同一根数据线用于发送和接收,不能同时进行。
  1. 多主多从架构
  • 支持多个主设备和多个从设备挂接在同一总线上。
  • 通过仲裁机制避免多个主设备同时通信造成冲突。
  1. 地址寻址机制
  • 每个从设备具有唯一的7位或10位地址。
  • 主设备通过地址选择目标从设备进行通信。
  1. 低速但可靠
  • 常见速率:标准模式(100 kbps)、快速模式(400 kbps)、高速模式(3.4 Mbps)。
  • 适合短距离板内通信。
  1. 开漏输出 + 上拉电阻
  • SDA 和 SCL 通常使用开漏(Open-Drain)输出结构,需要外接上拉电阻(通常为4.7kΩ)。
  • 支持“线与”逻辑,实现多设备共享总线。
信号 触发条件 方向 作用
Start SCL高,SDA由高→低 主设备 开始通信
Stop SCL高,SDA由低→高 主设备 结束通信
Repeated Start 同 Start,但之前无 Stop 主设备 切换读写不释放总线
Data Bit SCL低时变化,高时稳定 主/从 传输数据
ACK 接收方在第9位时拉低SDA 接收方 确认接收成功
NACK 接收方不拉低SDA 接收方 拒绝接收或结束

2. I2C应用示例

2.1 I2C初始化

#include "i2c.h"
#include "delay.h"

// I2C延时
static void i2c_delay(void)
{
    delay_us(2);
}

// I2C起始信号
void i2c_start(void)
{
    I2C_SDA(1);
    I2C_SCL(1);
    i2c_delay();
    I2C_SDA(0); /* START信号: 当SCL为高时, SDA从高变成低, 表示起始信号 */
    i2c_delay();
    I2C_SCL(0); /* 钳住I2C总线,准备发送或接收数据 */
    i2c_delay();
}

void i2c_stop(void)
{
    I2C_SDA(0); /* STOP信号: 当SCL为高时, SDA从低变成高, 表示停止信号 */
    i2c_delay();
    I2C_SCL(1);
    i2c_delay();
    I2C_SDA(1); /* 发送I2C总线结束信号 */
    i2c_delay();
}

// 等待应答信号 1:接收应答失败 0:接收应答成功
uint8_t i2c_wait_ack(void)
{
    uint8_t wait_time = 0;
    uint8_t wait_ack = 0;
    I2C_SDA(1);
    i2c_delay();
    I2C_SCL(1);
    i2c_delay();
    while(I2C_READ_SDA)
    {
        wait_time++;
        if(wait_time > 250)
        {
            i2c_stop();
            wait_ack = 1;
            break;
        }
    }
    I2C_SCL(0);
    i2c_delay();
    return wait_ack;
}

// 产生ACK应答
void i2c_ack(void)
{
    I2C_SDA(0);
    i2c_delay();
    I2C_SCL(1);
    i2c_delay();
    I2C_SCL(0);
    i2c_delay();
    I2C_SDA(1);
    i2c_delay();
}

// 产生NACK应答
void i2c_nack(void)
{
    I2C_SDA(1);
    i2c_delay();
    I2C_SCL(1);
    i2c_delay();
    I2C_SCL(0);
    i2c_delay();
}

// I2C发送一个字节
void i2c_send_byte(uint8_t data)
{
    uint8_t i;
    for(i=0;i<8;i++)
    {
        I2C_SDA((data&0x80)>>7);
        i2c_delay();
        I2C_SCL(1);
        i2c_delay();
        I2C_SCL(0);
        data <<= 1;
    }
    I2C_SDA(1);
}

// I2C读取一个字节
// ack=1时,发送ack; ack=0时,发送nack
uint8_t i2c_read_byte(uint8_t ack)
{
    uint8_t i,reve = 0;
    for(i=0;i<8;i++)
    {
        reve <<= 1;
        I2C_SCL(1);
        i2c_delay();
        if(I2C_READ_SDA)
        {
            reve++;
        }
        I2C_SCL(0);
        i2c_delay();
    }
    if(!ack)
    {
        i2c_nack();
    }
    else
    {
        i2c_ack();
    }
    return reve;
}

// I2C GPIO初始化
void i2c_init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    __HAL_RCC_GPIOB_CLK_ENABLE();
    // SCL-PB8 SDA-PB9
    GPIO_InitStructure.Pin = GPIO_PIN_8;
    GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出
    GPIO_InitStructure.Pull = GPIO_PULLUP; // 上拉
    GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
    GPIO_InitStructure.Pin = GPIO_PIN_9;
    GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_OD; // 开漏输出
    HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
    i2c_stop();
}

2.2 AT24CXX相关宏定义

#ifndef __24C02_H
#define __24C02_H

#include "sys.h"
#include "i2c.h"
#include "delay.h"

#define AT24C01     127
#define AT24C02     255
#define AT24C04     511
#define AT24C08     1023
#define AT24C16     2047
#define AT24C32     4095
#define AT24C64     8191
#define AT24C128    16383
#define AT24C256    32767

#define MY_TYPE AT24C02

void at24cxx_init(void);
uint8_t at24cxx_read_one_byte(uint16_t addr);
void at24cxx_write_one_byte(uint16_t addr, uint8_t data);
uint8_t at24cxx_check(void);
void at24cxx_read(uint16_t addr, uint8_t *pbuf, uint16_t len);
void at24cxx_write(uint16_t addr, uint8_t *pbuf, uint16_t len);

#endif /* __24C02_H */

2.3 AT24CXX读写相关函数

#include "24c02.h"

void at24cxx_init(void)
{
    i2c_init();
}

/**
 * @brief       在AT24CXX指定地址读出一个数据
 * @param       readaddr: 开始读数的地址
 * @retval      读到的数据
 */
uint8_t at24cxx_read_one_byte(uint16_t addr)
{
    uint8_t temp = 0;
    i2c_start(); // 发送起始信号
    /* 根据不同的24CXX型号, 发送高位地址
     * 1, 24C16以上的型号, 分2个字节发送地址
     * 2, 24C16及以下的型号, 分1个低字节地址 + 占用器件地址的bit1~bit3位 用于表示高位地址, 最多11位地址
     *    对于24C01/02, 其器件地址格式(8bit)为: 1  0  1  0  A2  A1  A0  R/W
     *    对于24C04,    其器件地址格式(8bit)为: 1  0  1  0  A2  A1  a8  R/W
     *    对于24C08,    其器件地址格式(8bit)为: 1  0  1  0  A2  a9  a8  R/W
     *    对于24C16,    其器件地址格式(8bit)为: 1  0  1  0  a10 a9  a8  R/W
     *    R/W      : 读/写控制位 0,表示写; 1,表示读;
     *    A0/A1/A2 : 对应器件的1,2,3引脚(只有24C01/02/04/8有这些脚)
     *    a8/a9/a10: 对应存储整列的高位地址, 11bit地址最多可以表示2048个位置, 可以寻址24C16及以内的型号
     */    
    if(MY_TYPE > AT24C16) // 24C16以上的型号, 分2个字节发送地址
    {
        i2c_send_byte(0xA0); // 发送写命令, IIC规定最低位是0, 表示写入 
        i2c_wait_ack(); // 每次发送完一个字节,都要等待ACK 
        i2c_send_byte(addr >> 8); // 发送高字节地址 
    }
    else 
    {
        i2c_send_byte(0xA0 + ((addr >> 8) << 1)); // 发送器件 0xA0 + 高位a8/a9/a10地址,写数据
    }

    i2c_wait_ack();           // 每次发送完一个字节,都要等待ACK
    i2c_send_byte(addr % 256);// 发送低位地址
    i2c_wait_ack();           // 等待ACK, 此时地址发送完成了
    i2c_start();              // 重新发送起始信号 
    i2c_send_byte(0xA1);      // 进入接收模式, IIC规定最低位是1, 表示读取
    i2c_wait_ack();           // 次发送完一个字节,都要等待ACK 
    temp = i2c_read_byte(0);  // 接收一个字节数据 
    i2c_stop();               // 产生一个停止条件 
    return temp;
}

/**
 * @brief       在AT24CXX指定地址写入一个数据
 * @param       addr: 写入数据的目的地址
 * @param       data: 要写入的数据
 * @retval      无
 */
void at24cxx_write_one_byte(uint16_t addr, uint8_t data)
{
    /* 原理说明见:at24cxx_read_one_byte函数, 本函数完全类似 */
    i2c_start();
    if (MY_TYPE > AT24C16)    
    {
        i2c_send_byte(0xA0);    
        i2c_wait_ack();        
        i2c_send_byte(addr >> 8);   
    }
    else
    {
        i2c_send_byte(0xA0 + ((addr >> 8) << 1));   
    }
    i2c_wait_ack();             
    i2c_send_byte(addr % 256);  
    i2c_wait_ack();            
    /* 因为写数据的时候,不需要进入接收模式了,所以这里不用重新发送起始信号了 */
    i2c_send_byte(data); // 发送1字节 
    i2c_wait_ack();            
    i2c_stop();                 
    delay_ms(10); // 注意: EEPROM 写入比较慢,必须等到10ms后再写下一个字节 
}

/**
 * @brief       检查AT24CXX是否正常
 *   @note      检测原理: 在器件的末地址写如0X55, 然后再读取, 如果读取值为0X55
 *              则表示检测正常. 否则,则表示检测失败.
 *
 * @param       无
 * @retval      检测结果
 *              0: 检测成功
 *              1: 检测失败
 */
uint8_t at24cxx_check(void)
{
    uint8_t temp;
    uint16_t addr = MY_TYPE;
    temp = at24cxx_read_one_byte(addr); // 避免每次开机都写AT24CXX
    if (temp == 0x55)  // 读取数据正常
    {
        return 0;
    }
    else
    {
        at24cxx_write_one_byte(addr, 0x55); // 先写入数据
        temp = at24cxx_read_one_byte(255);  // 再读取数据 
        if (temp == 0x55)return 0;
    }
    return 1;
}

// 在AT24CXX里面指定地址开始读出指定个数的数据
void at24cxx_read(uint16_t addr, uint8_t *pbuf, uint16_t len)
{
    while (len--)
    {
        *pbuf++ = at24cxx_read_one_byte(addr++);
    }
}

// 在AT24CXX里面的指定地址开始写入指定个数的数据
void at24cxx_write(uint16_t addr, uint8_t *pbuf, uint16_t len)
{
    while(len--)
    {
        at24cxx_write_one_byte(addr, *pbuf);
        addr++;
        pbuf++;
    }
}

2.4 主函数测试

#include "bsp_init.h"
#include "stdio.h"
#include "24c02.h"

// 要写入的字符串
const uint8_t text_buf[] = {"STM32 I2C TEST"};
#define TEXT_SIZE sizeof(text_buf)

int main(void)
{
    uint16_t i = 0;
    uint8_t data[TEXT_SIZE];
    bsp_init();
    at24cxx_init();
    LCD_ShowString(30,110,200,16,16,"KEY1:Write  KEY0:Read");
    while(at24cxx_check())
    {
        LCD_ShowString(30,130,200,16,16,"24C02 Check Failed!");
    }
    LCD_ShowString(30,130,200,16,16,"24C02 Ready!");
    while(1)
    {
        if(HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY0_GPIO_Pin) == 0) // KEY0按下写入
        {
            LCD_ShowString(30,150,200,16,16,"Start Write 24C02....");
            at24cxx_write(0, (uint8_t*)text_buf, TEXT_SIZE);
            LCD_ShowString(30,150,200,16,16,"24C02 Write Finished!");
        }
        if(HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY1_GPIO_Pin) == 0)
        {
            LCD_ShowString(30,150,200,16,16,"Start Read 24C02.... ");
            at24cxx_read(0, data, TEXT_SIZE);
            LCD_ShowString(30,150,200,16,16,"The Data Readed Is:  ");
            LCD_ShowString(30,170,200,16,16,(char*)data);
        }
        i++;
        if(i == 20)
        {
            LED_TOGGLE(LED0_GPIO_Pin);
            i = 0;
        }
    delay_ms(10);
    }
}

3. I2C常见函数(HAL库)

3.1 I2C 初始化与配置

3.1.1 HAL_I2C_Init()

函数原型:

HAL_StatusTypeDef HAL_I2C_Init(I2C_HandleTypeDef *hi2c)

参数:

  • hi2c: I2C 句柄指针

配置结构体:

typedef struct {
 I2C_TypeDef *Instance; // I2C实例 (I2C1, I2C2, I2C3)
 uint32_t ClockSpeed; // 时钟速度 (Hz) 标准模式100K, 快速模式400K
 uint32_t DutyCycle; // 快速模式占空比:
 // I2C_DUTYCYCLE_2 (Tlow/Thigh = 2)
 // I2C_DUTYCYCLE_16_9 (Tlow/Thigh = 16/9)
 uint32_t OwnAddress1; // 主设备自身地址 (7位或10位)
 uint32_t AddressingMode; // 地址模式:
 // I2C_ADDRESSINGMODE_7BIT
 // I2C_ADDRESSINGMODE_10BIT
 uint32_t DualAddressMode; // 双地址模式: I2C_DUALADDRESS_DISABLE/ENABLE
 uint32_t OwnAddress2; // 第二个自身地址 (仅双地址模式)
 uint32_t GeneralCallMode; // 广播呼叫模式: I2C_GENERALCALL_DISABLE/ENABLE
 uint32_t NoStretchMode; // 时钟拉伸模式: I2C_NOSTRETCH_DISABLE/ENABLE
} I2C_InitTypeDef;

示例配置 (主模式):

I2C_HandleTypeDef hi2c1;

void I2C_Init(void) {
    __HAL_RCC_I2C1_CLK_ENABLE();
    
    hi2c1.Instance = I2C1;
    hi2c1.Init.ClockSpeed = 400000;           // 400KHz (快速模式)
    hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;   // Tlow/Thigh = 2
    hi2c1.Init.OwnAddress1 = 0;               // 主设备不需要地址
    hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
    hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
    hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
    hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
    
    if (HAL_I2C_Init(&hi2c1) != HAL_OK) {
        Error_Handler();
    }
}

3.2 I2C 主模式操作

3.2.1 主设备发送数据

// 阻塞模式发送
HAL_StatusTypeDef HAL_I2C_Master_Transmit(
 I2C_HandleTypeDef *hi2c,
 uint16_t DevAddress,
 uint8_t *pData,
 uint16_t Size,
 uint32_t Timeout)

// 中断模式发送
HAL_StatusTypeDef HAL_I2C_Master_Transmit_IT(
 I2C_HandleTypeDef *hi2c,
 uint16_t DevAddress,
 uint8_t *pData,
 uint16_t Size)

// DMA模式发送
HAL_StatusTypeDef HAL_I2C_Master_Transmit_DMA(
 I2C_HandleTypeDef *hi2c,
 uint16_t DevAddress,
 uint8_t *pData,
 uint16_t Size)

示例 (发送数据到从设备):

#define I2C_ADDRESS 0x68 // DS3231 RTC地址
uint8_t data[2] = {0x00, 0x01}; // 寄存器地址和值

// 阻塞模式发送
HAL_I2C_Master_Transmit(&hi2c1, I2C_ADDRESS << 1, data, 2, 100);

3.2.2 主设备接收数据

// 阻塞模式接收
HAL_StatusTypeDef HAL_I2C_Master_Receive(
 I2C_HandleTypeDef *hi2c,
 uint16_t DevAddress,
 uint8_t *pData,
 uint16_t Size,
 uint32_t Timeout)

// 中断模式接收
HAL_StatusTypeDef HAL_I2C_Master_Receive_IT(
 I2C_HandleTypeDef *hi2c,
 uint16_t DevAddress,
 uint8_t *pData,
 uint16_t Size)

// DMA模式接收
HAL_StatusTypeDef HAL_I2C_Master_Receive_DMA(
 I2C_HandleTypeDef *hi2c,
 uint16_t DevAddress,
 uint8_t *pData,
 uint16_t Size)

示例 (从设备读取数据):

uint8_t reg_addr = 0x00; // 寄存器地址
uint8_t value = 0;

// 发送寄存器地址
HAL_I2C_Master_Transmit(&hi2c1, I2C_ADDRESS << 1, &reg_addr, 1, 100);

// 接收寄存器值
HAL_I2C_Master_Receive(&hi2c1, I2C_ADDRESS << 1, &value, 1, 100);

3.3 I2C 从模式操作

3.3.1 从设备接收数据

// 阻塞模式接收
HAL_StatusTypeDef HAL_I2C_Slave_Receive(
 I2C_HandleTypeDef *hi2c,
 uint8_t *pData,
 uint16_t Size,
 uint32_t Timeout)

// 中断模式接收
HAL_StatusTypeDef HAL_I2C_Slave_Receive_IT(
 I2C_HandleTypeDef *hi2c,
 uint8_t *pData,
 uint16_t Size)

// DMA模式接收
HAL_StatusTypeDef HAL_I2C_Slave_Receive_DMA(
 I2C_HandleTypeDef *hi2c,
 uint8_t *pData,
 uint16_t Size)

3.3.2 从设备发送数据

// 阻塞模式发送
HAL_StatusTypeDef HAL_I2C_Slave_Transmit(
 I2C_HandleTypeDef *hi2c,
 uint8_t *pData,
 uint16_t Size,
 uint32_t Timeout)

// 中断模式发送
HAL_StatusTypeDef HAL_I2C_Slave_Transmit_IT(
 I2C_HandleTypeDef *hi2c,
 uint8_t *pData,
 uint16_t Size)

// DMA模式发送
HAL_StatusTypeDef HAL_I2C_Slave_Transmit_DMA(
 I2C_HandleTypeDef *hi2c,
 uint8_t *pData,
 uint16_t Size)

3.4 I2C 内存访问 (常用于EEPROM/Sensor)

3.4.1 写内存

// 阻塞模式写内存
HAL_StatusTypeDef HAL_I2C_Mem_Write(
 I2C_HandleTypeDef *hi2c,
 uint16_t DevAddress,
 uint16_t MemAddress,
 uint16_t MemAddSize,
 uint8_t *pData,
 uint16_t Size,
 uint32_t Timeout)

// 中断模式写内存
HAL_StatusTypeDef HAL_I2C_Mem_Write_IT(
 I2C_HandleTypeDef *hi2c,
 uint16_t DevAddress,
 uint16_t MemAddress,
 uint16_t MemAddSize,
 uint8_t *pData,
 uint16_t Size)

// DMA模式写内存
HAL_StatusTypeDef HAL_I2C_Mem_Write_DMA(
 I2C_HandleTypeDef *hi2c,
 uint16_t DevAddress,
 uint16_t MemAddress,
 uint16_t MemAddSize,
 uint8_t *pData,
 uint16_t Size)

3.4.2 读内存

// 阻塞模式读内存
HAL_StatusTypeDef HAL_I2C_Mem_Read(
 I2C_HandleTypeDef *hi2c,
 uint16_t DevAddress,
 uint16_t MemAddress,
 uint16_t MemAddSize,
 uint8_t *pData,
 uint16_t Size,
 uint32_t Timeout)

// 中断模式读内存
HAL_StatusTypeDef HAL_I2C_Mem_Read_IT(
 I2C_HandleTypeDef *hi2c,
 uint16_t DevAddress,
 uint16_t MemAddress,
 uint16_t MemAddSize,
 uint8_t *pData,
 uint16_t Size)

// DMA模式读内存
HAL_StatusTypeDef HAL_I2C_Mem_Read_DMA(
 I2C_HandleTypeDef *hi2c,
 uint16_t DevAddress,
 uint16_t MemAddress,
 uint16_t MemAddSize,
 uint8_t *pData,
 uint16_t Size)

示例 (读写EEPROM):

#define EEPROM_ADDR 0xA0 // 24C02 EEPROM地址
uint16_t mem_addr = 0x0010; // 内存地址
uint8_t write_data[4] = {0x12, 0x34, 0x56, 0x78};
uint8_t read_data[4];

// 写数据到EEPROM
HAL_I2C_Mem_Write(&hi2c1, EEPROM_ADDR, mem_addr, I2C_MEMADD_SIZE_16BIT, write_data, 4, 100);
HAL_Delay(5); // EEPROM写入需要时间

// 从EEPROM读取数据
HAL_I2C_Mem_Read(&hi2c1, EEPROM_ADDR, mem_addr, I2C_MEMADD_SIZE_16BIT, read_data, 4, 100);

3.5 I2C 中断处理

3.5.1 中断服务函数

// I2C事件中断服务函数
void I2C1_EV_IRQHandler(void) {
 HAL_I2C_EV_IRQHandler(&hi2c1);
}

// I2C错误中断服务函数
void I2C1_ER_IRQHandler(void) {
 HAL_I2C_ER_IRQHandler(&hi2c1);
}

3.5.2 回调函数 (用户实现)

// 主发送完成回调
void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c)

// 主接收完成回调
void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c)

// 从发送完成回调
void HAL_I2C_SlaveTxCpltCallback(I2C_HandleTypeDef *hi2c)

// 从接收完成回调
void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c)

// 内存操作完成回调
void HAL_I2C_MemTxCpltCallback(I2C_HandleTypeDef *hi2c)
void HAL_I2C_MemRxCpltCallback(I2C_HandleTypeDef *hi2c)

// 错误回调
void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *hi2c)

示例实现:

// 主发送完成回调
void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c)

// 主接收完成回调
void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c)

// 从发送完成回调
void HAL_I2C_SlaveTxCpltCallback(I2C_HandleTypeDef *hi2c)

// 从接收完成回调
void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c)

// 内存操作完成回调
void HAL_I2C_MemTxCpltCallback(I2C_HandleTypeDef *hi2c)
void HAL_I2C_MemRxCpltCallback(I2C_HandleTypeDef *hi2c)

// 错误回调
void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *hi2c)

3.6 I2C 高级功能

3.6.1 SMBus 特定功能

// 发送SMBus警报
HAL_StatusTypeDef HAL_I2C_Slave_Default_Callback(I2C_HandleTypeDef *hi2c)

// 启用/禁用SMBus超时
HAL_StatusTypeDef HAL_I2C_EnableListen_IT(I2C_HandleTypeDef *hi2c)
HAL_StatusTypeDef HAL_I2C_DisableListen_IT(I2C_HandleTypeDef *hi2c)

3.6.2 多主机仲裁

// 检查总线是否空闲
HAL_StatusTypeDef HAL_I2C_IsDeviceReady(
 I2C_HandleTypeDef *hi2c,
 uint16_t DevAddress,
 uint32_t Trials,
 uint32_t Timeout)

示例 (检查设备存在):

// 检查地址0x68的设备是否存在
if (HAL_I2C_IsDeviceReady(&hi2c1, 0x68 << 1, 3, 100) == HAL_OK) {
 // 设备在线
} else {
 // 设备未响应
}

3.7 I2C 状态管理

3.7.1 状态获取函数

// 获取I2C状态
HAL_I2C_StateTypeDef HAL_I2C_GetState(I2C_HandleTypeDef *hi2c)

// 获取错误代码
uint32_t HAL_I2C_GetError(I2C_HandleTypeDef *hi2c)

3.7.2 错误代码常量

HAL_I2C_ERROR_NONE // 无错误
HAL_I2C_ERROR_BERR // BERR错误
HAL_I2C_ERROR_ARLO // 仲裁丢失
HAL_I2C_ERROR_AF // 应答失败
HAL_I2C_ERROR_OVR // 溢出/欠载错误
HAL_I2C_ERROR_DMA // DMA传输错误
HAL_I2C_ERROR_TIMEOUT // 超时错误
HAL_I2C_ERROR_SIZE // 大小错误

posted @ 2025-08-12 12:34  hazy1k  阅读(187)  评论(0)    收藏  举报