1.1 I2C电气特性
I2C(Inter-Integrated Circuit)总线是由
PHILIPS公司开发的两线式串行总线,用于连
接微控制器及其外围设备。
I2C总线只有两根双向信号线。
1.2 总线寻址
从设备的地址由固定部分和用户自定 义部分组成。
I2C总线只有两根双向信号线。
SDA: Serial Data Line-数据线
SCL :Serial Clock-时钟线
1.2 总线寻址
I2C总线协议规定:从设备采用7位的地址。D7~D1:从设
备地址。
D0位:数据传送方向位,为“0”时表示主设备向从 设备写数据,为“1”时表示主机由从设备读数据。
主设备发 送地址时,总线上的每个从设备都将这7位地址码与自己的 地址进行比较,如果相同,则认为自己正被主设备寻址,根 据R/W位将自己确定为发送器或接收器。
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保持高电平期间,数据线S DA上的电 平被拉低(即负跳变),定义为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控制器
i2c_algorithm:
i2c-s3c2410.c>>i2c_adap_s3c_init()>>s3c2410_i2c_driver>>s3c24xx_i2c_probe:
s3c24xx_i2c_init:
i2c_algorithm:
s3c24xx_i2c_xfer:
s3c24xx_i2c_doxfer>>s3c24xx_i2c_message_start:
s3c24xx_i2c_irq:
硬件设置A0,A1,A2拉低,I2C就可以按照000来寻找了。
二、I2C总线时序
2.1 空闲状态
I2C总线总线的SDA和SCL两条信号线同时处 于高电平时,规定为总线的空闲状态。
2.2 起始状态
在时钟线SCL保持高电平期间,数据线S DA上的电 平被拉低(即负跳变),定义为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控制器
- struct i2c_adapter {
- struct module *owner;
- unsigned int id;
- unsigned int class; /* classes to allow probing for */
- const struct i2c_algorithm *algo; /* the algorithm to access the bus */
- void *algo_data;
-
- /* --- administration stuff. */
- int (*client_register)(struct i2c_client *) __deprecated;
- int (*client_unregister)(struct i2c_client *) __deprecated;
-
- /* data fields that are valid for all devices */
- u8 level; /* nesting level for lockdep */
- struct mutex bus_lock;
- struct mutex clist_lock;
-
- int timeout; /* in jiffies */
- int retries;
- struct device dev; /* the adapter device */
-
- int nr;
- struct list_head clients; /* DEPRECATED */
- char name[48];
- struct completion dev_released;
- };
- struct i2c_algorithm {
-
- int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, //描述数据传输的一些方法
- int num);
- int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
- unsigned short flags, char read_write,
- u8 command, int size, union i2c_smbus_data *data);
-
- u32 (*functionality) (struct i2c_adapter *);
- };
- static int s3c24xx_i2c_probe(struct platform_device *pdev)
- {
- struct s3c24xx_i2c *i2c;
- struct s3c2410_platform_i2c *pdata;
- struct resource *res;
- int ret;
-
- ............................
-
- /* initialise the i2c controller */
-
- ret = s3c24xx_i2c_init(i2c); //初始化i2c_init
- if (ret != 0)
- goto err_iomap;
-
- /* find the IRQ for this unit (note, this relies on the init call to
- * ensure no current IRQs pending
- */
-
- i2c->irq = ret = platform_get_irq(pdev, 0);
- if (ret <= 0) {
- dev_err(&pdev->dev, "cannot find IRQ\n");
- goto err_iomap;
- }
-
- ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,
- dev_name(&pdev->dev), i2c);
-
- if (ret != 0) {
- dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq);
- goto err_iomap;
- }
-
- ret = s3c24xx_i2c_register_cpufreq(i2c);
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to register cpufreq notifier\n");
- goto err_irq;
- }
-
- /* Note, previous versions of the driver used i2c_add_adapter()
- * to add the bus at any number. We now pass the bus number via
- * the platform data, so if unset it will now default to always
- * being bus 0.
- */
-
- i2c->adap.nr = pdata->bus_num;
-
- ret = i2c_add_numbered_adapter(&i2c->adap); //注册i2c控制器(CPU内部),类似函数还有i2c_add_adpater(CPU外接控制器)。由I2C核心提供
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to add bus to i2c core\n");
- goto err_cpufreq;
- }
-
- platform_set_drvdata(pdev, i2c);
-
- dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev));
- return 0;
-
- err_cpufreq:
- s3c24xx_i2c_deregister_cpufreq(i2c);
-
- err_irq:
- free_irq(i2c->irq, i2c);
-
- err_iomap:
- iounmap(i2c->regs);
-
- err_ioarea:
- release_resource(i2c->ioarea);
- kfree(i2c->ioarea);
-
- err_clk:
- clk_disable(i2c->clk);
- clk_put(i2c->clk);
-
- err_noclk:
- kfree(i2c);
- return ret;
- }
- static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)
- {
- unsigned long iicon = S3C2410_IICCON_IRQEN | S3C2410_IICCON_ACKEN; //赋值IICCON寄存器
- struct s3c2410_platform_i2c *pdata;
- unsigned int freq;
-
- /* get the plafrom data */
-
- pdata = i2c->dev->platform_data;
-
- /* inititalise the gpio */ //初始化GPIO引脚
-
- if (pdata->cfg_gpio)
- pdata->cfg_gpio(to_platform_device(i2c->dev));
-
- /* write slave address */
-
- writeb(pdata->slave_addr, i2c->regs + S3C2410_IICADD); //写入从设备地址
-
- dev_info(i2c->dev, "slave address 0x%02x\n", pdata->slave_addr);
-
- writel(iicon, i2c->regs + S3C2410_IICCON);
-
- /* we need to work out the divisors for the clock... */
-
- if (s3c24xx_i2c_clockrate(i2c, &freq) != 0) {
- writel(0, i2c->regs + S3C2410_IICCON);
- dev_err(i2c->dev, "cannot meet bus frequency required\n"); //设置时钟频率
- return -EINVAL;
- }
-
- /* todo - check that the i2c lines aren't being dragged anywhere */
-
- dev_info(i2c->dev, "bus frequency set to %d KHz\n", freq);
- dev_dbg(i2c->dev, "S3C2410_IICCON=0x%02lx\n", iicon);
-
- /* check for s3c2440 i2c controller */
-
- if (s3c24xx_i2c_is2440(i2c))
- writel(0x0, i2c->regs + S3C2440_IICLC);
-
- return 0;
- }
- static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
- .master_xfer = s3c24xx_i2c_xfer,
- .functionality = s3c24xx_i2c_func,
- };
- static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
- struct i2c_msg *msgs, int num)
- {
- struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data;
- int retry;
- int ret;
-
- for (retry = 0; retry < adap->retries; retry++) {
-
- ret = s3c24xx_i2c_doxfer(i2c, msgs, num); //调用doxfer函数
-
- if (ret != -EAGAIN)
- return ret;
-
- dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry);
-
- udelay(100);
- }
-
- return -EREMOTEIO;
- }
- static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,
- struct i2c_msg *msg)
- {
- unsigned int addr = (msg->addr & 0x7f) << 1;
- unsigned long stat;
- unsigned long iiccon;
-
- stat = 0;
- stat |= S3C2410_IICSTAT_TXRXEN;
-
- if (msg->flags & I2C_M_RD) {
- stat |= S3C2410_IICSTAT_MASTER_RX; //设置IICSTAT模式
- addr |= 1;
- } else
- stat |= S3C2410_IICSTAT_MASTER_TX;
-
- if (msg->flags & I2C_M_REV_DIR_ADDR)
- addr ^= 1;
-
- /* todo - check for wether ack wanted or not */
- s3c24xx_i2c_enable_ack(i2c);
-
- iiccon = readl(i2c->regs + S3C2410_IICCON);
- writel(stat, i2c->regs + S3C2410_IICSTAT);
-
- dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS\n", stat, addr);
- writeb(addr, i2c->regs + S3C2410_IICDS); //设置从设备地址
-
- /* delay here to ensure the data byte has gotten onto the bus
- * before the transaction is started */
-
- ndelay(i2c->tx_setup);
-
- dev_dbg(i2c->dev, "iiccon, %08lx\n", iiccon);
- writel(iiccon, i2c->regs + S3C2410_IICCON);
-
- stat |= S3C2410_IICSTAT_START; //写入OXF0
- writel(stat, i2c->regs + S3C2410_IICSTAT); //传输
- }
- static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id)
- {
- struct s3c24xx_i2c *i2c = dev_id;
- unsigned long status;
- unsigned long tmp;
-
- status = readl(i2c->regs + S3C2410_IICSTAT);
-
- if (status & S3C2410_IICSTAT_ARBITR) {
- /* deal with arbitration loss */
- dev_err(i2c->dev, "deal with arbitration loss\n");
- }
-
- if (i2c->state == STATE_IDLE) {
- dev_dbg(i2c->dev, "IRQ: error i2c->state == IDLE\n");
-
- tmp = readl(i2c->regs + S3C2410_IICCON);
- tmp &= ~S3C2410_IICCON_IRQPEND;
- writel(tmp, i2c->regs + S3C2410_IICCON);
- goto out;
- }
-
- /* pretty much this leaves us with the fact that we've
- * transmitted or received whatever byte we last sent */
-
- i2s_s3c_irq_nextbyte(i2c, status); //处理程序
-
- out:
- return IRQ_HANDLED;
- }