• I2C学习


    一、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 ");
    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 ", 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 ");
    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 ");
    40.         goto err_cpufreq;
    41.     }

    42.     platform_set_drvdata(pdev, i2c);

    43.     dev_info(&pdev->dev, "%s: S3C I2C adapter ", 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 ", 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 ");                                 //设置时钟频率
    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 ", freq);
    23.     dev_dbg(i2c->dev, "S3C2410_IICCON=0x%02lx ", 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) ", 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 ", 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 ", 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 ");
    10.     }

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

    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. }

    无欲速,无见小利。欲速,则不达;见小利,则大事不成。
  • 相关阅读:
    使用python将字符串首字母转成大写,且字符串其余字母保持不变
    运用tenacity库来提高自动化用例的稳定性
    使用python调用钉钉开放接口,现实给员工单独发送钉钉通知消息
    Python中关于时间的使用场景
    vim 练习1 20200417
    概率论与数理统计 习题三 题目及答案
    概率论与数理统计 习题二 题目及答案
    (哈) 四种算法 MVP!!!
    (哈)先来先服务(FCFS)调度算法 --升级版
    (哈) 短作业调度算法
  • 原文地址:https://www.cnblogs.com/ch122633/p/7363295.html
Copyright © 2020-2023  润新知