linux 下i2c总线与时钟芯片PCF8563通信的介绍

来源:本站
导读:目前正在解读《linux 下i2c总线与时钟芯片PCF8563通信的介绍》的相关信息,《linux 下i2c总线与时钟芯片PCF8563通信的介绍》是由用户自行发布的知识型内容!下面请观看由(电工技术网 - www.9ddd.net)用户发布《linux 下i2c总线与时钟芯片PCF8563通信的介绍》的详细说明。
简介:介绍了linux 下i2c总线与时钟芯片PCF8563通信的源代码的设计

i2c总线是嵌入式驱动中不能绕过一个地方,为了更深入的了解linux下的i2c 通信原理,我用一个用户程序 模拟,

这个程序,简单的说就是可以使用一个addr, 一个offset,对i2c 的地址为addr的从设备进行读写访问,访 问的寄存器是这个从设备的寄存器地址为offset。 在我们的版卡上时钟芯片pcf8563的i2c地址为0x51, pcf8563有00—0f个寄存器,通过读写秒,分钟,小时等的寄存器,可以验证我们的程序是否执行成功。 需要pcf8563的datasheet,读者可在网上查找。

我们版卡的cpu是Cavium Networks OCTEON CN52XX

********************************************** *******************************

************ ****************************************************

{

struct pcf8563 *pcf8563 = i2c_get_clientdata(client);

int i, err;

unsigned char buf[9];

printk(KERN_DEBUG "%s: secs=%d, mins=%d, hours=%d,ecs=%d, mins=%d, hours=%d

n",

__func__,

tm->tm_sec, tm->tm_min, tm->tm_hour,

tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);

/* hours, minutes and seconds */

buf[PCF8563_REG_SC] = bin2bcd(tm->tm_sec);

buf[PCF8563_REG_MN] = bin2bcd(tm->tm_min);

buf[PCF8563_REG_HR] = bin2bcd(tm->tm_hour);

buf[PCF8563_REG_DM] = bin2bcd(tm->tm_mday);

/* month, 1 - 12 */

buf[PCF8563_REG_MO] = bin2bcd(tm->tm_mon + 1);

/* year and century */

buf[PCF8563_REG_YR] = bin2bcd(tm->tm_year % 100);

if (pcf8563->c_polarity ? (tm->tm_year >= 100) : (tm->tm_year < 100))

buf[PCF8563_REG_MO] |= PCF8563_MO_C;

buf[PCF8563_REG_DW] = tm->tm_wday & 0x07;

/* write register's data */

for (i = 0; i < 7; i++) {

unsigned char data[2] = { PCF8563_REG_SC + i,

buf[PCF8563_REG_SC + i] };

err = i2c_master_send(client, data, sizeof(data));

if (err != sizeof(data)) {

dev_err(&client->dev,

"%s: err=%d addr=%02x, data=%02xn",

__func__, err, data[0], data[1]);

return -EIO;

}

在 driver/i2c/i2c-core.c 中:

int i2c_master_send(struct i2c_client *client,const char *buf ,int count)

{

int ret;

struct i2c_adapter *adap=client->adapter;

struct i2c_msg msg;

msg.addr = client->addr;

msg.flags = client->flags & I2C_M_TEN;

msg.len = count;

msg.buf = (char *)buf;

//added by handwei.2012.7.5

//added by handwei.2012.7.5

printk(KERN_DEBUG "%s: msg.addr = %x,msg.flags = %x,msg.len = %d,msg.buf[0] = %x,msg.

buf[1] = %xn",__func__,msg.addr,msg.flags,msg.len,msg.buf[0],msg.buf[1]);

ret = i2c_transfer(adap, &msg, 1);

/* If everything went ok (i.e. 1 msg transmitted), return #bytes

transmitted, else error code. */

return (ret == 1) ? count : ret;

}

注意: i2c_transfer(adap, &msg, 1);

中的 1 决定了 进入 octeon_i2c_xfer ()后,要进入 if(num==1)中。

下面是 octeon_i2c_xfer的代码:

static int octeon_i2c_xfer(struct i2c_adapter *adap,

struct i2c_msg *msgs,

int num)

{

printk(KERN_DEBUG "here is octeon_i2c_xfer,num = %dn",num);

struct i2c_msg *pmsg;

int i;

int ret = 0;

struct octeon_i2c *i2c = i2c_get_adapdata(adap);

if (num == 1) {

if (msgs[0].len > 0 && msgs[0].len <= 8) {

if (msgs[0].flags & I2C_M_RD)

ret = octeon_i2c_simple_read(i2c, msgs);

else

ret = octeon_i2c_simple_write(i2c, msgs);

goto out;

}

} else if (num == 2) {

if ((msgs[0].flags & I2C_M_RD) == 0 &&

msgs[0].len > 0 && msgs[0].len <= 2 &&

msgs[1].len > 0 && msgs[1].len <= 8 &&

msgs[0].addr == msgs[1].addr) {

if (msgs[1].flags & I2C_M_RD)

ret = octeon_i2c_ia_read(i2c, msgs);

else

ret = octeon_i2c_ia_write(i2c, msgs);

goto out;

}

}

for (i = 0; ret == 0 && i < num; i++) {

pmsg = &msgs[i];

dev_dbg(i2c->dev,

"Doing %s %d byte(s) to/from 0x%02x - %d of %d messagesn",

pmsg->flags & I2C_M_RD ? "read" : "write",

pmsg->len, pmsg->addr, i + 1, num);

if (pmsg->flags & I2C_M_RD)

ret = octeon_i2c_read(i2c, pmsg->addr, pmsg->buf,

pmsg->len, i);

}

octeon_i2c_stop(i2c);

out:

return (ret != 0) ? ret : num;

}

通过在 i2c-core.c: i2c_master_send()中添加printk,

//added by handwei.2012.7.5

printk(KERN_DEBUG "%s: msg.addr = %x,msg.flags = %x,msg.len = %d,msg.buf(data) =

%sn",__func__,msg.addr,msg.flags,msg.len,msg.buf);

运行后可以看到 msg.flags 一直等于0 ,那么 在 octeon_i2c_xfer ()中

if (num == 1) {

if (msgs[0].len > 0 && msgs[0].len <= 8) {

if (msgs[0].flags & I2C_M_RD)

ret = octeon_i2c_simple_read(i2c, msgs);

else

ret = octeon_i2c_simple_write(i2c, msgs);

goto out;

会一直进入octeon_i2c_simple_write()执行写操作。 下面是octeon_i2c_simple_write()的代码:

static int octeon_i2c_simple_write(struct octeon_i2c *i2c, struct i2c_msg *msgs)

{

u64 cmd;

int i, j;

int ret = 0;

octeon_i2c_enable_hlc(i2c);

printk(KERN_DEBUG "%s:n",__func__);

retry:

cmd = SW_TWSI_V | SW_TWSI_SOVR;

/* SIZE */

cmd |= (u64)(msgs[0].len - 1) << SW_TWSI_SIZE_SHIFT;

/* A */

cmd |= (u64)(msgs[0].addr & 0x7full) << SW_TWSI_A_SHIFT;

if (msgs[0].flags & I2C_M_TEN)

cmd |= SW_TWSI_OP_10;

else

cmd |= SW_TWSI_OP_7;

printk(KERN_DEBUG "%s:cmd = %llxn",__func__,cmd);

for (i = 0, j = msgs[0].len - 1; i < msgs[0].len && i < 4; i++, j--){

cmd |= (u64)msgs[0].buf[j] << (8 * i);

printk(KERN_DEBUG "%s:msgs[0].buf[%d] = %x,cmd = %llxn",__func__,j,msgs[0].buf

[j],cmd);

}

if (msgs[0].len >= 4) {

u64 ext = 0;

for (i = 0; i < msgs[0].len - 4 && i < 4; i++, j--)

ext |= (u64)msgs[0].buf[j] << (8 * i);

printk(KERN_DEBUG "%s:ext = %llxn",__func__,ext);

__raw_writeq(ext, i2c->twsi_base + SW_TWSI_EXT);

}

octeon_i2c_hlc_int_clear(i2c);

__raw_writeq(cmd, i2c->twsi_base + SW_TWSI);

ret = octeon_i2c_hlc_wait(i2c);

if (ret)

goto err;

cmd = __raw_readq(i2c->twsi_base + SW_TWSI);

if ((cmd & SW_TWSI_R) == 0) {

if (octeon_i2c_lost_arb(cmd))

goto retry;

ret = -EIO;

goto err;

}

err:

return ret;

}

下面是版卡执行 hwclock -w 后的dmesg 中的一部分:

[ 38.989069] pcf8563_get_datetime: secs=4, mins=41, hours=18,ecs=5, mins=6, hours=11

2

[ 40.292306] pcf8563_set_datetime: secs=5, mins=41, hours=18,ecs=5, mins=6, hours=112

[ 40.292321] pcf8563_set_datetime:data[0] = 2,data[1] = 5

[ 40.292333] i2c_master_send: msg.addr = 51,msg.flags = 0,msg.len = 2,msg.buf[0] = 2,msg.

buf[1] = 5

[ 40.292346] here is octeon_i2c_xfer, num = 1

[ 40.292355] octeon_i2c_simple_write:

[ 40.292363] octeon_i2c_simple_write:cmd = 8090510000000000

[ 40.292374] octeon_i2c_simple_write:msgs[0].buf[1] = 5,cmd = 8090510000000005

[ 40.292386] octeon_i2c_simple_write:msgs[0].buf[0] = 2,cmd = 8090510000000205

[ 40.292687] pcf8563_set_datetime:data[0] = 3,data[1] = 41

[ 40.292699] i2c_master_send: msg.addr = 51,msg.flags = 0,msg.len = 2,msg.buf[0] = 3,msg.

buf[1] = 41

[ 40.292712] here is octeon_i2c_xfer, num = 1

[ 40.292719] octeon_i2c_simple_write:

[ 40.292727] octeon_i2c_simple_write:cmd = 8090510000000000

[ 40.292738] octeon_i2c_simple_write:msgs[0].buf[1] = 41,cmd = 8090510000000041

[ 40.292750] octeon_i2c_simple_write:msgs[0].buf[0] = 3,cmd = 8090510000000341

[ 40.293049] pcf8563_set_datetime:data[0] = 4,data[1] = 18

[ 40.293061] i2c_master_send: msg.addr = 51,msg.flags = 0,msg.len = 2,msg.buf[0] = 4,msg.

buf[1] = 18

[ 40.293074] here is octeon_i2c_xfer, num = 1

[ 40.293082] octeon_i2c_simple_write:

[ 40.293089] octeon_i2c_simple_write:cmd = 8090510000000000

[ 40.293100] octeon_i2c_simple_write:msgs[0].buf[1] = 18,cmd = 8090510000000018

[ 40.293112] octeon_i2c_simple_write:msgs[0].buf[0] = 4,cmd = 8090510000000418

[ 40.293411] pcf8563_set_datetime:data[0] = 5,data[1] = 5

[ 40.293423] i2c_master_send: msg.addr = 51,msg.flags = 0,msg.len = 2,msg.buf[0] = 5,msg.

buf[1] = 5

[ 40.293435] here is octeon_i2c_xfer, num = 1

[ 40.293443] octeon_i2c_simple_write:

[ 40.293451] octeon_i2c_simple_write:cmd = 8090510000000000

[ 40.293462] octeon_i2c_simple_write:msgs[0].buf[1] = 5,cmd = 8090510000000005

[ 40.293474] octeon_i2c_simple_write:msgs[0].buf[0] = 5,cmd = 8090510000000505

[ 40.293772] pcf8563_set_datetime:data[0] = 6,data[1] = 4

[ 40.293784] i2c_master_send: msg.addr = 51,msg.flags = 0,msg.len = 2,msg.buf[0] = 6,msg.

buf[1] = 4

[ 40.293796] here is octeon_i2c_xfer, num = 1

[ 40.293804] octeon_i2c_simple_write:

[ 40.293812] octeon_i2c_simple_write:cmd = 8090510000000000

[ 40.293822] octeon_i2c_simple_write:msgs[0].buf[1] = 4,cmd = 8090510000000004

[ 40.293835] octeon_i2c_simple_write:msgs[0].buf[0] = 6,cmd = 8090510000000604

[ 40.294133] pcf8563_set_datetime:data[0] = 7,data[1] = 7

[ 40.294145] i2c_master_send: msg.addr = 51,msg.flags = 0,msg.len = 2,msg.buf[0] = 7,msg.

buf[1] = 7

[ 40.294157] here is octeon_i2c_xfer, num = 1

[ 40.294165] octeon_i2c_simple_write:

[ 40.294173] octeon_i2c_simple_write:cmd = 8090510000000000

[ 40.294184] octeon_i2c_simple_write:msgs[0].buf[1] = 7,cmd = 8090510000000007

[ 40.294196] octeon_i2c_simple_write:msgs[0].buf[0] = 7,cmd = 8090510000000707

[ 40.294494] pcf8563_set_datetime:data[0] = 8,data[1] = 12

[ 40.294506] i2c_master_send: msg.addr = 51,msg.flags = 0,msg.len = 2,msg.buf[0] = 8,msg.

buf[1] = 12

[ 40.294519] here is octeon_i2c_xfer, num = 1

[ 40.294526] octeon_i2c_simple_write:

[ 40.294534] octeon_i2c_simple_write:cmd = 8090510000000000

[ 40.294545] octeon_i2c_simple_write:msgs[0].buf[1] = 12,cmd = 8090510000000012

[ 40.294557] octeon_i2c_simple_write:msgs[0].buf[0] = 8,cmd = 8090510000000812

pcf8563_get_datetime: 是读到的时间

pcf8563_set_datetime: 是要写的时间

在第一个部分:

[ 40.292321] pcf8563_set_datetime:data[0] = 2,data[1] = 5

[ 40.292333] i2c_master_send: msg.addr = 51,msg.flags = 0,msg.len = 2,msg.buf[0] = 2,msg.

buf[1] = 5

[ 40.292346] here is octeon_i2c_xfer, num = 1

[ 40.292355] octeon_i2c_simple_write:

[ 40.292363] octeon_i2c_simple_write:cmd = 8090510000000000

[ 40.292374] octeon_i2c_simple_write:msgs[0].buf[1] = 5,cmd = 8090510000000005

[ 40.292386] octeon_i2c_simple_write:msgs[0].buf[0] = 2,cmd = 8090510000000205

在pcf8563_set_datetime ()中:

for (i = 0; i < 7; i++) {

unsigned char data[2] = { PCF8563_REG_SC + i,

//注意: #define PCF8563_REG_SC 0x02

err = i2c_master_send(client, data, sizeof(data));

if (err != sizeof(data)) {

dev_err(&client->dev,

"%s: err=%d addr=%02x, data=%02xn",

__func__, err, data[0], data[1]);

}

通过i2c_master_send()进入octeon_i2c_xfer(),再进入 octeon_i2c_simple_write,

{

cmd = SW_TWSI_V | SW_TWSI_SOVR;

cmd |= (u64)(msgs[0].len - 1) << SW_TWSI_SIZE_SHIFT;

// Address field.:<49:40>

cmd |= (u64)(msgs[0].addr & 0x7full) << SW_TWSI_A_SHIFT;

if (msgs[0].flags & I2C_M_TEN)

cmd |= SW_TWSI_OP_10;

for (i = 0, j = msgs[0].len - 1; i < msgs[0].len && i < 4; i++, j--){

cmd |= (u64)msgs[0].buf[j] << (8 * i);

...

}

根据msgs中的len,addr,flags, buf[]中的内容填充cmd,执行cmd 并 返回执行结果

同样的, 在进入第二次循环时, 这时操作的是pcf8563 的第三个寄存器,03h: minutes 分钟寄存器。

[ 40.292687] pcf8563_set_datetime:data[0] = 3,data[1] = 41

[ 40.292699] i2c_master_send: msg.addr = 51,msg.flags = 0,msg.len = 2,msg.buf[0] = 3,msg.

buf[1] = 41

[ 40.292712] here is octeon_i2c_xfer, num = 1

[ 40.292719] octeon_i2c_simple_write:

[ 40.292727] octeon_i2c_simple_write:cmd = 8090510000000000

[ 40.292738] octeon_i2c_simple_write:msgs[0].buf[1] = 41,cmd = 8090510000000041

[ 40.292750] octeon_i2c_simple_write:msgs[0].buf[0] = 3,cmd = 8090510000000341

第三次,操作pcf8563的第四个寄存器:04h hours

[ 40.293049] pcf8563_set_datetime:data[0] = 4,data[1] = 18

[ 40.293061] i2c_master_send: msg.addr = 51,msg.flags = 0,msg.len = 2,msg.buf[0] = 4,msg.

buf[1] = 18

[ 40.293074] here is octeon_i2c_xfer, num = 1

[ 40.293082] octeon_i2c_simple_write:

[ 40.293089] octeon_i2c_simple_write:cmd = 8090510000000000

[ 40.293100] octeon_i2c_simple_write:msgs[0].buf[1] = 18,cmd = 8090510000000018

[ 40.293112] octeon_i2c_simple_write:msgs[0].buf[0] = 4,cmd = 8090510000000418

我们看到:

hwclock -w 通过 octeon_i2c_simple_write 来写pcf8563时钟芯片,

其中:msg.addr = 0x51 , msg.len = 2 ,msg.flags = 0,

msg.buf[0] = 相应地址偏移

msg.buf[1] = 给相应寄存器的值

那么我们的i2c-test, 也这样操作试试:

包装一个msg, 传入octeon_i2c_xfer(),再传入 octeon_i2c_simple_write,看看能不能得到期望的执

行结果。

另外 , 还要通过hwclock 来观察 ,为什么没有使用octeon_i2c_simple_read 来读寄存器的现象。

注意到:

在关于写的操作中: pcf8563_set_datetime()

i2c_master_send()

{.

.

msg.addr = client->addr;

msg.flags = client->flags & I2C_M_TEN;

msg.len = count;

msg.buf = (char *)buf;

ret = i2c_transfer(adap, &msg, 1);

..}

提醒:《linux 下i2c总线与时钟芯片PCF8563通信的介绍》最后刷新时间 2024-03-14 01:04:10,本站为公益型个人网站,仅供个人学习和记录信息,不进行任何商业性质的盈利。如果内容、图片资源失效或内容涉及侵权,请反馈至,我们会及时处理。本站只保证内容的可读性,无法保证真实性,《linux 下i2c总线与时钟芯片PCF8563通信的介绍》该内容的真实性请自行鉴别。