【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一样的),原理图如下:
文章图片
文章图片
自带的内核里没有内置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_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;
推荐阅读
- Java并发编程实战03对象的共享
- Python 图_系列之基于<链接表;实现无向图最短路径搜索
- Java并发编程实战04对象的组合
- MySQL 数据多久刷一次盘()
- 小胖学Linux day15(用户管理2)
- 佳豪哥哥教你学Linux的第十四天
- 记一次通过物理文件恢复MariaDB数据
- 虚拟分布式交换机(VDS)
- Docker 镜像导入导出