1. 程式人生 > 其它 >exynos4412之I2C client---sht3x-dev

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哈一口氣測試,數值有變化~