简介
记录树莓派上baremetal调试I2C的一些点
协议
总览
-
I2C一个master可以接多个slave
-
I2C总线使用两条线在master跟slave之间传输数据,SCL串行时钟线,SDA串行数据线
-
SCL跟SDA需要接上拉电阻,一般是4.7K,总线空闲时SCL和SDA处于高电平

-
标准模式
100Kbits/s,快速模式400Kbit/s

起始与结束
- 当 SCL 线是高电平时 SDA 线从高电平向低电平切换,这个情况表示通讯的起始。
- 当 SCL 是高电平时 SDA 线由低电平向高电平切换,表示通讯的停止。

示波器抓到的开始信号跟停止信号如下


数据传输
I2C 总线在数据传输的时候要保证在 SCL 高电平期间,SDA 上的数据稳定,因此 SDA 上 的数据变化只能在 SCL 低电平期间发生,所以数据的采集需要看I2C高电平的时候SDA线的状态

ACK跟NACK
- SDA线上,ACK信号(应答)是低电平,NACK信号(非应答)是高电平。这部分比较好理解,因为空闲的时候总线上的上拉电阻会让SDA线为高。
- 对于SDA线,master跟slave都可以控制其为输入或者输出状态
- ACK/NACK的发生在发送完SDA线上发送8bit的data或者是7bit addr + 1bit rw之后,也即是每次传输的时候的第9个bit
- ACK产生的情况:数据接收端(master/slave)如果希望对方继续发送数据,则发送ACK信号
- NACK产生的情况:数据接收端希望结束数据传输,则对对方发送NACK信号,发送端收到该信号后会生成stop信号,结束此次传输
- 当然如果是一个不存在的地址,自然SDA线再第九个时钟的时候自然是拉高的
读写过程
基本一帧的传输格式如下,每传输一个字节的内容,都要等1bit的应答信号
Read的时候为高电平,Write的时候为低电平

当然连续读,连续写的时候,不一定每次都要Start跟Stop信号都要来一遍

比如下面的波形图,先是发送器件地址,然后读了两个字节,返回结果分别是0x2跟0xa9


Start信号也可以重复,一般比如说读跟写交错这种情况(个人认为)

如图,这是参考的网上的图

尖峰毛刺
这个貌似比较容易出现,是一种控制真空的状态

本次波形情况是主设备向从设备写数据,在寻址结束确认应答后,从设备会拉低总线,在SCL切换到低电平时,立即释放SDA,主设备还没有反应过来,造成了“控制真空”,这时SDA被上拉电阻拉高,而如果下一步主设备将传输低电平数据位,就会造成这个尖峰毛刺,原理如下图所示。

具体波形
在baremetal代码中,实现了i2c_detect,其原理是向总线上所有地址都读一个字节,如下是从器件被扫描到的时候抓的波形

SDA线上的变化如下

其中读方向以及ACK放大,如下所示

最后的NACK以及Stop信号放大如下所示

而如下所示为扫描一个不存在的从机的波形

没有0x63这个从机,第9个bit的时候SDA就是高电平,也即NACK

编码
controller
使用I2C1
gpio
使用GPIO2跟GPIO3


寄存器
控制器寄存器比较简单

其中关于Status寄存器的描述,手册还是挺有意思的

总之就是RXF,TXE,RXR,TXW这四个跟中断的一些条件配合使用


RXD,TXD则是比较简单,查询模式用

控制器的代码主要参考这个链接
问题记录
在跑代码的时候,对一个局部数组进行初始化,触发了异常

从汇编一条条的跟,出问题的指令是xzr ,[sp, #34]

而ARM64必须要保证栈指针是8字节对齐的,这里是把SP+34的地址清零,所以这里触发了一个异常,不过现在也没有解决这个问题。反而单独对数组中的元素进行操作确实正常的,不理解。

参考链接
代办
-
10bit寻址 SMBUS- 中断模式,
DMA模式 - 总线挂死恢复
- 总线仲裁参考
gpio模拟