开发环境为Ubuntu16.04LTS+vscode,使用了正点原子IMX6ULL开发板,板子上运行的是正点原子的出厂系统
参考了这篇文章(温湿度传感器SHTC3驱动开发(一)小白也能轻松理解_启希的博客-CSDN博客_shtc3

SHTC3简介

SHTC3是一款I2C温湿度传感器,测量的精度为湿度:±2%,温度±0.2℃
一些详细的参数可以在数据手册里找到,这里列几个重要的
设备地址为0x70

一个测量周期包概括五个步骤:
1、唤醒SHTC3:先发送写入指令(0xE0),再发送唤醒指令高位(0x35),再发送唤醒指令低位(0x17)。
2、等待唤醒:数据手册上写的最大唤醒时间是240us,等待的时间大于这个就行了。
3、发送采集指令:先发送写入指令(0xE0),再发送采集指令的高位和低位。采集指令有多个,根据需要自行选择,见上文。
4、接收数据:发送读取指令(0xE1),连续接收6个字节数据。如果采集的指令是先存温度,那么这6个字节的第1-2个字节就是温度数值,第3个字节是温度校验。第4-5个字节是湿度数值,第6个字节是湿度校验。如果采集的指令是先存湿度,则前3个字节和后3个字节相反。
5、进入睡眠:发送写入指令,再发送睡眠指令进入睡眠。

不同的采集指令对应不同的采集模式:

温湿度计算公式:

驱动程序、测试程序编写

修改设备树

芯片与SHTC3通过I2C交流,首先需要在设备树里添加设备节点
在i2c1节点下添加设备:

编译设备树,并拷贝到nfs目录或tftp目录:
make dtbs
cp imx6ull-14x14-nand-4.3-800x480-c.dtb imx6ull-14x14-nand-4.3-480x272-c.dtb imx6ull-14x14-nand-7-800x480-c.dtb imx6ull-14x14-nand-7-1024x600-c.dtb imx6ull-14x14-nand-hdmi.dtb imx6ull-14x14-nand-vga.dtb /home/zjz/linux/nfs/ -f
在uboot通过网络测试dtb文件:
tftp 83000000 imx6ull-alientek-nand.dtb
或nfs 83000000 192.168.1.123:/home/zjz/linux/nfs/imx6ull-alientek-nand.dtb (实际使用时出现卡死,下载不动,但是指令应该是没问题的)
更新设备树查看i2c设备:

驱动程序

linux I2C驱动框架分为两部分,I2C总线驱动和I2C设备驱动。总线部分驱动由厂商提供,设备驱动部分由用户自行编写。
驱动程序框架搭建可以参考文章开头提到的博客,驱动源码放下面:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include "shtc3reg.h"
#include <linux/semaphore.h>#define SHTC3_CNT       1
#define SHTC3_NAME      "shtc3"struct shtc3_dev{dev_t devid;struct cdev cdev;int major;int minor;struct class* class;struct device* device;void * private_data;unsigned short T,RH;/*温湿度数据*///设置互斥量,同一时间只能有一个应用在读取温湿度struct semaphore sem;
};struct shtc3_dev shtc3dev;int shtc3_open(struct inode *inode,struct file *filp)
{filp->private_data = &shtc3dev;/*上锁*/down(&shtc3dev.sem);return 0;
}static ssize_t shtc3_read(struct file *filp, char __user *buf,size_t cnt, loff_t *offt)
{unsigned char data[SHTC1_RESPONSE_LENGTH];short real_data[2];int ret = 0;int val;//获取私有数据struct shtc3_dev* dev = (struct shtc3_dev *)filp->private_data;/*向用户返回SHTC3的原始数据*///发送唤醒指令,等待唤醒成功ret = i2c_master_send(dev->private_data, shtc1_cmd_wakeup, SHTC1_CMD_LENGTH);if (ret != SHTC1_CMD_LENGTH) {printk( "failed to send wakeup command: %d\n", ret);return ret < 0 ? ret : -EIO;}mdelay(1);//延时1ms等待唤醒,大于240us即可//发送读取数据指令,选择读取模式,这里的是0x7ca2,长度是2字节ret = i2c_master_send(dev->private_data, shtc1_cmd_normalmode_rec_clockstr_enable_Tfirst, SHTC1_CMD_LENGTH);//模式为normal,clock stretching enable,先读取温度数据,后读取湿度数据if (ret != SHTC1_CMD_LENGTH) {printk("failed to send receive command: %d\n", ret);return ret < 0 ? ret : -EIO;}mdelay(1);//延时1ms,等待数据获取//接收数据,保存在data中ret = i2c_master_recv(dev->private_data, data, SHTC1_RESPONSE_LENGTH);if (ret != SHTC1_RESPONSE_LENGTH) {printk("failed to read values: %d\n", ret);return ret < 0 ? ret : -EIO;}/** From datasheet:* T = -45 + 175 * ST / 2^16* RH = 100 * SRH / 2^16** Adapted for integer fixed point (3 digit) arithmetic.*///这里温度的数据处理要特别注意,因为Linux内核不支持浮点运算,//所以想要多保留小数点后几位需要特别处理//我们保留小数点后三位,所以放大1000倍,正常来说,根据公式//会这样写((175000 * val) >> 16) - 45000;//但是会有问题,val是一个5位数得值,所以175000*val超过了int数据类型范围了//所以这里直接把175000 >> 3后,再乘以val,然后再 >> 13,减去45000//就得到这个了((21875 * val) >> 13) - 45000//湿度保留小数点后两位//先把10000 >> 3后,再做处理val = be16_to_cpup((__be16 *)data);dev->T = ((21875 * val) >> 13) - 45000;val = be16_to_cpup((__be16 *)(data + 3));dev->RH = ((1250 * val) >> 13);real_data[0] = dev->T;real_data[1] = dev->RH;ret = -1;ret = copy_to_user(buf,real_data,sizeof(real_data));if(ret < 0){printk("copy to user fail\r\n");}//printk("kernel message T=%d RH=%d\r\n",dev->T,dev->RH);//发送睡眠指令ret = i2c_master_send(dev->private_data, shtc1_cmd_sleep, SHTC1_CMD_LENGTH);if (ret != SHTC1_CMD_LENGTH) {printk("failed to send sleep command: %d\n", ret);return ret < 0 ? ret : -EIO;}return ret;
}int shtc3_release (struct inode *inode, struct file *filp)
{int ret = 0;//获取私有数据struct shtc3_dev* dev = (struct shtc3_dev *)filp->private_data;//发送睡眠指令ret = i2c_master_send(dev->private_data, shtc1_cmd_sleep, SHTC1_CMD_LENGTH);if (ret != SHTC1_CMD_LENGTH) {printk("failed to send sleep command: %d\n", ret);return ret < 0 ? ret : -EIO;}/*释放锁*/up(&shtc3dev.sem);return ret;
}struct file_operations shtc3_fops={.owner = THIS_MODULE,.open = shtc3_open,.read = shtc3_read,.release = shtc3_release,
};static int shtc3_probe(struct i2c_client *client,const struct i2c_device_id *id)
{int ret;struct device *dev = &client->dev;char id_reg[2];sema_init(&shtc3dev.sem,1);/*搭建字符设备驱动框架*/shtc3dev.major = 0;if(shtc3dev.major){//给定设备号shtc3dev.devid = MKDEV(shtc3dev.major,0);ret = register_chrdev_region(shtc3dev.devid,SHTC3_CNT,SHTC3_NAME);}else{//未给定设备号ret = alloc_chrdev_region(&shtc3dev.devid,0,SHTC3_CNT,SHTC3_NAME);shtc3dev.major = MAJOR(shtc3dev.devid);shtc3dev.minor = MINOR(shtc3dev.devid);}if(ret < 0){printk("shtc3dev module fail devid\r\n");goto fail_devid;}else{printk("major:%d minor:%d\r\n",shtc3dev.major,shtc3dev.minor);}/*注册字符设备*/shtc3dev.cdev.owner = THIS_MODULE;cdev_init(&shtc3dev.cdev,&shtc3_fops);ret = cdev_add(&shtc3dev.cdev,shtc3dev.devid,SHTC3_CNT);if(ret < 0){printk("ap3216c module fail cdev\r\n");goto fail_cdev;}/*自动添加设备节点*///初始化类shtc3dev.class = class_create(THIS_MODULE,SHTC3_NAME);if(IS_ERR(shtc3dev.class)){ret = PTR_ERR(shtc3dev.class);printk("key module failed class\r\n");goto fail_class;}//初始化设备shtc3dev.device = device_create(shtc3dev.class,NULL,shtc3dev.devid,NULL,SHTC3_NAME);if(IS_ERR(shtc3dev.device)){ret = PTR_ERR(shtc3dev.device);printk("key module failed device\r\n");goto fail_device;}//将从机地址传入shtc3devshtc3dev.private_data = client;//shtc3自检//唤醒ret = i2c_master_send(client, shtc1_cmd_wakeup, SHTC1_CMD_LENGTH);if (ret != SHTC1_CMD_LENGTH) {dev_err(dev, "could not send wakeup command: %d\n", ret);return ret < 0 ? ret : -ENODEV;}mdelay(1);//延时1ms,等待延时完成/* 由数据手册可知,有一个efc8寄存器用于验证传感器是否能正常沟通,所以我们这里读取该寄存器 */ret = i2c_master_send(client, shtc1_cmd_read_id_reg, SHTC1_CMD_LENGTH);if (ret != SHTC1_CMD_LENGTH) {dev_err(dev, "could not send read_id_reg command: %d\n", ret);return ret < 0 ? ret : -ENODEV;}/* 读取完,再接收数据,进行验证 */ret = i2c_master_recv(client, id_reg, sizeof(id_reg));if (ret != sizeof(id_reg)) {dev_err(dev, "could not read ID register: %d\n", ret);return -ENODEV;}if ((id_reg[1] & SHTC1_ID_REG_MASK) != SHTC1_ID) {dev_err(dev, "ID register doesn't match\n");return -ENODEV;}printk("shtc3 check ID success\r\n");printk("shtc3_probe\r\n");return 0;fail_device:device_destroy(shtc3dev.class,shtc3dev.devid);
fail_class:class_destroy(shtc3dev.class);
fail_cdev:cdev_del(&shtc3dev.cdev);
fail_devid:unregister_chrdev_region(shtc3dev.devid,SHTC3_CNT);return ret;
}static int shtc3_remove(struct i2c_client* client)
{//发送睡眠指令int ret = i2c_master_send(shtc3dev.private_data, shtc1_cmd_sleep, SHTC1_CMD_LENGTH);if (ret != SHTC1_CMD_LENGTH) {printk("failed to send sleep command: %d\n", ret);return ret < 0 ? ret : -EIO;}//注销cdevcdev_del(&shtc3dev.cdev);//注销设备号unregister_chrdev_region(shtc3dev.devid,SHTC3_CNT);//摧毁设备device_destroy(shtc3dev.class,shtc3dev.devid);//摧毁类class_destroy(shtc3dev.class);printk("shtc3 remove\r\n");return 0;
}//传统匹配方法
static const struct i2c_device_id shtc3_id[]={{"shtc3",0},{ }
};//设备数匹配方法
static struct of_device_id shtc3_dt_id[]={{.compatible = "fzu,shtc3"},{ }
};static struct i2c_driver shtc3_i2c_driver={.probe = shtc3_probe,.remove = shtc3_remove,.id_table = shtc3_id,.driver = {.name = "shtc3",.owner = THIS_MODULE,.of_match_table = of_match_ptr(shtc3_dt_id),},
};static int __init shtc3_init(void)
{printk("i2c module init\r\n");return i2c_add_driver(&shtc3_i2c_driver);
}static void __exit shtc3_exit(void)
{i2c_del_driver(&shtc3_i2c_driver);printk("i2c module exit\r\n");
}module_init(shtc3_init);
module_exit(shtc3_exit);

测试程序

编写一个读取温湿度的测试程序

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/time.h>/*
*argc:应用程序参数个数
*argv[]:参数内容字符串
*./shtc3 /dev/shtc3 读取数据
*/
int main(int argc,char *argv[])
{int fd;unsigned short data[2];float T,RH;int ret = -1;if(argc!=2){printf("usage error\r\n");return -1;}char *filename;filename = argv[1];fd = open(filename,O_RDONLY);if(fd < 0){printf("open file %s failed\r\n",filename);return -1;}while(1){ret = read(fd,data,sizeof(data));//ret = fread(data, sizeof(unsigned short), 2, (FILE*)fdopen(fd, "r"));printf("reading shtc3:%d\r\n",ret);printf("sizeof data:%d\r\n",sizeof(data));if(ret >= 0){        T = (float)data[0]/1000 ;RH = (float)data[1]/1000;printf("T:%.2f RH:%.2f \r\n",T,RH);}usleep(500000);}close(fd);return ret;
}

linux温湿度传感器SHTC3驱动开发相关推荐

  1. 温湿度传感器SHTC3驱动开发(一)小白也能轻松理解

    一 .首先了解设备硬件原理图 首先在公司干活,要你开发一个设备驱动,那你的老大必须得给你的东西如下: 开发板主板硬件原理图 驱动设备的硬件原理图 驱动的设备的数据手册(datasheet) 一个硬件正 ...

  2. 嵌入式linux ASoC架构声卡驱动开发

    嵌入式linux ASoC架构声卡驱动开发 文章目录 嵌入式linux ASoC架构声卡驱动开发 需求分析 ASoC架构下声卡驱动代码结构 codec驱动 snd_soc_register_codec ...

  3. Linux下LED设备驱动开发(LED灯实现闪烁)

    文章目录 一.配置连接说明 二.更新设备树 (1)将led灯引脚添加到pinctrl子系统 (2)设备树中添加LDE灯的设备树节点 (3)编译更新设备树 三.驱动开发与测试 (1)编写设备驱动代码 ( ...

  4. linux open函数_Linux驱动开发 / 字符设备驱动内幕 (1)

    哈喽,我是老吴,继续记录我的学习心得. 一.保持专注的几个技巧 将最重要的事放在早上做. 待在无干扰环境下,比如图书馆. 意识到刚坐下开始投入工作前,有点负面小情绪是特别正常的现象. 让"开 ...

  5. AHT20温湿度传感器STM32-I2C驱动,替代DHT11/DHT12/AM2320/SHT20/SHT30,IIC代码兼容AHT10/15-MEMS温湿度传感器

    AHT20是国内奥松生成的I2C接口的MEMS温湿度传感器,ADC位数为20Bit,具有体积小.精度高.成本低等优点.相较于AHT10,最显著的变化是体积由 5*4*1.6mm,缩小到 3*3*1.0 ...

  6. pcDuino的linux移植五GPIO驱动开发

    2019独角兽企业重金招聘Python工程师标准>>> 为首的亮,灭.同时如果你GPIO4,GPIO5接个LED,也会跟着亮,灭. 开发环境: 系统:Ubuntu的 一,硬件介绍 仔 ...

  7. Linux DSA Net Switch驱动开发

    参考 Atheros QCA8337交换芯片驱动开发 Linux虚拟网络设备之bridge(桥) phy 驱动与 switch 驱动 ALinux网桥的实现分析与使用 DSA switch confi ...

  8. 实时linux下的PCI驱动开发(上)

    第一篇博客,忆苦思甜下先,当然,我尽量长话短说,但说来话长倒也无妨......这是我研究生阶段写的第一个Linux驱动,一入Linux深似海,从此Windows是路人.那是2009年冬天的第一场雪,王 ...

  9. Linux下按键设备驱动开发以及对中断的上半部分和下半部分详细介绍

    文章目录 一.编写并且加载设备树插件 (1)检测管脚是否占用 (2)添加设备树插件 (3)加载设备树插件 二.中断相关函数 (1)request_irq中断注册函数 (2)free_irq中断注销函数 ...

  10. linux can总线接收数据串口打包上传_使用Zedboard开发板学习Linux的移植及驱动开发...

    本文是昨天发的文章<龙芯杯CPU设计竞赛与ZYNQ设计流程介绍>接续部分.重点介绍传统方式的Linux移植和Xilinx的Petalinux的快速移植开发两种. 部分硬件设计中需要CPU完 ...

最新文章

  1. nginx 开发一个简单的 HTTP 模块
  2. java游戏下载ios_java浏览器下载
  3. python什么是高阶函数_说说 Python 中的高阶函数
  4. 全球最难翻译的十大单词
  5. oracle怎么判断是裸设备安装,Oracle 9i下以裸设备方式安装数据库
  6. Promethus搭建 K8S 集群节点资源监控系统
  7. 面向对象的特征有哪些方面?
  8. 诗与远方:无题(七十五)
  9. DAOSquare将于今晚9点-12点进行两轮ITO
  10. 前端中怎么把网页多个文件夹的内容整合成一个_web前端学习笔记
  11. FreeCAD快速开始
  12. 网页中使用iconfont图标
  13. Wireshark配置安装以及抓包教程详解(win10版)(包含TCP IP ICMP 三次握手 半扫描等相关知识 )
  14. threejs修改camera的lookAt无效怎么办
  15. I2C协议---I2C时序图解析
  16. windows命令强制关闭登录用户
  17. 适合所有手环的app_Keep 手环评测:一只手环就能指导运动?Keep 想做你手腕上的魔鬼教练...
  18. Linux内核(十五)sysrq 详解 I —— 使用手册
  19. C++学习之路(六),C++primer plus 第七章 函数--C++的编程模块
  20. `英语` 2022/8/21

热门文章

  1. linux C获取本地IP地址
  2. Javaweb的初级(Servlet接口)
  3. Java游戏开发超级玛丽总结_java超级玛丽游戏计算机(毕业设计)论文.doc
  4. 将 VSTO 与 SharePoint 内容类型相集成
  5. php实时股票,PHP实现股票趋势图和柱形图
  6. Cisco ASA 9.16(Adaptive Security Appliance (ASA) Software)
  7. python中利用ARIMA模型对时间序列问题进行预测(以洗发水销售预测为例)
  8. cdr宏教程_cdr软件怎么使用宏批量导出文件?
  9. B站banner图片随鼠标移动虚化效果摸索
  10. 用网盘和git来管理资源