搜索
bottom↓
回复: 4

请教关于I2C总线时序的问题

[复制链接]

出0入0汤圆

发表于 2011-9-25 08:43:26 | 显示全部楼层 |阅读模式
我是个初学者,最近买了马老师的第二版新书,正在学习中。
关于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);  
}




I2C时序 (原文件名:IMG_3908.JPG)


有疑问的程序(马老师的书上第2版484页) (原文件名:IMG_3909.JPG)

阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!

出0入0汤圆

发表于 2011-9-28 23:39:18 | 显示全部楼层
* 重新开始执行for循环的时候(即for的首尾相接时),会出现前后两个delay连在一起的情况,导致scl处于低电平的时间更长,请问这个对传输有无影响?

==》对传输无影响,稍微影响系统的运行效率

* 可否把程序改成下面的样子,即SDA_Hight-delay,然后for循环( scl hight——delay——scl low——delay)这样就不会出现重复delay的情况。
  不知道这样是否正确?

可以,更加好些。

出0入0汤圆

 楼主| 发表于 2011-9-29 08:13:34 | 显示全部楼层
谢谢马老师的解答,这么晚还专门回帖子!
看了之前马老师对类似问题的解答,现在对spi的时序稍稍明白了一些。正在用实验板验证中...

出0入0汤圆

发表于 2011-10-1 21:26:18 | 显示全部楼层
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;
}

出0入0汤圆

 楼主| 发表于 2011-10-6 14:41:20 | 显示全部楼层
回复【3楼】millwood0
-----------------------------------------------------------------------

非常感谢millwood0的回复和指导!

关于程序的可读性、程序结构的清晰(无歧义)您的建议非常好,受教了!

if (ack==I2C_ACK) 这个非常好
而且if else 总是加{ },好!这个是GNU风格么?

另外您好像比较喜欢用do...while
是这样好还是个人喜好?
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-24 01:10

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表