如果对i2c设备驱动不了解的, 请先看这里的简单的驱动i2c识别:http://blog.csdn.net/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); // 这个函数实际上就是i2c-3c2410.c里面的适配器所指向的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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
/* 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的地址

1万+

被折叠的 条评论
为什么被折叠?



