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;


这里有四种芯片的地址表示,其中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的话就有不同了,通过手册可以看到,


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



这里与之前的几种不同之处是没有了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 <stdio.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
#include <fcntl.h> 
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 <linux/kernel.h> 
#include <linux/module.h> 
#include <linux/init.h> 
#include <linux/i2c.h>  
#include <linux/fs.h> 
#include <linux/uaccess.h> 
#include <linux/types.h>

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 <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 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 <linux/kernel.h> 
#include <linux/module.h> 
#include <linux/init.h> 
#include <linux/i2c.h>  
#include <linux/fs.h> 
#include <linux/uaccess.h> 
#include <linux/types.h>

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"); 




  • 0
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Linux at24c512是一个在Linux操作系统上使用的EEPROM设备驱动程序,可以用于读写AT24C512系列芯片。AT24C512是一款串行EEPROM芯片,容量为512Kbit,可以存储大约64KB的数据。 在Linux系统中,驱动程序被用于管理和控制硬件设备。AT24C512驱动程序允许用户通过Linux内核提供的接口来读写AT24C512芯片中的数据。这样,用户就可以在Linux系统中方便地使用AT24C512芯片进行数据存储和读取操作。 通过使用AT24C512驱动程序,用户可以通过简单的Linux命令或编程接口来读写AT24C512芯片中的数据。这为用户提供了一个灵活的方式来进行数据存储和管理。用户可以选择使用命令行工具,如dd或i2c-tools,或编写自己的应用程序来与AT24C512芯片进行交互。 与其他Linux驱动程序类似,AT24C512驱动程序也需要与相关的硬件设备连接并配置正确的引脚连接。通过在Linux内核中加载AT24C512驱动程序,操作系统就可以正确地识别和控制该硬件设备。 总之,Linux at24c512是一个用于管理和控制AT24C512芯片的设备驱动程序。通过使用这个驱动程序,用户可以在Linux系统中方便地读写AT24C512芯片中的数据。这为用户提供了一种灵活的方式来进行数据存储和管理。 ### 回答2: AT24C512是一种EEPROM(Electrically Erasable Programmable Read-Only Memory)芯片,它具有512K位的存储容量。在Linux操作系统中,可以使用相应的驱动程序来读取和写入AT24C512芯片中的数据。 在使用AT24C512芯片之前,需要先加载相应的内核模块。Linux系统已经提供了at24驱动程序,可以通过modprobe命令加载该驱动模块。加载成功后,系统会自动创建一个与AT24C512芯片对应的设备节点,通常是/dev/at24。 通过该设备节点,可以使用工具或者编写相应的程序来访问和操作AT24C512芯片。常用的工具有i2cset和i2cget命令,可以通过这两个命令向指定地址写入数据或者读取数据。具体命令格式可以通过man命令查看。 在编写程序时,可以使用Linux提供的I2C接口库如libi2c-dev来实现对AT24C512芯片的读写操作。首先需要打开I2C总线,在打开设备的过程中需要指定设备地址和总线号。打开成功后,可以使用相应的读写函数来操作芯片的数据。 例如,在C语言中可以使用open函数打开设备,使用ioctl函数来设置设备地址和总线号。然后就可以使用read和write函数来读写芯片的数据。读取时需要指定读取的起始地址和读取的长度,写入时需要指定写入的起始地址和写入的数据。 总体来说,使用Linux操作系统可以方便地访问和操作AT24C512芯片。无论是使用工具还是编写程序,都可以通过相应的命令或者API来读取和写入芯片中的数据。 ### 回答3: Linux at24c512是指Linux操作系统中使用的一种支持at24c512芯片驱动程序。at24c512是一款24CXXX系列的串行EEPROM芯片,容量为512K。在Linux中,通过at24c512驱动程序,可以通过I2C总线与at24c512芯片进行通信,实现对芯片的读取和写入操作。 在Linux系统中,我们可以使用命令`i2cdetect`来检测I2C总线上是否连接有at24c512设备。如果检测到设备的地址,就说明我们可以通过I2C总线与at24c512进行通信。 在Linux操作系统中,通常需要加载at24c512驱动模块,以便能够使用该芯片。加载驱动模块的命令是`modprobe at24`。加载成功后,系统会根据硬件信息自动识别和分配设备节点。 一旦驱动加载成功,并且设备节点创建完成,我们就可以在用户空间操作at24c512芯片了。通过查看文件系统的`/sys/class/i2c-dev`目录,我们可以找到at24c512设备节点的信息。 在用户空间,我们可以使用`i2cget`和`i2cset`命令来读取和写入at24c512芯片的数据。例如,使用`i2cget -y 1 0x50 0x00`命令可以读取at24c512芯片地址0x00处的数据。 除了通过命令行工具,还可以通过C或Python等编程语言编写程序,使用相应的库函数来进行at24c512芯片的操作。 总之,Linux at24c512是指Linux操作系统中使用的支持at24c512芯片驱动程序,通过它可以实现对at24c512芯片的读取和写入操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值