编译从设备驱动编程的前奏:
1, 将i2c core层和i2c adapter控制器层编译进内核
make menuconfig
Device Drivers —>
<*> I2C support —> // i2c-core.c
I2C Hardware Bus support —>
<*> S3C2410 I2C Driver // i2c-adatper的驱动–i2c-s3c2410.c
2, 为创建i2c client提供信息,内核就会根据信息自动创建i2c client
在ubuntu中进入linux内核文件路径(/home/ubuntu/s5pv210/kernel/linux-3.0.8),vim arch/arm/mach-s5pv210/mach-smdkv210.c ,修改里面内容
//根据硬件原理图,将i2c client的信息填入到对应的数组
// 硬件连接在第几个总线上,就填入对应的数组
static struct i2c_board_info smdkv210_i2c_devs0[] __initdata = {
{ I2C_BOARD_INFO("at240c2", 0x50), // 参数1--表示用于和i2c driver进行匹配的名字--自定义--需要记住
//参数2--从设备地址--7bit--从芯片手册查询得到
// .type = "24c08", .addr = 0x50
}, /* Samsung S524AD0XD1 */
{ I2C_BOARD_INFO("wm8580", 0x1b), },
};
static struct i2c_board_info smdkv210_i2c_devs1[] __initdata = {
/* To Be Updated */
};
static struct i2c_board_info smdkv210_i2c_devs2[] __initdata = {
/* To Be Updated */
{ I2C_BOARD_INFO("test", 0x2b), }, //这个用于测试---实际没有这个硬件
};
smdkv210_machine_init(void)//该函数在开机就自动执行
|
i2c_register_board_info(0, smdkv210_i2c_devs0,ARRAY_SIZE(smdkv210_i2c_devs0));
i2c_register_board_info(1, smdkv210_i2c_devs1,ARRAY_SIZE(smdkv210_i2c_devs1));
i2c_register_board_info(2, smdkv210_i2c_devs2,ARRAY_SIZE(smdkv210_i2c_devs2));
3.重新编译内核:make zImage
4.更新内核:cp -raf arch/arm/boot/zImage /tftpboot/
5.重启开发板,
[root@farsight devices]# pwd
/sys/bus/i2c/devices
[root@farsight devices]# ls
0-001b 0-0050 i2c-0 i2c-1 i2c-2
[root@farsight 0-0050]# pwd
/sys/bus/i2c/devices/0-0050
[root@farsight 0-0050]# cat name
at240c2
接下来编写i2c driver的驱动代码—at24c02驱动
构建i2c_driver,注册到总线中:
|
—probe(*i2c_client)
|
0, 记录匹配之后的i2c client
1,申请主设备号
2,创建设备节点
3,实现fops
4,根据实际情况去初始化从设备硬件
驱动中涉及到的各种结构体:
//描述了一个从设备的操作方法
struct i2c_driver
{
/* Standard driver model interfaces */
int (*probe)(struct i2c_client *, const struct i2c_device_id *);
int (*remove)(struct i2c_client *);
struct device_driver driver; //父类
const struct i2c_device_id *id_table; //用于和i2c client进行匹配的名字列表
}
//描述了一个从设备的信息---从设备地址,名字
struct i2c_client {
unsigned short addr; //7bit--从设备地址
char name[I2C_NAME_SIZE]; //名字
struct i2c_adapter *adapter; // 指向创建自己的adapter
struct i2c_driver *driver; //指向匹配成功之后的i2c driver
struct device dev; //父类
};
//描述控制器, 比如编号,算法
struct i2c_adapter {
const struct i2c_algorithm *algo; /* the algorithm to access the bus */
|
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,int num);
struct device dev; //父类
int nr; //编号
char name[48];
};
//传输的数据包
struct i2c_msg {
__u16 addr; //数据包传输給那个从设备
__u16 flags;//读还是写-- 1读,0写
__u16 len; /* msg length */
__u8 *buf; /* pointer to msg data */
};
i2c 子系统提供读写从设备的接口:
i2c_master_recv(const struct i2c_client * client, char * buf, int count)
i2c_master_send(const struct i2c_client * client, const char * buf, int count)
|
//重点学会用i2c_transfer
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
从设备驱动at24c02的程序:at24_e2prom_i2c_drv.c
#include
#include
#include
#include
#include
#include
#include
//设计一个驱动针对不同的设备的不同数据(私有)
struct e2prom_private{
char *name; //描述
int size; //容量
int version;//版本
};
int at24_i2c_e2prom_open (struct inode *inode, struct file *filp)
{
printk("------%s--------\n", __FUNCTION__);
//一般也是做初始化
return 0;
}
//设计一个全局的设备对象
struct i2c_e2prom_global{
struct i2c_client *client;//描述了一个从设备的信息---从设备地址,名字
struct e2prom_private *private;
struct miscdevice i2c_misc;
};
//全局的指针
struct i2c_e2prom_global *at24_dev;
//i2c_master_recv(const struct i2c_client * client, char * buf, int count);
int at24_i2c_recv(const struct i2c_client * client, char * buf, int count)
{
int ret;
struct i2c_adapter *adapter = client->adapter;//描述控制器, 比如编号,算法
struct i2c_msg msg;//传输的数据包
msg.addr = client->addr;//数据包传输給哪个从设备
msg.flags = I2C_M_RD; //读还是写-- 1读,0写
msg.len = count;//数据大小bytes
msg.buf = buf;
//参数1--适配器,来自于client
//参数2--传输的消息体--数据包
//参数3--消息体的个数
//返回值--正确为消息的个数,错误为负数
//i2c_transfer(struct i2c_adapter * adap, struct i2c_msg * msgs, int num)
ret = i2c_transfer(adapter, &msg, 1);
return ret==1?count:ret;
}
//重写类似于i2c_master_send/i2c_master_recv的方法
int at24_i2c_send(struct i2c_client *client,char *buf, int count)
{
int ret;
//i2c_adapter描述控制器,比如编号,算法
struct i2c_adapter *adapter = client->adapter;
struct i2c_msg msg;
msg.addr = client->addr;
msg.buf = buf;
msg.flags = 0;
msg.len = count;
//参数1--适配器,来自于client
//参数2--传输的消息体--数据包
//参数3--消息体的个数
ret = i2c_transfer(adapter, &msg, 1);
return ret==1?count:ret;
}
ssize_t at24_i2c_e2prom_read (struct file *filp, char __user *buf, size_t count, loff_t *fops)
{
printk("------%s--------\n", __FUNCTION__);
int ret;
//简单的参数判断
if(count <0 || count > at24_dev->private->size)
return -EINVAL;
//动态的分配一个空间,用于存放从硬件获取到的数据
char *temp = kzalloc(count, GFP_KERNEL);
//i2c_master_recv(const struct i2c_client * client, char * buf, int count);
//i2c_master_send(const struct i2c_client * client, const char * buf, int count);
// 1,从硬件中获取数据
ret = at24_i2c_recv(at24_dev->client, temp, count);
if(ret < 0)
{
printk("at24_i2c_recv error\n");
goto err_free;
}
// 2, 将数据传递给用户
ret = copy_to_user(buf, temp, count);
if(ret > 0)
{
printk("copy to user error\n");
ret = -EFAULT;
goto err_free;
}
kfree(temp);
return count;
err_free:
kfree(temp);
return ret;
}
ssize_t at24_i2c_e2prom_write (struct file *filp, const char __user *buf, size_t count, loff_t *fpos)
{
printk("------%s--------\n", __FUNCTION__);
int ret;
//简单的参数判断
if(count < 0 || count > at24_dev->private->size)
return -EINVAL;
//动态的分配一个空间
char *temp = kzalloc(count, GFP_KERNEL);
// 1,从用户空间得到数据
ret = copy_from_user(temp, buf, count);
if(ret > 0)
{
printk("copy_from_user error\n");
ret = -EFAULT;
goto err_free;
}
// 2, 将数据写入到硬件
ret =at24_i2c_send(at24_dev->client, temp, count);
if(ret < 0)
{
printk("at24_i2c_send error\n");
goto err_free;
}
kfree(temp);
return count;
err_free:
kfree(temp);
return ret;
}
int at24_i2c_e2prom_close (struct inode *inode, struct file *flip)
{
printk("------%s--------\n", __FUNCTION__);
return 0;
}
const struct file_operations at24_i2c_fops = {
.open = at24_i2c_e2prom_open,
.read = at24_i2c_e2prom_read,
.write = at24_i2c_e2prom_write,
.release = at24_i2c_e2prom_close,
};
int at24_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)//为什么是id
{
printk("-------%s-------\n",__FUNCTION__);
printk("client name = %s\n", id->name);//
struct e2prom_private *p = (struct e2prom_private *)id->driver_data;
printk("name descriptoin : %s \n", p->name);
printk("size : %d \n", p->size);
printk("verison : 0x%x \n", p->version);
at24_dev = kzalloc(sizeof(struct i2c_e2prom_global), GFP_KERNEL);//GFP_KERNEL
//记录当前匹配之后的i2c client
at24_dev->client = client;
at24_dev->private = (struct e2prom_private *)id->driver_data;
//实现fops
at24_dev->i2c_misc.fops = &at24_i2c_fops;
at24_dev->i2c_misc.minor = 199; //MISC_DYNAMIC_MINOR,
at24_dev->i2c_misc.name = "i2c_e2prom"; //指定设备节点的名字 /dev/i2c_e2prom
//注册一个混杂设备驱动,函数里面已经封装了申请设备号,创建设备文件等过程
misc_register(&at24_dev->i2c_misc);
// 4,根据实际情况去初始化从设备硬件---at24 上电就可以工作
return 0;
}
int at24_i2c_remove(struct i2c_client *client)
{
printk("------%s--------\n", __FUNCTION__);
misc_deregister(&at24_dev->i2c_misc);
kfree(at24_dev);
return 0;
}
struct e2prom_private c02_private = {
.name = "at240c02 in test LZH",
.size = 256,
.version = 0x1234,
};
struct e2prom_private c04_private = {
.name = "at240c04 in test LZH",
.size = 512,
.version = 0x4444,
};
struct e2prom_private c08_private = {
.name = "at240c08 in test LZH",
.size = 2014,
.version = 0x4321,
};
//名字必须与内核中的arch/arm/mach-s5pv210/mach-smdkv210.c中i2c_board_info结构体对应
const struct i2c_device_id at24_i2c_id_table[] = {
{"at240c2",(unsigned long)&c02_private},//注意
{"at240c4",(unsigned long)&c04_private},
{"at240c8",(unsigned long)&c08_private},
};
//描述了一个从设备的操作方法
struct i2c_driver at24_i2c_drv = {
.probe = at24_i2c_probe,
.remove = at24_i2c_remove,
.driver = { //父类,一定要初始化
.name = "at24_drv_LZH", //该名字不会用于进行匹配#
}, //主要的作用是用来产生一个文件 /sys/bus/i2c/drivers/at24_drv
.id_table = at24_i2c_id_table, //用于和i2c client进行匹配的名字列表
};
static int __init at24_drv_init(void)
{
//注册一个i2c driver
return i2c_add_driver(&at24_i2c_drv);
}
static void __exit at24_drv_exit(void)
{
i2c_del_driver(&at24_i2c_drv);
}
module_init(at24_drv_init);
module_exit(at24_drv_exit);
MODULE_LICENSE("GPL");
应用程序:at24_app.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define I2C_SLAVE 0x0703
void print_usage(char *str)
{
printf("%s r : read at24c02 addresss 0\n", str);
printf("%s w val : write at24c02 addresss 0\n", str);
}
int main(int argc,char **argv)
{
int fd;
unsigned char val;//字节
char register_addr = 0x08; /* Device register to access 片内地址*/
int res;
char wbuf[10];
char rbuf[10];
if (argc < 2)
{
print_usage(argv[0]);
exit(1);
}
/*打开设备文件*/
fd = open("/dev/i2c_e2prom", O_RDWR);
if (fd < 0)
{
perror("open failed");
exit(1);
}
if (strcmp(argv[1], "r") == 0)
{
if (write(fd, ®ister_addr, 1)!=1)
{
perror("write failed");
exit(1);
}
if (read(fd, rbuf, 5) != 5)
{
perror("read failed");
exit(1);
} else {
printf("rbuf[%d] =0x%x\n",0,rbuf[0]);
printf("rbuf[%d] =0x%x\n",1,rbuf[1]);
printf("rbuf[%d] =0x%x\n",2,rbuf[2]);
printf("rbuf[%d] =0x%x\n",3,rbuf[3]);
printf("rbuf[%d] =0x%x\n",4,rbuf[4]);
}
}
else if ((strcmp(argv[1], "w") == 0) && (argc == 3))
{
// ./test w 0x99
val = strtoul(argv[2], NULL, 0);
wbuf[0] = register_addr; // 片内地址0x08
wbuf[1] = val;
wbuf[2] = 0x2b;
wbuf[3] = 0x3b;
wbuf[4] = 0x4b;
wbuf[5] = 0x5b;
if (write(fd, wbuf, 6)!=6)
{
perror("write failed");
exit(1);
}
printf("write data ok!\n");
}
close(fd);
return 0;
}
在s5pv210开发板上运行的
并且写入的数据,掉电不丢失。(因为存储到e2prom里面)
程序文件夹:链接:https://pan.baidu.com/s/1mjFLjoW 密码:5kzf