如果对i2c设备驱动不了解的, 请先看这里的简单的驱动i2c识别:/zhutoubenben/article/details/8003781
#define DRIVER_LICENSE "GPL"
#define DRIVER_AUTHOR "zhutoubenben"
#define DRIVER_DESC "i i c iic_at24c08..."
#define I2C_DEVICE "24c08"
#define MISC_DEVICE_NAME "at24c08"
static struct i2c_at24c08_data {
struct i2c_client *i2c_client;
const struct i2c_device_id *i2c_id;
}T_i2c_at24c08_data;
static int i2c_at24c08_open(struct inode *inode, struct file *filp){
filp->private_data = (void *)&T_i2c_at24c08_data;// 把结构体存入filp私有指针,驱动惯用手段
return 0;
}
static ssize_t i2c_at24c08_read(struct file *filp, char __user *buf, size_t count, loff_t *loff){
struct i2c_at24c08_data *i2c_at24c08_data = (struct i2c_at24c08_data*)filp->private_data;// 获取open中的私有指针
struct i2c_client * i2c_client = i2c_at24c08_data->i2c_client;
unsigned char address;
unsigned char data;
struct i2c_msg msg[2];
int ret;
if (count != 1)
return -EINVAL;
copy_from_user(&address, buf, 1);/*应用程序需要把地址放在buf中,才能调用读*/
/* 读AT24CXX时,要先把要读的存储空间的地址发给它 */
msg[0].addr = i2c_client->addr; /*i2c 器件地址 实际上就是我们的0x50*/
msg[0].buf = &address; /* 读的地址 */
msg[0].len = 1; /* 地址=1 byte */
msg[0].flags = 0; /* 表示写 */
/* 然后启动读操作 */
msg[1].addr = i2c_client->addr; /*i2c 器件地址 */
msg[1].buf = &data; /* 读的数据放的位置 */
msg[1].len = 1; /* 数据=1 byte */
msg[1].flags = I2C_M_RD; /* 表示读 */
ret = i2c_transfer(i2c_client->adapter, msg, 2);/*启动发送, 这个是重点, 待会主要分析这一块*/
if (ret == 2)
{
copy_to_user(buf, &data, 1);
return 1;
}
else
return -EIO;
}
static ssize_t i2c_at24c08_write(struct file *filp, const char __user *buf, size_t count, loff_t *loff){
struct i2c_at24c08_data *i2c_at24c08_data = filp->private_data;
struct i2c_client * i2c_client = i2c_at24c08_data->i2c_client;
unsigned char val[2];
struct i2c_msg msg[1];
int ret;
/* address = buf[0] , data = buf[1] */
if (count != 2)
return -EINVAL;
copy_from_user(val, buf, 2);
msg[0].addr = i2c_client->addr; /*i2c 器件地址 */
msg[0].buf = val; /* 地址和数据 */
msg[0].len = 2; /* 地址+数据=2 byte */
msg[0].flags = 0; /* 表示写 */
ret = i2c_transfer(i2c_client->adapter, msg, 1);
if (ret == 1)
return 2;
else
return -EIO;
return count;
}
static struct file_operations i2c_fops={
.owner = THIS_MODULE,
.open = i2c_at24c08_open,
.read = i2c_at24c08_read,
.write = i2c_at24c08_write,
};
static struct miscdevice i2c_misc = {// 混杂设备
.minor = MISC_DYNAMIC_MINOR,
.name = MISC_DEVICE_NAME,
.fops = &i2c_fops,
};
static int i2c_at24c08_probe(struct i2c_client *i2c_client, const struct i2c_device_id *i2c_device_id){
printk("i2c_at24c08_probe , probe the device ..\n");
misc_register(&i2c_misc);
T_i2c_at24c08_data.i2c_client = i2c_client;
T_i2c_at24c08_data.i2c_id = i2c_device_id;
return 0;
}
static int i2c_at24c08_remove(struct i2c_client *i2c_client){
misc_deregister(&i2c_misc);
printk("i2c_at24c08_remove , remove the device ..\n");
return 0;
}
struct i2c_device_id at24c08_id[] = {
{I2C_DEVICE,0},{}
};
MODULE_DEVICE_TABLE(i2c, at24c08_id);
static struct i2c_driver i2c_at24c08_driver = {
.driver = {
.name = I2C_DEVICE
},
.probe = i2c_at24c08_probe,
.remove = i2c_at24c08_remove,
.id_table = at24c08_id,
};
static int __init i2c_at24c08_init(void){
int result;
result = i2c_add_driver(&i2c_at24c08_driver);
if (result)
printk("i2c_add_driver failed. Error number %d", result);
return result;
}
static void __exit i2c_at24c08_exit(void){
i2c_del_driver(&i2c_at24c08_driver);
}
module_init(i2c_at24c08_init);
module_exit(i2c_at24c08_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE(DRIVER_LICENSE);
上面的程序主要的是调用了
i2c_transfer(i2c_client->adapter, msg, 2);/*启动发送, 这个是重点, 待会主要分析这一块*/
这个函数进行发送, 所以我们主要是分析这个函数, 看看他具体做了什么
i2c_transfer --> adap->algo->master_xfer(adap, msgs, num); // 这个函数实际上就是里面的适配器所指向的master_xfer也就是s3c24xx_i2c_xfer函数
二话不说, 我们进入s3c24xx_i2c_xfer()中, 在直接跳过简单的代码进入s3c24xx_i2c_doxfer()中
....
i2c->msg = msgs; // 把msg写入i2c结构体中, 这样就可以丢掉原来的msgs
i2c->msg_num = num;
i2c->msg_ptr = 0;
i2c->msg_idx = 0;
i2c->state = STATE_START; // 这个状态很重要, 后面用到了
s3c24xx_i2c_enable_irq(i2c);
s3c24xx_i2c_message_start(i2c, msgs); // 这句很重要~~~~下面分析
spin_unlock_irq(&i2c->lock);
timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);// 等待事件
...
接下来分析s3c24xx_i2c_message_start
static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,
struct i2c_msg *msg)
{
unsigned int addr = (msg->addr & 0x7f) << 1; // 这里实际上是设置i2c的bus上的从器件地址,也就是我们的高7位应该是器件地址,最后一位就是读/写
unsigned long stat; // 这个是用来存放状态的
unsigned long iiccon;
stat = 0;
stat |= S3C2410_IICSTAT_TXRXEN; // 使能i2c bus的发送接收位, 具体参考2440数据手册
if (msg->flags & I2C_M_RD) { // 如果是读的话
stat |= S3C2410_IICSTAT_MASTER_RX; // 设置状态为 读
addr |= 1; // 把addr的最低位置1, 表示读
} else
stat |= S3C2410_IICSTAT_MASTER_TX;// 设置状态为 写
if (msg->flags & I2C_M_REV_DIR_ADDR) // 为0
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); // 把上面设置的stat写入iic_stat寄存器
dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS\n", stat, addr);
writeb(addr, i2c->regs + S3C2410_IICDS); // 把addr这个从器件地址写入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; // 设置stat的 启动传输位, 这里的启动传输, 那么他就会启动发送器件地址,当器件响应的时候就会调用中断函数
writel(stat, i2c->regs + S3C2410_IICSTAT); // 把stat 写入iic_stat寄存器
/*
IIC中断发生在以下三种情况:当发出地址信息或接收到一个从机地址并吻合时,当总线总裁失败时,当发送/接收完一个字节的数据(包括响应位)时。当发出地址信息或接收到一个从机地址并吻合时产生中断,在中断处理函数中要准备发送或者接收数据,即读取或设备IICDS寄存器,或者发出P信号。当总线总裁失败时产生中断,在中断处理函数中决定时候延时后再次竞争总线等。当发送/接收完一个字节的数据(包括响应位)时产生中断,在中断处理函数中要准备下次要发送或者接收数据,即读取或设备IICDS寄存器,或者发出P信号。
}*/
那么我们接下来就是查看我们的中断函数了:
static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id) { ...
status = readl(i2c->regs + S3C2410_IICSTAT);// 还记得我们之前s3c24xx_i2c_doxfer设置的state么?i2c->state = STATE_START;故下面的if先不调用 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; } i2s_s3c_irq_nextbyte(i2c, status);// 这个函数非常重要, 下面分析 out: return IRQ_HANDLED; }
static int i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat) { unsigned long tmp; unsigned char byte; int ret = 0; switch (i2c->state) {// 由于i2c->state == STATE_START;故到case STATE_START: case STATE_IDLE: dev_err(i2c->dev, "%s: called in STATE_IDLE\n", __func__); goto out; break; case STATE_STOP: dev_err(i2c->dev, "%s: called in STATE_STOP\n", __func__); s3c24xx_i2c_disable_irq(i2c); goto out_ack; case STATE_START: ......
if (i2c->msg->flags & I2C_M_RD)// 读 i2c->state = STATE_READ; // 设置状态为 读 else i2c->state = STATE_WRITE;// 设置状态为 写 /* terminate the transfer if there is nothing to do * as this is used by the i2c probe to find devices. */ if (is_lastmsg(i2c) && i2c->msg->len == 0) { s3c24xx_i2c_stop(i2c, 0); goto out_ack; } if (i2c->state == STATE_READ) goto prepare_read; /* fall through to the write state, as we will need to * send a byte as well */ case STATE_WRITE: /* we are writing data to the device... check for the * end of the message, and if so, work out what to do */ if (!(i2c->msg->flags & I2C_M_IGNORE_NAK)) { if (iicstat & S3C2410_IICSTAT_LASTBIT) { dev_dbg(i2c->dev, "WRITE: No Ack\n"); s3c24xx_i2c_stop(i2c, -ECONNREFUSED); goto out_ack; } } retry_write: if (!is_msgend(i2c)) { byte = i2c->msg->buf[i2c->msg_ptr++]; writeb(byte, i2c->regs + S3C2410_IICDS); /* delay after writing the byte to allow the * data setup time on the bus, as writing the * data to the register causes the first bit * to appear on SDA, and SCL will change as * soon as the interrupt is acknowledged */ ndelay(i2c->tx_setup); } else if (!is_lastmsg(i2c)) { /* we need to go to the next i2c message */ dev_dbg(i2c->dev, "WRITE: Next Message\n"); i2c->msg_ptr = 0; i2c->msg_idx++; i2c->msg++; /* check to see if we need to do another message */ if (i2c->msg->flags & I2C_M_NOSTART) { if (i2c->msg->flags & I2C_M_RD) { /* cannot do this, the controller * forces us to send a new START * when we change direction */ s3c24xx_i2c_stop(i2c, -EINVAL); } goto retry_write; } else { /* send the new start */ s3c24xx_i2c_message_start(i2c, i2c->msg); i2c->state = STATE_START; } } else { /* send stop */ s3c24xx_i2c_stop(i2c, 0); } break; case STATE_READ: /* we have a byte of data in the data register, do * something with it, and then work out wether we are * going to do any more read/write */ byte = readb(i2c->regs + S3C2410_IICDS); i2c->msg->buf[i2c->msg_ptr++] = byte; prepare_read: if (is_msglast(i2c)) { /* last byte of buffer */ if (is_lastmsg(i2c)) s3c24xx_i2c_disable_ack(i2c); } else if (is_msgend(i2c)) { /* ok, we've read the entire buffer, see if there * is anything else we need to do */ if (is_lastmsg(i2c)) { /* last message, send stop and complete */ dev_dbg(i2c->dev, "READ: Send Stop\n"); s3c24xx_i2c_stop(i2c, 0); } else { /* go to the next transfer */ dev_dbg(i2c->dev, "READ: Next Transfer\n"); i2c->msg_ptr = 0; i2c->msg_idx++; i2c->msg++; } } break; } /* acknowlegde the IRQ and get back on with the work */ out_ack: tmp = readl(i2c->regs + S3C2410_IICCON); tmp &= ~S3C2410_IICCON_IRQPEND; writel(tmp, i2c->regs + S3C2410_IICCON); out: return ret; }
应用层测试程序:#include <> #include <> #include <> #include <sys/> #include <sys/> #include <> /* i2c_test r addr * i2c_test w addr val */ void print_usage(char *file) { printf("%s r addr\n", file); printf("%s w addr val\n", file); } int main(int argc, char **argv) { int fd; unsigned char buf[2]; if ((argc != 3) && (argc != 4)) { print_usage(argv[0]); return -1; } fd = open("/dev/at24c08", O_RDWR); if (fd < 0) { printf("can't open /dev/at24c08\n"); return -1; } if (strcmp(argv[1], "r") == 0) { buf[0] = strtoul(argv[2], NULL, 0); read(fd, buf, 1); printf("data: %c, %d, 0x%2x\n", buf[0], buf[0], buf[0]); } else if (strcmp(argv[1], "w") == 0) { buf[0] = strtoul(argv[2], NULL, 0); buf[1] = strtoul(argv[3], NULL, 0); write(fd, buf, 2); } else { print_usage(argv[0]); return -1; } return 0; }
测试方法:./xx w 30 50 在30的地址写入字符50
./xx r 30 读出30的地址