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 Writewbuf[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 Readif(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;
}
第四步:验证结果
测试成功!