linux at24cxxx驱动编写【全地址范围】

最近的项目里面用到了IIC芯片存储,由于数据量比较多,所以使用了大点容量的EEPROM芯片,型号为AT24C512,中发买的6元一片,直插的。贴片会便宜一半。

之前考虑使用AT24C256,驱动都写了,但是方案有改动,发现容量不够使用了,而且看芯片手册发现256与512的通信协议并不完全相同。AT系列从02-256的都是一字节地址+一字节数据,所以如果驱动只是指定了msg[0].addr  = at24cxx_client->addr;的方式,则只能最多访问到256个地址范围。如果想访问全地址范围就需要加上页偏移地址,如下:msg[0].addr  = at24cxx_client->addr+dev_addr[0]%4;//基址0x50 + A1 A0;

linux at24cxxx驱动编写【全地址范围】_第1张图片

这里有四种芯片的地址表示,其中a8,a9,a10即对应上面的A1,A0;A1和A0,共可以组成四页的访问,这样就可以通过应用程序给驱动传入两个表示地址的buf来访问所有的地址范围了,低八位用来定义某一页的具体地址范围从0-256,高八位【其实只用了两位来保存A1,A0的值】来指定访问的是四页中的哪一页。不同的片子使用不同的位数,举例来说,AT24C08:

它的存储阵列地址字的地址使用两位a9,a8,来扩充,因为08的最大可访问地址为8*1024/8=1024个地址范围,而我们的驱动那个read或者write函数的可传入buf是只能定义成unsigned char型的变量,所以单单使用那个buf的话最多只能访问到256这个地址范围,这时可以通过将应用程序里面的buf定义成这种形式的:

 buf[0]=i/256; //保存高八位中的两位表示具体哪一页的数据
 buf[1]=i%256; //保存低八位某一页具体的地址
 buf[2]=i%2; //要写入的数据,可随意

然后驱动里面就写成这个样子:

	 /*使驱动程序能写e2prom全部的页*/ 
	 struct i2c_msg msg[1]; 
	 unsigned char dev_addr[3];/*第一二个元素是数据地址并认为数组的0为高位1为低位,第三个元素是数据值*/ 
	 /*dev_addr数组的0元素为页数,1元素为该页中的地址,2元素为写入的数据*/ 
	 int ret; 
	 if(size!=3){ 
	  printk("error param:write size is 3\n"); 
	  return -1; 
	 } 
	 copy_from_user(dev_addr,buf,3);


	 /* 数据传输:源地址,目的地址,数据长度 */ 
	 msg[0].addr  = at24cxx_client->addr+dev_addr[0]%4;//基址0x50 + P1 P0
	 msg[0].len   = 2;                //length 地址+数据 长度=2byte 根据这个值决定buf读几次
	 msg[0].buf   = &dev_addr[1];     /*source第一个元素是数据地址,第二个元素是数据值*/ 
	 msg[0].flags = 0;                //write flag

以上是AT24c256或者比他容量小的EEPROM访问的方式,但是如果是512的话就有不同了,通过手册可以看到,

linux at24cxxx驱动编写【全地址范围】_第2张图片

这里就需要在以字节写的方式写入数据时,先要发送两个八位的地址字节,然后再跟着一个八位的数据字节,而且


linux at24cxxx驱动编写【全地址范围】_第3张图片

这里与之前的几种不同之处是没有了a8,a9,a10,只有A0,A1,用来扩展芯片个数,所以就没有了上面的所谓页偏移的概念,这样驱动就不同了,如下:

	 /* 数据传输:源地址,目的地址,数据长度 */ 
	 msg[0].addr  = at24c512_client->addr;	//基址0x50
	 msg[0].len   = 3;                				//length 地址2+数据1 长度=3byte 根据这个值决定buf读几次
	 msg[0].buf   = &dev_addr[0];     			/*source第一和第二个个元素是数据地址,第三个元素是数据值*/ 
	 msg[0].flags = 0;                				//write flag
这里不再需要加进来什么页偏移了,直接将那个len改成3个数据,buf里面放三个数据的首地址即可,这样就可以通过应用程序传进来三个buf即可,分别存放连续的两个地址,加上一个数据位就行了。

-----------------------------------------------------------------------------------------------------------------------------

以上是原理分析,下面上代码:

at24c08test.c

#include  
#include  
#include  
#include  
int main(int argc,char **argv) 
{ 
	 /*测试读写at24c08的全部页*/ 
	 int fd; 
	 unsigned char buf[3]={3,0xfe,0x01}; 
	 unsigned short read_addr=1000; 
	 int i; 
	 fd = open("/dev/at24cxx",O_RDWR); 
	 if(fd<0) 
	 { 
		  printf("open error\n"); 
		  return -1; 
	 } 
	 for(i=0;i<1024;i++){ 
		  if(i%4==0) 
		   printf("\n"); 
		  buf[0]=i/256; 
		  buf[1]=i%256; 
		  buf[2]=i%2; 
		  if(2==write(fd,buf,3)) 
		  { 
		   	printf("write %d : data 0x%2x__",i,buf[2]); 
		  }

	 } 
	  
	 for(i=0;i<1024;i++){   
		  read_addr = i; 
		  if(1==read(fd,&read_addr,2)) 
		  {  
			   printf("read %d : data:0x%2x__\n",i,read_addr&0xff); 
		  } 
	 } 

	 close(fd); 
#endif 
 return 0; 
} 
at24c08.c

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

static int gq_at24cxx_detect(struct i2c_adapter *adapter, int addrss, int kind);


static unsigned short normal_gq[] = {I2C_CLIENT_END}; 
static unsigned short ignore_gq[] = {ANY_I2C_BUS,0x50,I2C_CLIENT_END}; 
static struct i2c_client_address_data gq_address_data = { 
 .normal_i2c = normal_gq, 
 .probe      = ignore_gq, 
 .ignore     = ignore_gq, 
 //.forces     = , 
}; 
static struct i2c_client *at24cxx_client;

static struct class *cls; 
static struct class_device *cls_dev; 
static int major;

static ssize_t at24cxx_read(struct file *filp, char __user *buf, size_t size, loff_t *offset) 
{ 
	/*使驱动程序能读e2prom全部的页*/ 
	struct i2c_msg msg[2]; 
	unsigned char write_buf[2];/*数组的0元素为该大页中的地址,1元素为大页数*/ 
	unsigned char read_buf; 
	int ret; 
	if(size!=2){ 
		printk("error param:write size is 2\n"); 
		return -1; 
	}  
	copy_from_user(write_buf,buf,2);
	

	/* 数据传输:源地址,目的地址,数据长度 */ 
	/* 先把要读的数据地址写入 */ 
	msg[0].addr  = at24cxx_client->addr + write_buf[1]%4;//distination 
	msg[0].len   = 1;                    //length 地址长度=1byte 
	msg[0].buf   = &write_buf[0];            //source 
	msg[0].flags = 0;                    //write flag 
	/* 再把数据读出 */ 
	msg[1].addr  = at24cxx_client->addr + write_buf[1]%4;//source 
	msg[1].len   = 1;                    //length 数据长度=1byte 
	msg[1].buf   = &read_buf;           //distination 
	msg[1].flags = I2C_M_RD;             //read flag 
	printk("write_buf[0] %d,write_buf[1] %d\n",write_buf[0],write_buf[1]);
	ret = i2c_transfer(at24cxx_client->adapter, msg, 2); 
	if(ret==2){ 
	copy_to_user(buf,&read_buf,1); 
  	return 1; 
 	}else 
	return -1; 
} 
static ssize_t at24cxx_write(struct file *filp, char __user *buf, size_t size, loff_t *offset) 
{ 
	 /*使驱动程序能写e2prom全部的页*/ 
	 struct i2c_msg msg[1]; 
	 unsigned char dev_addr[3];/*第一二个元素是数据地址并认为数组的0为高位1为低位,第三个元素是数据值*/ 
	 /*dev_addr数组的0元素为页数,1元素为该页中的地址,2元素为写入的数据*/ 
	 int ret; 
	 if(size!=3){ 
	  printk("error param:write size is 3\n"); 
	  return -1; 
	 } 
	 copy_from_user(dev_addr,buf,3);

	 /* 数据传输:源地址,目的地址,数据长度 */ 
	 msg[0].addr  = at24cxx_client->addr+dev_addr[0]%4;//基址0x50 + P1 P0
	 msg[0].len   = 2;                //length 地址+数据 长度=2byte 根据这个值决定buf读几次
	 msg[0].buf   = &dev_addr[1];     /*source第一个元素是数据地址,第二个元素是数据值*/ 
	 msg[0].flags = 0;                //write flag

	 ret = i2c_transfer(at24cxx_client->adapter, msg, 1); 
	 if(ret==1){ 
	   
	  return 2; 
	 } 
	 else{ 
	  printk("error:i2c_transfer failed!\n"); 
	  return -1; 
	 } 

}

static struct file_operations at24cxx_fops = { 
	 .read  = at24cxx_read, 
	 .write = at24cxx_write, 
	 .owner = THIS_MODULE, 
}; 
static int gq_at24cxx_attach_adapter(struct i2c_adapter *adapter) 
{ 
	 printk("attach_adapter\n"); 
	 return i2c_probe(adapter, &gq_address_data, gq_at24cxx_detect); 
} 
static int gq_at24cxx_detach_client(struct i2c_adapter *adapt) 
{ 
	 printk("detach_client\n"); 
	  
	 class_device_destroy(cls,MKDEV(major,0)); 
	 class_destroy(cls); 
	 unregister_chrdev(major,"at24cxx");

	 /* detach the client */ 
	 i2c_detach_client(at24cxx_client); 
	 kfree(at24cxx_client); 
	 return 0; 
}


static struct i2c_driver gq_at24cxx_driver = { 
	 .driver ={ 
	  .name = "at24cxx", 
	 }, 
	 .id =  -1, 
	 .attach_adapter = gq_at24cxx_attach_adapter, 
	 .detach_client  = gq_at24cxx_detach_client, 
};

static int gq_at24cxx_detect(struct i2c_adapter *adapter, int addrss, int kind) 
{ 
	 printk("detect the at24c08 device 0x%2x\n",addrss); 
	 at24cxx_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); 
	 if(at24cxx_client==NULL) {
	 	printk("alloc client failed\n"); 
	 	return -1; }

	 /* 设置i2c_client */ 
	 at24cxx_client->adapter = adapter; 
	 at24cxx_client->addr    = addrss; 
	 at24cxx_client->driver  = &gq_at24cxx_driver; 
	 at24cxx_client->flags   = 0; 
	 strcpy(at24cxx_client->name, "at24cxx");

	 /* 通知协议层已经生成一个client,并attach */ 
	 i2c_attach_client(at24cxx_client);

	 /* 下来就提供字符设备接口 */ 
	 major = register_chrdev(0, "at24cxx", &at24cxx_fops); 
	 cls = class_create(THIS_MODULE, "gq_at24c08"); 
	 cls_dev = class_device_create(cls, NULL, MKDEV(major,0), NULL, "at24cxx"); 
	 return 0; 
}

static int gq_at24cxx_init(void) 
{ 
 	i2c_add_driver(&gq_at24cxx_driver); 
 	return 0; 
}

static void gq_at24cxx_exit(void) 
{ 
	i2c_del_driver(&gq_at24cxx_driver); 
}

module_init(gq_at24cxx_init); 
module_exit(gq_at24cxx_exit); 
MODULE_LICENSE("GPL"); 

----------------------------------------------------------------------------------------------------------------------------------------------

at24c512test.c

#include 
#include 
#include 
#include 
#include 
#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 ret;
	int eepromfd;
	unsigned int i;
	unsigned char buf[2];
	
	if ((argc != 3) && (argc != 4))
	{
		print_usage(argv[0]);
		return -1;
	}
	if((strcmp(argv[1],"w")==0)&&(argc !=4))
	{
		print_usage(argv[0]);
		return -1;
	}

        eepromfd = open("/dev/at24c512", O_RDWR);
        if (eepromfd < 0) {
            printf("open eeprom device error\n");
            return -1;
        }		
/*
         //对512进行数据初始化
	for(i=0;i<2048;i++){ 
		buf[0]=i/256; //地址高位
		buf[1]=i%256; //地址低位
		buf[2]=0;
		ret = write(eepromfd, buf, 3);
		if(-1==ret)
		printf("write error!\n");
	 } 
*/
	if (strcmp(argv[1], "r") == 0)  
	{
		i = strtoul(argv[2],NULL,0);
		buf[0] = i/256;   //高位地址
		buf[1] = i%256; //低位地址
		//printf("buf[0]=%d buf[1]=%d\n",buf[0],buf[1]);		
		read(eepromfd, buf, 2);
		printf("data: %d, 0x%2x\n", buf[0],buf[0]);
	}
	else if (strcmp(argv[1], "w") == 0)
	{
		i = strtoul(argv[2],NULL,0);
		buf[0] = i/256;   //高位地址
		buf[1] = i%256; //低位地址
		buf[2] = strtoul(argv[3],NULL,0);//数据值
		ret = write(eepromfd, buf, 3);
		if(-1==ret)
			printf("write error!\n");
		
	}
	else
	{
		print_usage(argv[0]);
		return -1;
	}
	
	return 0;
}

at24c512.c

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

static int at24c512_detect(struct i2c_adapter *adapter, int addrss, int kind);


static unsigned short normal[] = {I2C_CLIENT_END}; 
static unsigned short ignore[] = {ANY_I2C_BUS,0x50,I2C_CLIENT_END}; 
static struct i2c_client_address_data address_data = { 
	.normal_i2c = normal, 
	.probe         = ignore, 
	.ignore        = ignore, 
}; 
static struct i2c_client *at24c512_client;

static struct class *cls; 
static struct class_device *cls_dev; 
static int major;

static ssize_t at24c512_read(struct file *filp, char __user *buf, size_t size, loff_t *offset) 
{ 
	/*使驱动程序能读e2prom全部的页*/ 
	struct i2c_msg msg[2]; 
	unsigned char write_buf[2];/*数组的0元素为该地址的高位,1元素为该地址的低位0x0000-0xFFFF*/ 
	unsigned char read_buf; 
	int ret; 
	if(size!=2){ 
		printk("error param:write size is 2\n"); 
		return -1; 
	}  
	if(copy_from_user(write_buf,buf,2))
		printk("when read copy_from_user error!\n");	

	/* 数据传输:源地址,目的地址,数据长度 */ 
	/* 先把要读的数据地址写入 */ 
	msg[0].addr  = at24c512_client->addr;	//distination基址 
	msg[0].len   = 2;                    		  	//length 地址长度=2byte 
	msg[0].buf   = &write_buf[0];            		//source 连续两个地址长度
	msg[0].flags = 0;                    			//write flag 
	/* 再把数据读出 */ 
	msg[1].addr  = at24c512_client->addr;	//source 
	msg[1].len   = 1;                    			//length 数据长度=1byte 
	msg[1].buf   = &read_buf;           			//distination 
	msg[1].flags = I2C_M_RD;             		//read flag 
	ret = i2c_transfer(at24c512_client->adapter, msg, 2); 
	if(ret==2){ 
		if(copy_to_user(buf,&read_buf,1))
			printk("when read copy_to_user error!\n");
		return 1; 
 	} else 
	return -1; 
} 
static ssize_t at24c512_write(struct file *filp, char __user *buf, size_t size, loff_t *offset) 
{ 
	 /*使驱动程序能写e2prom全部的页*/ 
	 struct i2c_msg msg[1]; 
	 unsigned char dev_addr[3];/*第一二个元素是数据地址并认为数组的0为高位1为低位,第三个元素是数据值*/ 
	 int ret; 
	 if(size!=3){ 
	  printk("error param:write size is 3\n"); 
	  return -1; 
	 } 
	 if(copy_from_user(dev_addr,buf,3))
	 	printk("when write copy_from_user error!\n");

	 /* 数据传输:源地址,目的地址,数据长度 */ 
	 msg[0].addr  = at24c512_client->addr;	//基址0x50
	 msg[0].len   = 3;                				//length 地址2+数据1 长度=3byte 根据这个值决定buf读几次
	 msg[0].buf   = &dev_addr[0];     			/*source第一和第二个个元素是数据地址,第三个元素是数据值*/ 
	 msg[0].flags = 0;                				//write flag

	 ret = i2c_transfer(at24c512_client->adapter, msg, 1); 
	 if(ret==1){ 
	   
	  return 2; 
	 }else{ 
		  printk("error:i2c_transfer failed!\n"); 
		  return -1; 
	 } 
}

static struct file_operations at24c512_fops = { 
	 .read  = at24c512_read, 
	 .write = at24c512_write, 
	 .owner = THIS_MODULE, 
}; 
static int at24c512_attach_adapter(struct i2c_adapter *adapter) 
{ 
	 printk("attach_adapter\n"); 
	 return i2c_probe(adapter, &address_data, at24c512_detect); 
} 
static int at24c512_detach_client(struct i2c_adapter *adapt) 
{ 
	 printk("detach_client\n"); 
	  
	 class_device_destroy(cls,MKDEV(major,0)); 
	 class_destroy(cls); 
	 unregister_chrdev(major,"at24c512");

	 /* detach the client */ 
	 i2c_detach_client(at24c512_client); 
	 kfree(at24c512_client); 
	 return 0; 
}


static struct i2c_driver at24c512_driver = { 
	 .driver ={ 
	  .name = "at24c512", 
	 }, 
	 .id =  -1, 
	 .attach_adapter = at24c512_attach_adapter, 
	 .detach_client  = at24c512_detach_client, 
};

static int at24c512_detect(struct i2c_adapter *adapter, int addrss, int kind) 
{ 
	 printk("detect the at24c512 device 0x%2x\n",addrss); 
	 at24c512_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); 
	 if(at24c512_client==NULL) {
	 	printk("alloc client failed\n"); 
	 	return -1; }

	 /* 设置i2c_client */ 
	 at24c512_client->adapter = adapter; 
	 at24c512_client->addr    = addrss; 
	 at24c512_client->driver  = &at24c512_driver; 
	 at24c512_client->flags   = 0; 
	 strcpy(at24c512_client->name, "at24c512");

	 /* 通知协议层已经生成一个client,并attach */ 
	 i2c_attach_client(at24c512_client);

	 /* 下来就提供字符设备接口 */ 
	 major = register_chrdev(0, "at24c512", &at24c512_fops); 
	 cls = class_create(THIS_MODULE, "at24c512"); 
	 cls_dev = class_device_create(cls, NULL, MKDEV(major,0), NULL, "at24c512"); 
	 return 0; 
}

static int at24c512_init(void) 
{ 
 	i2c_add_driver(&at24c512_driver); 
 	return 0; 
}

static void at24c512_exit(void) 
{ 
	i2c_del_driver(&at24c512_driver); 
}

module_init(at24c512_init); 
module_exit(at24c512_exit); 
MODULE_LICENSE("GPL"); 




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