Skip to content

I2C

一.I2C总线(主站模式)

I2C方式读取aht20传感器数据的示例代码

1.I2C总线简介

I2C总线(Inter-Integrated Circuit)是一种串行通信总线,由飞利浦半导体公司(现为NXP)在1982年开发。I2C总线是一种两线制总线,由一条双向的数据线(SDA)和一条时钟线(SCL)组成。I2C总线支持多主多从结构,允许多个设备通过总线进行通信。

2.I2C总线的工作原理

I2C总线的工作原理基于时钟和数据信号的同步传输。主设备通过SCL线发送时钟信号,从设备通过SCL线接收时钟信号,并根据时钟信号的上升沿或下降沿同步传输数据。数据传输时,主设备通过SDA线发送数据,从设备通过SDA线接收数据。数据传输采用8位数据格式,每个字节后跟随一个应答位。

3.I2C总线的通信协议

(1)I2C总线_发送数据

  1. 主机发送起始信号
  2. 发送从机地址 + 写方向(最低位,第八位为0)
  3. 等待从机应答ACK
  4. 发送寄存器地址和数据
  5. 每发送1字节都需从机应答
  6. 最后发送停止信号

(2)I2C总线_接收数据

  1. 主机发送起始信号
  2. 发送从机地址 + 写方向(0)
  3. 等待应答,发送寄存器地址
  4. 发送重新开始信号(重复START): 主机要改变通信模式(主机将由发送变为接收,从机将由接收变为发送),所以主机重新发送一个开始start信号,然后紧跟着发送一个从机地址,注意此时该地址的第8位为1,表明将主机设置成接收模式,开始读取数据
  5. 发送从机地址 + 读方向(1)
  6. 接收数据,每接收一个字节主机需发送 ACK
  7. 最后一个字节发 NACK + 停止信号

(3)I2C总线_通信协议_时序图

请参考: I2C总线_时序图

二.关于STC8G单片机中的I2C主机总线

1.单片机的i2c的引脚确定

对于 stc8g1k08a单片机,其i2c引脚可以是 p3.2 和 p3.3, 也可以是 p5.4 和 p5.5

引脚

2.单片机i2c的寄存器问题

由于stc8g1k08a单片机中i2c的寄存器的地址是在 0FA00H~0FFFFH, 这个地址属于扩展 SFR范围. 要访问这里面的寄存器,需要将P_SW2的BIT7设置为1, 也就是将P_SW2的BIT7设置为1, 这样就可以访问到扩展 SFR范围了.

寄存器的地址

扩展 SFR范围

3.单片机i2c的寄存器操作流程

重点关注一下: 标题为<<20.2.2 I2C 主机控制寄存器(I2CMSCR)>> 也就是 手册 上的第 700页.

三.I2C的代码实现

i2c.c源文件

c
#include "stc8g.h"

/* 指定对应的引脚. 这个引脚是固定的
  SCL 引脚  =  P3.2
  SDA 引脚  =  P3.3
  这个引脚可以更换为 
  SCL 引脚  =  P5.4
  SDA 引脚  =  P5.5
  需要 P_SW2 |= 1<<4;
  对应的引脚也要设置为开漏输出
*/
// I2C初始化函数
void I2C_Init(void) {

    P_SW2 |= 0x80;  //设置允许读取EXTERN SFR范围的SFR

    //默认输出引脚,配置P3.2(SCL)和P3.3(SDA)为开漏输出
    P3M1 |= 0x0C;   // P3.2和P3.3设置为开漏输出
    P3M0 |= 0x0C;  

    // 配置使用P5.4和P5.5作为SCL和SDA输出引脚
    // P_SW2 |= 1<<4;
    // P5M1 |= 0x30;   // P3.2和P3.3设置为开漏输出
    // P5M0 |= 0x30;  
    
    I2CCFG = 0xE0;  // 设置I2C时钟频率(约400kHz)
    I2CMSST = 0x00; // 清除状态寄存器
}

// I2C启动信号
void I2C_Start(void) {
    I2CMSCR = 0x01; // 发送START信号
    while(!(I2CMSST & 0x80)); // 等待 MSBUSY 为1
    I2CMSST &= ~0x40; // 清除中断标志
}

// I2C停止信号
void I2C_Stop(void) {
    I2CMSCR = 0x06; // 发送STOP信号
}

// I2C发送一个字节
bit I2C_WriteByte(unsigned char dat) {
    I2CTXD = dat;
    I2CMSCR = 0x0A; // 发送数据并接收ack信号
    while(!(I2CMSST & 0x40)); // 等待发送完成
    I2CMSST &= ~0x40;
    return 1; 
}

// I2C读取一个字节
unsigned char I2C_ReadByte(bit ack) {
    I2CMSCR = ack ? 0x0B : 0x0C; // 设置ACK/NACK
    while(!(I2CMSST & 0x40)); // 等待接收完成
    I2CMSST &= ~0x40;
    return I2CRXD;
}