Linux I2C设备驱动

1. 环境:

1.1 开发板:正点原子 I.MX6U ALPHA V2.2

1.2 开发PC:Ubuntu20.04

1.3 U-boot:uboot-imx-rel_imx_4.1.15_2.1.0_ga.tar.bz2

1.4 LInux内核:linux-imx-rel_imx_4.1.15_2.1.0_ga.tar.bz2

1.5 rootfs:busybox-1.29.0.tar.bz2制作

1.6 交叉编译工具链:gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz

 

2. 硬件说明

2.1 I2C器件:AP3216C(光强度、近距离、红外LED三合一传感器)

3. 设备树修改

3.1 在节点iomuxc/imx6ul-evk下新增pinctrl_i2c1节点,其实这个节点默认已经有了,且配置正确,如下

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         };

 

3.2 在节点"&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 = "i2c-ap3216c";
 9         reg = <0x1e>;
10     };
11 };

 

4. 驱动代码

  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 
 19 #define AP3216C_ADDR        0X1E    /* AP3216C器件地址  */
 20 
 21 /* AP3316C寄存器 */
 22 #define AP3216C_SYSTEMCONG    0x00    /* 配置寄存器       */
 23 #define AP3216C_INTSTATUS    0X01    /* 中断状态寄存器   */
 24 #define AP3216C_INTCLEAR    0X02    /* 中断清除寄存器   */
 25 #define AP3216C_IRDATALOW    0x0A    /* IR数据低字节     */
 26 #define AP3216C_IRDATAHIGH    0x0B    /* IR数据高字节     */
 27 #define AP3216C_ALSDATALOW    0x0C    /* ALS数据低字节    */
 28 #define AP3216C_ALSDATAHIGH    0X0D    /* ALS数据高字节    */
 29 #define AP3216C_PSDATALOW    0X0E    /* PS数据低字节     */
 30 #define AP3216C_PSDATAHIGH    0X0F    /* PS数据高字节     */
 31 
 32 struct ap3216c_device {
 33     dev_t   devno;
 34     struct cdev ap3216c_cdev;
 35     struct class *ap3216c_class;
 36     struct device *ap3216c_dev;
 37     struct device_node *node;
 38     int major;
 39     void *private_data;
 40     unsigned short ir;
 41     unsigned short als;
 42     unsigned short ps;
 43 };
 44 
 45 static struct ap3216c_device *ap3216c_dev;
 46 
 47 
 48 //对寄存器多个写
 49 static s32 ap3216c_write_regs(struct ap3216c_device *dev, u8 reg, u8 *data, u8 len)
 50 {
 51     u8 buf[256];
 52     struct i2c_msg msg;
 53     
 54     struct i2c_client *client = (struct i2c_client *)dev->private_data;
 55     buf[0] = reg;
 56     memcpy(&buf[1], data, len);
 57     msg.addr = client->addr;
 58     msg.flags = 0;
 59     msg.buf = buf;
 60     msg.len = len + 1;
 61 
 62     return i2c_transfer(client->adapter, &msg, 1);
 63 
 64 } 
 65 
 66 //对寄存器单个写
 67 static void ap3216c_write_reg(struct ap3216c_device *dev, u8 reg, u8 data)
 68 {
 69     u8 buf = 0;
 70     buf = data;
 71     ap3216c_write_regs(dev, reg, &buf, 1);
 72 }
 73 
 74 static int ap3216c_read_regs(struct ap3216c_device *dev, u8 reg, void *val, int len)
 75 {
 76     int ret;
 77     struct i2c_msg msg[2];
 78     struct i2c_client *client = (struct i2c_client *)dev->private_data;
 79 
 80     msg[0].addr = client->addr;
 81     msg[0].flags = 0;
 82     msg[0].buf = &reg;
 83     msg[0].len = 1;
 84 
 85     msg[1].addr = client->addr;
 86     msg[1].flags = I2C_M_RD;
 87     msg[1].buf = val;
 88     msg[1].len = len;
 89 
 90     ret = i2c_transfer(client->adapter, msg, 2);
 91     if(2 == ret)
 92     {
 93         ret = 0;
 94     }
 95     else
 96     {
 97         printk("i2c read fail!\n");
 98         ret = -EREMOTEIO;
 99     }
100 
101     return ret;
102 }
103 
104 static unsigned char ap3216c_read_reg(struct ap3216c_device *dev, u8 reg)
105 {
106     u8 data = 0;
107     ap3216c_read_regs(dev, reg, &data, 1);
108     return data;
109 }
110 
111 
112 //读取两个sensor及红外LED的六个寄存器值
113 static void ap3216c_readdata(struct ap3216c_device * dev)
114 {
115     unsigned char i = 0;
116     unsigned char buf[6];
117 
118     for(i = 0; i < 6; i++)
119     {
120         buf[i] = ap3216c_read_reg(dev, AP3216C_IRDATALOW + i);
121     }
122 
123     if(buf[0] & 0x80)
124         dev->ir = 0;
125     else
126         dev->ir = ((unsigned short)buf[1] << 2) | (buf[0] & 0x03);
127     
128     dev->als = ((unsigned short)buf[3] << 8) | buf[2];
129 
130     if(buf[4] & 0x40)
131         dev->ps = 0;
132     else
133         dev->ps = ((unsigned short)(buf[5] & 0x3f) << 4) | (buf[4] & 0x0f);
134 }
135 
136 
137 
138 static int ap3216c_read(struct file *filp, char __user *buf, size_t size, loff_t *f_ops)
139 {
140     short data[3];
141     long err = 0;
142 
143     struct ap3216c_device *dev = (struct ap3216c_device *)filp->private_data;
144     ap3216c_readdata(dev);
145 
146     data[0] = dev->ir;
147     data[1] = dev->als;
148     data[2] = dev->ps;
149 
150     err = copy_to_user(buf, data, sizeof(data));
151 
152     return err;
153 }
154 
155 
156 static int ap3216c_open(struct inode *inode, struct file *filp)
157 {
158     //将ap3216c_dev赋值给文件指针私有数据,以便后续的读写操作
159     filp->private_data = ap3216c_dev;
160 
161     //对ap3216c初始化
162     ap3216c_write_reg(ap3216c_dev, AP3216C_SYSTEMCONG, 0x04);       //复位
163     mdelay(50);     //复位至少10ms,参考ap3216c复位要求
164     ap3216c_write_reg(ap3216c_dev, AP3216C_SYSTEMCONG, 0x03);       //开启ALS、PS、IR功能
165     return 0;
166 }
167 
168 static int ap3216c_release(struct inode *inode, struct file *filp)
169 {
170     return 0;
171 }
172 
173 
174 static const struct file_operations ap3216c_fops = {
175     .owner      =   THIS_MODULE,
176     .open       =   ap3216c_open,
177     .release    =   ap3216c_release,
178     .read       =   ap3216c_read,
179 };
180 
181 
182 static int ap3216c_probe(struct i2c_client *client, const struct i2c_device_id *id)
183 {
184     int ret = -1;
185 
186     //为自定义机构提申请内核内存
187     ap3216c_dev = kmalloc(sizeof(struct ap3216c_device), GFP_KERNEL);
188     if(ap3216c_dev == NULL)
189     {
190         printk(KERN_ERR "kmalloc fail!\n");
191         return -ENOMEM;
192     }
193 
194     //动态申请设备号
195     ret = alloc_chrdev_region(&ap3216c_dev->devno, 0, 1, "ap3216c");
196     if(ret < 0)
197     {
198         printk(KERN_ERR "register major failed!\n");
199         ret = -EINVAL;
200         goto err1;
201     }
202 
203     //获取主设备号
204     ap3216c_dev->major = MAJOR(ap3216c_dev->devno);
205 
206     ///将设备与文件操作函数关联
207     cdev_init(&ap3216c_dev->ap3216c_cdev, &ap3216c_fops);
208 
209     //注册设备
210     cdev_add(&ap3216c_dev->ap3216c_cdev, ap3216c_dev->devno, 1);
211 
212     //创建设备类
213     ap3216c_dev->ap3216c_class = class_create(THIS_MODULE, "ap3216c");
214     if(IS_ERR(ap3216c_dev->ap3216c_class))
215     {
216         printk(KERN_ERR "failed to create class!\n");
217         ret = PTR_ERR(ap3216c_dev->ap3216c_class);
218         goto err2;
219     }
220 
221     //创建设备文件,此函数最后一个参数ap3216c,其实就是设备名,应用程序open打开的也是这个文件名
222     ap3216c_dev->ap3216c_dev = device_create(ap3216c_dev->ap3216c_class, NULL, ap3216c_dev->devno, NULL, "ap3216c");
223     if(IS_ERR(ap3216c_dev->ap3216c_dev))
224     {
225         printk(KERN_ERR "failed to create device!\n");
226         ret = PTR_ERR(ap3216c_dev->ap3216c_dev);
227         goto err3;
228     }
229 
230     //将i2c_client赋值给自定义结构体私有数据,以便后续的读写操作
231     ap3216c_dev->private_data = client;
232 
233     printk("insmod ap3216 driver ok!\n");
234 
235     return 0;
236 
237 err3:
238     class_destroy(ap3216c_dev->ap3216c_class);
239 
240 err2:
241     unregister_chrdev(ap3216c_dev->major, "ap3216c");
242 
243 err1:
244     kfree(ap3216c_dev);
245     
246     return ret;
247 }
248 
249 static int ap3216c_remove(struct i2c_client *client)
250 {
251     cdev_del(&ap3216c_dev->ap3216c_cdev);
252     unregister_chrdev_region(ap3216c_dev->devno, 1);
253     device_destroy(ap3216c_dev->ap3216c_class, ap3216c_dev->devno);
254     class_destroy(ap3216c_dev->ap3216c_class);
255 
256     return 0;
257 }
258 
259 //传统驱动匹配方式
260 static const struct i2c_device_id ap3216c_id[] = {};
261 
262 //设备树驱动匹配方式
263 static const struct of_device_id ap3216c_of_match[] = {
264     {.compatible = "i2c-ap3216c"},
265 };
266 
267 //填充I2C驱动结构其
268 static struct i2c_driver ap3216c_driver = {
269     .probe = ap3216c_probe,
270     .remove = ap3216c_remove,
271     .driver = {
272         .owner = THIS_MODULE,
273         .name = "ap3216c",
274         .of_match_table = ap3216c_of_match,
275     },
276     .id_table = ap3216c_id,     //即使不兼容传统的驱动匹配方式,也要此成员,否则设备树的方式也匹配不上!!!!!!
277 };
278 
279 
280 
281 static int __init i2c_ap3216c_init(void)
282 {
283     int ret = 0;
284 
285     ret = i2c_add_driver(&ap3216c_driver);
286     return ret;
287 }
288 
289 
290 static void __exit i2c_ap3216c_exit(void)
291 {
292     i2c_del_driver(&ap3216c_driver);
293 }
294 
295 
296 
297 module_init(i2c_ap3216c_init);
298 module_exit(i2c_ap3216c_exit);
299 MODULE_LICENSE("Dual BSD/GPL");
View Code

 

5. 测试代码

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <sys/types.h>
 4 #include <sys/stat.h>
 5 #include <fcntl.h>
 6 
 7 int main(int argc, char **argv)
 8 {
 9     int fd, ret = -1;
10     unsigned short databuf[3];
11     unsigned short ir, als, ps;
12 
13     fd = open("/dev/ap3216c", O_RDWR);
14 
15     printf("fd = %d\n", fd);
16     if(fd < 0)
17     {
18         perror("open fail!\n");
19         exit(1);
20     }
21 
22     while(1)
23     {
24         ret = read(fd, databuf, sizeof(databuf));
25         if(0 == ret)
26         {
27             ir = databuf[0];
28             als = databuf[1];
29             ps = databuf[2];
30 
31            printf("ir = %d, als = %d, ps = %d\r\n", ir, als, ps);
32         }
33         usleep(200000); /*100ms */
34     }
35 
36    
37     close(fd);
38     return 0;
39 }
View Code

 

总结:

1. I2C的驱动的probe函数,与平台总线字符设备驱动大致流程相似

2. I2C的读,需要先虚写,才能读,硬件协议如此

3. 结构体i2c_driver成员id_table,一定要填充,否则设备树驱动无法匹配上!

 

posted @ 2021-11-06 14:36  秋水寒林  阅读(281)  评论(0编辑  收藏  举报