[总线]I2C通信

I2C总线

SCL:时钟,通常时钟都是由master提供的
SDA:数据

1. 接线

主设备——从设备(两根线都要接上拉电阻)
SDA脚——SDA脚
SCL脚——SCL脚
在这里插入图片描述

2. 协议

  1. I2C设备都有设备地址,可以多对多(多个主设备,多个从设备)
    一对多:这种比较常见(我还没遇到过多对多,手动滑稽),也不会有太多问题
    多对多:需要确保时钟(SCL)同步
  2. SCL时钟速率不要高于从设备

2.1 数据传输步骤如下

  1. 开始条件:SDA为高电平(1),SCL为高电平时(1),SDA由高向低跳变(下降沿)
  2. 发送数据:8个数据位,先传最高位(MSB),然后释放SDA线(1)(拉高电平)
    SCL为低电平时(0),SDA可进行电平转换;
    SCL为高电平时(1),SDA保持不变,接收设备(主设备或从设备)读取数据。
  3. 接收响应:ACK,在第九个周期(可以说是第九个数据位)
    接收设备拉低SDA(0);
    答复发送设备,接收完成;
  4. 结束条件:SDA为低电平(0),SCL为高电平时(1),SDA由低向高跳变(上升沿)
  5. 重复开始条件:master需要在一次通信中进行多次消息交换(例如与不同的slave传输消息,或切换读写操作)
    开始和结束不一一对应,可以发送多次开始条件,然后只有一个结束条件
    a. 主设备发送地址帧(地址+写),发送要读取的寄存器地址(如0xA0)
    b. 主设备发送地址帧(地址+读),接收(0xA0)寄存器数据
    在这里插入图片描述
  6. 时钟拉伸(clock stretching)
    有时候,低速slave可能由于上一个请求还没处理完,尚无法继续接收master的后续请求,即master的数据传输速率超过了slave的处理能力。这种情况下,slave可以进行时钟拉伸来要求master暂停传输数据 —— 通常时钟都是由master提供的,slave只是在SDA上放数据或读数据。
    而时钟拉伸则是slave在master释放SCL后,将SCL主动拉低并保持,此时要求master停止在SCL上产生脉冲以及在SDA上发送数据,直到slave释放SCL(SCL为高电平)。之后,master便可以继续正常的数据传输了。可见时钟拉伸实际上是利用了时钟同步的机制,只是时钟由slave产生。
    如果系统中存在这种低速slave并且slave实现了clock stretching,则master必须实现为能够处理这种情况,实际上大部分slave设备中不包含SCL驱动器的,因此无法拉伸时钟。
    所以更完整的I2C数据传输时序图为:
    在这里插入图片描述

2.2 传输详细内容

  1. 地址帧(address frame):(用于master指明消息发往哪个slave)
    地址帧:7个地址位+1个读写位(0写1读),也支持10bit地址
    地址位:确保芯片Datasheet给的是几位地址位,有的给的7位,有的给的8位(需要左移);直接给地址帧,带读/写
    10bit地址帧:b1111 0XX(W/R)+bXXXX XXXX;
    如下图所示
    在这里插入图片描述
  2. 数据帧(data frames): 由master发往slave的数据(或由slave发往master),每一帧是8-bit的数据
  3. 根据 (1地址帧) 的读写位,确定发送/接收设备
    :和发送地址帧一样,主设备(发送),从设备(接收);
    :反过来,主设备(接收),从设备(发送);
    后续的数据传输中,发送设备发送数据,接收设备需要发送ACK
调试方法
硬件

如果是无法配置pad的芯片,需要接(SCL、SDA)引脚要上拉

软件
配置时钟RCC
配置管脚复用PINMUX
配置管脚分配定义PAD
配置管脚配置
	/* Enable I2C clock */
	RCC_PeriphClockCmd(OLED_I2C_APBPeriph, OLED_I2C_APBPeriph_CLOCK, ENABLE);
	
	/* pinmux */
	Pinmux_Config(OLED_I2C_SCL, OLED_I2C_CLK);
	Pinmux_Config(OLED_I2C_SDA, OLED_I2C_DAT);
	
	/* pad */
	Pad_Config(OLED_I2C_SCL, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_DISABLE, PAD_OUT_HIGH);
	Pad_Config(OLED_I2C_SDA, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_DISABLE, PAD_OUT_HIGH);

	/* I2C IO config*/
	I2C_InitTypeDef  I2C_InitStructure;
	I2C_StructInit(&I2C_InitStructure);
	I2C_InitStructure.I2C_ClockSpeed = OLED_I2C_SPEED;
	I2C_InitStructure.I2C_DeviveMode = I2C_DeviveMode_Master;
	I2C_InitStructure.I2C_AddressMode = I2C_AddressMode_7BIT;
	I2C_InitStructure.I2C_SlaveAddress = OLED_I2C_ADDR;
	I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
	I2C_Init(OLED_I2C_NUM, &I2C_InitStructure);
	I2C_Cmd(OLED_I2C_NUM, ENABLE);
  1. 通过看datesheet确定IC的I2C地址,注意给的是8位还是7位,会影响到寻址
  2. 在软件上写条发送命令,通过逻辑分析仪查看发送数据,
  3. 首先确定配置的没有问题,是走的I2C协议
  4. 然后看发送的地址是多少,是否是我们要的地址;不同的硬件平台,有的会将地址左移(7位地址),有的不会移动(8位地址)
  5. 在确定发送的地址正确的情况下,看是否有应答
  6. 以上都没有问题的话,硬件上基本调通,然后就是发送数据了;在发送数据前确保有地址,如果是一个硬件的话,可以不用(拓展性会不好,后续要增加设备就会不方便了);
  7. 发送多个数据时,不同的平台可能会不一样,我遇到过发两个字节的数据,连发的话,无法初始化硬件(QAQ,可能速度跟不上~),分开一个个发送,就他喵的好了
  8. 综上OK都的话就可以IC进行初始化,读写数据,愉快的玩耍了^^
    (每次换个平台,就调的我吐血,总结一波,这个坑我希望不要出现了,手动狗头)
  • 8
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值