i2c总线可以通过bitbang两个gpio口来实现, bitbang就是位拉高拉低的意思,英文手册中经常出现的一个词。

drivers/i2c/algos/i2c-algo-bit.c, 就是这个i2c的algos程序,通过bitbang SCL 和SDA两个引脚,来实现I2C总线协议。

1. 实现bitbang,  有两个最基本的操作,一个是设置GPIO的输出(高或者低),另一个就是读取GPIO的状态。

因为实现I2C总线协议,必须要有时间来控制SCL的脉冲宽度(这里用的是udelay),所以需要看GPIO口的拉高或者拉低到底有没有实现,就需要读取GPIO的状态来判断。

这里相关的代码如下:

/* --- setting states on the bus with the right timing: --------------- */#define setsda(adap,val) adap->setsda(adap->data, val)
#define setscl(adap,val) adap->setscl(adap->data, val)
#define getsda(adap) adap->getsda(adap->data)
#define getscl(adap) adap->getscl(adap->data)

设置sda 和scl的输出,读取sda和scl的状态, 这两个函数的具体实现需要用到i2c busses下的一个代码,这里使用的是通用的drivers/i2c/busses/i2c-gpio.c。
在i2c_gpio_probe()函数中,实现了上面几个函数指针的赋值:

static int __init i2c_gpio_probe(struct platform_device *pdev)
{struct i2c_gpio_platform_data *pdata;struct i2c_algo_bit_data *bit_data;struct i2c_adapter *adap;int ret;pdata = pdev->dev.platform_data;if (!pdata)return -ENXIO;ret = -ENOMEM;adap = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL);if (!adap)goto err_alloc_adap;bit_data = kzalloc(sizeof(struct i2c_algo_bit_data), GFP_KERNEL);if (!bit_data)goto err_alloc_bit_data;gpio_request_mux(pdata->sda_pin, GPIO_MUX_GPIO);gpio_request_mux(pdata->scl_pin, GPIO_MUX_GPIO);gpio_set_puen(pdata->sda_pin, 1);gpio_set_puen(pdata->scl_pin, 1);if (pdata->sda_is_open_drain) {mxc_set_gpio_direction(pdata->sda_pin, 0);bit_data->setsda = i2c_gpio_setsda_val;} else {mxc_set_gpio_direction(pdata->sda_pin, 1);bit_data->setsda = i2c_gpio_setsda_dir;}if (pdata->scl_is_open_drain || pdata->scl_is_output_only) {mxc_set_gpio_direction(pdata->scl_pin, 0);bit_data->setscl = i2c_gpio_setscl_val;} else {mxc_set_gpio_direction(pdata->scl_pin, 1);bit_data->setscl = i2c_gpio_setscl_dir;}if (!pdata->scl_is_output_only)bit_data->getscl = i2c_gpio_getscl;bit_data->getsda = i2c_gpio_getsda;if (pdata->udelay)bit_data->udelay = pdata->udelay;else if (pdata->scl_is_output_only)bit_data->udelay = 50;                  /* 10 kHz */elsebit_data->udelay = 5;                   /* 100 kHz */if (pdata->timeout)bit_data->timeout = pdata->timeout;elsebit_data->timeout = HZ / 10;            /* 100 ms */bit_data->data = pdata;adap->owner = THIS_MODULE;snprintf(adap->name, sizeof(adap->name), "i2c-gpio%d", pdev->id);adap->algo_data = bit_data;adap->dev.parent = &pdev->dev;ret = i2c_bit_add_bus(adap);if (ret)goto err_add_bus;platform_set_drvdata(pdev, adap);dev_info(&pdev->dev, "using pins %x (SDA) and %x (SCL%s)\n",pdata->sda_pin, pdata->scl_pin,pdata->scl_is_output_only? ", no clock stretching" : "");return 0;err_add_bus:mxc_free_gpio(pdata->scl_pin);
err_request_scl:mxc_free_gpio(pdata->sda_pin);
err_request_sda:kfree(bit_data);
err_alloc_bit_data:kfree(adap);
err_alloc_adap:return ret;
}

首先是获得GPIO, 把GPIO设成高阻抗状态。

        gpio_request_mux(pdata->sda_pin, GPIO_MUX_GPIO);gpio_request_mux(pdata->scl_pin, GPIO_MUX_GPIO);gpio_set_puen(pdata->sda_pin, 1);gpio_set_puen(pdata->scl_pin, 1);

然后就是开始赋值这四个函数指针:

        if (pdata->sda_is_open_drain) {mxc_set_gpio_direction(pdata->sda_pin, 0);bit_data->setsda = i2c_gpio_setsda_val;} else {mxc_set_gpio_direction(pdata->sda_pin, 1);bit_data->setsda = i2c_gpio_setsda_dir;}if (pdata->scl_is_open_drain || pdata->scl_is_output_only) {mxc_set_gpio_direction(pdata->scl_pin, 0);bit_data->setscl = i2c_gpio_setscl_val;} else {mxc_set_gpio_direction(pdata->scl_pin, 1);bit_data->setscl = i2c_gpio_setscl_dir;}if (!pdata->scl_is_output_only)bit_data->getscl = i2c_gpio_getscl;bit_data->getsda = i2c_gpio_getsda;

再接下来是设置 udelay 和timeout, 这连个值在bitbang中都会使用到,udelay是用来控制scl的脉冲周期的,timeout是用来控制最长脉冲宽度,比如有时CPU被中断,被别的进程使用,这时bitbang就会有延时,timeout就是用来控制最大的延时的,如果延时超出timeout,就会报错之类的出现,后面会有详细代码说明。

        if (pdata->udelay)bit_data->udelay = pdata->udelay;else if (pdata->scl_is_output_only)bit_data->udelay = 50;                  /* 10 kHz */elsebit_data->udelay = 5;                   /* 100 kHz */if (pdata->timeout)bit_data->timeout = pdata->timeout;elsebit_data->timeout = HZ / 10;            /* 100 ms */

最后就是i2c adapter 的赋值,并向i2c core 添加i2c adapter:

        bit_data->data = pdata;adap->owner = THIS_MODULE;snprintf(adap->name, sizeof(adap->name), "i2c-gpio%d", pdev->id);adap->algo_data = bit_data;adap->dev.parent = &pdev->dev;ret = i2c_bit_add_bus(adap);if (ret)goto err_add_bus;platform_set_drvdata(pdev, adap);

i2c_bit_add_bus() 中可以先测试I2C bus bitbang功能行不行,如果测试失败,就返回错误。如果成功,就添加adapter到i2c core中。

int i2c_bit_add_bus(struct i2c_adapter *adap)
{int err;err = i2c_bit_prepare_bus(adap);if (err)return err;return i2c_add_adapter(adap);
}
EXPORT_SYMBOL(i2c_bit_add_bus);

i2c_bit_prepare_bus() 就是用来测试i2c最基本的拉低拉高bitbang功能的,并添加几个adapter数据。

/** registering functions to load algorithms at runtime*/
static int i2c_bit_prepare_bus(struct i2c_adapter *adap)
{struct i2c_algo_bit_data *bit_adap = adap->algo_data;if (bit_test) {int ret = test_bus(bit_adap, adap->name);if (ret<0)return -ENODEV;}/* register new adapter to i2c module... */adap->algo = &i2c_bit_algo;adap->timeout = 100;    /* default values, should       */adap->retries = 3;      /* be replaced by defines       */return 0;
}

如果设置了bit_test, 就会测试bitbang bus基本拉低拉高功能,这个可以根据自己需要设置。

test_bus中,有测试 getscl(), getsda(), setscl()和setsda()等内容。

/** Sanity check for the adapter hardware - check the reaction of* the bus lines only if it seems to be idle.*/
static int test_bus(struct i2c_algo_bit_data *adap, char* name) {int scl,sda;if (adap->getscl==NULL)pr_info("%s: Testing SDA only, SCL is not readable\n", name);sda=getsda(adap);scl=(adap->getscl==NULL?1:getscl(adap));if (!scl || !sda ) {printk(KERN_WARNING "%s: bus seems to be busy\n", name);goto bailout;}sdalo(adap);sda=getsda(adap);scl=(adap->getscl==NULL?1:getscl(adap));if ( 0 != sda ) {printk(KERN_WARNING "%s: SDA stuck high!\n", name);goto bailout;}if ( 0 == scl ) {printk(KERN_WARNING "%s: SCL unexpected low ""while pulling SDA low!\n", name);goto bailout;}sdahi(adap);sda=getsda(adap);scl=(adap->getscl==NULL?1:getscl(adap));if ( 0 == sda ) {printk(KERN_WARNING "%s: SDA stuck low!\n", name);goto bailout;}if ( 0 == scl ) {printk(KERN_WARNING "%s: SCL unexpected low ""while pulling SDA high!\n", name);goto bailout;}scllo(adap);sda=getsda(adap);scl=(adap->getscl==NULL?0:getscl(adap));if ( 0 != scl ) {printk(KERN_WARNING "%s: SCL stuck high!\n", name);goto bailout;}if ( 0 == sda ) {printk(KERN_WARNING "%s: SDA unexpected low ""while pulling SCL low!\n", name);goto bailout;}sclhi(adap);sda=getsda(adap);scl=(adap->getscl==NULL?1:getscl(adap));if ( 0 == scl ) {printk(KERN_WARNING "%s: SCL stuck low!\n", name);goto bailout;}if ( 0 == sda ) {printk(KERN_WARNING "%s: SDA unexpected low ""while pulling SCL high!\n", name);goto bailout;}pr_info("%s: Test OK\n", name);return 0;
bailout:sdahi(adap);sclhi(adap);return -ENODEV;
}

具体这四个函数的实现如下, 归根到底就是控制GPIO输入输出方向和输入输出值,还有读取gpio值。

/* Toggle SDA by changing the direction of the pin */
static void i2c_gpio_setsda_dir(void *data, int state)
{struct i2c_gpio_platform_data *pdata = data;if (state){mxc_set_gpio_direction(pdata->sda_pin, 1);}else{mxc_set_gpio_direction(pdata->sda_pin, 0);}
}/** Toggle SDA by changing the output value of the pin. This is only* valid for pins configured as open drain (i.e. setting the value* high effectively turns off the output driver.)*/
static void i2c_gpio_setsda_val(void *data, int state)
{struct i2c_gpio_platform_data *pdata = data;mxc_set_gpio_dataout(pdata->sda_pin, state);
}/* Toggle SCL by changing the direction of the pin. */
static void i2c_gpio_setscl_dir(void *data, int state)
{struct i2c_gpio_platform_data *pdata = data;if (state){mxc_set_gpio_direction(pdata->scl_pin, 1);}else{mxc_set_gpio_direction(pdata->scl_pin, 0);}
}
/** Toggle SCL by changing the output value of the pin. This is used* for pins that are configured as open drain and for output-only* pins. The latter case will break the i2c protocol, but it will* often work in practice.*/
static void i2c_gpio_setscl_val(void *data, int state)
{struct i2c_gpio_platform_data *pdata = data;mxc_set_gpio_dataout(pdata->scl_pin, state);
}int i2c_gpio_getsda(void *data)
{int ret = 0;struct i2c_gpio_platform_data *pdata = data;return mxc_get_gpio_datain(pdata->sda_pin);
}int i2c_gpio_getscl(void *data)
{struct i2c_gpio_platform_data *pdata = data;return mxc_get_gpio_datain(pdata->scl_pin);
}

这些基本就是 drivers/i2c/busses/i2c-gpio.c 的全部了。
2. 由这四个基本操作函数寅生出来的几个bitbang函数:

static inline void sdalo(struct i2c_algo_bit_data *adap)
{setsda(adap,0);udelay((adap->udelay + 1) / 2);
}static inline void sdahi(struct i2c_algo_bit_data *adap)
{setsda(adap,1);udelay((adap->udelay + 1) / 2);
}static inline void scllo(struct i2c_algo_bit_data *adap)
{setscl(adap,0);udelay(adap->udelay / 2);
}

这里的udelay 1/2的时间都是有用意的,会和后面的delay的时间配合起来实现一个scl周期。

sclhi()函数特殊一些,这是协议要求的,它会反复check scl拉高了没有,如果没有就用while等,直到timeout。

cond_resched()用来睡眠,切换到别的进程。

/** Raise scl line, and do checking for delays. This is necessary for slower* devices.*/
static int sclhi(struct i2c_algo_bit_data *adap)
{unsigned long start;setscl(adap,1);/* Not all adapters have scl sense line... */if (!adap->getscl)goto done;start=jiffies;while (! getscl(adap) ) {/* the hw knows how to read the clock line,* so we wait until it actually gets high.* This is safer as some chips may hold it low* while they are processing data internally.*/if (time_after_eq(jiffies, start+adap->timeout)) {printk("get clock low timeout.\n");return -ETIMEDOUT;}//cond_resched();}
#ifdef DEBUGif (jiffies != start && i2c_debug >= 3)pr_debug("i2c-algo-bit: needed %ld jiffies for SCL to go ""high\n", jiffies - start);
#endifdone:udelay(adap->udelay);return 0;
}

3. 下面开始就是i2c协议层的函数了, 其实都是由上面这几个函数组合而成的。

a. i2c_start(), 就是master向slave发送一个开始信号:

在scl高电平时,sda从高到低下降沿时,就是一个start信号。

/* --- other auxiliary functions -------------------------------------- */
static void i2c_start(struct i2c_algo_bit_data *adap)
{
#ifdef BUGGY_SLMunsigned long flags;
#endif#ifdef BUGGY_SLMlocal_irq_save(flags);local_irq_disable();
#endif/* assert: scl, sda are high */setsda(adap, 0);udelay(adap->udelay);scllo(adap);#ifdef BUGGY_SLMlocal_irq_restore(flags);local_irq_enable();
#endif
}

b. i2c_stop(), master 向slave发送一个stop信号:

当scl高电平时,sda从低到高上升沿时,就是一个stop信号。

static void i2c_stop(struct i2c_algo_bit_data *adap)
{
#ifdef BUGGY_SLMunsigned long flags;
#endif#ifdef BUGGY_SLMlocal_irq_save(flags);local_irq_disable();
#endif/* assert: scl is low */sdalo(adap);sclhi(adap);setsda(adap, 1);udelay(adap->udelay);#ifdef BUGGY_SLMlocal_irq_restore(flags);local_irq_enable();
#endif
}

c. i2c_restart(),  这个函数还是发送一个start信号:

只不过是在没有stop信号之前重新开始的意思,如果已经stop了,就得用i2c_start()函数了。

static void i2c_repstart(struct i2c_algo_bit_data *adap)
{
#ifdef BUGGY_SLMunsigned long flags;
#endif#ifdef BUGGY_SLMlocal_irq_save(flags);local_irq_disable();
#endif/* assert: scl is low */sdahi(adap);sclhi(adap);setsda(adap, 0);udelay(adap->udelay);scllo(adap);#ifdef BUGGY_SLMlocal_irq_restore(flags);local_irq_enable();
#endif
}

d. i2c_outb() , 发送一字节数据:

一个字节有8位,bitbang就是一位一位发出数据的,所以里面有个for 8次循环。

/* send a byte without start cond., look for arbitration,check ackn. from slave */
/* returns:* 1 if the device acknowledged* 0 if the device did not ack* -ETIMEDOUT if an error occurred (while raising the scl line)*/
static int i2c_outb(struct i2c_adapter *i2c_adap, unsigned char c)
{int i;int sb;int ack;struct i2c_algo_bit_data *adap = i2c_adap->algo_data;#ifdef BUGGY_SLMunsigned long flags;
#endif#ifdef BUGGY_SLMlocal_irq_save(flags);local_irq_disable();
#endif/* assert: scl is low */for ( i=7 ; i>=0 ; i-- ) {sb = (c >> i) & 1;setsda(adap,sb);udelay((adap->udelay + 1) / 2);if (sclhi(adap)<0) { /* timed out */bit_dbg(1, &i2c_adap->dev, "i2c_outb: 0x%02x, ""timeout at bit #%d\n", (int)c, i);
#ifdef BUGGY_SLMlocal_irq_restore(flags);local_irq_enable();
#endifreturn -ETIMEDOUT;};/* do arbitration here:* if ( sb && ! getsda(adap) ) -> ouch! Get out of here.*/scllo(adap);}sdahi(adap);if (sclhi(adap)<0){ /* timeout */bit_dbg(1, &i2c_adap->dev, "i2c_outb: 0x%02x, ""timeout at ack\n", (int)c);
#ifdef BUGGY_SLMlocal_irq_restore(flags);local_irq_enable();
#endifreturn -ETIMEDOUT;};/* read ack: SDA should be pulled down by slave */ack = !getsda(adap);    /* ack: sda is pulled low -> success */bit_dbg(2, &i2c_adap->dev, "i2c_outb: 0x%02x %s\n", (int)c,ack ? "A" : "NA");scllo(adap);#ifdef BUGGY_SLMlocal_irq_restore(flags);local_irq_enable();
#endifreturn ack;/* assert: scl is low (sda undef) */
}

i2c协议中规定,sda数据在scl高电平时保持不变,则该sda数据有效,sda数据在scl低电平时变化,这样就实现一个scl时钟周期发送一位数据,代码实现就是上面的for 8次循环。

        /* assert: scl is low */for ( i=7 ; i>=0 ; i-- ) {sb = (c >> i) & 1;setsda(adap,sb);udelay((adap->udelay + 1) / 2);if (sclhi(adap)<0) { /* timed out */bit_dbg(1, &i2c_adap->dev, "i2c_outb: 0x%02x, ""timeout at bit #%d\n", (int)c, i);
#ifdef BUGGY_SLMlocal_irq_restore(flags);local_irq_enable();
#endifreturn -ETIMEDOUT;};/* do arbitration here:* if ( sb && ! getsda(adap) ) -> ouch! Get out of here.*/scllo(adap);}

数据发送完了之后,会等待slave 端的acknowledge。

i2c协议规定, master产生8个用来传输数据的scl脉冲之后, 还必须在产生一个脉冲,用来获取ack信号。

首先master会拉高sda信号,如果slave端产生ack信号,slave端会拉低sda信号,并在scl高电平时,始终保持sda低电平,这样就表示产生了一个acknowledge信号。

代码实现如下:

        sdahi(adap);if (sclhi(adap)<0){ /* timeout */bit_dbg(1, &i2c_adap->dev, "i2c_outb: 0x%02x, ""timeout at ack\n", (int)c);
#ifdef BUGGY_SLMlocal_irq_restore(flags);local_irq_enable();
#endifreturn -ETIMEDOUT;};/* read ack: SDA should be pulled down by slave */ack = !getsda(adap);    /* ack: sda is pulled low -> success */bit_dbg(2, &i2c_adap->dev, "i2c_outb: 0x%02x %s\n", (int)c,ack ? "A" : "NA");scllo(adap);

e. sendbytes(), 发送多个字节数据:

这个函数就是 i2c_outb() 发展而来,多个字节数据发送,数据个数由 msg->len 传递进来。

static int sendbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
{const unsigned char *temp = msg->buf;int count = msg->len;unsigned short nak_ok = msg->flags & I2C_M_IGNORE_NAK;int retval;int wrcount=0;while (count > 0) {retval = i2c_outb(i2c_adap, *temp);if ((retval>0) || (nak_ok && (retval==0)))  { /* ok or ignored NAK */count--;temp++;wrcount++;} else { /* arbitration or no acknowledge */printk("retval = %d\n", retval);dev_err(&i2c_adap->dev, "sendbytes: error - bailout.\n");return (retval<0)? retval : -EFAULT;/* got a better one ?? */}}return wrcount;
}

f. i2c_inb(), 从设备端读取一个字节数据

读取一个字节,也是一个for 8次循环, indata *=2, 就是左移一位(i=0不移),如果getsda(adap)高电平,就表示接受到数据为1,就indata |=1; 如果是低电平就是0了。

这个算法实现的真实精湛呀。

static int i2c_inb(struct i2c_adapter *i2c_adap)
{/* read byte via i2c port, without start/stop sequence  *//* acknowledge is sent in i2c_read.                     */int i;unsigned char indata=0;struct i2c_algo_bit_data *adap = i2c_adap->algo_data;/* assert: scl is low */sdahi(adap);for (i=0;i<8;i++) {if (sclhi(adap)<0) { /* timeout */bit_dbg(1, &i2c_adap->dev, "i2c_inb: timeout at bit ""#%d\n", 7 - i);return -ETIMEDOUT;};indata *= 2;if ( getsda(adap) )indata |= 0x01;setscl(adap, 0);udelay(i == 7 ? adap->udelay / 2 : adap->udelay);}/* assert: scl is low */return indata;
}

g. readbytes(),  读取多字节数据,这函数是扩展i2c_inb()来实现的。

i2c_inb 读取一字节数据之后, master必须发送一个ack信号给slave端,这个就是在readbytes()函数中实现的。

static int readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
{int inval;int rdcount=0;          /* counts bytes read */struct i2c_algo_bit_data *adap = i2c_adap->algo_data;unsigned char *temp = msg->buf;int count = msg->len;#ifdef BUGGY_SLMunsigned long flags;
#endif#ifdef BUGGY_SLMlocal_irq_save(flags);local_irq_disable();
#endifwhile (count > 0) {inval = i2c_inb(i2c_adap);if (inval>=0) {*temp = inval;rdcount++;} else {   /* read timed out */break;}temp++;count--;if (msg->flags & I2C_M_NO_RD_ACK) {bit_dbg(2, &i2c_adap->dev, "i2c_inb: 0x%02x\n",inval);continue;}/* assert: sda is high */if (count)              /* send ack */setsda(adap, 0);udelay((adap->udelay + 1) / 2);bit_dbg(2, &i2c_adap->dev, "i2c_inb: 0x%02x %s\n", inval,count ? "A" : "NA");if (sclhi(adap)<0) {    /* timeout */dev_err(&i2c_adap->dev, "readbytes: timeout at ack\n");
#ifdef BUGGY_SLMlocal_irq_restore(flags);local_irq_enable();
#endifreturn -ETIMEDOUT;};scllo(adap);/* Some SMBus transactions require that we receive thetransaction length as the first read byte. */if (rdcount == 1 && (msg->flags & I2C_M_RECV_LEN)) {if (inval <= 0 || inval > I2C_SMBUS_BLOCK_MAX) {dev_err(&i2c_adap->dev, "readbytes: invalid ""block length (%d)\n", inval);
#ifdef BUGGY_SLMlocal_irq_restore(flags);local_irq_enable();
#endifreturn -EREMOTEIO;}/* The original count value accounts for the extrabytes, that is, either 1 for a regular transaction,or 2 for a PEC transaction. */count += inval;msg->len += inval;}}
#ifdef BUGGY_SLMlocal_irq_restore(flags);local_irq_enable();
#endifreturn rdcount;
}

这里count是要读取的字节数,由i2c_msg->len 从应用层传递进来。

每读取完一个字节数据,也就是i2c_inb()之后,都会产生一个ack信号,如果没读()完就产生ack信号

                /* assert: sda is high */if (count)              /* send ack */setsda(adap, 0);udelay((adap->udelay + 1) / 2);bit_dbg(2, &i2c_adap->dev, "i2c_inb: 0x%02x %s\n", inval,count ? "A" : "NA");if (sclhi(adap)<0) {    /* timeout */dev_err(&i2c_adap->dev, "readbytes: timeout at ack\n");
#ifdef BUGGY_SLMlocal_irq_restore(flags);local_irq_enable();
#endifreturn -ETIMEDOUT;};scllo(adap);

h. bit_doAddress(),   发送地址信号

i2c协议规定,发送外start信号后,需要跟着发送I2C slave地址。

同一根I2C总线上可以同时挂很多个不同设备,设备之间用slave地址来区分,每个设备的手册上会有地址信息,这个是I2C协会规定的,估计就是各个I2C设备厂商从I2C标准协会那去申请或购买的。

/* doAddress initiates the transfer by generating the start condition (in* try_address) and transmits the address in the necessary format to handle* reads, writes as well as 10bit-addresses.* returns:*  0 everything went okay, the chip ack'ed, or IGNORE_NAK flag was set* -x an error occurred (like: -EREMOTEIO if the device did not answer, or*      -ETIMEDOUT, for example if the lines are stuck...)*/
static int bit_doAddress(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
{unsigned short flags = msg->flags;unsigned short nak_ok = msg->flags & I2C_M_IGNORE_NAK;struct i2c_algo_bit_data *adap = i2c_adap->algo_data;unsigned char addr;int ret, retries;retries = nak_ok ? 0 : i2c_adap->retries;if ( (flags & I2C_M_TEN)  ) {/* a ten bit address */addr = 0xf0 | (( msg->addr >> 7) & 0x03);bit_dbg(2, &i2c_adap->dev, "addr0: %d\n", addr);/* try extended address code...*/ret = try_address(i2c_adap, addr, retries);if ((ret != 1) && !nak_ok)  {dev_err(&i2c_adap->dev,"died at extended address code\n");return -EREMOTEIO;}/* the remaining 8 bit address */ret = i2c_outb(i2c_adap,msg->addr & 0x7f);if ((ret != 1) && !nak_ok) {/* the chip did not ack / xmission error occurred */dev_err(&i2c_adap->dev, "died at 2nd address code\n");return -EREMOTEIO;}if ( flags & I2C_M_RD ) {bit_dbg(3, &i2c_adap->dev, "emitting repeated ""start condition\n");i2c_repstart(adap);/* okay, now switch into reading mode */addr |= 0x01;ret = try_address(i2c_adap, addr, retries);if ((ret!=1) && !nak_ok) {dev_err(&i2c_adap->dev,"died at repeated address code\n");return -EREMOTEIO;}}} else {                /* normal 7bit address  */addr = ( msg->addr << 1 );if (flags & I2C_M_RD )addr |= 1;if (flags & I2C_M_REV_DIR_ADDR )addr ^= 1;ret = try_address(i2c_adap, addr, retries);if ((ret!=1) && !nak_ok)return -EREMOTEIO;}return 0;
}

这个代码其实很简单,I2C设备分两种地址类型,一个是10位的,一个是8位的,到现在为止,我还没见过10位I2C地址的设备。

8位I2C地址的代码就是else中的部分, 先是确定是读还是写,读的话最低位是1, 写的话最低位是0(用addr ^=1 来实现还挺逗的)。

i. try_address(), 就是真正发送I2C地址的函数了:

这里会重试retries 次, retries 在之前赋值是3,就是重试3次,如果没有slave端ack响音,就表示要寻找的设备不通。

/* try_address tries to contact a chip for a number of* times before it gives up.* return values:* 1 chip answered* 0 chip did not answer* -x transmission error*/
static int try_address(struct i2c_adapter *i2c_adap,unsigned char addr, int retries)
{struct i2c_algo_bit_data *adap = i2c_adap->algo_data;int i,ret = -1;for (i=0;i<=retries;i++) {ret = i2c_outb(i2c_adap,addr);if (ret == 1 || i == retries)break;bit_dbg(3, &i2c_adap->dev, "emitting stop condition\n");i2c_stop(adap);udelay(adap->udelay);yield();bit_dbg(3, &i2c_adap->dev, "emitting start condition\n");i2c_start(adap);}if (i && ret)bit_dbg(1, &i2c_adap->dev, "Used %d tries to %s client at ""0x%02x: %s\n", i + 1,addr & 1 ? "read from" : "write to", addr >> 1,ret == 1 ? "success" : "failed, timeout?");return ret;
}

j. bit_xfer(), 真正的传输函数:

所有i2c总线的操作都回调用到这个函数,这个函数是与外面通信的接口:

static int bit_xfer(struct i2c_adapter *i2c_adap,struct i2c_msg msgs[], int num)
{struct i2c_msg *pmsg;struct i2c_algo_bit_data *adap = i2c_adap->algo_data;int i,ret;unsigned short nak_ok;bit_dbg(3, &i2c_adap->dev, "emitting start condition\n");i2c_start(adap);for (i=0;i<num;i++) {pmsg = &msgs[i];nak_ok = pmsg->flags & I2C_M_IGNORE_NAK;if (!(pmsg->flags & I2C_M_NOSTART)) {if (i) {bit_dbg(3, &i2c_adap->dev, "emitting ""repeated start condition\n");i2c_repstart(adap);}ret = bit_doAddress(i2c_adap, pmsg);if ((ret != 0) && !nak_ok) {bit_dbg(1, &i2c_adap->dev, "NAK from ""device addr 0x%02x msg #%d\n",msgs[i].addr, i);goto bailout;}}if (pmsg->flags & I2C_M_RD ) {/* read bytes into buffer*/ret = readbytes(i2c_adap, pmsg);if (ret >= 1)bit_dbg(2, &i2c_adap->dev, "read %d byte%s\n",ret, ret == 1 ? "" : "s");if (ret < pmsg->len) {if (ret >= 0)ret = -EREMOTEIO;goto bailout;}} else {/* write bytes from buffer */ret = sendbytes(i2c_adap, pmsg);if (ret >= 1)bit_dbg(2, &i2c_adap->dev, "wrote %d byte%s\n",ret, ret == 1 ? "" : "s");if (ret < pmsg->len) {if (ret >= 0)ret = -EREMOTEIO;goto bailout;}}}ret = i;bailout:bit_dbg(3, &i2c_adap->dev, "emitting stop condition\n");i2c_stop(adap);return ret;
}

这函数分3种访问类型: 发送slave地址, 从设备端读取数据,向设备发送数据,都是上面有讲过的。

I2C协议方面也就是这些内容,这些搞懂了,I2C就没啥的了,这个协议还真挺简单的。

通过 bitbang GPIO来实现i2c总线协议相关推荐

  1. SPI与I2C总线协议

    目录: 一.SPI总线协议 1.什么是SPI 2.技术性能 3.接口定义与硬件连接 4.内部结构 5.传输时序 6.高速SPI 1)硬件电路   2)1MHz SPI传输问题 二.I2C总线协议 1. ...

  2. [I2C]I2C总线协议图解

    转自:http://blog.csdn.net/w89436838/article/details/38660631 1  I2C总线物理拓扑结构       I2C 总线在物理连接上非常简单,分别由 ...

  3. 总线全称_一篇文章讲透I2C总线协议

    最近一段时间工作上比较忙,一直没有抽出空来写文章与大家分享,这两天腾出些时间静下心来沉淀一番.看标题大家已经知道了是来总结I2C总线,我相信大家或多或少的都接触过I2C总线,这篇文章我们就由浅入深的仔 ...

  4. TM1637芯片使用(I2C总线协议学习),含完整程序

    目录 1.TM1637芯片(大自然的搬运工) 芯片介绍 引脚图 时序图 其他关键 管脚功能 命令格式 封装 2. 51单片机程序编写 I2C_START(): I2C_WR(): I2C_ACK(): ...

  5. i2c总线协议的工作原理详解

    一.概述 1.I2C总线只有两根双向信号线.一根是数据线SDA,另一根是时钟线SCL. SCL:上升沿将数据输入到每个EEPROM器件中:下降沿驱动EEPROM器件输出数据.(边沿触发) SDA:双向 ...

  6. I2C总线协议和控制器解析

    I2C总线协议 1.1 I2C总线知识 1.1.1  I2C总线物理拓扑结构       I2C 总线在物理连接上非常简单,分别由SDA(串行数据线)和SCL(串行时钟线)及上拉电阻组成.通信原理是通 ...

  7. I2C总线协议的verilog实现

    最近一直在学习各种接口,今天要讲的是I2C 总线.I2C是是一种简单的同步串行总线. 它只需要两根线即可在连接于总线上的器件之间传送信息. 主器件用于启动总线传送数据,并产生时钟以开放传送的器件,此时 ...

  8. 浅谈 IIC I2C 总线协议

    简介 IIC(也称I2C或I2C)总线是Philips公司开发的一种简单.双向二线制同步串行总线,是Inter-Integrated Circuit的缩写. IIC只用两条双向线,一条SDA(Seri ...

  9. 对I2C总线协议的一些理解

    1.无论读与写,都是在时钟线为低时把数据送到数据总线上,在高时采样数据,把数据锁存到内部,所以读之前先把时钟线拉低,做好准备(数据线为高表示释放数据线),为接下来读数据做好准备.也就是时钟信号为低时, ...

  10. 使用GPIO模拟I2C总线进行通信

    I2C总线的通信过程(见图4-8)主要包含三个主要阶段:起始阶段.数据传输阶段和终止阶段. 1. 起始阶段 在I2C总线不工作的情况下,SDA(数据线)和SCL(时钟线)上的信号均为高电平.如果此时主 ...

最新文章

  1. Shell中的Wget 的用法
  2. SQL Server镜像自动生成脚本
  3. 实现java RPC框架
  4. (原)torch中微调某层参数
  5. mysql 组复制 不一致_MySQL主从复制什么原因会造成不一致,如何预防及解决
  6. 解决Android App启动页背景图片拉伸变形问题
  7. 免费WiFi上网软件之WiFi共享精灵
  8. 使用Arduino开发板和颜色传感器区分不同颜色
  9. MFC小游戏之坦克大战
  10. Bootloader的启动
  11. 统计考勤报表 oracle对多个列求和 sum() 函数
  12. oracle sqlnet配置,sqlnet.ora文件配置详解
  13. 【优化调度】基于粒子群算法求解梯级水电站调度问题matlab代码
  14. Hook其他程序中的StringGrid的内容(内牛满面,终于找到了。)
  15. Pytorch丨expand函数
  16. 必备的20个电路分析
  17. Mybatis入门到精通:helloworld
  18. IT行业中说话最不靠谱的商业领袖
  19. 电脑测试瓶颈的软件,只需一招教你找出电脑的性能瓶颈-怎么测试电脑性能
  20. 21-ICLR-Prototypical Contrastive Learning of Unsupervised Representations

热门文章

  1. 含泪整理最优质草食动物unity3d模型素材,你想要的这里都有
  2. 智慧(灯杆)路灯系统集成解决方案详解
  3. 如何调用TUIO中的源码
  4. 浙江5G+智能制造迅速推进连点成片。
  5. 【JS逆向破解】爬虫抓取哦oh漫画实例Java/Python实现
  6. 服务器安装julia_科学网—Julia 在windows下安装说明(国内) - 王虹宇的博文
  7. Avaya Aura™ 独家观察报告
  8. 16位图xxxxxxxxxxxx
  9. 31省农村居民人均可支配收入 (2002-2018年)
  10. 操作系统实验——进程通信