i.MX RT1010的I2C Slave时钟延展功能小记
最近客户在使用i.MX RT1010的I2C作为从机设备与主机通讯,使用了时钟延展的功能(clock stretching)。在开发过程中遇到了一些小烦恼和小细节,在此呢,也写下一篇文档予以总结。
什么是时钟延展
首先,简单介绍一下什么是时钟延展。时钟延展是指从机通过将SCL拉低以暂停数据传输的一个过程,在暂停过程中,从机可以有更多的时间处理接收到的数据,或者准备即将发送的数据。在相关的处理和准备完成之后,将交出SCL的控制权,主机继续控制SCL的节奏进行数据的收发。换句话说,时钟延展是从机跟不上主机的速度,让老司机等等它,啊不,是让主机等等它的操作。
时钟延展通常分为两种,一种是字节级(byte)的时钟延展,一种是比特级(bit)的时钟延展。字节级的时钟延展是按照字节为单位开展,每一个字节收发结束之后启动。比特级则是每个数据bit都进行延展,强行让master慢下来。
在查阅相关资料的过程中发现,并不是所有的I2C从机设备都支持时钟延展,例如I2C的传感器,部分存储设备;也并不是所有的主机设备也支持时钟延展,例如使用IO口模拟实现的I2C或者是FPGA上实现的I2C。因此在使用之前,需要检查器件自身是否支持时钟延展。
四种时钟延展功能
在我们i.MX RT1010上总共支持4种字节级的时钟延展:
<ignore_js_op>
下面我们将对这四种时钟延展的功能进行简要介绍,以下介绍均为从机视角。
延展功能1:收完地址等一等
在从机接收完主机发送的地址信息后,且AVF 置位,则此时从机获得SCL的控制权,开始持续拉低SCL开启时钟延展。那么什么时候结束呢,从硬件状态机的角度看,当AVF bit被清除,时钟延展结束,主机获得SCL的控制权。
举个例子,用我们SDK工程在接收到地址信息后强行延时500us再去清除AVF bit, 看看波形是怎样的:
延展功能2:发送之前等一等
在从机接收到主机的地址信息和读指令后,TDF将会置位,则此时从机获得SCL的控制权。同样是在清除TDF bit之后,主机再次获得SCL的控制权。在这个过程中,从机可以根据主机发送的信息,把要发送的数据准备好,再释放SCL的控制权。同样举个例子,再清除TDF bit 之前等一等,这次等的长一点800us。
在从机接收到数据之后,RDF会置位,此时从机会获得SCL的控制权。同样是在清除TDF bit之后,主机再次获得SCL的控制权。拉个波形看看,在清标志位前拉个1000us的延时,波形如下图所示。
这里是指,当主机把数据(地址信息或者数据信息)发送给从机后,在传送完第八个bit(或者说是第八个时钟)之后,从机即获得了SCL的控制权限,在此时需要手动向STAR寄存器中最后一位写0或者1,以向主机反馈ACK 或者NACK。在写0之前,我们增加一个500us的延时,地址信息的波形见下图,可以看到第八个和第九个CLK时钟的间隔被拉长。
在使用时钟延展的功能后,同样不能忽略的一个要点是要满足I2C的AC timing,即数据的建立时间和保持时间。对于不同的工作速度,I2C对于这两个参数的时间要求也是不同的,下图为I2C的规范上的截图。
CLKHOLD: I2C的数据的建立时间,需要根据不同的通讯速度进行设置
DATAVD: I2C的数据保持时间,通常来看保持为0即可。
造成时序混乱,当SCL的驱动能力较强,且SDA的负载较重的时候,甚至会引起SCL上升的比SDA速度快,那么想一下I2C的停止条件,当SCL处于高电平时,SDA拉高。那么就会让总线停止传输,除此以外,可能还会有其他的未知问题发生。
其次,关于CLKHOLD时间的计算,目前的驱动程序是有些小问题的。因此建议徒手撸寄存器,自己把想要的数值填写到CLKHOLD寄存器内。对于具体的时间,计算公式如下:
t =(CLKHOLD + 3) * Tclk
这里的Tclk是指I2C的functional clock的周期。因此,真正的建立时间,是CLKHOLD的数值和I2C 的输入时钟频率共同作用的结果。
哦对了,如何使能时钟延展功能?
如果你喜欢寄存器操作,那么可以在SCFGR1寄存器中的bit[3:0]直接开启对应的功能:
什么是时钟延展
首先,简单介绍一下什么是时钟延展。时钟延展是指从机通过将SCL拉低以暂停数据传输的一个过程,在暂停过程中,从机可以有更多的时间处理接收到的数据,或者准备即将发送的数据。在相关的处理和准备完成之后,将交出SCL的控制权,主机继续控制SCL的节奏进行数据的收发。换句话说,时钟延展是从机跟不上主机的速度,让老司机等等它,啊不,是让主机等等它的操作。
时钟延展通常分为两种,一种是字节级(byte)的时钟延展,一种是比特级(bit)的时钟延展。字节级的时钟延展是按照字节为单位开展,每一个字节收发结束之后启动。比特级则是每个数据bit都进行延展,强行让master慢下来。
在查阅相关资料的过程中发现,并不是所有的I2C从机设备都支持时钟延展,例如I2C的传感器,部分存储设备;也并不是所有的主机设备也支持时钟延展,例如使用IO口模拟实现的I2C或者是FPGA上实现的I2C。因此在使用之前,需要检查器件自身是否支持时钟延展。
四种时钟延展功能
在我们i.MX RT1010上总共支持4种字节级的时钟延展:
<ignore_js_op>
下面我们将对这四种时钟延展的功能进行简要介绍,以下介绍均为从机视角。
延展功能1:收完地址等一等
在从机接收完主机发送的地址信息后,且AVF 置位,则此时从机获得SCL的控制权,开始持续拉低SCL开启时钟延展。那么什么时候结束呢,从硬件状态机的角度看,当AVF bit被清除,时钟延展结束,主机获得SCL的控制权。
举个例子,用我们SDK工程在接收到地址信息后强行延时500us再去清除AVF bit, 看看波形是怎样的:
<ignore_js_op>
从图中可以看到,两次时钟间隔约491us,基本上与500us的预期相匹配。并不是完美的500us的原因是,延时函数没有使用定时器精确延时。延展功能2:发送之前等一等
在从机接收到主机的地址信息和读指令后,TDF将会置位,则此时从机获得SCL的控制权。同样是在清除TDF bit之后,主机再次获得SCL的控制权。在这个过程中,从机可以根据主机发送的信息,把要发送的数据准备好,再释放SCL的控制权。同样举个例子,再清除TDF bit 之前等一等,这次等的长一点800us。
<ignore_js_op>
延展功能3:接收之前等一等在从机接收到数据之后,RDF会置位,此时从机会获得SCL的控制权。同样是在清除TDF bit之后,主机再次获得SCL的控制权。拉个波形看看,在清标志位前拉个1000us的延时,波形如下图所示。
<ignore_js_op>
延展功能4:手动回ACK这里是指,当主机把数据(地址信息或者数据信息)发送给从机后,在传送完第八个bit(或者说是第八个时钟)之后,从机即获得了SCL的控制权限,在此时需要手动向STAR寄存器中最后一位写0或者1,以向主机反馈ACK 或者NACK。在写0之前,我们增加一个500us的延时,地址信息的波形见下图,可以看到第八个和第九个CLK时钟的间隔被拉长。
<ignore_js_op>
从机接收数据的信息见下图,同样可以看到,第八个和第九个的时钟被拉长了。<ignore_js_op>
时钟延展的时序要求在使用时钟延展的功能后,同样不能忽略的一个要点是要满足I2C的AC timing,即数据的建立时间和保持时间。对于不同的工作速度,I2C对于这两个参数的时间要求也是不同的,下图为I2C的规范上的截图。
<ignore_js_op>
对于i.MX RT1010来说,我们在开启时钟延展功能后需要对下面的两个参数进行设置,以满足I2C的timing要求。CLKHOLD: I2C的数据的建立时间,需要根据不同的通讯速度进行设置
DATAVD: I2C的数据保持时间,通常来看保持为0即可。
<ignore_js_op>
如果不能正确设置CLKHOLD的时间会怎样呢?造成时序混乱,当SCL的驱动能力较强,且SDA的负载较重的时候,甚至会引起SCL上升的比SDA速度快,那么想一下I2C的停止条件,当SCL处于高电平时,SDA拉高。那么就会让总线停止传输,除此以外,可能还会有其他的未知问题发生。
其次,关于CLKHOLD时间的计算,目前的驱动程序是有些小问题的。因此建议徒手撸寄存器,自己把想要的数值填写到CLKHOLD寄存器内。对于具体的时间,计算公式如下:
t =(CLKHOLD + 3) * Tclk
这里的Tclk是指I2C的functional clock的周期。因此,真正的建立时间,是CLKHOLD的数值和I2C 的输入时钟频率共同作用的结果。
哦对了,如何使能时钟延展功能?
如果你喜欢寄存器操作,那么可以在SCFGR1寄存器中的bit[3:0]直接开启对应的功能:
<ignore_js_op>
如果你习惯SDK操作,那么在这个结构体里把对应的功能写true吧:<ignore_js_op>