您的位置:首页 > 运维架构 > Linux

at24c08 E2PROM的I2C设备驱动实例——基于mini2440

2017-06-02 21:19 351 查看
AT24C08提供8192位的串行电可擦写可编程只读存储器(EEPROM),16字节页写模式。
内核版本:2.6.32。

实验所用开发板mini2440。

第一步:在arch/arm/mach-s3c2440/mach-mini2440.c添加E2PROM的板级信息

1、添加头文件:

#include <linux/i2c.h>

#include <linux/i2c/at24.h>

2、添加平台板级信息

static struct at24_platform_data at24_platdata = {

        .byte_len = 8192,//字节大小

        .page_size = 16,//页数大小

};


static struct i2c_board_info mini2440_i2c_devices[] = {

        {

                I2C_BOARD_INFO("24c08",0x50),//第一个参数是硬件名称,驱动的名字匹配不上的时候,会和这个匹配,第二个参数是IIC硬件地址。

                .platform_data = &at24_platdata,

        }

};


3、在mini2440_machine_init(void)函数中添加

i2c_register_board_info(0,mini2440_i2c_devices,ARRAY_SIZE(mini2440_i2c_devices));//注册板级信息


4、编译内核并重新烧写开发板的zImage

make zImage


第二步:编写驱动代码

#include<linux/init.h>

#include<linux/module.h>

#include<linux/i2c.h>

#include<linux/fs.h>

#include<linux/device.h>

#include<linux/slab.h>

#include<asm/uaccess.h>


#define E2PROM_MAJOR 250//可以通过cat /proc/devices查看哪些设备号没有被使用


MODULE_LICENSE("GPL");

struct e2prom_device{

struct i2c_client *at24c02_client;

struct class   *at24c02_class;

struct device     *at24c02_device;

};


struct e2prom_device *e2prom_dev;

struct i2c_device_id e2prom_table[]={

[0]={

.name="24c02",

.driver_data=0,

},

[1]={

.name="24c08",

.driver_data=0,

},

};


static int i2c_read_byte(char *buf,int count)

{

int ret=0;

struct i2c_msg msg;


msg.addr=e2prom_dev->at24c02_client->addr;//0x05

msg.flags=1;//1 代表读     0 代表写

msg.len=count;

msg.buf=buf;


ret=i2c_transfer(e2prom_dev->at24c02_client->adapter,&msg,1);

if(ret<0){

printk("i2c transfer failed!\n");

return-EINVAL;

}

return ret;

}



static int i2c_write_byte(char *buf,int count)

{

int ret=0;

struct i2c_msg msg;


msg.addr=e2prom_dev->at24c02_client->addr;//0x05

msg.flags=0;//写

msg.len=count;

msg.buf=buf;


ret=i2c_transfer(e2prom_dev->at24c02_client->adapter,&msg,1);

if(ret<0){

printk("i2c transfer failed!\n");

return-EINVAL;

}

return ret;

}


static int e2prom_open(struct inode *inode, struct file *file)

{

return 0;

}


static size_t e2prom_read(struct file *filep, char __user *buf, size_t size, 

loff_t *ppos)

{

int ret = 0;

char *tmp;

tmp = kmalloc(size,GFP_KERNEL);

if(tmp==NULL){

printk("malloc failed!\n");

return -ENOMEM;

}


ret = i2c_read_byte(tmp,size);

if(ret<0){

printk("read byte failed!\n");

ret = -EINVAL;

goto err0;

}


ret = copy_to_user(buf,tmp,size);

if(ret){

printk("copy data failed!\n");

ret =-EINVAL;

goto err0;

}

kfree(tmp);

return size;


err0:

    kfree(tmp);

    return ret;

}


static ssize_t e2prom_write(struct file *filep, const char __user *buf, size_t size,

  loff_t *ppos)

{

int ret = 0;

char *tmp;

tmp = kmalloc(size,GFP_KERNEL);

if(tmp == NULL){

printk("malloc failed!\n");

return -ENOMEM;

goto err0;

}


ret = copy_from_user(tmp,buf,size);

if(ret){

printk("copy data failed!\n");

ret =-EFAULT;

goto err0;

}


ret = i2c_write_byte(tmp,size);

if(ret<0){

printk("write byte failed!\n");

ret = -EINVAL;

goto err0;

}


kfree(tmp);

return size;


err0:

kfree(tmp);

return ret;

}


struct file_operations e2prom_fops = {

.owner=THIS_MODULE,

.open=e2prom_open,

.read   =e2prom_read,

.write  =e2prom_write,

};


static int e2prom_probe(struct i2c_client *client,

       const struct i2c_device_id *id)

{

int ret;

printk("enter probe!\n");

e2prom_dev = kmalloc(sizeof(struct e2prom_device),GFP_KERNEL);

if(e2prom_dev == NULL){

printk("malloc failed\n");

return -ENOMEM;

}


e2prom_dev->at24c02_client = client;


/*给用户提供接口*/

ret = register_chrdev(E2PROM_MAJOR,"e2prom_module",&e2prom_fops);

if(ret < 0)

{

printk("register major failded\n");

ret =-EINVAL;

goto err0;

}


/*创建设备类*/

e2prom_dev->at24c02_class= class_create(THIS_MODULE,"e2prom_class");

if(IS_ERR(e2prom_dev->at24c02_class)){

printk("class create failed\n");

ret =PTR_ERR(e2prom_dev->at24c02_client);

goto err1;

}


/*创建设备文件*/

e2prom_dev->at24c02_device=device_create(e2prom_dev->at24c02_class,NULL,MKDEV(E2PROM_MAJOR,0),NULL,"at24c08");

if(IS_ERR(e2prom_dev->at24c02_device)){

printk("class create failed\n");

ret =PTR_ERR(e2prom_dev->at24c02_device);

goto err1;

}

return 0;

err1:

unregister_chrdev(E2PROM_MAJOR,"e2prom_module");

err0:

kfree(e2prom_dev);


return ret;

}


static int e2prom_remove(struct i2c_client *client)

{

unregister_chrdev(E2PROM_MAJOR,"e2prom_module");

device_destroy(e2prom_dev->at24c02_class, MKDEV(E2PROM_MAJOR,0));

class_destroy(e2prom_dev->at24c02_class);

kfree(e2prom_dev);

return 0;


}

/*构建一个struct i2c_driver结构体*/

static struct i2c_driver e2prom_driver={

.probe=e2prom_probe,

.remove=e2prom_remove,

.id_table   =e2prom_table,//记录此驱动服务于哪些设备

.driver={

.name="e2prom",//

},

};


static int __init e2prom_init(void)

{

/*注册平台特定驱动

*1)将i2c驱动加入i2c总线的驱动链表

*2)搜索i2c总线的设备链表,每搜索一个都会调用i2c总线的match

*实现client->name与id->name进行匹配,匹配成功就会调用

*i2c驱动中的probe函数

*/

i2c_add_driver(&e2prom_driver);

return 0;

}


static void __exit e2prom_exit(void)

{

i2c_del_driver(&e2prom_driver);

}


module_init(e2prom_init);

module_exit(e2prom_exit);


第三步:编写测试程序

读写的操作方式采用Byte Write方式读,和Random Read方式写。
其中Random Read方式需要对写入的地址进行确认。也就是读操作的时候,需要写一次,再读一次。具体步骤如下图所示:



 

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <stdio.h>

#include <errno.h>

#include <unistd.h>

#include <stdlib.h>

#include <string.h>


/*

    ./i2c_test w data    

    ./i2c_test r 

*/

int main(int argc,char **argv)

{

   int fd;

   char register_addr = 0x78;//要写入的地址

   char wbuf[2];//写缓冲区

   char rbuf[2];//读缓冲区

   //打开设备

   fd = open("/dev/at24c08", O_RDWR);

   if (fd < 0) 

   {

       perror("open error\n");

       exit(1);

   }


   if(strcmp(argv[1],"w") == 0)

   {//写操作
Byte Write
       wbuf[0] = register_addr;

       wbuf[1] = atoi(argv[2]);

       
/向
register_addr地址中写入数据,因为设备地址已经在板级信息中确定了,所以不需要通过ioctl设置设备地址*/
   if(res = write(fd, wbuf, 2) != 2) 

       {

           perror("write error\n"); 

           exit(1);

       }


   else 

   {
//读操作 Random Read
       if(write(fd, ®ister_addr, 1) != 1) //
验证是否从
register_addr地址读出

           perror("write error\n"); 

          exit(1);

       }

       if(read(fd, &rbuf, 1) != 1) 

   {

      perror("read error\n");

           exit(1);

       } 

       else 

       {

           printf("rbuf[0] = %d\n",rbuf[0]);

       }  

}
   return 0;

}


第四步:验证结果



 



 测试成功!
 

 

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  内核 c语言 linux 驱动