最近的项目里面用到了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
这里就需要在以字节写的方式写入数据时,先要发送两个八位的地址字节,然后再跟着一个八位的数据字节,而且
这里与之前的几种不同之处是没有了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;
}
#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");