Linux驱动开发-编写(EEPROM)AT24C02驱动

【Linux驱动开发-编写(EEPROM)AT24C02驱动】胸怀万里世界, 放眼无限未来。这篇文章主要讲述Linux驱动开发-编写(EEPROM)AT24C02驱动相关的知识,希望能为你提供帮助。
1. 前言AT24C02是IIC接口的EEPROM存储芯片,这颗芯片非常经典,百度搜索可以找到非常多的资料,大多都是51、STM32单片机的示例代码,大多采用模拟时序、裸机系统运行。当前文章介绍在Linux系统里如何编写AT24C02的驱动,并且在应用层完成驱动读写测试,将AT24C02的存储空间映射成文件,在应用层,用户可以直接将AT24C02当做一个普通文件的形式进行读写,偏移文件指针;在Linux内核里有一套标准的IIC子系统框架专门读写IIC接口设备,采用平台设备模型框架,编写驱动非常方便。
当前开发板采用友善之臂的Tiny4412,CPU是三星的EXYNOS4412,4412是三星的第一款四核处理器,主频是1.5GHZ,稳定频率是1.4GHZ。
2. 硬件原理图当前的开发板上自带了一颗EEPROM存储芯片(具体型号是24AA025E48,代码与AT24C02一样的),原理图如下:

Linux驱动开发-编写(EEPROM)AT24C02驱动

文章图片

Linux驱动开发-编写(EEPROM)AT24C02驱动

文章图片

自带的内核里没有内置EEPROM的驱动:
Linux驱动开发-编写(EEPROM)AT24C02驱动

文章图片

存储芯片的数据手册介绍:
Linux驱动开发-编写(EEPROM)AT24C02驱动

文章图片

设备地址:
Linux驱动开发-编写(EEPROM)AT24C02驱动

文章图片

写字节、页写时序:
Linux驱动开发-编写(EEPROM)AT24C02驱动

文章图片

读数据时序:
Linux驱动开发-编写(EEPROM)AT24C02驱动

文章图片

3. 示例代码 3.1 EEPROM驱动端代码
#include < linux/kernel.h> #include < linux/module.h> #include < linux/platform_device.h> #include < linux/i2c.h> #include < linux/delay.h> #include < linux/interrupt.h> #include < asm/uaccess.h> #include < linux/init.h> #include < linux/module.h> #include < linux/fs.h> #include < linux/cdev.h> #include < linux/device.h> #include < linux/slab.h> #include < asm/uaccess.h> #include < linux/miscdevice.h> static struct work_struct work; static struct i2c_client *eeprom_client; #define MAX_SIZE 255//EEPROM大小 #define EEPROM_PAGE16 //页字节大小static u8 eeprom_buff[255]; static int tiny4412_open(struct inode *inode, struct file *file)printk("tiny4412_open--> ok\\n"); return 0; static ssize_t tiny4412_read(struct file *file, char __user *buf, size_t size, loff_t *seek)unsigned long err; //判断位置是否超出范围 if(*seek+size> MAX_SIZE)size=MAX_SIZE-*seek; //读取数据 i2c_smbus_read_i2c_block_data(eeprom_client,*seek,size,eeprom_buff); err=copy_to_user(buf,eeprom_buff,size); if(err!=0)return -1; *seek+=size; return size; static ssize_t tiny4412_write(struct file *file, const char __user *buf, size_t size, loff_t *seek)size_t write_ok_cnt=0; unsigned long err; err=copy_from_user(eeprom_buff,buf,size); if(err!=0)return -1; //判断位置是否超出范围 if(*seek+size> MAX_SIZE)size=MAX_SIZE-*seek; int write_byte=0; u8 *write_p=eeprom_buff; while(1)if(size> EEPROM_PAGE)write_byte=EEPROM_PAGE; size-=EEPROM_PAGE; elsewrite_byte=size; //写数据 i2c_smbus_write_i2c_block_data(eeprom_client,*seek,write_byte,write_p); *seek+=write_byte; write_p+=write_byte; write_ok_cnt+=write_byte; //记录写成功的字节数 //等待写完成 msleep(10); if(write_byte==size)break; //写完毕return write_ok_cnt; /* filp:待操作的设备文件file结构体指针 off:待操作的定位偏移值(可正可负) whence:待操作的定位起始位置 返回:返回移位后的新文件读、写位置,并且新位置总为正值 定位起始位置 SEEK_SET:0,表示文件开头 SEEK_CUR:1,表示当前位置 SEEK_END:2,表示文件尾 */ static loff_t tiny4412_llseek(struct file *filp, loff_t offset, int whence)loff_t newpos = 0; switch(whence)case SEEK_SET: newpos = offset; break; case SEEK_CUR: newpos = filp-> f_pos + offset; break; case SEEK_END: if(MAX_SIZE+offset> =MAX_SIZE)newpos=MAX_SIZE; elsenewpos = MAX_SIZE + offset; break; default: return -EINVAL; //无效的参数filp-> f_pos = newpos; return newpos; static int tiny4412_release(struct inode *inode, struct file *file)printk("tiny4412_release--> ok\\n"); return 0; static struct file_operations fops=.open=tiny4412_open, .read=tiny4412_read, .write=tiny4412_write, .release=tiny4412_release, .llseek=tiny4412_llseek ; /* Linux内核管理驱动---设备号 设备号是一个unsigned int 的变量--32位。 设备号=主设备号+次设备号 */ static struct miscdevice misc=.minor = MISC_DYNAMIC_MINOR,/*次设备号填255表示自动分配主设备号固定为10*/ .name = "tiny4412_eeprom",/*/dev目录下文件名称*/ .fops = & fops, /*文件操作接口*/ ; static int tiny4412_probe(struct i2c_client *client, const struct i2c_device_id *device_id)printk("probe调用成功:%#X\\n",client-> addr); eeprom_client=client; /*1. 杂项设备的注册函数*/ misc_register(& misc); return 0; static int tiny4412_remove(struct i2c_client *client)/*2. 杂项设备的注销函数*/ misc_deregister(& misc); printk("remove调用成功.\\n"); return 0; static struct i2c_device_id id_table[]="tiny4412_eeprom",0,; static struct i2c_driver drv=.probe=tiny4412_probe, .remove=tiny4412_remove, .driver=.name="eeprom_iic" , .id_table=id_table ; static int __init tiny4412_drv_init(void)/*注册IIC驱动端*/ i2c_add_driver(& drv); printk("IIC驱动端: 驱动安装成功\\n"); return 0; static void __exit tiny4412_drv_cleanup(void)/*注销IIC驱动端*/ i2c_del_driver(& drv); printk("IIC驱动端: 驱动卸载成功\\n"); module_init(tiny4412_drv_init); /*驱动入口--安装驱动的时候执行*/ module_exit(tiny4412_drv_cleanup); /*驱动出口--卸载驱动的时候执行*/MODULE_LICENSE("GPL"); /*设置模块的许可证--GPL*/

3.2 EEPROM设备端代码
#include < linux/kernel.h> #include < linux/module.h> #include < linux/platform_device.h> #include < linux/i2c.h> #include < linux/gpio.h> #include < mach/gpio.h> #include < plat/gpio-cfg.h> static struct i2c_client *i2c_dev=NULL; static struct i2c_adapter *adap=NULL; static struct i2c_board_info info=.type="tiny4412_eeprom", .addr=0x50, /*设备地址*/ ; static int __init tiny4412_drv_init(void)/*根据总线编号获取是适配器*/ adap=i2c_get_adapter(0); /*注册IIC设备端*/ i2c_dev=i2c_new_device(adap,& info); printk("IIC设备端: 驱动安装成功\\n"); return 0; static void __exit tiny4412_drv_cleanup(void)/*注销IIC设备*/ i2c_unregister_device(i2c_dev); i2c_put_adapter(adap); printk("IIC设备端: 驱动卸载成功\\n"); module_init(tiny4412_drv_init); /*驱动入口--安装驱动的时候执行*/ module_exit(tiny4412_drv_cleanup); /*驱动出口--卸载驱动的时候执行*/MODULE_LICENSE("GPL"); /*设置模块的许可证--GPL*/

3.3 应用层测试代码
#include < stdio.h> #include < sys/types.h> #include < sys/stat.h> #include < fcntl.h> #define EEPROM_DEV "/dev/tiny4412_eeprom"int main(int argc,char **argv)/*1. 打开设备文件*/ int fd=open(EEPROM_DEV,O_RDWR); if(fd< 0)printf("%s 设备驱动打开失败.\\n",EEPROM_DEV); return 0; /*3.读写数据*/ unsigned char buff[255]; int cnt; int i; for(i=0; i< 255; i++)buff[i]=i; cnt=write(fd,buff,255); printf("write成功:%d Byte\\n",cnt); //偏移文件指针 lseek(fd,SEEK_SET,0); unsigned char buff_r[255]; cnt=read(fd,buff_r,255); printf("read成功:%d Byte\\n",cnt); for(i=0; i< cnt; i++)printf("%d ",buff_r[i]); printf("\\n"); return 0;


    推荐阅读