1,IIC软件(驱动)框架
user层:open、read、write、ioctl、close
kernel:设备驱动层:驱动工程师编写,编写字符设备驱动给应用层提供访问接口;
封装本次读写的数据包,交给总线驱动,完成硬件的访问。(i2c-dev.c通用的设备驱动)
1,分配对象初始化
struct i2c_driver mma8451q;
struct i2c_driver{
int (*probe)(struct i2c_client *, const struct i2c_device_id *);
//匹配成功后执行的函数
int(*remove)(struct i2c_client *);
struct device_driver driver;//.name = "hello"; .owner = THIS_MODULE
const struct i2c_device_id *id_table;//idtable 匹配方式
};
2,注册、注销
#define i2c_add_driver(driver)/ i2c_register_driver(THIS_MODULE, driver)
void i2c_del_driver(struct i2c_driver *);
3,总线驱动端名字如何提交
struct i2c_board_info {
char type[i2c_name_size];//用户匹配的名字
unsigned short flags; //读写的标志位,一般不用
unsigned short addr; //从机地址(SA0--0x1c\SA1---0x1d);
};
int i2c_register_board_info(int busnum,struct i2c_board_info const *info,
unsigned len);
功能:将i2c_board_info 的结构体放到内核的链表上,由总线驱动解析这条链表
参数:总线号、i2c_board_info结构体指针、传递结构体个数
返回值: 成功:0;失败:错误码
4,存放设备信息的驱动文件:(mach-s5p6818)
kernel-3.4.39/arch/arm/mach-s5p6818/fs6818$ vi device.c
#include <linux/i2c.h>
static struct i2c_board_info mma8451q = {
.type = "mma8451q",
.addr = 0x1c,
};
5,重新编译内核 make uImage
6,数据的封装和数据的发送
#define I2C_M_RD 0x0001 //read data, from slave to master
当总线驱动和设备驱动匹配成功的时候创建的对象,传递给驱动的的probe函数
struct i2c_client {
unsigned short addr; //从机地址
char name[I2C_NAME_SIZE];//名字
struct i2c_adapter *adapter; //总线驱动的对象
struct i2c_driver *driver; //设备驱动的对象
};
消息的结构体
struct i2c_msg {
__u16 addr; //从机地址 client->addr
__u16 flags; //读1写0标志
__u16 len; //数据的长度,字节为单位
__u8 *buf; //数据的首地址
};
备注:有多少起始位就有多少个消息,消息的长度是以字节来表示
a,封装写的函数
int i2c_write_reg(char reg, char data)
{
char w_buf[] = {reg, data};
//消息的封装
struct i2c_msg w_msg[] = {
[0] = {
.addr = client->addr,
.flags = 0,
.len = ARRAY_SIZE(w_buf),
.buf = w_buf,
},
};
//消息的发送
ret = i2c_transfer(client->adapter, w_msg, ARRAY_SIZE(w_msg))
if(ret != 1) {
printk("write msg send error\n");
return -EAGAIN;
}
return 0;
}
a,封装读的函数
int i2c_read_reg(char reg)
{
int ret;
chr val;
char r_buf[] = {reg};
//消息的封装
struct i2c_msg r_msg[] = {
[0] = {
.addr = client->addr,
.flags = 0,
.len = ARRAY_SIZE(r_buf),
.buf = r_buf,
},
[1] = {
.addr = client->addr,
.flags = I2C_M_RD,
.len = 1,
.buf = &val,
},
};
//消息的发送
ret = i2c_transfer(client->adapter, w_msg, ARRAY_SIZE(w_msg))
if(ret != 2) {
printk("read msg send error\n");
return -EAGAIN;
}
return val;
}
将消息包发送给总线驱动
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
参数:adap:总线驱动的对象;msgs:消息的首地址;num:消息的个数;
返回值:成功返回发送消息的个数,失败(成功取反)
核心层:内核实现,提供总线驱动和设备驱动的注册和注销方法,完成设备驱动
总线驱动匹配的过程。(i2c-core.c)
总线驱动(控制驱动层):厂商实现,已经封装好了数据收发时序。(i2c-3c2410.c)
IIC工作原理:
1,上拉电阻:当芯片不控制管脚时,管脚处于悬空状态(高阻态),即总线默认为高电平;
2,工作原理:主机发送从机地址前,发送起始信号,从机发送应答到主机;主机发送读写位,
再发送从机地址,从机发送应答到主机;写数据,从机发送应答到主机;主机发送停止信号;
3,起始信号:SCL高、SDA从高到低;停止信号:SCL高、SDA从低到高;
应答信号:从机给主机发送信号,SDA从高到低;
4,写时序:起始信号(1位)、设备地址(7位)、写位0、应答位(1位)、
寄存器地址(8位)、应答位、数据(8位)、应答位、停止位(1位)
5,读时序:起始信号(1位)、设备地址(7位)、写位0、应答位(1位)、
寄存器地址(8位)、应答位、/起始信号(1位)、设备地址(7位)、读位1
、应答位(1位)、数据(8位)、无应答位、停止位(1位)
6,SCL为高电平的时候改变数据,低电平时不允许修改数据
备注:kmalloc、kzalloc:分配连续的内存空间,最大时128k,分配内存必须是2的整数倍
vmalloc:分配的内存大小没有限制,分配的内存不一定连续;
get_free_page(gfp_mask) 分配一个页的大小4096 GFP_KERNEL GFP_ATOMIC
get_free_pages(gfp_mask, 0)分配多个页
原文:https://www.cnblogs.com/device/p/11408761.html