嵌入式Linux开发: 编写EEPROM驱动_采用杂项字符设备框架
程序员文章站
2022-07-14 16:31:10
...
一、环境介绍
宿主机: Redhat 6.3 32位
目标开发板型号: 友善之臂Tiny4412
目标开发板操作系统: 使用busybox制作,内核使用官方3.5内核。
使用的编译器版本: 友善之臂光盘里自带的交叉编译器版本4.5.1
二、EEPROM芯片介绍
三、驱动代码
下面使用IIC子系统框架编写EEPROM的驱动,驱动端代码使用杂项字符设备框架,并且实现了文件指针偏移;在应用层可以将EEPROM当做一个255字节大小的文件进行编程读写。
3.1 设备端代码
#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.2 驱动端代码
#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.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;
}
3.4 编译Makefile
KER_DRI=/home/wbyq/work/linux-3.5/linux-3.5
all:
make -C $(KER_DRI) M=`pwd` modules
cp *.ko /home/wbyq/work/rootfs/code -f
make -C $(KER_DRI) M=`pwd` modules clean
arm-linux-gcc app.c -o app
cp app /home/wbyq/work/rootfs/code -f
rm app -f
obj-m += iic_drv.o
obj-m += iic_dev.o
3.5 测试效果
下面微信公众号里有全套的C语言、C++、单片机、物联网、QT、Python、Linux教程,欢迎关注: