exynos4412之I2C client---sht3x-div
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哈一口气测试,数值有变化~
浙公网安备 33010602011771号