foggia2004

i2c之at24c08驱动及应用程序

1-->修改板级文件arch/arm/mach-s3c2440/mach-mini2440.c

步骤:
1->添加引用 #include <linux/i2c.h>
2->添加IIC设备信息
static struct i2c_board_info i2c_devices[] __initdata = {
    {I2C_BOARD_INFO("at24c08", 0x50),},    /* 设备名称,设备地址 */
};
3->在mini2440_machine_init(void)添加函数 
i2c_register_board_info(0, i2c_devices, ARRAY_SIZE(i2c_devices));    /* 注册板级设备:总线数量,设备信息,设备数量 */

2-->驱动文件at24c08.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/stat.h>
#include <linux/device.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/i2c.h>
#include <asm/uaccess.h>        /* copy_to_user & copy_from_user */

#define AT24C08MAJOR    155     /* 预定义主设备号,如果有冲突,则使用alloc_devno */
static struct i2c_driver at24c08_driver;        /* 设备驱动 */
/*static struct i2c_adapter *at24c08_adapter;    设备适配器 */
static struct i2c_client *at24c08_client; /* i2c设备 */
static unsigned short addr = 0x50;      /* i2c设备地址 */
static dev_t alloc_devno;       /* 动态申请的设备号 */
static struct class at24c08_class =
{
        .name = "at24c08_class",
};

/* at24c08读函数用户给出要读取的地址=args;读取数据后放到data中,再返回给用户 */
static ssize_t at24c08_read(struct file *filp, char __user *buffer, size_t size, loff_t *off)
{
        struct i2c_msg msg[2];  /* 封装消息 */
        unsigned char args;     /* 参数和数据 */
        unsigned char data; /* 要返回的数据 */
        if(size != 1)
        {
                return -EINVAL;
        }
        /* 从用户空间读取一个参数赋值给args,也就是把要读取的地址传递给内核,args就是要读取的地址,由用户给出 */
        copy_from_user(&args, buffer, 1);
        /* 封装消息->先写地址,再读取数据,一共2次通讯 */
        msg[0].addr = addr; /* 设备地址 */
        msg[0].buf = &args; /* 要读取的地址 */
        msg[0].len = 1; /* 消息的长度 */
        msg[0].flags = 0; /* 标志位,写0读1 */

        /* 再读 */
        msg[1].addr = addr;
        msg[1].buf = &data; /* 接收读取的数据 */
        msg[1].len = 1; /* 要读取的数据长度 */
        msg[1].flags = I2C_M_RD;        /**/

        /* 与目标设备进行2次通讯 */
        if(i2c_transfer(at24c08_client->adapter, msg, 2) == 2)
        {
                /* 返回2,表示成功通讯2次 */
                copy_to_user(buffer, &data, 1);
                printk(KERN_INFO "at24c08_read succeed\n");
                return 1;
        }
        else
        {
                printk(KERN_INFO "at24c08_read failed\n");
                return -EIO;
        }
}

/* at24c08写函数 */
static ssize_t at24c081_write(struct file *filp, const char __user *buffer, size_t size, loff_t *off)
{
        struct i2c_msg msg[1];
        unsigned char args[2];
        /* args:保存从用户空间过来的数据 *buffer:用户空间的数据,包含了要写入的地址和药写入的数据*2:字节数 */
        printk(KERN_INFO "at24c08_write......\n");
        copy_from_user(&args, buffer, 2);       /* 成功返回0;失败返回未完成字节数 */
        printk(KERN_INFO "write parameters : args[0] = 0x%x, args[1] = 0x%x\n", args[0], args[1]);

        /* args[0]:addr, args[1]:value */
        msg[0].addr = addr; /* 设备地址 */
        msg[0].buf = args; /* 写入的数据 */
        msg[0].len = 2; /* 长度 */
        msg[0].flags = at24c08_client->flags & I2C_M_TEN;  /* 写标志 */
        if(i2c_transfer(at24c08_client->adapter, msg, 1) == 1)
        {
                printk(KERN_INFO "at24c08_write succeed\n");
                return 2;
        }
        else
        {
                printk(KERN_INFO "at24c08_write failed\n");
                return -EIO;
        }

}

int at24c08_open(struct inode *inode, struct file *filp)
{
        printk(KERN_INFO "at24c08 open\n");
        return 0;
}

/* at24c08操作集 */
static struct file_operations at24c08_fops = {
        .owner = THIS_MODULE,
        .read = at24c08_read,
        .write = at24c081_write,
        .open = at24c08_open,
};


static struct cdev i2c_cdev;    /* at24c08设备文件 */
/* 设备初始化 */
static int at24c08_probe(struct i2c_client *client, const struct i2c_device_id *i2c_device)
{
        int result;
        result = register_chrdev_region(AT24C08MAJOR, 1, "at24c08");    /* 申请字符设备主设备号 */
        if(result < 0)
        {
                printk(KERN_WARNING "register major number [%d] failed!\n", AT24C08MAJOR);
                alloc_chrdev_region(&alloc_devno, 0, 1, "at24c08");
                printk(KERN_INFO "alloc device number : major:[%d], minor:[%d] succeed!\n", MAJOR(alloc_devno), MINOR(alloc_devno));
        }
        else
                printk(KERN_INFO "register device number : major:[%d], minor:[0] succeed!\n", AT24C08MAJOR);
        cdev_init(&i2c_cdev, &at24c08_fops);
        cdev_add(&i2c_cdev, MKDEV(AT24C08MAJOR, 0), 1); /* 向系统注册字符设备文件,此时还未创建 */
        class_register(&at24c08_class); /* 设备总线类别 */
        device_create(&at24c08_class, NULL, MKDEV(AT24C08MAJOR, 0), NULL, "at24c08"); /* 创建at24c08字符设备文件 */
        printk(KERN_INFO "create device file 'at24c08' succeed!\n");

        at24c08_client = client;
        printk(KERN_INFO "get i2c_client, client name = %s, addr = 0x%x\n", at24c08_client->name, at24c08_client->addr);
        printk(KERN_INFO "get i2c_adapter, adapter name = %s\n", at24c08_client->adapter->name);

        printk(KERN_INFO "at24c08 probe()\n");
        return 0;
}

/* 移除设备 */
static int at24c08_remove(struct i2c_client *client)
{
        device_destroy(&at24c08_class, MKDEV(AT24C08MAJOR, 0)); /* 删除设备 */
        class_destroy(&at24c08_class);  /* 移除设备类别 */
        cdev_del(&i2c_cdev);
        unregister_chrdev_region(MKDEV(AT24C08MAJOR, 0), 1);
        printk(KERN_INFO "at24c08 remove()\n");
        return 0;
}

/* 设备检测函数 */
static int at24c08_detect(struct i2c_client *client, int kind, struct i2c_board_info *i2c_bd_info)
{
        printk(KERN_INFO "do nothing, at24c08 detect()\n");
        return 0;
}

/* i2c设备id列表 */
static const struct i2c_device_id at24c08_id[] = {
        { "at24c08", 0 }, 
        { } /* 最后一个必须为空,表示结束 */
};

static unsigned short ignore[] = { I2C_CLIENT_END };
static const unsigned short normal_i2c[] = { 0x50, I2C_CLIENT_END };
static const struct i2c_client_address_data addr_data = {
        .normal_i2c = normal_i2c,
        .probe = ignore,
        .ignore = ignore,
};

static struct i2c_driver at24c08_driver = {
        .probe = at24c08_probe,
        .remove = at24c08_remove,
        .driver = {
                .name = "at24c08",      /* 驱动名称 */
                .owner = THIS_MODULE,
        },
        .id_table = at24c08_id, /* id列表 */
        .detect = at24c08_detect,
        .address_data = &addr_data,
        .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,       /* 这些都是什么? */
};


static int __init at24c08_init(void)
{
        i2c_add_driver(&at24c08_driver); /* 将i2c_driver注册到系统中去 */
        printk(KERN_INFO "at24c08 i2c_driver was added into the system.\n");
        return 0;
}

static void __exit at24c08_exit(void)
{
        i2c_del_driver(&at24c08_driver);
        printk(KERN_INFO "at24c08 i2c_driver was deleted from the system.\n");
        return ;
}

module_init(at24c08_init);
module_exit(at24c08_exit);

MODULE_AUTHOR("edison ren");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("at24c08 device driver, 2016-10-08");

3-->应用程序

/* 配套的应用程序app.c */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <linux/types.h>

#define i2c_dev "/dev/at24c08"

int main(void)
{
    int fd, count = 0;
    fd = open(i2c_dev, O_RDWR);
    unsigned char rbuffer; /* 读取的数据:片内地址+1个数据 */
    rbuffer = 0x08; /* 片内地址 */
    unsigned char wbuffer[2]; /* 片内地址+h */
    wbuffer[0] = 0x08; /* 片内地址 */
    wbuffer[1] = 'h';
    if(fd < 0)
    {
        printf("open %s failed!\n", i2c_dev);
        exit(1);
    }
    else
    {
        printf("open %s succeed!\n", i2c_dev);

        printf("#####################################");
        count = read(fd, &rbuffer, 1);
        if(count > 0)
            printf("read data succeed, data = [%d].\n", rbuffer);
        rbuffer = 0x08;
        printf("#####################################");
        printf("write ...\n");
        count = write(fd, wbuffer, 2);
        if(count > 0)
            printf("write data succeed, write data count = %d.\n", count);
        else
        {
            printf("write data failed, write data count = %d.\n", count);
            exit(1);
        }
        printf("#####################################");
        count = read(fd, &rbuffer, 1);
        printf("read ...\n");
        if(count > 0)
            printf("read data succeed, data = [%d].\n", rbuffer);
    }
}


/* 连续读和写app2.c */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <linux/types.h>

#define i2c_dev "/dev/at24c08"

int main(void)
{
        int fd, i = 0, count = 0;
        fd = open(i2c_dev, O_RDWR);
        unsigned char rbuffer; /* 读取的数据:片内地址+1个数据 */
        unsigned char address[5];
        address[0] = 0x05;
        address[1] = 0x06;
        address[2] = 0x07;
        address[3] = 0x08;
        address[4] = 0x09;
        unsigned char data[5] = {'h', 'e', 'l', 'l', 'o'};
        unsigned char wbuffer[2]; /* 片内地址+h */
        if(fd < 0)
        {
                printf("open %s failed!\n", i2c_dev);
                exit(1);
        }
        else
        {
                printf("open %s succeed!\n", i2c_dev);
                printf("\n#####################################\n");
                for(i = 0; i < 5; i++)
                {
                        rbuffer = address[i];
                        read(fd, &rbuffer, 1);
                        printf("-0x[%x]-", rbuffer);
                }
                printf("\n#####################################\n");
                for(i = 0; i < 5; i++)
                {
                        wbuffer[0] = address[i];
                        wbuffer[1] = data[i];
                        write(fd, wbuffer, 2);
                }
                printf("\n#####################################\n");
                for(i = 0; i < 5; i++)
                {
                        rbuffer = address[i];
                        read(fd, &rbuffer, 1);
                        printf("-0x[%x]-", rbuffer);
                }
                printf("\n");
        }
}

4-->Makefile

ifneq ($(KERNELRELEASE),)
obj-m := at24c08.o
else
#KDIR := /lib/modules/2.6.18-53.el5/build
KDIR := /home/edison/linux-kernel/linux-2.6.32.2

all:
        make -C $(KDIR) M=$(PWD) modules arch=arm cross_compile=arm-linux-
clean:
        rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.order *.ko.unsigned
endif

当然也可以将驱动文件添加到对应的内核文件中,通过修改对应的KConfig和Makefile来将驱动编译到内核中

posted on 2016-10-17 09:20  foggia2004  阅读(995)  评论(0编辑  收藏  举报

导航