回顾:
1.linux内核platform机制
platform机制实现linux内核分离思想
linux内核分离思想就是将硬件和软件分离,软件一旦写好无需改动,只需维护硬件相关即可
让驱动代码的可移植性变得非常好
具体参见platform.bmp
*******************************************************
2.linux内核I2C驱动编程
2.1.回顾I2C总线的特性
面试题:谈谈对I2C总线的理解
UART:波特率115200,8o1
数据0x59
画出操作时序图
I2C:CPU向I2C总线发送START,发送设备地址0x21,对设备进行写
画出这部分的具体操作时序图
2.2.linux内核I2C驱动包含两部分内容
I2C总线驱动
操作的硬件对象仅仅是I2C控制器
根据用户需求最终操作SCL和SDA两根信号线
此驱动由各个芯片厂家完成实现,驱动开发者
只需配置linux内核选择为*即可!
以S5P6818处理器为例,对应的I2C总线驱动:
cd /opt/kernel
make menuconfig
Device Drivers->
I2C supports->
I2C Hardware Bus support --->
<*> Slsiap I2C //S5P6818 I2C控制器的驱动
//也就是I2C总线驱动的支持
问:对应的I2C总线驱动的源码是什么呢?
答:Kconfig->配置项->Makefile->源码
I2C设备驱动:
操作的硬件对象就是I2C外设本身
I2C设备驱动就是发起I2C外设本身所需的时序
当然这种时序最终由I2C控制器来完成!
驱动开发者重点关注I2C设备驱动的开发!
2.3.linux内核I2C驱动框架
I2C总线驱动和I2C设备驱动如何配合
以CPU获取MMA8653三轴加速度传感器的ID为例
(CPU通过I2C总线读取MMA8653片内寄存器0x0D的ID(0x5A))
应用层:驱动工程师
struct mma8653 {
unsigned char addr; //片内寄存器地址
unsigned char data; //片内寄存器数据
};
struct mma8653 mma; //分配用户缓冲区
mma.addr = 0x0D; //指定要访问的片内寄存器地址
mma.data = ?
ioctl(fd, MMA8653_READ, &mma);//发起读取MMA8653动作
printf("ID=%#x\n", mma.data); //mma.data=0x5A
-------------------------------------------------
I2C设备驱动层:驱动工程师
long mma8653_ioctl(file, cmd, arg) {
//1.分配内核缓冲区
struct mma8653 kmma;
//2.拷贝用户数据到内核缓冲区
copy_from_user(&kmma,
(struct mma8653*)arg, sizeof(kmma));
//此时:kmma.addr=0x0D;kmma.data=?
//3.I2C设备驱动发起硬件操作时序要求
此要求最终由I2C总线驱动来完成
I2C总线驱动操作I2C控制器发起
I2C设备驱动要求的时序
I2C设备驱动只需调用内核提供的SMBUS
接口函数即可完成相关的请求:
kmma.data = i2c_smbus_*(...,0x0D);
//kmma.data = 0x5A
//4.拷贝内核缓冲区的数据到用户缓冲区
copy_to_user((strut mma8653*)arg,
&kmma, sizeof(kmma));
return 0;
}
----------------------------------------------
SMBUS接口层:内核提供
作为I2C设备驱动和I2C总线驱动的桥梁
此接口都是给I2C设备驱动层使用
0x0D //由I2C设备驱动提供
----------------------------------------------
I2C总线驱动层:芯片厂家
根据I2C设备驱动的要求最终发起操作
I2C控制器,发起最终的I2C硬件时序
0x0D //由SMBUS接口提供
ST->0x1D<<1|0->[ACK]->0x0D->[ACK]->RST->0x1D<<1|1->[ACK]->[0x5A]->NACK->SP
最后I2C总线驱动将获取的0x5A数据返回给SMBUS接口
SMBUS接口将0x5A再返回给I2C设备驱动
I2C设备驱动最后将0x5A返回到用户
----------------------------------------------
硬件层:硬件工程师
I2C控制器<------>MMA8653硬件外设
画出一个调用操作流程图:smbus.bmp
2.4.问:如何编写一个I2C设备驱动程序呢?
答:同样利用内核的分离思想,但是不是platform机制
同样也是设备-总线-驱动编程模型
platform机制本身也是采用设备-总线-驱动编程模型
实现原理:
1.首先内核已经帮你定义好了一个虚拟总线叫i2c_bus_type
在这个总线上维护着两个链表:dev链表和drv链表
2.dev链表上每一个节点描述的I2C外设的纯硬件信息
对应的数据结构为struct i2c_client,每当向dev
链表添加一个I2C外设的硬件信息节点时,只需用此
数据结构定义初始化一个对象即可,然后向dev链表
添加,一旦添加完毕,内核会帮你遍历drv链表,从
drv链表上取出每一个软件节点跟这个要注册的硬件
节点进行匹配,内核通过调用总线提供的match函数进行比较
比较i2c_client的name和i2c_driver的id_table的name
如果匹配成功,硬件找到了对应的软件,内核会调用i2c_driver
的probe函数,并且把匹配成功的硬件节点的首地址传递给probe函数
最终完成硬件和软件的再次结合
3.drv链表上每一个节点描述的I2C外设的纯软件信息
对应的数据结构为struct i2c_driver,每当向drv
链表添加一个I2C外设的软件信息节点时,只需用此
数据结构定义初始化一个对象即可,然后向drv链表
添加,一旦添加完毕,内核会帮你遍历dev链表,从
dev链表上取出每一个硬件节点跟这个要注册的软件
节点进行匹配,内核通过调用总线提供的match函数进行比较
比较i2c_client的name和i2c_driver的id_table的name
如果匹配成功,软件找到了对应的硬件,内核会调用i2c_driver
的probe函数,并且把匹配成功的硬件节点的首地址传递给probe函数
最终完成硬件和软件的再次结合
4.驱动开发者要实现一个I2C设备驱动,只需关注以下两个数据结构:
struct i2c_client
struct i2c_driver
2.5.struct i2c_client
struct i2c_client {
unsigned short addr;
char name[I2C_NAME_SIZE];
struct device dev;
int irq;
...
};
功能:用于描述I2C外设的纯硬件信息
成员:
addr:I2C外设的设备地址,用于找外设,必须初始化!
name:用于匹配,必须初始化!
dev:重点关注其中的void *platform_data字段
此字段将来用于装载自定义的用于描述I2C
外设的硬件信息
irq:如果I2C外设和CPU之间有中断,此字段保存
对应的中断号
切记:linux内核对i2c_client的操作和platform_device
所有区别,驱动开发者不用去自己定义初始化和注册一个
struct i2c_client硬件节点对象,定义初始化和注册
过程统一由内核来帮你完成
问:内核定义初始化注册i2c_client硬件节点对象
尤其初始化过程,内核怎么知道要初始化具体
哪些硬件信息(addr/name/platform_data)
答:驱动开发者只需利用以下数据结构将要初始化的
信息注册到内核中即可,也就是告诉内核将来要初始化的
具体信息,将来内核根据你提供的信息来去初始化
struct i2c_client硬件节点对象:
struct i2c_board_info {
char type[I2C_NAME_SIZE];
unsigned short addr;
void *platform_data;
int irq;
...
};
功能:驱动开发者利用此数据结构将I2C外设的
硬件信息告诉给linux内核,将来内核根据
提供的I2C外设的硬件信息帮你定义初始化
和注册一个i2c_client硬件节点对象到dev链表
成员:
type:指定硬件节点的名称,此字段将来会自动的
赋值给i2c_client的name,将来用于匹配
所以必须初始化!
addr:指定I2C外设的设备地址,此字段将来会自动
赋值给i2c_client的addr,将来用于找外设
所以必须初始化!
platform_data:用于装载自定义的硬件信息,将来
会自动赋值给i2c_client.dev.platform_data
irq:如果CPU和外设需要中断,此字段用来指定中断号
将来会赋值给i2c_client.irq
配套函数:
i2c_register_board_info(int busnum,
struct i2c_board_info const *info,
unsigned len)
函数功能:注册I2C外设硬件信息到内核,将来
linux内核会帮你定义初始化和注册i2c_client
硬件节点到dev链表,内核初始化i2c_client
所需的内容都是根据i2c_board_info来进行提供
参数:
busnum:I2C外设所在的CPU的I2C总线编号
必须根据原理图获取
例如:X6818开发板上的mma8653所对应的
I2C总线编号为2(第三路I2C总线)
info:传递要注册的I2C硬件信息
len:用i2c_board_info描述的硬件信息的个数
切记切记切记:struct i2c_board_info的定义
初始化和注册不能采用insmod/rmmod进行,必须将
代码和uImage写到一起,一般要写到开发板对应的
平台文件中(内核源码/arch/arm/plat-s5p6818/x6818/device.c)!
案例:编写MMA8653三轴加速度传感器的I2C设备驱动
首先搞定MMA8653的硬件信息的注册过程
实施步骤:
上位机执行:
1.首先去除官方的MMA8653的三轴加速度传感器驱动
cd /opt/kernel
make menuconfig
Deivce Drivers->
Hardware Monitoring support->
//按N键去除
<*>Freescale MMA865X 3-Axis Accelerometer
保存退出
make uImage
cp arch/arm/boot/uImage /tftpboot
重启下位机,进入uboot执行:
tftp 48000000 uImage
bootm 48000000 //此时的内核不再有MMA8653的官方驱动
2.向内核添加MMA8653的硬件信息
cd /opt/kernel
vim arch/arm/plat-s5p6818/x6818/device.c 在文件的最开头添加如下代码:
//定义初始化MMA8653外设的硬件信息对象
static struct i2c_board_info mma8653[] = {
{
.type = "mma8653",//用于匹配
//会赋值给i2c_client.name
.addr = 0x1D //用于找外设
//会赋值给i2c_client.addr
}
};
然后函数nxp_board_devs_register,在函数最开头添加:
i2c_register_board_info(2, mma8653, ARRAY_SIZE(mma8653));
//心里念叨:只要将来这条语句执行,内核就会帮我
//定义初始化和注册一个i2c_client硬件节点对象到dev
//链表,开始各种遍历,匹配,调用,传递参数
//i2c_client对象中的所有成员都是我这注册的i2c_board_info
来提供的(i2c_client.name=type,i2c_client.addr,
i2c_client.dev.platform_data=platform_data,
i2c_client.irq = irq)
保存退出
make uImage
cp arch/arm/boot/uImage /tftpboot/
重启下位机,uboot执行:
tftp 48000000 uImage
bootm 48000000 //此时uImage就会有MMA8653的硬件节点对象i2c_client
2.6.struct i2c_driver
struct i2c_driver {
int (*probe)(struct i2c_client *client, const struct i2c_device_id *id);
int (*remove)(struct i2c_client *client);
const struct i2c_device_id *id_table;
...
};
说明:描述I2C外设的软件信息
成员:
probe:硬件节点和软件节点匹配成功,内核调用
形参client指针指向匹配成功的I2C外设的硬件节点
//心里念叨:此client指针里面的内容都是i2c_board_info提供
client->addr //获取I2C外设的设备地址
client->irq //获取I2C外设的中断号
client->dev.platform_data //获取自定义的硬件信息
remove:卸载软件节点,内核调用此函数
形参client指针指向匹配成功的I2C外设的硬件节点
//心里念叨:此client指针里面的内容都是i2c_board_info提供
client->addr //获取I2C外设的设备地址
client->irq //获取I2C外设的中断号
client->dev.platform_data //获取自定义的硬件信息
id_table:重点关注其中的name字段,此字段将来用于匹配
struct i2c_device_id {
char name[I2C_NAME_SIZE]; //用于匹配
unsigned long driver_data //用于给probe函数传递参数
};
配套函数:
i2c_add_driver(&软件节点对象)
向内核drv链表注册添加I2C外设软件节点对象
将来内核会帮你遍历,匹配,调用probe函数,传递参数
i2c_del_driver(&软件节点对象)
从内核drv链表删除软件节点对象
内核会帮你调用remove函数
案例:编写MMA8653三轴加速度传感器的I2C设备驱动
然后搞定MMA8653的软件信息的注册过程
参考代码:ftp://DRV/day11/mma8653.rar/2.0
认真阅读分析此驱动程序
mkdir /opt/drivers/day11/
cp 2.0 /opt/drivers/day11/
cd /opt/drivers/day11/2.0
make
cp mma8653_drv.ko /opt/rootfs/home/drivers
下位机执行:
cd /home/drivers
insmod mma8653_drv.ko //查看probe函数是否被调用
rmmod mma8653_drv //查看remove函数是否被调用
案例:最终完成MMA8653三轴加速度传感器的I2C设备驱动
参考代码:ftp://DRV/day11/mma8653.rar/3.0
认真阅读分析此驱动程序
mkdir /opt/drivers/day11/
cp 3.0 /opt/drivers/day11/
cd /opt/drivers/day11/3.0
make
cp mma8653_drv.ko /opt/rootfs/home/drivers
arm...gcc -o mma8653_test mma8653_test.c
下位机执行:
cd /home/drivers
insmod mma8653_drv.ko
./mma8653_test
注意:
//SMBUS接口函数的使用步骤:
// 1.打开SMBUS接口函数的说明使用文档,在内核源码的Documentation\i2c\smbus-protocol
// 打开此文件
// 2.再打开MMA8653的芯片手册,找到对应的读时序图
// 3.根据读时序图在文档smbus-protocol中找到对应的实现函数
// 4.找到对应的函数以后,在sourceinsight中找到这个函数的定义
//获取到函数的参数和返回值
//注意:smbus接口函数中的client指针一定要传递匹配成功的硬件节点对象指针
1.linux内核platform机制
platform机制实现linux内核分离思想
linux内核分离思想就是将硬件和软件分离,软件一旦写好无需改动,只需维护硬件相关即可
让驱动代码的可移植性变得非常好
具体参见platform.bmp
*******************************************************
2.linux内核I2C驱动编程
2.1.回顾I2C总线的特性
面试题:谈谈对I2C总线的理解
UART:波特率115200,8o1
数据0x59
画出操作时序图
I2C:CPU向I2C总线发送START,发送设备地址0x21,对设备进行写
画出这部分的具体操作时序图
2.2.linux内核I2C驱动包含两部分内容
I2C总线驱动
操作的硬件对象仅仅是I2C控制器
根据用户需求最终操作SCL和SDA两根信号线
此驱动由各个芯片厂家完成实现,驱动开发者
只需配置linux内核选择为*即可!
以S5P6818处理器为例,对应的I2C总线驱动:
cd /opt/kernel
make menuconfig
Device Drivers->
I2C supports->
I2C Hardware Bus support --->
<*> Slsiap I2C //S5P6818 I2C控制器的驱动
//也就是I2C总线驱动的支持
问:对应的I2C总线驱动的源码是什么呢?
答:Kconfig->配置项->Makefile->源码
I2C设备驱动:
操作的硬件对象就是I2C外设本身
I2C设备驱动就是发起I2C外设本身所需的时序
当然这种时序最终由I2C控制器来完成!
驱动开发者重点关注I2C设备驱动的开发!
2.3.linux内核I2C驱动框架
I2C总线驱动和I2C设备驱动如何配合
以CPU获取MMA8653三轴加速度传感器的ID为例
(CPU通过I2C总线读取MMA8653片内寄存器0x0D的ID(0x5A))
应用层:驱动工程师
struct mma8653 {
unsigned char addr; //片内寄存器地址
unsigned char data; //片内寄存器数据
};
struct mma8653 mma; //分配用户缓冲区
mma.addr = 0x0D; //指定要访问的片内寄存器地址
mma.data = ?
ioctl(fd, MMA8653_READ, &mma);//发起读取MMA8653动作
printf("ID=%#x\n", mma.data); //mma.data=0x5A
-------------------------------------------------
I2C设备驱动层:驱动工程师
long mma8653_ioctl(file, cmd, arg) {
//1.分配内核缓冲区
struct mma8653 kmma;
//2.拷贝用户数据到内核缓冲区
copy_from_user(&kmma,
(struct mma8653*)arg, sizeof(kmma));
//此时:kmma.addr=0x0D;kmma.data=?
//3.I2C设备驱动发起硬件操作时序要求
此要求最终由I2C总线驱动来完成
I2C总线驱动操作I2C控制器发起
I2C设备驱动要求的时序
I2C设备驱动只需调用内核提供的SMBUS
接口函数即可完成相关的请求:
kmma.data = i2c_smbus_*(...,0x0D);
//kmma.data = 0x5A
//4.拷贝内核缓冲区的数据到用户缓冲区
copy_to_user((strut mma8653*)arg,
&kmma, sizeof(kmma));
return 0;
}
----------------------------------------------
SMBUS接口层:内核提供
作为I2C设备驱动和I2C总线驱动的桥梁
此接口都是给I2C设备驱动层使用
0x0D //由I2C设备驱动提供
----------------------------------------------
I2C总线驱动层:芯片厂家
根据I2C设备驱动的要求最终发起操作
I2C控制器,发起最终的I2C硬件时序
0x0D //由SMBUS接口提供
ST->0x1D<<1|0->[ACK]->0x0D->[ACK]->RST->0x1D<<1|1->[ACK]->[0x5A]->NACK->SP
最后I2C总线驱动将获取的0x5A数据返回给SMBUS接口
SMBUS接口将0x5A再返回给I2C设备驱动
I2C设备驱动最后将0x5A返回到用户
----------------------------------------------
硬件层:硬件工程师
I2C控制器<------>MMA8653硬件外设
画出一个调用操作流程图:smbus.bmp
2.4.问:如何编写一个I2C设备驱动程序呢?
答:同样利用内核的分离思想,但是不是platform机制
同样也是设备-总线-驱动编程模型
platform机制本身也是采用设备-总线-驱动编程模型
实现原理:
1.首先内核已经帮你定义好了一个虚拟总线叫i2c_bus_type
在这个总线上维护着两个链表:dev链表和drv链表
2.dev链表上每一个节点描述的I2C外设的纯硬件信息
对应的数据结构为struct i2c_client,每当向dev
链表添加一个I2C外设的硬件信息节点时,只需用此
数据结构定义初始化一个对象即可,然后向dev链表
添加,一旦添加完毕,内核会帮你遍历drv链表,从
drv链表上取出每一个软件节点跟这个要注册的硬件
节点进行匹配,内核通过调用总线提供的match函数进行比较
比较i2c_client的name和i2c_driver的id_table的name
如果匹配成功,硬件找到了对应的软件,内核会调用i2c_driver
的probe函数,并且把匹配成功的硬件节点的首地址传递给probe函数
最终完成硬件和软件的再次结合
3.drv链表上每一个节点描述的I2C外设的纯软件信息
对应的数据结构为struct i2c_driver,每当向drv
链表添加一个I2C外设的软件信息节点时,只需用此
数据结构定义初始化一个对象即可,然后向drv链表
添加,一旦添加完毕,内核会帮你遍历dev链表,从
dev链表上取出每一个硬件节点跟这个要注册的软件
节点进行匹配,内核通过调用总线提供的match函数进行比较
比较i2c_client的name和i2c_driver的id_table的name
如果匹配成功,软件找到了对应的硬件,内核会调用i2c_driver
的probe函数,并且把匹配成功的硬件节点的首地址传递给probe函数
最终完成硬件和软件的再次结合
4.驱动开发者要实现一个I2C设备驱动,只需关注以下两个数据结构:
struct i2c_client
struct i2c_driver
2.5.struct i2c_client
struct i2c_client {
unsigned short addr;
char name[I2C_NAME_SIZE];
struct device dev;
int irq;
...
};
功能:用于描述I2C外设的纯硬件信息
成员:
addr:I2C外设的设备地址,用于找外设,必须初始化!
name:用于匹配,必须初始化!
dev:重点关注其中的void *platform_data字段
此字段将来用于装载自定义的用于描述I2C
外设的硬件信息
irq:如果I2C外设和CPU之间有中断,此字段保存
对应的中断号
切记:linux内核对i2c_client的操作和platform_device
所有区别,驱动开发者不用去自己定义初始化和注册一个
struct i2c_client硬件节点对象,定义初始化和注册
过程统一由内核来帮你完成
问:内核定义初始化注册i2c_client硬件节点对象
尤其初始化过程,内核怎么知道要初始化具体
哪些硬件信息(addr/name/platform_data)
答:驱动开发者只需利用以下数据结构将要初始化的
信息注册到内核中即可,也就是告诉内核将来要初始化的
具体信息,将来内核根据你提供的信息来去初始化
struct i2c_client硬件节点对象:
struct i2c_board_info {
char type[I2C_NAME_SIZE];
unsigned short addr;
void *platform_data;
int irq;
...
};
功能:驱动开发者利用此数据结构将I2C外设的
硬件信息告诉给linux内核,将来内核根据
提供的I2C外设的硬件信息帮你定义初始化
和注册一个i2c_client硬件节点对象到dev链表
成员:
type:指定硬件节点的名称,此字段将来会自动的
赋值给i2c_client的name,将来用于匹配
所以必须初始化!
addr:指定I2C外设的设备地址,此字段将来会自动
赋值给i2c_client的addr,将来用于找外设
所以必须初始化!
platform_data:用于装载自定义的硬件信息,将来
会自动赋值给i2c_client.dev.platform_data
irq:如果CPU和外设需要中断,此字段用来指定中断号
将来会赋值给i2c_client.irq
配套函数:
i2c_register_board_info(int busnum,
struct i2c_board_info const *info,
unsigned len)
函数功能:注册I2C外设硬件信息到内核,将来
linux内核会帮你定义初始化和注册i2c_client
硬件节点到dev链表,内核初始化i2c_client
所需的内容都是根据i2c_board_info来进行提供
参数:
busnum:I2C外设所在的CPU的I2C总线编号
必须根据原理图获取
例如:X6818开发板上的mma8653所对应的
I2C总线编号为2(第三路I2C总线)
info:传递要注册的I2C硬件信息
len:用i2c_board_info描述的硬件信息的个数
切记切记切记:struct i2c_board_info的定义
初始化和注册不能采用insmod/rmmod进行,必须将
代码和uImage写到一起,一般要写到开发板对应的
平台文件中(内核源码/arch/arm/plat-s5p6818/x6818/device.c)!
案例:编写MMA8653三轴加速度传感器的I2C设备驱动
首先搞定MMA8653的硬件信息的注册过程
实施步骤:
上位机执行:
1.首先去除官方的MMA8653的三轴加速度传感器驱动
cd /opt/kernel
make menuconfig
Deivce Drivers->
Hardware Monitoring support->
//按N键去除
<*>Freescale MMA865X 3-Axis Accelerometer
保存退出
make uImage
cp arch/arm/boot/uImage /tftpboot
重启下位机,进入uboot执行:
tftp 48000000 uImage
bootm 48000000 //此时的内核不再有MMA8653的官方驱动
2.向内核添加MMA8653的硬件信息
cd /opt/kernel
vim arch/arm/plat-s5p6818/x6818/device.c 在文件的最开头添加如下代码:
//定义初始化MMA8653外设的硬件信息对象
static struct i2c_board_info mma8653[] = {
{
.type = "mma8653",//用于匹配
//会赋值给i2c_client.name
.addr = 0x1D //用于找外设
//会赋值给i2c_client.addr
}
};
然后函数nxp_board_devs_register,在函数最开头添加:
i2c_register_board_info(2, mma8653, ARRAY_SIZE(mma8653));
//心里念叨:只要将来这条语句执行,内核就会帮我
//定义初始化和注册一个i2c_client硬件节点对象到dev
//链表,开始各种遍历,匹配,调用,传递参数
//i2c_client对象中的所有成员都是我这注册的i2c_board_info
来提供的(i2c_client.name=type,i2c_client.addr,
i2c_client.dev.platform_data=platform_data,
i2c_client.irq = irq)
保存退出
make uImage
cp arch/arm/boot/uImage /tftpboot/
重启下位机,uboot执行:
tftp 48000000 uImage
bootm 48000000 //此时uImage就会有MMA8653的硬件节点对象i2c_client
2.6.struct i2c_driver
struct i2c_driver {
int (*probe)(struct i2c_client *client, const struct i2c_device_id *id);
int (*remove)(struct i2c_client *client);
const struct i2c_device_id *id_table;
...
};
说明:描述I2C外设的软件信息
成员:
probe:硬件节点和软件节点匹配成功,内核调用
形参client指针指向匹配成功的I2C外设的硬件节点
//心里念叨:此client指针里面的内容都是i2c_board_info提供
client->addr //获取I2C外设的设备地址
client->irq //获取I2C外设的中断号
client->dev.platform_data //获取自定义的硬件信息
remove:卸载软件节点,内核调用此函数
形参client指针指向匹配成功的I2C外设的硬件节点
//心里念叨:此client指针里面的内容都是i2c_board_info提供
client->addr //获取I2C外设的设备地址
client->irq //获取I2C外设的中断号
client->dev.platform_data //获取自定义的硬件信息
id_table:重点关注其中的name字段,此字段将来用于匹配
struct i2c_device_id {
char name[I2C_NAME_SIZE]; //用于匹配
unsigned long driver_data //用于给probe函数传递参数
};
配套函数:
i2c_add_driver(&软件节点对象)
向内核drv链表注册添加I2C外设软件节点对象
将来内核会帮你遍历,匹配,调用probe函数,传递参数
i2c_del_driver(&软件节点对象)
从内核drv链表删除软件节点对象
内核会帮你调用remove函数
案例:编写MMA8653三轴加速度传感器的I2C设备驱动
然后搞定MMA8653的软件信息的注册过程
参考代码:ftp://DRV/day11/mma8653.rar/2.0
认真阅读分析此驱动程序
mkdir /opt/drivers/day11/
cp 2.0 /opt/drivers/day11/
cd /opt/drivers/day11/2.0
make
cp mma8653_drv.ko /opt/rootfs/home/drivers
下位机执行:
cd /home/drivers
insmod mma8653_drv.ko //查看probe函数是否被调用
rmmod mma8653_drv //查看remove函数是否被调用
案例:最终完成MMA8653三轴加速度传感器的I2C设备驱动
参考代码:ftp://DRV/day11/mma8653.rar/3.0
认真阅读分析此驱动程序
mkdir /opt/drivers/day11/
cp 3.0 /opt/drivers/day11/
cd /opt/drivers/day11/3.0
make
cp mma8653_drv.ko /opt/rootfs/home/drivers
arm...gcc -o mma8653_test mma8653_test.c
下位机执行:
cd /home/drivers
insmod mma8653_drv.ko
./mma8653_test
注意:
//SMBUS接口函数的使用步骤:
// 1.打开SMBUS接口函数的说明使用文档,在内核源码的Documentation\i2c\smbus-protocol
// 打开此文件
// 2.再打开MMA8653的芯片手册,找到对应的读时序图
// 3.根据读时序图在文档smbus-protocol中找到对应的实现函数
// 4.找到对应的函数以后,在sourceinsight中找到这个函数的定义
//获取到函数的参数和返回值
//注意:smbus接口函数中的client指针一定要传递匹配成功的硬件节点对象指针