Chinaunix首页 | 论坛 | 博客
  • 博客访问: 474682
  • 博文数量: 223
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 2145
  • 用 户 组: 普通用户
  • 注册时间: 2014-03-01 10:23
个人简介

该坚持的时候坚持,该妥协的时候妥协,该放弃的时候放弃

文章分类

全部博文(223)

文章存档

2017年(56)

2016年(118)

2015年(3)

2014年(46)

我的朋友

分类: 嵌入式

2016-12-06 22:57:16

一、I2C总线介绍
1.1 I2C电气特性
I2C(Inter-Integrated Circuit)总线是由PHILIPS公司开发的两线式串行总线,用于连接微控制器及其外围设备。

I2C总线只有两根双向信号线。
SDA: Serial Data Line-数据线
SCL :Serial Clock-时钟线

1.2 总线寻址
I2C总线协议规定:从设备采用7位的地址。D7~D1:从设备地址。
D0位:数据传送方向位,为“0”时表示主设备向从
设备写数据,为“1”时表示主机由从设备读数据。
主设备发
送地址时,总线上的每个从设备都将这7位地址码与自己的地址进行比较,如果相同,则认为自己正被主设备寻址,根据R/W位将自己确定为发送器或接收器。

从设备的地址由固定部分和用户自定义部分组成。
1. 固定部分:D7-D4 共4位决定的。这是由从设备的生产厂商生产时就已确定的值。
2. 用户自定义部分: D3-D1 3位。这3位通常对应设备的3个引脚(A0~A2)。把3个引脚接到不同的电平上,就可以形成一个3位的数值。

硬件设置A0,A1,A2拉低,I2C就可以按照000来寻找了。

二、I2C总线时序
2.1 空闲状态
I2C总线总线的SDA和SCL两条信号线同时处于高电平时,规定为总线的空闲状态。

2.2 起始状态
在时钟线SCL保持高电平期间,数据线SDA上的电平被拉低(即负跳变),定义为I2C总线总线的启动信号,它标志着一次数据传输的开始。

2.3 结束状态
在时钟线SCL保持高电平时,数据线SDA被释放,使得SDA返回高电平(即正跳变),称为I2C总线的停止信号。

2.4 数据位传送
I2C总线上的所有数据(地址和数据)都是以8位一个字节为单位传送的。

2.5 应答位
发送器每发送一个字节,就在时钟脉冲第9位释放数据线,由接收器反馈一个应答信号。 应答信号为低电平时,定为有效应答位ACK,表示接收器已经成功地接收了该字节;应答信号为高电平时,定为非应答位(NACK),表示接收器没有成功接收该字节。

三、LINUX-I2C子系统
3.1 I2C子系统架构
①I2C设备驱动:用户自写驱动、通用驱动+用户层驱动
②I2C总线驱动:I2C-ADAPTER;adapter.algo
③I2C核心:①和②联系作用,包含注册、注销的方法


3.2 I2C总线驱动

描述一个I2C控制器
  1. struct i2c_adapter {
  2.     struct module *owner;
  3.     unsigned int id;
  4.     unsigned int class;         /* classes to allow probing for */
  5.     const struct i2c_algorithm *algo; /* the algorithm to access the bus */
  6.     void *algo_data;

  7.     /* --- administration stuff. */
  8.     int (*client_register)(struct i2c_client *) __deprecated;
  9.     int (*client_unregister)(struct i2c_client *) __deprecated;

  10.     /* data fields that are valid for all devices    */
  11.     u8 level;             /* nesting level for lockdep */
  12.     struct mutex bus_lock;
  13.     struct mutex clist_lock;

  14.     int timeout;            /* in jiffies */
  15.     int retries;
  16.     struct device dev;        /* the adapter device */

  17.     int nr;
  18.     struct list_head clients;    /* DEPRECATED */
  19.     char name[48];
  20.     struct completion dev_released;
  21. };
i2c_algorithm:
  1. struct i2c_algorithm {

  2.     int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,                                     //描述数据传输的一些方法
  3.              int num);
  4.     int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
  5.              unsigned short flags, char read_write,
  6.              u8 command, int size, union i2c_smbus_data *data);

  7.     u32 (*functionality) (struct i2c_adapter *);
  8. };
i2c-s3c2410.c>>i2c_adap_s3c_init()>>s3c2410_i2c_driver>>s3c24xx_i2c_probe:
  1. static int s3c24xx_i2c_probe(struct platform_device *pdev)
  2. {
  3.     struct s3c24xx_i2c *i2c;
  4.     struct s3c2410_platform_i2c *pdata;
  5.     struct resource *res;
  6.     int ret;

  7.     ............................

  8.     /* initialise the i2c controller */

  9.     ret = s3c24xx_i2c_init(i2c);                                          //初始化i2c_init
  10.     if (ret != 0)
  11.         goto err_iomap;

  12.     /* find the IRQ for this unit (note, this relies on the init call to
  13.      * ensure no current IRQs pending
  14.      */

  15.     i2c->irq = ret = platform_get_irq(pdev, 0);
  16.     if (ret <= 0) {
  17.         dev_err(&pdev->dev, "cannot find IRQ\n");
  18.         goto err_iomap;
  19.     }

  20.     ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,
  21.              dev_name(&pdev->dev), i2c);

  22.     if (ret != 0) {
  23.         dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq);
  24.         goto err_iomap;
  25.     }

  26.     ret = s3c24xx_i2c_register_cpufreq(i2c);
  27.     if (ret < 0) {
  28.         dev_err(&pdev->dev, "failed to register cpufreq notifier\n");
  29.         goto err_irq;
  30.     }

  31.     /* Note, previous versions of the driver used i2c_add_adapter()
  32.      * to add the bus at any number. We now pass the bus number via
  33.      * the platform data, so if unset it will now default to always
  34.      * being bus 0.
  35.      */

  36.     i2c->adap.nr = pdata->bus_num;

  37.     ret = i2c_add_numbered_adapter(&i2c->adap);                              //注册i2c控制器(CPU内部),类似函数还有i2c_add_adpater(CPU外接控制器)。由I2C核心提供
  38.     if (ret < 0) {
  39.         dev_err(&pdev->dev, "failed to add bus to i2c core\n");
  40.         goto err_cpufreq;
  41.     }

  42.     platform_set_drvdata(pdev, i2c);

  43.     dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev));
  44.     return 0;

  45.  err_cpufreq:
  46.     s3c24xx_i2c_deregister_cpufreq(i2c);

  47.  err_irq:
  48.     free_irq(i2c->irq, i2c);

  49.  err_iomap:
  50.     iounmap(i2c->regs);

  51.  err_ioarea:
  52.     release_resource(i2c->ioarea);
  53.     kfree(i2c->ioarea);

  54.  err_clk:
  55.     clk_disable(i2c->clk);
  56.     clk_put(i2c->clk);

  57.  err_noclk:
  58.     kfree(i2c);
  59.     return ret;
  60. }
s3c24xx_i2c_init:
  1. static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)
  2. {
  3.     unsigned long iicon = S3C2410_IICCON_IRQEN | S3C2410_IICCON_ACKEN;                            //赋值IICCON寄存器
  4.     struct s3c2410_platform_i2c *pdata;
  5.     unsigned int freq;

  6.     /* get the plafrom data */

  7.     pdata = i2c->dev->platform_data;

  8.     /* inititalise the gpio */                                                                    //初始化GPIO引脚

  9.     if (pdata->cfg_gpio)
  10.         pdata->cfg_gpio(to_platform_device(i2c->dev));

  11.     /* write slave address */

  12.     writeb(pdata->slave_addr, i2c->regs + S3C2410_IICADD);                                          //写入从设备地址

  13.     dev_info(i2c->dev, "slave address 0x%02x\n", pdata->slave_addr);

  14.     writel(iicon, i2c->regs + S3C2410_IICCON);

  15.     /* we need to work out the divisors for the clock... */

  16.     if (s3c24xx_i2c_clockrate(i2c, &freq) != 0) {
  17.         writel(0, i2c->regs + S3C2410_IICCON);
  18.         dev_err(i2c->dev, "cannot meet bus frequency required\n");                                 //设置时钟频率
  19.         return -EINVAL;
  20.     }

  21.     /* todo - check that the i2c lines aren't being dragged anywhere */

  22.     dev_info(i2c->dev, "bus frequency set to %d KHz\n", freq);
  23.     dev_dbg(i2c->dev, "S3C2410_IICCON=0x%02lx\n", iicon);

  24.     /* check for s3c2440 i2c controller */

  25.     if (s3c24xx_i2c_is2440(i2c))
  26.         writel(0x0, i2c->regs + S3C2440_IICLC);

  27.     return 0;
  28. }
i2c_algorithm:
  1. static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
  2.     .master_xfer        = s3c24xx_i2c_xfer,
  3.     .functionality        = s3c24xx_i2c_func,
  4. };
s3c24xx_i2c_xfer:
  1. static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
  2.             struct i2c_msg *msgs, int num)
  3. {
  4.     struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data;
  5.     int retry;
  6.     int ret;

  7.     for (retry = 0; retry < adap->retries; retry++) {

  8.         ret = s3c24xx_i2c_doxfer(i2c, msgs, num);                      //调用doxfer函数

  9.         if (ret != -EAGAIN)
  10.             return ret;

  11.         dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry);

  12.         udelay(100);
  13.     }

  14.     return -EREMOTEIO;
  15. }
s3c24xx_i2c_doxfer>>s3c24xx_i2c_message_start:
  1. static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,
  2.                  struct i2c_msg *msg)
  3. {
  4.     unsigned int addr = (msg->addr & 0x7f) << 1;
  5.     unsigned long stat;
  6.     unsigned long iiccon;

  7.     stat = 0;
  8.     stat |= S3C2410_IICSTAT_TXRXEN;

  9.     if (msg->flags & I2C_M_RD) {
  10.         stat |= S3C2410_IICSTAT_MASTER_RX;                             //设置IICSTAT模式
  11.         addr |= 1;
  12.     } else
  13.         stat |= S3C2410_IICSTAT_MASTER_TX;

  14.     if (msg->flags & I2C_M_REV_DIR_ADDR)
  15.         addr ^= 1;

  16.     /* todo - check for wether ack wanted or not */
  17.     s3c24xx_i2c_enable_ack(i2c);

  18.     iiccon = readl(i2c->regs + S3C2410_IICCON);
  19.     writel(stat, i2c->regs + S3C2410_IICSTAT);

  20.     dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS\n", stat, addr);
  21.     writeb(addr, i2c->regs + S3C2410_IICDS);                                          //设置从设备地址

  22.     /* delay here to ensure the data byte has gotten onto the bus
  23.      * before the transaction is started */

  24.     ndelay(i2c->tx_setup);                                                            

  25.     dev_dbg(i2c->dev, "iiccon, %08lx\n", iiccon);
  26.     writel(iiccon, i2c->regs + S3C2410_IICCON);

  27.     stat |= S3C2410_IICSTAT_START;                                                     //写入OXF0
  28.     writel(stat, i2c->regs + S3C2410_IICSTAT);                                         //传输
  29. }
s3c24xx_i2c_irq:
  1. static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id)
  2. {
  3.     struct s3c24xx_i2c *i2c = dev_id;
  4.     unsigned long status;
  5.     unsigned long tmp;

  6.     status = readl(i2c->regs + S3C2410_IICSTAT);

  7.     if (status & S3C2410_IICSTAT_ARBITR) {
  8.         /* deal with arbitration loss */
  9.         dev_err(i2c->dev, "deal with arbitration loss\n");
  10.     }

  11.     if (i2c->state == STATE_IDLE) {
  12.         dev_dbg(i2c->dev, "IRQ: error i2c->state == IDLE\n");

  13.         tmp = readl(i2c->regs + S3C2410_IICCON);
  14.         tmp &= ~S3C2410_IICCON_IRQPEND;
  15.         writel(tmp, i2c->regs + S3C2410_IICCON);
  16.         goto out;
  17.     }

  18.     /* pretty much this leaves us with the fact that we've
  19.      * transmitted or received whatever byte we last sent */

  20.     i2s_s3c_irq_nextbyte(i2c, status);                                           //处理程序

  21.  out:
  22.     return IRQ_HANDLED;
  23. }

阅读(542) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~