exynos4412之I2C client---sht3x-dev
linux kernel version:4.4.38
hardware version:exynos4412-tiny4412 1312B, Sht3x sensor
最近在學習IIC UART SPI,就撿了一個軟的IIC先捏了~
在TB買了sht3x sensor,店家有提供datasheet和PCB
I2C client還是比較簡單的,我所使用的kernel已經支援了I2C adapter, 只要硬體管腳接對,將I2C client的DTS放置到對應的adapter下,就ok了~
下面記錄一下我除錯過程中遇到的坑~
首先,我看tiny4412的PCB,發現CON15上有I2C channel 5引出,那我就簡單點,直接拿來用,發現sensor不能執行,一度懷疑,店家的東西是壞的,為了驗證自己的判斷,還特意去公司其他部門借了一臺示波器抓波形(這個也是臨時學的,之前只會使用傻瓜按鍵autoset)
抓不到波形,因為是臨時學的使用示波器,不自信又懷疑操作不對,但是幸運的是,tiny4412上帶了一塊好的mma7660,於是抓mma7660的波形,發現抓的波形ok~
繼續找問題,去看exynos4412的datasheet, PCB和linux kernel的DTS
1.PCB顯示I2C channel 5的SCL和SDA是接在GPB_6和GPB_7
2.但是datasheet顯示GPB_6和GPB_7並沒有I2C的選項
3.原理圖和datasheet對不上???
4.決定放棄I2C channel 5
5.既然I2C的channel 3是ok的,那我看下有沒有開發板是否有引出
6.很幸運,有引出在con3,於是使用四根杜邦線連線好。進行下一步測試~
7.以為後面會一切順利,結果發現NO,使用i2cdetect等i2c-tools除錯工具進行除錯,甚至還抓波形,發現沒有ACK訊號,說明IIC adapter是ok的,再度懷疑商家產品是壞的???
8.這可咋辦呀!!!接下來就進入了盲猜階段
9.去量了一下MMA7660的電源電壓,發現是3.3V,但是我的sht3x供電是接到CON3上的5V的
10.來一波盲猜換供電引腳(Wide supply voltage range, from 2.4 V to 5.5 V)雖然sht3x說明供電在2.4V~~~5.5V,但是我也不知道咋想的就瞎換。
11.哎,好了,i2cdetect -r -y 3可以發現了
12.此時算是完成了一半了,只需要寫程式和DTS就好了
上程式碼~~~
1 &i2c_3 { 2 samsung,i2c-sda-delay = <100>; 3 samsung,i2c-max-bus-freq = <200000>; 4 status = "okay"; 5 6 mma7660: mma7660@4c { 7 compatible = "fsl,mma7660"; 8 reg = <0x4c>; 9 interrupt-parent = <&gpx3>; 10 interrupts = <1 IRQ_TYPE_EDGE_FALLING>; 11 poll_interval = <100>; 12 input_fuzz = <4>; 13 input_flat = <4>; 14 status = "disabled"; 15 }; 16 17 sht3xdis: sht3xdis@44 { 18 compatible = "ethan,sht3xdis"; 19 reg = <0x44>; 20 poll_interval = <100>; 21 input_fuzz = <4>; 22 input_flat = <4>; 23 status = "okay"; 24 }; 25 };
首先是DTS的部分,在i2c_3的node下,直接將mma7660的dts複製一份,按照格式修改
其中compatible是需要和驅動程式匹配的,reg即為client address
sht3x還有終端引腳,目前還沒實現,後續補上~
1 #include <linux/kernel.h> 2 #include <linux/module.h> 3 #include <linux/slab.h> 4 #include <linux/interrupt.h> 5 #include <linux/irq.h> 6 #include <linux/delay.h> 7 #include <linux/i2c.h> 8 #include <linux/input-polldev.h> 9 #include <linux/hwmon.h> 10 #include <linux/hwmon-sysfs.h> 11 #include <linux/of.h> 12 #include <linux/of_device.h> 13 14 #define DEBUG 1 15 16 #define sht3x_NAME "sht3x" 17 static struct i2c_client *sht3x_client; 18 19 20 #ifdef CONFIG_OF 21 static const struct of_device_id sht3x_dt_ids[] = { 22 { .compatible = "ethan,sht3xdis", }, 23 { /* sentinel */ } 24 }; 25 MODULE_DEVICE_TABLE(of, sht3x_dt_ids); 26 #endif 27 28 static int sht3x_soft_reset(struct i2c_client *client) 29 { 30 int ret = -1; 31 char data[2] = {0x30, 0xa2}; 32 ret = i2c_master_send(client, data, sizeof(data)); 33 if(ret != 2) 34 printk(KERN_INFO "sht3x_soft_reset fail.\n"); 35 return ret; 36 } 37 38 static int sht3x_read_temperature_and_humidity(struct i2c_client *client, unsigned short *temperature, unsigned short *humidity) 39 { 40 int ret = -1; 41 char value[6]; 42 char data[2] = {0x2c, 0x06}; 43 44 45 ret = i2c_master_send(client, data, sizeof(data)); 46 if(ret == sizeof(data)){ 47 memset(value, 0, sizeof(value)); 48 ret = i2c_master_recv(client, value, sizeof(value)); 49 if(ret <= 0){ 50 return -1; 51 } 52 else{ 53 #if 0 54 for(i = 0; i < sizeof(value); i++){ 55 printk(KERN_INFO "value[%d] = 0x%x\n", i, value[i]); 56 } 57 #endif 58 } 59 60 61 } 62 63 if(temperature != NULL){ 64 *temperature = (value[0]<<8)|value[1]; 65 66 } 67 68 if(humidity != NULL){ 69 *humidity = (value[3]<<8)|value[4]; 70 } 71 72 return ret; 73 74 } 75 76 static ssize_t sht3x_show_temperature(struct device *dev, 77 struct device_attribute *attr, char *buf) 78 { 79 unsigned short val = 0; 80 sht3x_read_temperature_and_humidity(sht3x_client, &val, NULL); 81 return sprintf(buf, "%d\n", val); 82 } 83 84 static ssize_t sht3x_show_humidity(struct device *dev, 85 struct device_attribute *attr, char *buf) 86 { 87 unsigned short val = 0; 88 sht3x_read_temperature_and_humidity(sht3x_client, NULL, &val); 89 return sprintf(buf, "%d\n", val); 90 } 91 92 static SENSOR_DEVICE_ATTR(temperature, S_IRUGO, sht3x_show_temperature, NULL, 0); 93 static SENSOR_DEVICE_ATTR(humidity, S_IRUGO, sht3x_show_humidity, NULL, 0); 94 static struct attribute* sht3x_attrs[] = { 95 &sensor_dev_attr_temperature.dev_attr.attr, 96 &sensor_dev_attr_humidity.dev_attr.attr, 97 NULL 98 }; 99 100 static const struct attribute_group sht3x_group = { 101 .attrs = sht3x_attrs, 102 }; 103 104 static int sht3x_probe(struct i2c_client *client, 105 const struct i2c_device_id *id) 106 { 107 int ret; 108 109 sht3x_client = client; 110 sht3x_soft_reset(client); 111 ret = sysfs_create_group(&client->dev.kobj, &sht3x_group); 112 if (ret) { 113 dev_err(&client->dev, "create sysfs group failed!\n"); 114 } 115 printk(KERN_INFO "sht3x_probe success.\n"); 116 return 0; 117 } 118 static int sht3x_remove(struct i2c_client *client) 119 { 120 sysfs_remove_group(&client->dev.kobj, &sht3x_group); 121 printk(KERN_INFO "sht3x_remove success.\n"); 122 return 0; 123 } 124 125 static const struct i2c_device_id sht3x_ids[] = { 126 { "sht3x", 0 }, 127 { }, 128 }; 129 MODULE_DEVICE_TABLE(i2c, sht3x_ids); 130 131 static struct i2c_driver i2c_sht3x_driver = { 132 .driver = { 133 .name = sht3x_NAME, 134 #ifdef CONFIG_OF 135 .of_match_table = sht3x_dt_ids, 136 #endif 137 }, 138 139 .probe = sht3x_probe, 140 .remove = sht3x_remove, 141 .id_table = sht3x_ids, 142 }; 143 144 static int __init init_sht3x(void) 145 { 146 int ret; 147 ret = i2c_add_driver(&i2c_sht3x_driver); 148 printk(KERN_INFO "sht3x sensor driver registered.\n"); 149 return ret; 150 } 151 152 static void __exit exit_sht3x(void) 153 { 154 i2c_del_driver(&i2c_sht3x_driver); 155 printk(KERN_INFO "sht3x sensor driver removed.\n"); 156 } 157 158 module_init(init_sht3x); 159 module_exit(exit_sht3x); 160 161 MODULE_AUTHOR("Freescale Semiconductor, Inc."); 162 MODULE_DESCRIPTION("sht3x sensor driver"); 163 MODULE_LICENSE("GPL");
主要實現了溫度和溼度的獲取,在sysfs註冊了兩個檔案,temperature和humidity,但是exynos4412不支援浮點運算,所以只能先獲取原始資料在應用層在計算轉換成溫溼度~
驅動程式主要實現了兩個函式,reset和read,驅動初始化的時候reset sensor,每次開啟temperature和humidity檔案時執行read函式,0x2c06表示高重複度一次性讀取資料~
應用層程式讀取temperature檔案,然後按照公式計算溫溼度就ok了~
第一次是正常測試,後面一次對著sensor哈一口氣測試,數值有變化~