1、添加pinctrl信息
首先在设备树文件中添加AP3216C设备的引脚信息,AP3216C使用的是I2C1接口,对应的是UART4_TXD 和 UART4_RXD两个引脚,所以需要在设备树中将这两个引脚复用成I2C1功能,AP3216C设备上还有一个中断引脚,使用了GIO1_IO01,如果用到中断功能的话,同样需要添加引脚信息。在本实验中暂时不使用中断。
打开topeet_emmc_4_3.dts文件,有如下内容:
1 pinctrl_i2c1: i2c1grp {
2 fsl,pins = <
3 MX6UL_PAD_UART4_TX_DATA__I2C1_SCL 0x4001b8b0
4 MX6UL_PAD_UART4_RX_DATA__I2C1_SDA 0x4001b8b0
5 >;
6 };
这里将 UART4_TXD 和 UART4_RXD 这两个 IO 分别复用为 I2C1_SCL 和 I2C1_SDA,电气属性都设置为 0x4001b8b0。
2、在I2C1的设备节点中添加AP3216C设备子节点
打开topeet_emmc_4_3.dts文件,I2C1节点下默认有如下内容:
1 &i2c1 {
2 clock-frequency = <100000>;
3 pinctrl-names = "default";
4 pinctrl-0 = <&pinctrl_i2c1>;
5 status = "okay";
6
7 mag3110@0e {
8 compatible = "fsl,mag3110";
9 reg = <0x0e>;
10 position = <2>;
11 };
12
13 fxls8471@1e {
14 compatible = "fsl,fxls8471";
15 reg = <0x1e>;
16 position = <0>;
17 interrupt-parent = <&gpio5>;
18 interrupts = <0 8>;
19 };
20 };
第 2 行,clock-frequency 属性为 I2C 频率,这里设置为 100KHz。
第 4 行,pinctrl-0 属性指定 I2C 所使用的 IO 为pinctrl_i2c1 子节点。
第 7~11 行,mag3110 是个磁力计,NXP 官方的 EVK 开发板上接了 mag3110,因此 NXP在 i2c1 节点下添加了 mag3110 这个子节点。i.MX6UL终结者开发板上没有用到mag3110,因此可以将此节点删除掉。
第 13~19 行,NXP 官方 EVK 开发板也接了一个 fxls8471,i.MX6UL终结者开发板上同样没有此器件,所以也要将其删除掉。将 i2c1 节点里面原有的 mag3110 和 fxls8471 这两个 I2C 子节点删除,然后添加 ap3216c子节点信息,完成以后的 i2c1 节点内容如下所示:
1 &i2c1 {
2 clock-frequency = <100000>;
3 pinctrl-names = "default";
4 pinctrl-0 = <&pinctrl_i2c1>;
5 status = "okay";
6
7 ap3216c@1e {
8 compatible = "ap3216c";
9 reg = <0x1e>;
10 };
11 };
第 7 行,ap3216c 子节点,@后面的“1e”是 ap3216c 的器件地址。
第 8 行,设置 compatible 值为“ap3216c”。
第 9 行,reg 属性也是设置 ap3216c 器件地址的,因此 reg 设置为 0x1e。
设备树文件修改完成后,使用“make dtbs”命令重新编译下载到开发板上,启动Linux系统。在/sys/bus/i2c/devices 目录下存放着所有 I2C 设备,如果设备树修改正确的话,会在sys/bus/i2c/devices 目录下看到一个名为“0-001e”的子目录,如图 2.1所示:
上图中的“0-001e”目录就是ap3216c的设备目录,“1e”是ap3216c设备的地址,进入0-001e目录可以看到ap3216c设备的相关信息,有一个name文件,用于保存设备名称,可以使用“cat”命令查看,如图 2.2所示:
本实验例程路径:i.MX6UL终结者光盘资料/06_Linux驱动例程/18_ap3216
创建ap3216c_iic.c和ap3216c_reg.h两个文件,ap3216c_reg.h用于保存ap3216c设备的寄存器信息,ap3216c_iic.c是驱动文件。
ap3216c_reg.h文件内容如下:
1 #ifndef AP3216C_H
2 #define AP3216C_H
3
4 #define AP3216C_ADDR 0X1E /* AP3216C器件地址 */
5
6 /* AP3316C寄存器 */
7 #define AP3216C_SYSTEMCONG 0x00 /* 配置寄存器 */
8 #define AP3216C_INTSTATUS 0X01 /* 中断状态寄存器 */
9 #define AP3216C_INTCLEAR 0X02 /* 中断清除寄存器 */
10 #define AP3216C_IRDATALOW 0x0A /* IR数据低字节 */
11 #define AP3216C_IRDATAHIGH 0x0B /* IR数据高字节 */
12 #define AP3216C_ALSDATALOW 0x0C /* ALS数据低字节 */
13 #define AP3216C_ALSDATAHIGH 0X0D /* ALS数据高字节 */
14 #define AP3216C_PSDATALOW 0X0E /* PS数据低字节 */
15 #define AP3216C_PSDATAHIGH 0X0F /* PS数据高字节 */
16
17 #endif
ap3216c_iic.c文件内容如下:
1 #include <linux/types.h>
2 #include <linux/kernel.h>
3 #include <linux/delay.h>
4 #include <linux/ide.h>
5 #include <linux/init.h>
6 #include <linux/module.h>
7 #include <linux/errno.h>
8 #include <linux/gpio.h>
9 #include <linux/cdev.h>
10 #include <linux/device.h>
11 #include <linux/of_gpio.h>
12 #include <linux/semaphore.h>
13 #include <linux/timer.h>
14 #include <linux/i2c.h>
15 #include <asm/mach/map.h>
16 #include <asm/uaccess.h>
17 #include <asm/io.h>
18 #include "ap3216c_reg.h"
19
20 #define AP3216C_CNT 1
21 #define AP3216C_NAME "ap3216c"
22
23 struct ap3216c_dev {
24 dev_t devid; /* 设备号 */
25 struct cdev cdev; /* cdev */
26 struct class *class; /* 类 */
27 struct device *device; /* 设备 */
28 struct device_node *nd; /* 设备节点 */
29 int major; /* 主设备号 */
30 void *private_data; /* 私有数据 */
31 unsigned short ir, als, ps; /* 三个光传感器数据 */
32 };
33
34 static struct ap3216c_dev ap3216cdev;
35
36 /*
37 * @description : 从ap3216c读取多个寄存器数据
38 * @param - dev: ap3216c设备
39 * @param - reg: 要读取的寄存器首地址
40 * @param - val: 读取到的数据
41 * @param - len: 要读取的数据长度
42 * @return : 操作结果
43 */
44 static int ap3216c_read_regs(struct ap3216c_dev *dev, u8 reg, void *val, int len)
45 {
46 int ret;
47 struct i2c_msg msg[2];
48 struct i2c_client *client = (struct i2c_client *)dev->private_data;
49
50 /* msg[0]为发送要读取的首地址 */
51 msg[0].addr = client->addr; /* ap3216c地址 */
52 msg[0].flags = 0; /* 标记为发送数据 */
53 msg[0].buf = ® /* 读取的首地址 */
54 msg[0].len = 1; /* reg长度*/
55
56 /* msg[1]读取数据 */
57 msg[1].addr = client->addr; /* ap3216c地址 */
58 msg[1].flags = I2C_M_RD; /* 标记为读取数据*/
59 msg[1].buf = val; /* 读取数据缓冲区 */
60 msg[1].len = len; /* 要读取的数据长度*/
61
62 ret = i2c_transfer(client->adapter, msg, 2);
63 if(ret == 2) {
64 ret = 0;
65 } else {
66 printk("i2c rd failed=%d reg=%06x len=%d\n",ret, reg, len);
67 ret = -EREMOTEIO;
68 }
69 return ret;
70 }
71
72 /*
73 * @description : 向ap3216c多个寄存器写入数据
74 * @param - dev: ap3216c设备
75 * @param - reg: 要写入的寄存器首地址
76 * @param - val: 要写入的数据缓冲区
77 * @param - len: 要写入的数据长度
78 * @return : 操作结果
80 static s32 ap3216c_write_regs(struct ap3216c_dev *dev, u8 reg, u8 *buf, u8 len)
81 {
82 u8 b[256];
83 struct i2c_msg msg;
84 struct i2c_client *client = (struct i2c_client *)dev->private_data;
85
86 b[0] = reg; /* 寄存器首地址 */
87 memcpy(&b[1],buf,len); /* 将要写入的数据拷贝到数组b里面 */
88
89 msg.addr = client->addr; /* ap3216c地址 */
90 msg.flags = 0; /* 标记为写数据 */
91
92 msg.buf = b; /* 要写入的数据缓冲区 */
93 msg.len = len + 1; /* 要写入的数据长度 */
94
95 return i2c_transfer(client->adapter, &msg, 1);
96 }
97
98 /*
99 * @description : 读取ap3216c指定寄存器值,读取一个寄存器
100 * @param - dev: ap3216c设备
101 * @param - reg: 要读取的寄存器
102 * @return : 读取到的寄存器值
103 */
104 static unsigned char ap3216c_read_reg(struct ap3216c_dev *dev, u8 reg)
105 {
106 u8 data = 0;
107
108 ap3216c_read_regs(dev, reg, &data, 1);
109 return data;
110
111 #if 0
112 struct i2c_client *client = (struct i2c_client *)dev->private_data;
113 return i2c_smbus_read_byte_data(client, reg);
114 #endif
115 }
116
117 /*
118 * @description : 向ap3216c指定寄存器写入指定的值,写一个寄存器
119 * @param - dev: ap3216c设备
120 * @param - reg: 要写的寄存器
121 * @param - data: 要写入的值
122 * @return : 无
123 */
124 static void ap3216c_write_reg(struct ap3216c_dev *dev, u8 reg, u8 data)
125 {
126 u8 buf = 0;
127 buf = data;
128 ap3216c_write_regs(dev, reg, &buf, 1);
129 }
130
131 /*
132 * @description : 读取AP3216C的数据,读取原始数据,包括ALS,PS和IR, 注意!
133 * : 如果同时打开ALS,IR+PS的话两次数据读取的时间间隔要大于112.5ms
134 * @param - ir : ir数据
135 * @param - ps : ps数据
136 * @param - ps : als数据
137 * @return : 无。
138 */
139 void ap3216c_readdata(struct ap3216c_dev *dev)
140 {
141 unsigned char i =0;
142 unsigned char buf[6];
143
144 /* 循环读取所有传感器数据 */
145 for(i = 0; i < 6; i++)
146 {
147 buf[i] = ap3216c_read_reg(dev, AP3216C_IRDATALOW + i);
148 }
149
150 if(buf[0] & 0X80) /* IR_OF位为1,则数据无效 */
151 dev->ir = 0;
152 else /* 读取IR传感器的数据 */
153 dev->ir = ((unsigned short)buf[1] << 2) | (buf[0] & 0X03);
154
155 dev->als = ((unsigned short)buf[3] << 8) | buf[2];/* 读取ALS传感器的数据*/
156
157 if(buf[4] & 0x40) /* IR_OF位为1,则数据无效 */
158 dev->ps = 0;
159 else /* 读取PS传感器的数据 */
160 dev->ps = ((unsigned short)(buf[5] & 0X3F) << 4) | (buf[4] & 0X0F);
161 }
162
163 /*
164 * @description : 打开设备
165 * @param - inode : 传递给驱动的inode
166 * @param - filp : 设备文件,file结构体有个叫做private_data的成员变量
167 * 一般在open的时候将private_data指向设备结构体。
168 * @return : 0 成功;其他 失败
169 */
170 static int ap3216c_open(struct inode *inode, struct file *filp)
171 {
172 filp->private_data = &ap3216cdev;
173
174 /* 初始化AP3216C */
175 ap3216c_write_reg(&ap3216cdev, AP3216C_SYSTEMCONG, 0x04);/* 复位
AP3216C */
176 mdelay(50); /* AP3216C复位最少10ms */
177 ap3216c_write_reg(&ap3216cdev, AP3216C_SYSTEMCONG, 0X03); /* 开启ALS、
PS+IR */
178 return 0;
179 }
180
181 /*
182 * @description : 从设备读取数据
183 * @param - filp : 要打开的设备文件(文件描述符)
184 * @param - buf : 返回给用户空间的数据缓冲区
185 * @param - cnt : 要读取的数据长度
186 * @param - offt : 相对于文件首地址的偏移
187 * @return : 读取的字节数,如果为负值,表示读取失败
188 */
189 static ssize_t ap3216c_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
190 {
191 short data[3];
192 long err = 0;
193
194 struct ap3216c_dev *dev = (struct ap3216c_dev *)filp->private_data;
195
196 ap3216c_readdata(dev);
197
198 data[0] = dev->ir;
199 data[1] = dev->als;
200 data[2] = dev->ps;
201 err = copy_to_user(buf, data, sizeof(data));
202 return 0;
203 }
204
205 /*
206 * @description : 关闭/释放设备
207 * @param - filp : 要关闭的设备文件(文件描述符)
208 * @return : 0 成功;其他 失败
209 */
210 static int ap3216c_release(struct inode *inode, struct file *filp)
211 {
212 return 0;
213 }
214
215 /* AP3216C操作函数 */
216 static const struct file_operations ap3216c_ops = {
217 .owner = THIS_MODULE,
218 .open = ap3216c_open,
219 .read = ap3216c_read,
220 .release = ap3216c_release,
221 };
222
223 /*
224 * @description : i2c驱动的probe函数,当驱动与
225 * 设备匹配以后此函数就会执行
226 * @param - client : i2c设备
227 * @param - id : i2c设备ID
228 * @return : 0,成功;其他负值,失败
229 */
230 static int ap3216c_probe(struct i2c_client *client, const struct i2c_device_id *id)
231 {
232 /* 1、构建设备号 */
233 if (ap3216cdev.major) {
234 ap3216cdev.devid = MKDEV(ap3216cdev.major, 0);
235 register_chrdev_region(ap3216cdev.devid, AP3216C_CNT, AP3216C_NAME);
236 } else {
237 alloc_chrdev_region(&ap3216cdev.devid, 0, AP3216C_CNT, AP3216C_NAME);
238 ap3216cdev.major = MAJOR(ap3216cdev.devid);
239 }
240
241 /* 2、注册设备 */
242 cdev_init(&ap3216cdev.cdev, &ap3216c_ops);
243 cdev_add(&ap3216cdev.cdev, ap3216cdev.devid, AP3216C_CNT);
244
245 /* 3、创建类 */
246 ap3216cdev.class = class_create(THIS_MODULE, AP3216C_NAME);
247 if (IS_ERR(ap3216cdev.class)) {
248 return PTR_ERR(ap3216cdev.class);
249 }
250
251 /* 4、创建设备 */
252 ap3216cdev.device = device_create(ap3216cdev.class, NULL,
ap3216cdev.devid, NULL, AP3216C_NAME);
253 if (IS_ERR(ap3216cdev.device)) {
254 return PTR_ERR(ap3216cdev.device);
255 }
256
257 ap3216cdev.private_data = client;
258
259 return 0;
260 }
261
262 /*
263 * @description : i2c驱动的remove函数,移除i2c驱动的时候此函数会执行
264 * @param - client : i2c设备
265 * @return : 0,成功;其他负值,失败
266 */
267 static int ap3216c_remove(struct i2c_client *client)
268 {
269 /* 删除设备 */
270 cdev_del(&ap3216cdev.cdev);
271 unregister_chrdev_region(ap3216cdev.devid, AP3216C_CNT);
272
273 /* 注销掉类和设备 */
274 device_destroy(ap3216cdev.class, ap3216cdev.devid);
275 class_destroy(ap3216cdev.class);
276 return 0;
277 }
278
279 /* 传统匹配方式ID列表 */
280 static const struct i2c_device_id ap3216c_id[] = {
281 {"ap3216c", 0},
282 {}
283 };
284
285 /* 设备树匹配列表 */
286 static const struct of_device_id ap3216c_of_match[] = {
287 { .compatible = "ap3216c" },
288 { /* Sentinel */ }
289 };
290
291 /* i2c驱动结构体 */
292 static struct i2c_driver ap3216c_driver = {
293 .probe = ap3216c_probe,
294 .remove = ap3216c_remove,
295 .driver = {
296 .owner = THIS_MODULE,
297 .name = "ap3216c",
298 .of_match_table = ap3216c_of_match,
299 },
300 .id_table = ap3216c_id,
301 };
302
303 /*
304 * @description : 驱动入口函数
305 * @param : 无
306 * @return : 无
307 */
308 static int __init ap3216c_init(void)
309 {
310 int ret = 0;
311
312 ret = i2c_add_driver(&ap3216c_driver);
313 return ret;
314 }
315
316 /*
317 * @description : 驱动出口函数
318 * @param : 无
319 * @return : 无
320 */
321 static void __exit ap3216c_exit(void)
322 {
323 i2c_del_driver(&ap3216c_driver);
324 }
325
326 /* module_i2c_driver(ap3216c_driver) */
327
328 module_init(ap3216c_init);
329 module_exit(ap3216c_exit);
330 MODULE_LICENSE("GPL");
331 MODULE_AUTHOR("topeet");
第 23~32 行,ap3216c 设备结构体,第 30 行的 private_data 成员变量用于存放 ap3216c 对应的 i2c_client。第 31 行的 ir、als 和 ps 分别存储 AP3216C 的 IR、ALS 和 PS 数据。
第 34 行,定义一个 ap3216c_dev 类型的设备结构体变量 ap3216cdev。
第 44~70 行,ap3216c_read_regs 函数实现多字节读取,但是 AP3216C 好像不支持连续多字节读取,此函数在测试其他 I2C 设备的时候可以实现多给字节连续读取,但是在 AP3216C 上不能连续读取多个字节。不过读取一个字节没有问题的。
第 80~96 行,ap3216c_write_regs 函数实现连续多字节写操作。
第 104~115 行,ap3216c_read_reg 函数用于读取 AP3216C 的指定寄存器数据,用于一个寄存器的数据读取。
第 124~129 行,ap3216c_write_reg 函数用于向 AP3216C 的指定寄存器写入数据,用于一个寄存器的数据写操作。
第 139~161 行,读取 AP3216C 的 PS、ALS 和 IR 等传感器原始数据值。
第 170~221 行,标准的字符设备驱动框架。
第 230~260 行,ap3216c_probe 函数,当 I2C 设备和驱动匹配成功以后此函数就会执行,和platform 驱动框架一样。此函数前面都是标准的字符设备注册代码,最后面会将此函数的第一个参数 client 传递给 ap3216cdev 的 private_data 成员变量。
第 280~283 行,ap3216c_id 匹配表,i2c_device_id 类型。用于传统的设备和驱动匹配,也就是没有使用设备树的时候。
第 286~289 行,ap3216c_of_match 匹配表,of_device_id 类型,用于设备树设备和驱动匹配。这里只写了一个 compatible 属性,值为“ap3216c”。
第 292~301 行,ap3216c_driver 结构体变量,i2c_driver 类型。
第 308~314 行,驱动入口函数 ap3216c_init,此函数通过调用 i2c_add_driver 来向 Linux 内核注册 i2c_driver,也就是 ap3216c_driver。
第 321~324 行,驱动出口函数 ap3216c_exit,此函数通过调用 i2c_del_driver 来注销掉前面注册的 ap3216c_driver。
创建ap3216c_test.c文件,具体内容如下:
1 #include "stdio.h"
2 #include "unistd.h"
3 #include "sys/types.h"
4 #include "sys/stat.h"
5 #include "sys/ioctl.h"
6 #include "fcntl.h"
7 #include "stdlib.h"
8 #include "string.h"
9 #include <poll.h>
10 #include <sys/select.h>
11 #include <sys/time.h>
12 #include <signal.h>
13 #include <fcntl.h>
14
15 /*
16 * @description : main主程序
17 * @param - argc : argv数组元素个数
18 * @param - argv : 具体参数
19 * @return : 0 成功;其他 失败
20 */
21 int main(int argc, char *argv[])
22 {
23 int fd;
24 char *filename;
25 unsigned short databuf[3];
26 unsigned short ir, als, ps;
27 int ret = 0;
28
29 if (argc != 2) {
30 printf("Error Usage!\r\n");
31 return -1;
32 }
33
34 filename = argv[1];
35 fd = open(filename, O_RDWR);
36 if(fd < 0) {
37 printf("can't open file %s\r\n", filename);
38 return -1;
39 }
40
41 while (1) {
42 ret = read(fd, databuf, sizeof(databuf));
43 if(ret == 0) { /* 数据读取成功 */
44 ir = databuf[0]; /* ir传感器数据 */
45 als = databuf[1]; /* als传感器数据 */
46 ps = databuf[2]; /* ps传感器数据 */
47 printf("ir = %d, als = %d, ps = %d\r\n", ir, als, ps);
48 }
49 usleep(200000); /*100ms */
50 }
51 close(fd); /* 关闭文件 */
52 return 0;
53 }
应用测试程序就是在 while 循环中不断的读取 AP3216C 的设备文件,从而得到 ir、als 和 ps 这三个数据值,然后将其输出到终端上。