上一节里讲到了I2C的基础部分以及字节写操作,本节继续I2C操作,进行当前地址读操作。
I2C当前地址读,所谓当前地址,指从芯片有一个地址指针,当前所指向的地址。可以把I2C通讯地址看成一个一维数组,或者一个队列,地址编号从小到大依次排列,芯片内有个地址指针指向某个地址。指针可以滑动,滑动一次,地址加一。连续读/连续写就是利用指针不停向后滑动进行的。
那么当前地址是哪个地址呢?
如果上次是我们自己使用的话,在芯片不断电的情况下会保持上次的地址,如果芯片断电了,当前地址会回到0地址(大部分芯片会)。如果上次通讯不是我们自己使用的,那就不知道当前地址是什么了。那这样使用当前地址读还有什么用?它的好处是,在我们知道当前地址的情况下,可以多次读取而不需要重新定位地址。比随机地址读节省时间和资源。
当前地址读的操作步骤如下:
0、空闲状态 SCL SDA 都是高电平
1、主机发送开始信号
2、主机发送设备地址(从机地址),同时发送读命令(8bit长度 从机地址 7 bit位,读写命令1bit位,0-写命令,1-读命令)。
3、从机应答(ack)
4、从机发送寄存器的数据8bit
5、主机发送应答(非应答 NACK)
6、主机发送结束命令
步骤上并不复杂,具体操作和写操作一样,不同的是第五步和第六步。
第五步:主机发送“非”应答。“非”应答表示主机想要结束本次通讯,不再需要后续数据(发“是”应答,则从机会继续发送后续寄存器数据,也就是“连续读”)。“非”应答的具体操作是:主机在SCL 为低电平时,发送SDA高电平,并持续一个周期。
第六步:发送结束命令。一般发送结束命令只要“SCL为高时,让SDA从低到高跳变”即可。但是由于第5步,SDA已经是高电平了,所以需要在第5步之后的SCL低电平先拉低SDA,再在SCL高电平时拉高SDA信号。
这部分的代码如下:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
S_NACK : if ( !scl ) //主机发送,在scl为低时候,发送sda为1 begin sda_sel <= 1; sda_buf <= 1; cstate <= S_STOP; end S_STOP : if ( num == 0 ) //SCL高电平时,SDA从低到高的跳变 num <= 1; else if ( num == 1 ) begin sda_buf <= 0; num <= 2; end else if ( num == 2 ) begin sda_buf <= 1; cstate <= S_IDLE; end |
代码文件:代码使用PRA006开发板。每0.5秒读取一次。
[content_control]
i2c_r_pcf8591_quartus_pra006.zip
[/content_control]
运行效果:
