linux驱动开发之i2c从设备驱动at24c02的编程

编译从设备驱动编程的前奏:
1, 将i2c core层和i2c adapter控制器层编译进内核
make menuconfig
Device Drivers —>
<*> I2C support —> // i2c-core.c
I2C Hardware Bus support —>
<*> S3C2410 I2C Driver // i2c-adatper的驱动–i2c-s3c2410.c
2, 为创建i2c client提供信息,内核就会根据信息自动创建i2c client
在ubuntu中进入linux内核文件路径(/home/ubuntu/s5pv210/kernel/linux-3.0.8),vim arch/arm/mach-s5pv210/mach-smdkv210.c ,修改里面内容

    //根据硬件原理图,将i2c client的信息填入到对应的数组
    // 硬件连接在第几个总线上,就填入对应的数组
        static struct i2c_board_info smdkv210_i2c_devs0[] __initdata = {
            { I2C_BOARD_INFO("at240c2", 0x50), // 参数1--表示用于和i2c driver进行匹配的名字--自定义--需要记住
                                              //参数2--从设备地址--7bit--从芯片手册查询得到
                // .type = "24c08", .addr = 0x50
            },     /* Samsung S524AD0XD1 */
            { I2C_BOARD_INFO("wm8580", 0x1b), },
        };

        static struct i2c_board_info smdkv210_i2c_devs1[] __initdata = {
            /* To Be Updated */
        };

        static struct i2c_board_info smdkv210_i2c_devs2[] __initdata = {
            /* To Be Updated */
            { I2C_BOARD_INFO("test", 0x2b), }, //这个用于测试---实际没有这个硬件
        };

    smdkv210_machine_init(void)//该函数在开机就自动执行
        |
        i2c_register_board_info(0, smdkv210_i2c_devs0,ARRAY_SIZE(smdkv210_i2c_devs0));
        i2c_register_board_info(1, smdkv210_i2c_devs1,ARRAY_SIZE(smdkv210_i2c_devs1));
        i2c_register_board_info(2, smdkv210_i2c_devs2,ARRAY_SIZE(smdkv210_i2c_devs2));

3.重新编译内核:make zImage
4.更新内核:cp -raf arch/arm/boot/zImage /tftpboot/
5.重启开发板,
[root@farsight devices]# pwd
/sys/bus/i2c/devices
[root@farsight devices]# ls
0-001b 0-0050 i2c-0 i2c-1 i2c-2
[root@farsight 0-0050]# pwd
/sys/bus/i2c/devices/0-0050
[root@farsight 0-0050]# cat name
at240c2

接下来编写i2c driver的驱动代码—at24c02驱动
构建i2c_driver,注册到总线中:
|
—probe(*i2c_client)
|
0, 记录匹配之后的i2c client
1,申请主设备号
2,创建设备节点
3,实现fops
4,根据实际情况去初始化从设备硬件

驱动中涉及到的各种结构体:

//描述了一个从设备的操作方法
struct i2c_driver 
{
    /* Standard driver model interfaces */
    int (*probe)(struct i2c_client *, const struct i2c_device_id *);
    int (*remove)(struct i2c_client *);
    struct device_driver driver;            //父类
    const struct i2c_device_id *id_table;   //用于和i2c client进行匹配的名字列表

}
//描述了一个从设备的信息---从设备地址,名字
struct i2c_client { 
    unsigned short addr;            //7bit--从设备地址
    char name[I2C_NAME_SIZE];       //名字
    struct i2c_adapter *adapter;    // 指向创建自己的adapter
    struct i2c_driver *driver;      //指向匹配成功之后的i2c driver
    struct device dev;              //父类

};
//描述控制器, 比如编号,算法
struct i2c_adapter { 

    const struct i2c_algorithm *algo; /* the algorithm to access the bus */
                        |
                        int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,int num);
    struct device dev;  //父类

    int nr;             //编号
    char name[48];

};

//传输的数据包 
struct i2c_msg { 
    __u16 addr;  //数据包传输給那个从设备
    __u16 flags;//读还是写--  1读,0写
    __u16 len;      /* msg length               */
    __u8 *buf;      /* pointer to msg data          */
};


i2c 子系统提供读写从设备的接口:

    i2c_master_recv(const struct i2c_client * client, char * buf, int count)
    i2c_master_send(const struct i2c_client * client, const char * buf, int count)
        |
        //重点学会用i2c_transfer
        int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)

从设备驱动at24c02的程序:at24_e2prom_i2c_drv.c

#include 
#include 
#include 
#include 
#include 
#include 

#include 

//设计一个驱动针对不同的设备的不同数据(私有)
struct e2prom_private{
    char *name; //描述
    int size;   //容量
    int version;//版本
};

int at24_i2c_e2prom_open (struct inode *inode, struct file *filp)
{
    printk("------%s--------\n", __FUNCTION__);
    //一般也是做初始化

    return 0;
}

//设计一个全局的设备对象
struct i2c_e2prom_global{
    struct i2c_client *client;//描述了一个从设备的信息---从设备地址,名字
    struct e2prom_private *private;
    struct miscdevice i2c_misc;
};



//全局的指针
struct i2c_e2prom_global *at24_dev;

//i2c_master_recv(const struct i2c_client * client, char * buf, int count);
int at24_i2c_recv(const struct i2c_client * client, char * buf, int count)
{
    int ret;
    struct i2c_adapter *adapter = client->adapter;//描述控制器, 比如编号,算法

    struct i2c_msg msg;//传输的数据包 

    msg.addr = client->addr;//数据包传输給哪个从设备
    msg.flags = I2C_M_RD; //读还是写--  1读,0写
    msg.len = count;//数据大小bytes
    msg.buf = buf;

    //参数1--适配器,来自于client
    //参数2--传输的消息体--数据包
    //参数3--消息体的个数
    //返回值--正确为消息的个数,错误为负数
    //i2c_transfer(struct i2c_adapter * adap, struct i2c_msg * msgs, int num)
    ret = i2c_transfer(adapter,  &msg,  1);

    return ret==1?count:ret;

}

//重写类似于i2c_master_send/i2c_master_recv的方法
int at24_i2c_send(struct i2c_client *client,char *buf, int count)
{
    int ret;
    //i2c_adapter描述控制器,比如编号,算法
    struct i2c_adapter *adapter = client->adapter;

    struct i2c_msg msg;
    msg.addr = client->addr;
    msg.buf = buf;
    msg.flags = 0;
    msg.len = count;

    //参数1--适配器,来自于client
    //参数2--传输的消息体--数据包
    //参数3--消息体的个数
    ret = i2c_transfer(adapter, &msg, 1);

    return ret==1?count:ret;
}

ssize_t at24_i2c_e2prom_read (struct file *filp, char __user *buf, size_t count, loff_t *fops)
{
    printk("------%s--------\n", __FUNCTION__);
    int ret;

    //简单的参数判断
    if(count <0 || count > at24_dev->private->size)
        return -EINVAL;

    //动态的分配一个空间,用于存放从硬件获取到的数据
    char *temp = kzalloc(count, GFP_KERNEL);

    //i2c_master_recv(const struct i2c_client * client, char * buf, int count);
    //i2c_master_send(const struct i2c_client * client, const char * buf, int count);
    // 1,从硬件中获取数据
    ret = at24_i2c_recv(at24_dev->client,  temp,  count);
    if(ret < 0)
    {
        printk("at24_i2c_recv error\n");
        goto err_free;
    }


    // 2, 将数据传递给用户
    ret = copy_to_user(buf, temp, count);
    if(ret > 0)
        {
            printk("copy to user error\n");
            ret = -EFAULT;
            goto err_free;

        }
    kfree(temp);

    return count;
err_free:
    kfree(temp);
    return ret;

}

ssize_t at24_i2c_e2prom_write (struct file *filp, const char __user *buf, size_t count, loff_t *fpos)
{
    printk("------%s--------\n", __FUNCTION__);
    int ret;
    //简单的参数判断
    if(count < 0 || count > at24_dev->private->size)
            return -EINVAL;


    //动态的分配一个空间
    char *temp = kzalloc(count, GFP_KERNEL);

    // 1,从用户空间得到数据
    ret = copy_from_user(temp, buf, count);
    if(ret > 0)
    {
        printk("copy_from_user error\n");
        ret = -EFAULT;
        goto err_free;
    }   

    // 2, 将数据写入到硬件
    ret =at24_i2c_send(at24_dev->client,  temp,  count);
    if(ret < 0)
    {
        printk("at24_i2c_send error\n");
        goto err_free;
    }

    kfree(temp);

    return count;

err_free:
    kfree(temp);
    return ret;
}



int at24_i2c_e2prom_close (struct inode *inode, struct file *flip)
{
    printk("------%s--------\n", __FUNCTION__);
    return 0;   
}



const struct file_operations at24_i2c_fops = {
    .open = at24_i2c_e2prom_open,
    .read = at24_i2c_e2prom_read,
    .write = at24_i2c_e2prom_write,
    .release = at24_i2c_e2prom_close,
};

int at24_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)//为什么是id
{
    printk("-------%s-------\n",__FUNCTION__);

    printk("client name = %s\n", id->name);//
    struct e2prom_private *p = (struct e2prom_private *)id->driver_data;

    printk("name descriptoin : %s \n", p->name);
    printk("size : %d \n", p->size);
    printk("verison  : 0x%x \n", p->version);

    at24_dev = kzalloc(sizeof(struct i2c_e2prom_global), GFP_KERNEL);//GFP_KERNEL

    //记录当前匹配之后的i2c client
    at24_dev->client = client;
    at24_dev->private = (struct e2prom_private *)id->driver_data;

    //实现fops
    at24_dev->i2c_misc.fops = &at24_i2c_fops;
    at24_dev->i2c_misc.minor = 199;           //MISC_DYNAMIC_MINOR,
    at24_dev->i2c_misc.name = "i2c_e2prom"; //指定设备节点的名字 /dev/i2c_e2prom

    //注册一个混杂设备驱动,函数里面已经封装了申请设备号,创建设备文件等过程
    misc_register(&at24_dev->i2c_misc);

    // 4,根据实际情况去初始化从设备硬件---at24 上电就可以工作

    return 0;

}

int at24_i2c_remove(struct i2c_client *client)
{
    printk("------%s--------\n", __FUNCTION__);
    misc_deregister(&at24_dev->i2c_misc);
    kfree(at24_dev);

    return 0;
}


struct e2prom_private c02_private = {
    .name = "at240c02 in test LZH",
    .size = 256,
    .version = 0x1234,
};

struct e2prom_private c04_private = {
    .name = "at240c04 in test LZH",
    .size = 512,
    .version = 0x4444,
};

struct e2prom_private c08_private = {
    .name = "at240c08 in test LZH",
    .size = 2014,
    .version = 0x4321,
};

//名字必须与内核中的arch/arm/mach-s5pv210/mach-smdkv210.c中i2c_board_info结构体对应 
const struct i2c_device_id at24_i2c_id_table[] = {
    {"at240c2",(unsigned long)&c02_private},//注意
    {"at240c4",(unsigned long)&c04_private},
    {"at240c8",(unsigned long)&c08_private},

};

//描述了一个从设备的操作方法
struct i2c_driver at24_i2c_drv = {
    .probe = at24_i2c_probe,
    .remove = at24_i2c_remove,
    .driver = {                     //父类,一定要初始化
        .name = "at24_drv_LZH",     //该名字不会用于进行匹配#
    },                              //主要的作用是用来产生一个文件 /sys/bus/i2c/drivers/at24_drv
    .id_table = at24_i2c_id_table,  //用于和i2c client进行匹配的名字列表
};

static int __init at24_drv_init(void)
{
    //注册一个i2c driver
    return i2c_add_driver(&at24_i2c_drv);

}

static void __exit at24_drv_exit(void)
{
    i2c_del_driver(&at24_i2c_drv);
}

module_init(at24_drv_init);
module_exit(at24_drv_exit);

MODULE_LICENSE("GPL");

应用程序:at24_app.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define I2C_SLAVE       0x0703

void print_usage(char *str)
{
    printf("%s r     : read at24c02 addresss 0\n", str);
    printf("%s w val : write at24c02 addresss 0\n", str);
}

int main(int argc,char **argv)
{
    int fd;
    unsigned char val;//字节

    char register_addr = 0x08; /* Device register to access 片内地址*/
    int  res;
    char wbuf[10];
    char rbuf[10];

    if (argc < 2)
    {
        print_usage(argv[0]);
        exit(1);
    }

    /*打开设备文件*/
    fd = open("/dev/i2c_e2prom", O_RDWR);
    if (fd < 0) 
    {
        perror("open failed");
        exit(1);
    }
    if (strcmp(argv[1], "r") == 0)
    {
        if (write(fd, ®ister_addr, 1)!=1) 
        {
            perror("write failed");
            exit(1);
        }

        if (read(fd, rbuf, 5) != 5)
        {
            perror("read failed");
            exit(1);
        } else {
            printf("rbuf[%d] =0x%x\n",0,rbuf[0]);
            printf("rbuf[%d] =0x%x\n",1,rbuf[1]);
            printf("rbuf[%d] =0x%x\n",2,rbuf[2]);
            printf("rbuf[%d] =0x%x\n",3,rbuf[3]);
            printf("rbuf[%d] =0x%x\n",4,rbuf[4]);

        }   

    }
    else if ((strcmp(argv[1], "w") == 0) && (argc == 3))
    {
        //  ./test  w  0x99
        val = strtoul(argv[2], NULL, 0);        

        wbuf[0] = register_addr; // 片内地址0x08
        wbuf[1] = val;
        wbuf[2] = 0x2b;
        wbuf[3] = 0x3b;
        wbuf[4] = 0x4b;
        wbuf[5] = 0x5b;

        if (write(fd, wbuf, 6)!=6) 
        {
            perror("write failed");
            exit(1);
        }             
        printf("write data ok!\n");

    }

    close(fd);
    return 0;
}


在s5pv210开发板上运行的

linux驱动开发之i2c从设备驱动at24c02的编程_第1张图片
linux驱动开发之i2c从设备驱动at24c02的编程_第2张图片
并且写入的数据,掉电不丢失。(因为存储到e2prom里面)
程序文件夹:链接:https://pan.baidu.com/s/1mjFLjoW 密码:5kzf

你可能感兴趣的:(linux驱动开发)