1. 程式人生 > 其它 >Linux I2C裝置驅動

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,一定要填充,否則裝置樹驅動無法匹配上!