【STM32系列教程】0x09 I2C

通过I2C外设读写AT24Cxx片外EEPROM

介绍

教程:

https://www.bilibili.com/video/BV1th411z7sn?p=31

  • 两根通信线:SCL(时钟)SDA(数据)

  • 一主多从,一条总线挂载多个设备,SCL连在一起,SDA连在一起

  • 支持指定设备指定寄存器的读写

  • 支持以下操作:指定地址写

  • 只有主设备呼叫后,从设备才可以向SDA发送数据

通信电平

  • SCL和SDA配置为开漏模式,由外置上拉电阻来上拉
  • 只要有一个设备发送低电平,总线就为低电平
  • 空闲:SCL和SDA都为高电平
  • 开始一轮通信:在SCL高电平的前提下拉低SDA
  • 发送数据:发送方在SCL低电平期间调整SDA电平,接收方在SCL高电平期间读取SDA电平,一次发送一个字节
  • 应答:每收发完一个字节,后面紧跟一位应答位SDA为低电平表示应答有效
  • 应答完毕紧接着可以发送第二个字节
  • 结束一轮通信:在SCL高电平的前提下拉高SDA

通信协议

对于大部分的I2C设备来说:

  • 每个从设备有7bit的地址;在一轮通信中,主设备通过地址选择一个从设备进行通信
  • 每个从设备在一系列地址上有寄存器,每个寄存器负责不同的功能,具体参考技术手册
  • 支持对从指定从设备指定寄存器读/写操作
  • 从设备有个寄存器作为指针指向“当前处理的寄存器的地址”,每次读/写了一个byte就+1
  • 在发送数据部分如果读/写完一个byte,没有收到应答,则会继续发送。由于“当前处理的寄存器”指针自增,实际发送的是下一个字节。即:发送数据部分读/写完一个byte,发送方没有收到应答,则会发送下一个byte

指定寄存器写:

  • 第1个byte:主设备发送;前7位表示从设备地址,第8位为0表示主设备写从设备;从设备拉低SDA进行ACK
  • 第2个byte:主设备发送寄存器地址;从设备拉低SDA进行ACK
  • 第3+byte:主设备发送数据,写入从设备寄存器;从设备拉低SDA进行ACK

指定寄存器读:

  • 第1个byte:主设备发送;前7位表示从设备地址,第8位为0表示主设备写从设备;从设备拉低SDA进行ACK
  • 第2个byte:主设备发送寄存器地址;从设备拉低SDA进行ACK
  • 第3个byte:主设备发送;前7位表示从设备地址;第8位为1表示主设备读从设备;从设备拉低SDA进行ACK
  • 第4+byte:主设备输出时钟,从设备发送数据,主设备读取;主设备进行NACK表示将会继续读取;主设备拉低SDA进行ACK表示读取结束

设备地址的两种书写方式:

  • 7bit长度:不包括读写位,使用时实际传参ADDR<<1 | RW
  • 8bit长度:分为读地址和写地址,使用时实际传参ADDR
  • 比如128x64的OLED屏幕,只有写操作,其默认写地址为:0x78/0b0111000(8bit) = 0x3c/0b011100(7bit)

I2C外设

image-20240702155854077
  • 一个数据寄存器DR
  • 发送数据时:将数据从DR转移到移位寄存器中,然后高位先行移出至SDA
  • 接收数据时:移位寄存器从SDA读入最低位,读入8位后将数据转移到DR

HAL库

函数命名是这样的:

  • 是否阻塞
    • Blocking
    • Interrupt
    • DMA
  • 收发方式
    • 不带Mem的:通常为直接与另一个MCU通信时使用
      • Master/Slave
      • Seq模式:连续读写一系列数组(大量数据)时使用
    • 带Mem的:对芯片的指定寄存器地址读写
      (区分于寄存器地址与设备地址)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// Blocking mode functions 
HAL_I2C_Master_Transmit()
HAL_I2C_Master_Receive()
HAL_I2C_Slave_Transmit()
HAL_I2C_Slave_Receive()
    
HAL_I2C_Mem_Write()
HAL_I2C_Mem_Read()
    
HAL_I2C_IsDeviceReady()
    
// No-Blocking mode functions with Interrupt
HAL_I2C_Master_Transmit_IT()
HAL_I2C_Master_Receive_IT()
HAL_I2C_Slave_Transmit_IT()
HAL_I2C_Slave_Receive_IT()
    
HAL_I2C_Mem_Write_IT()
HAL_I2C_Mem_Read_IT()
    
HAL_I2C_Master_Seq_Transmit_IT()
HAL_I2C_Master_Seq_Receive_IT()
HAL_I2C_Slave_Seq_Transmit_IT()
HAL_I2C_Slave_Seq_Receive_IT()
    
HAL_I2C_EnableListen_IT()
HAL_I2C_DisableListen_IT()
HAL_I2C_Master_Abort_IT()
    
// No-Blocking mode functions with DMA
HAL_I2C_Master_Transmit_DMA()
HAL_I2C_Master_Receive_DMA()
HAL_I2C_Slave_Transmit_DMA()
HAL_I2C_Slave_Receive_DMA()
    
HAL_I2C_Mem_Write_DMA()
HAL_I2C_Mem_Read_DMA()
    
HAL_I2C_Master_Seq_Transmit_DMA()
HAL_I2C_Master_Seq_Receive_DMA()
HAL_I2C_Slave_Seq_Transmit_DMA()
HAL_I2C_Slave_Seq_Receive_DMA()
    
// Callbacks in BOTH interrupt mode and dma mode
HAL_I2C_MasterTxCpltCallback()
HAL_I2C_MasterRxCpltCallback()
HAL_I2C_SlaveTxCpltCallback()
HAL_I2C_SlaveRxCpltCallback()

HAL_I2C_MemTxCpltCallback()
HAL_I2C_MemRxCpltCallback()

HAL_I2C_ListenCpltCallback()
HAL_I2C_ErrorCallback()
HAL_I2C_AbortCpltCallback()

实践:24C02的读写

24C02芯片

datasheet:

请阅读其中有关 Write 和 Read 有关的内容!

  • I2C协议的EEPROM,可以断电保存数据,具有2kB的空间,以8或者16字节为一页
  • 使用上述读写寄存器的方法直接读写数据
  • 读取
    • 支持读取单个字节,或者连续读取多个字节
    • 读取多个字节时可以跨页读取
  • 写入
    • 支持写入单个字节,或者连续写入多个字节
    • 连续写入多个字节时不能跨页。如果数据长度超过页边界,将会从页起始开始覆盖写入
    • 从I2C写入的内容将会保存到缓存中,在连续写入结束时写回非易失性存储中
    • 在写回的过程中不会响应任何I2C指令,需要等待写回完成后、判断其有响应后,再进行其它操作