当前位置: 首页 > news >正文

imx6ull——I2C驱动

I2C基本介绍

在这里插入图片描述

SCL 为高电平,SDA 出现下降沿:起始位

在这里插入图片描述
在这里插入图片描述

SCL 位高电平,SDA出现上升沿:停止位

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

主机——从机地址(ack)——寄存器地址(ack)——数据(ack)

在这里插入图片描述

重点:先是写,因为要写入从机让从机知道主机要读,再是读
主机——从机地址(写)(ack)——寄存器地址(ack)——从机地址(读)(ack)
从机——发送数据(主机发送no ack)

在这里插入图片描述

1-7位放要访问的从机地址
I2c频率不能随便分,只能选

I2C源码分析

I2C适配器驱动
i2c_adapter
-> i2c_algorithm
->master_xfer : I2C适配器的传输函数,可以通过此函数来完成与 IIC设备之间的通信。

I2C流程:
1. 初始化i2c_adapter结构体变量
2. 设置 i2c_algorithm中的 master_xfer函数。
3. 向系统注册设置好的 i2c_adapter,通过 i2c_add_numbered_adapter或 i2c_add_adapter这两个函数

I2C适配器是 platform驱动, I2C总线为别的设备提供了一种总线驱动框架
I2C在Platform虚拟总线上,Platform驱动I2C
从机设备在I2C总线上,I2C驱动从机
I2c注册

I2C设备驱动
I2c_client 描述设备信息,主要在设备树写外设信息,I2c_client注册等操作从设备树中查找写好
I2c_dirver 驱动内容
drivers/i2c/i2c-core.c 总线定义,设备和驱动匹配

static int i2c_device_match(struct device *dev, struct device_driver *drv)
{struct i2c_client	*client = i2c_verify_client(dev);//从i2c中获取从机名单struct i2c_driver	*driver;if (!client)return 0;/* Attempt an OF style match */if (of_driver_match_device(dev, drv))return 1;/* Then ACPI style match */if (acpi_driver_match_device(dev, drv))return 1;driver = to_i2c_driver(drv);/* match on an id table if there is one */if (driver->id_table)return i2c_match_id(driver->id_table, client) != NULL;//设备树匹配return 0;
}

驱动编写

1. I2C设备树设置

&i2c1 {clock-frequency = <100000>;pinctrl-names = "default";pinctrl-0 = <&pinctrl_i2c1>;status = "okay";

设备树写I2C的SCL SDA端口:pinctrl_i2c1

		pinctrl_i2c1: i2c1grp {fsl,pins = <MX6UL_PAD_UART4_TX_DATA__I2C1_SCL 0x4001b8b0MX6UL_PAD_UART4_RX_DATA__I2C1_SDA 0x4001b8b0>;};

2. ap3216c子节点添加

&i2c1 {clock-frequency = <100000>;pinctrl-names = "default";pinctrl-0 = <&pinctrl_i2c1>;status = "okay";ap3216@1e{compatible = "alientek,ap3216c";reg=<0x1e>;};};

3. 编译dtbs,并添加

make dtbs
sudo cp arch/arm/boot/dts/imx6ull-alientek-emmc.dtb /home/liu/linux/tftpboot/ -f

4. 查看

在这里插入图片描述

cd /sys/bus/i2c/devices/
cd 0-001e/
cat name

5. ap3216c.c编写

1.ap3216c_driver :从机设备驱动结构体

static struct i2c_driver ap3216c_driver = {.probe = ap3216c_probe,.remove = ap3216c_remove,.driver = {.owner = THIS_MODULE,.name = "ap3216c",.of_match_table = ap3216c_of_match, },.id_table = ap3216c_id,
};

驱动和设备树的匹配

/* 传统匹配方式ID列表 */
static const struct i2c_device_id ap3216c_id[] = {{"alientek,ap3216c", 0},  {}
};/* 设备树匹配列表 */
static const struct of_device_id ap3216c_of_match[] = {{ .compatible = "alientek,ap3216c" },{ /* Sentinel */ }
};

ap3216c_probe:驱动的执行函数

  1. 构建设备号:对ap3216cdev设备号赋值
  2. 注册设备cdev:字符设备注册,包含ap3216cdev的操作函数ops
  3. 创建类:class内包含dev需要的一些成员如总线,设备链表,还有需要的函数
  4. 创建设备:device_create:
static int ap3216c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{/* 1、构建设备号 */if (ap3216cdev.major) {ap3216cdev.devid = MKDEV(ap3216cdev.major, 0);register_chrdev_region(ap3216cdev.devid, AP3216C_CNT, AP3216C_NAME);} else {alloc_chrdev_region(&ap3216cdev.devid, 0, AP3216C_CNT, AP3216C_NAME);ap3216cdev.major = MAJOR(ap3216cdev.devid);}/* 2、注册设备 */cdev_init(&ap3216cdev.cdev, &ap3216c_ops);cdev_add(&ap3216cdev.cdev, ap3216cdev.devid, AP3216C_CNT);/* 3、创建类 */ap3216cdev.class = class_create(THIS_MODULE, AP3216C_NAME);if (IS_ERR(ap3216cdev.class)) {return PTR_ERR(ap3216cdev.class);}/* 4、创建设备 */ap3216cdev.device = device_create(ap3216cdev.class, NULL, ap3216cdev.devid, NULL, AP3216C_NAME);if (IS_ERR(ap3216cdev.device)) {return PTR_ERR(ap3216cdev.device);}ap3216cdev.private_data = client;return 0;
}

寄存器读写

读,需要发送两次信息,i2c_transfer可以发送多个信息
msgs[1],要发的信息排序发送,msgs[0]标志位为读,内含寄存器地址:msgs[1]标志位为读,读取数据放入缓冲区
I2C具体流程不需要写,官方写好了

在这里插入图片描述

I2C数据传输函数,内含主机从机地址,根据标志位,决定读还是写

int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)

msgs数据结构体(地址,发送类型,信息长度,信息)

struct i2c_msg {__u16 addr;	/* slave address			*/__u16 flags;
#define I2C_M_RD		0x0001	/* read data, from slave to master *//* I2C_M_RD is guaranteed to be 0x0001! */
#define I2C_M_TEN		0x0010	/* this is a ten bit chip address */
#define I2C_M_DMA_SAFE		0x0200	/* the buffer of this message is DMA safe *//* makes only sense in kernelspace *//* userspace buffers are copied anyway */
#define I2C_M_RECV_LEN		0x0400	/* length will be first received byte */
#define I2C_M_NO_RD_ACK		0x0800	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK	0x1000	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_REV_DIR_ADDR	0x2000	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NOSTART		0x4000	/* if I2C_FUNC_NOSTART */
#define I2C_M_STOP		0x8000	/* if I2C_FUNC_PROTOCOL_MANGLING */__u16 len;		/* msg length				*/__u8 *buf;		/* pointer to msg data			*/
};
/** @description	: 从ap3216c读取多个寄存器数据* @param - dev:  ap3216c设备* @param - reg:  要读取的寄存器首地址* @param - val:  读取到的数据* @param - len:  要读取的数据长度* @return 		: 操作结果*/
static int ap3216c_read_regs(struct ap3216c_dev *dev, u8 reg, void *val, int len)
{int ret;struct i2c_msg msg[2];struct i2c_client *client = (struct i2c_client *)dev->private_data;/* msg[0]为发送要读取的首地址 */msg[0].addr = client->addr;			/* ap3216c地址 */msg[0].flags = 0;					/* 标记为发送数据 */msg[0].buf = &reg;					/* 读取的首地址 */msg[0].len = 1;						/* reg长度*//* msg[1]读取数据 */msg[1].addr = client->addr;			/* ap3216c地址 */msg[1].flags = I2C_M_RD;			/* 标记为读取数据*/msg[1].buf = val;					/* 读取数据缓冲区 */msg[1].len = len;					/* 要读取的数据长度*/ret = i2c_transfer(client->adapter, msg, 2);if(ret == 2) {ret = 0;} else {printk("i2c rd failed=%d reg=%06x len=%d\n",ret, reg, len);ret = -EREMOTEIO;}return ret;
}

写:msgs标志位为写,输入寄存器地址,告诉写入信息,i2c_transfer传输,其他不用管

在这里插入图片描述

/** @description	: 向ap3216c多个寄存器写入数据* @param - dev:  ap3216c设备* @param - reg:  要写入的寄存器首地址* @param - val:  要写入的数据缓冲区* @param - len:  要写入的数据长度* @return 	  :   操作结果*/
static s32 ap3216c_write_regs(struct ap3216c_dev *dev, u8 reg, u8 *buf, u8 len)
{u8 b[256];struct i2c_msg msg;struct i2c_client *client = (struct i2c_client *)dev->private_data;b[0] = reg;					/* 寄存器首地址 */memcpy(&b[1],buf,len);		/* 将要写入的数据拷贝到数组b里面 */msg.addr = client->addr;	/* ap3216c地址 */msg.flags = 0;				/* 标记为写数据 */msg.buf = b;				/* 要写入的数据缓冲区 */msg.len = len + 1;			/* 要写入的数据长度 */return i2c_transfer(client->adapter, &msg, 1);
}

ap3216c初始化

#define AP3216C_ADDR    	0X1E	/* AP3216C器件地址  *//* AP3316C寄存器 */
#define AP3216C_SYSTEMCONG	0x00	/* 配置寄存器       */
#define AP3216C_INTSTATUS	0X01	/* 中断状态寄存器   */
#define AP3216C_INTCLEAR	0X02	/* 中断清除寄存器   */
#define AP3216C_IRDATALOW	0x0A	/* IR数据低字节     */
#define AP3216C_IRDATAHIGH	0x0B	/* IR数据高字节     */
#define AP3216C_ALSDATALOW	0x0C	/* ALS数据低字节    */
#define AP3216C_ALSDATAHIGH	0X0D	/* ALS数据高字节    */
#define AP3216C_PSDATALOW	0X0E	/* PS数据低字节     */
#define AP3216C_PSDATAHIGH	0X0F	/* PS数据高字节     */#endif
static int ap3216c_open(struct inode *inode, struct file *filp)
{filp->private_data = &ap3216cdev;/* 初始化AP3216C */ap3216c_write_reg(&ap3216cdev, AP3216C_SYSTEMCONG, 0x04);		/* 复位AP3216C 			*/mdelay(50);														/* AP3216C复位最少10ms 	*/ap3216c_write_reg(&ap3216cdev, AP3216C_SYSTEMCONG, 0X03);		/* 开启ALS、PS+IR 		*/return 0;
}
http://www.lryc.cn/news/22053.html

相关文章:

  • Spring Cache的基本使用与分析
  • 【安全知识】——端口复用隐藏后门
  • Tina_Linux量产测试使用指南_new
  • STC32单片机 普通 I/O 口中断功能介绍和使用
  • 计算机学生如何找到第一份实习?
  • 《Python机器学习》基础代码
  • 【前端】JS异步加载
  • 【MySQL】SQL语言的五个部分
  • 详细的IO面试题汇总
  • 在Linux终端管理你的密码!
  • 【设计模式】策略模式在Java工程中应用
  • Linux驱动开发工程师需要掌握哪些技能?
  • 【人脸识别】FROM:提升遮挡状态下的人脸识别效果
  • 浏览器缓存
  • 【软考 系统架构设计师】论文范文③ 论数据访问层设计技术及其应用
  • 802.11 MCS 的最低SNR分析
  • 用于C++的对象关系映射库—YB.ORM
  • Cesium 100K数据加载 支持弹窗 动态更改位置
  • MySQL管理表
  • 【Java 面试合集】打印一个int整数的32位表示
  • 这样在管理后台里实现 403 页面实在是太优雅了
  • c++提高篇——STL常用算法
  • Materials - DistanceField Nodes
  • 【ARMv8 编程】ARMv8 指令集介绍
  • 大数据之Phoenix基本介绍
  • 算法leetcode|38. 外观数列(多语言实现)
  • 异步交互的关键——Ajax
  • Android自定义View实现打钩签到动画
  • python+pytest接口自动化(3)-接口测试一般流程及方法
  • 《MySQL学习》 表中随机取记录的方式