请教关于I2C总线时序的问题
我是个初学者,最近买了马老师的第二版新书,正在学习中。关于I2C的时序,不太明白,请马老师和各位前辈指点迷津,谢谢啦!
这是一段通过软件模拟I2c时序,接收数据的程序。
下面是我的理解:
* 接收第一个数据的之前SDA_Hight,意思是主机释放sda总线,等待从机设置好数据。
* 在for中先delay——scl hight——delay——scl low——delay
等待数据稳定 主机在上升沿接收 scl下降 延迟
原程序:
//读一字节 ack: 1时应答,0时不应答
unsigned char I2C_read(unsigned char ack)
{
unsigned char i,ret;
SDA_Hight;
for(i=0;i<8;i++)
{
I2C_Delay;
SCL_Low;
I2C_Delay;
SCL_Hight;
I2C_Delay;
ret<<=1;
if(SDA_Input)
ret++;
}
SCL_Low;
I2C_Delay;
if(ack) //非应答
SDA_Low;
else //应答
SDA_Hight;
I2C_Delay;
SCL_Hight;
I2C_Delay;
SCL_Low;
I2C_Delay;
return(ret);
}
我的问题:
* 重新开始执行for循环的时候(即for的首尾相接时),会出现前后两个delay连在一起的情况,导致scl处于低电平的时间更长,请问这个对传输有无影响?
* 可否把程序改成下面的样子,即SDA_Hight-delay,然后for循环( scl hight——delay——scl low——delay)这样就不会出现重复delay的情况。
不知道这样是否正确?
修改成以下样子,是否正确?
修改的程序
原程序:
//读一字节 ack: 1时应答,0时不应答
unsigned char I2C_read(unsigned char ack)
{
unsigned char i,ret;
SDA_Hight;
I2C_Delay; //这一句原来在for循环的开始,挪到此处
for(i=0;i<8;i++)
{
SCL_Low;
I2C_Delay;
SCL_Hight;
I2C_Delay;
ret<<=1;
if(SDA_Input)
ret++;
}
SCL_Low;
I2C_Delay;
if(ack) //非应答
SDA_Low;
else //应答
SDA_Hight;
I2C_Delay;
SCL_Hight;
I2C_Delay;
SCL_Low;
I2C_Delay;
return(ret);
}
http://cache.amobbs.com/bbs_upload782111/files_46/ourdev_679431KFUV3S.JPG
I2C时序 (原文件名:IMG_3908.JPG)
http://cache.amobbs.com/bbs_upload782111/files_46/ourdev_679432DYIURT.JPG
有疑问的程序(马老师的书上第2版484页) (原文件名:IMG_3909.JPG) * 重新开始执行for循环的时候(即for的首尾相接时),会出现前后两个delay连在一起的情况,导致scl处于低电平的时间更长,请问这个对传输有无影响?
==》对传输无影响,稍微影响系统的运行效率
* 可否把程序改成下面的样子,即SDA_Hight-delay,然后for循环( scl hight——delay——scl low——delay)这样就不会出现重复delay的情况。
不知道这样是否正确?
可以,更加好些。 谢谢马老师的解答,这么晚还专门回帖子!
看了之前马老师对类似问题的解答,现在对spi的时序稍稍明白了一些。正在用实验板验证中... some quick comments:
"//读一字节 ack: 1时应答,0时不应答
unsigned char I2C_read(unsigned char ack) "
define two constants:
#define I2C_ACK 0 //send ack
#define I2C_NOACK 1 //send noack
so when you call I2C_read(), use I2C_read(I2C_ACK); or I2C_read(I2C_NOACK); to make the code easier to read and more portable.
"SDA_Hight; "
use SDA_High, not SDA_Hight - that "t" makes zero sense and sounds silly.
"I2C_Delay; //这一句原来在for循环的开始,挪到此处 "
I would implement that delay into SDA_HIGH or SDA_LOW, just to make it more readable.
"for(i=0;i<8;i++) "
I would use mask
" if(SDA_Input)
ret++; "
this will yield uneven timing: slower when SDA_Input = 1 and faster when SDA_Input = 0. try this
if(SDA_Input) ret+=1;
else ret+=0;
"if(ack) //非应答
SDA_Low;
else //应答
SDA_Hight; "
use
if(I2C_ACK==ack) //send ack
SDA_Low;
else
SDA_High;
for stylistic purposes, I would use I2C_LOW(I2C_SCL / I2C_SDA) or I2C_HIGH(I2C_SCL / I2C_SDA) instead. they are far more consistent than what you have here.
this is what I do:
//-----------------------i2c read------------------------------
//to be consistent with i2c protocol, use negative logic
//ack = 0 -> send ack
//ack = 1 -> no ack
unsigned char i2c_read(unsigned char ack) {
unsigned char i, data_t=0;
I2C_HIZ(I2C_SDA); //let sda float
i=0x80;
do {
I2C_LOW(I2C_SCL); //clear i2c_scl
data_t <<=1; //left shift the data
i = i >> 1;
I2C_HIGH(I2C_SCL); //let scl float to high
if (I2C_PORT_IN & I2C_SDA) data_t |= 0x01; //set the last bit high
else data_t |= 0x00;
} while (i);
I2C_LOW(I2C_SCL); //pull scl low
if (ack==I2C_ACK)
{I2C_LOW(I2C_SDA);} //send ack
else
{I2C_HIGH(I2C_SDA);} //send no-ack
I2C_HIGH(I2C_SCL); //send ack/no-ack
I2C_LOW(I2C_SCL);
return data_t;
} 回复【3楼】millwood0
-----------------------------------------------------------------------
非常感谢millwood0的回复和指导!
关于程序的可读性、程序结构的清晰(无歧义)您的建议非常好,受教了!
if (ack==I2C_ACK) 这个非常好
而且if else 总是加{ },好!这个是GNU风格么?
另外您好像比较喜欢用do...while
是这样好还是个人喜好?
页:
[1]