写点什么

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

作者:DS小龙哥
  • 2022 年 4 月 05 日
  • 本文字数:3390 字

    阅读完需:约 11 分钟

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 一样的),原理图如下:




自带的内核里没有内置 EEPROM 的驱动:



存储芯片的数据手册介绍:



设备地址:



写字节、页写时序:



读数据时序:


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_PAGE 16 //页字节大小
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; } else { write_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; } else { newpos = 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;}
复制代码


发布于: 刚刚阅读数: 2
用户头像

DS小龙哥

关注

之所以觉得累,是因为说的比做的多。 2022.01.06 加入

熟悉C/C++、51单片机、STM32、Linux应用开发、Linux驱动开发、音视频开发、QT开发. 目前已经完成的项目涉及音视频、物联网、智能家居、工业控制领域

评论

发布
暂无评论
Linux驱动开发-编写(EEPROM)AT24C02驱动_4月月更_DS小龙哥_InfoQ写作平台