I2C的各种信号

I2C的各种信号

第一: Start或者Repeat Start信号 (Repeat Start是没有Stop之前继续Start)

1. 先拉高SDA

2. 后拉高SCL

tick

3. 拉低SDA

tick

4. 拉低SCL

tick

void IIC_Tick(void) {

// 每两个tick是一个SCL周期,因此:

// 100K Hz 则延时5us

// 400K Hz 则延时1.25us

// 1M Hz则延时 0.5us

// PS: 其实函数调用和拉高拉低操作也需要占用时间,所以函数体内延时时间应该更小

}

void IIC_Start(void) {

SDA_H;

SCL_H;

IIC_Tick();

SDA_L;

IIC_Tick();

SCL_L;

IIC_Tick(); // PS: 总共开销1.5个SCL周期

}

第二: WriteBit操作, 写数据时, 8个bit的每个bit

1. 根据MSB的位值0或者1拉低或者拉高SDA (可以断言进行这个操作时SCL还是低的, 请观察start的最后状态或者本操作的最后状态)

2. 拉高SCL

tick

3. 拉低SCL

tick

void IIC_Write(uint8_t data) {

for (uint8_t mask = 0x80; mask; mask >>= 1) {

if (data & mask) {

SDA_H;

} else {

SDA_L;

}

SCL_H;

IIC_Tick();

SCL_L;

IIC_Tick(); // PS, 每个BIT一个SCL周期

}

}

第三: WaitACK信号, 每写8个bit后, 需要读入一个对方的ACK, 如果读到的SDA为低电平, 代表对方ACK, 否则是NACK, 就需要发送Stop信号

1. 拉高SDA (这时对方可能已经ACK, 这样SDA还是出于低电平状态)

2. 设置SDA为输入模式

3. 拉高SCL

tick

4. 读入SDA

5. 拉低SCL

tick

6. 设置SDA为输出模式(因为第一步拉高SDA, 这时这里设置为输出模式后, SDA可以断言为高)

uint8_t IIC_WaitAck(void) {

SDA_H;

SDA_IN_MODE;

SCL_H;

IIC_tick();

uint8_t ack = GET_SDA;

SCL_L;

IIC_tick();

SDA_OUT_MODE;

return ack; // PS: 本操作占用1个SCL周期

}

第四: Stop信号, 一次操作完成的最后, 通过STOP使得IIC回到空闲状态

1. 先拉低SDA (因为SDA在WaitACK最后状态是高)

2. 拉高SCL (无论Start, WriteBit, WaitAck等SCL的最后状态都是低)

tick

3. 拉高SDA

tick

void IIC_Stop(void) {

SDA_L;

SCL_H;

IIC_Tick();

SDA_H;

IIC_Tick(); // PS: 消耗一个SCL周期

}

// 总结: Start消耗1.5个SCL周期

// n个data消耗 n * (8个bit + 1个ack) = 9n个SCL周期

// STOP消耗1个SCL周期

// 因此发送n个data 总消耗是 9n + 2.5, 按100K频率发送, 则1个周期是10us

// 总消耗时间是90n + 25, 也就是说发送100字节, 需要9025us, 需要9.025毫秒

// 总体上100K频率发送, 则1秒钟大约可以发10k字节

// 串口115200波特率, 1个起始位,8个数据位, 1个停止位, 115200/10= 11520, 也就是1秒钟最多可以发送11520个字节, 大约也就是11k字节

总结:

1. 开始信号是 SCL_H的时候, SDA_H -> SDA_L

2. 结束信号是 SCL_H的时候, SDA_L -> SDA_H

3. 其他操作都是在SCL_L的时候, 设置SDA状态,然后SCL_H, tick, SCL_L, tick (SCL最后回到低状态)

例如WriteBit操作是先操作SDA, 后SCL_H, tick, SCL_L, tick完成一个SCL周期

第五: ReadBit操作

// PS: 在Read之前记得SDA_IN_MODE; 最后NACK时记得SDA_IOUT_MODE;

uint8_t IIC_Read(void) {

uint8_t ch = 0;

for (uint8_t i = 0; i < 8; i++) {

SCL_H;

IIC_Tick();

ch <<= 1;

ch |= (GET_SDA ? 1 : 0);

SCL_L;

IIC_Tick();

}

return ch; // 消耗1个周期的SCL

}

第六, ACK, NACK信号

void IIC_Ack(uint8_t flag) {

SDA_OUT_MODE;

if (flag) {

SDA_H;

} else {

SDA_L;

}

SCL_H;

IIC_Tick();

SCL_L;

IIC_Tick();

SDA_IN_MODE: // PS: 消耗一个SCL周期

}

PS. 第一个字节: 设备地址(7位) + 读写(1位), 其中0是写, 1是读

通常读是,

Start,

WriteI2cAddr, WaitACK,

WriteRegAddr, WaitAck,

Start,

WriteI2cAddr, WaitAck

Read, Ack

Read, Ack

...

Read, NAck

Stop

🎀 相关推荐

日子过得怎么样是什么歌 这首歌的完整歌词
365名品汇推荐码多少

日子过得怎么样是什么歌 这首歌的完整歌词

📅 07-25 👀 687
女主角遭遇侮辱的五部剧,第一个太惨,最后一个观众心痛
弹弹堂离婚多少钱 弹弹堂离婚多少点券
365名品汇推荐码多少

弹弹堂离婚多少钱 弹弹堂离婚多少点券

📅 06-30 👀 4591