一:传感器与树莓派接线
sht30的vcc——树莓派的3.3V
sht30的gnd——树莓派的GND
sht30的sda——树莓派的SDA(BMC编码2)
sht30的scl——树莓派的SCL(BMC编码3)
二:开启树莓派的i2c接口
我们所用的sht30传感器是i2c通信的,所以我们还必须将树莓派的i2c接口打开才能使用它,输入下面命令进入树莓派设置界面
sudo raspi-config
选择第五项:Interfacing Options ,在选择 I2C 按回车确认开启
然后输入sudo reboot 把树莓派重启一下。这就完成树莓派的设置。
二、sht30获取温湿度的命令地址
关于sht30的官方解释文档我们可以查看官网的产品介绍
传感器获取温湿度的命令地址如下 single shot:在这种模式下,一个发出的测量命令触发- -个数据对的采集。每个数据对由一个16 位温度,和一个16位湿度值(按此顺序)组成。在传输过程中,每个数据值总是紧跟着一个CRC校验和,可以选择不同的测量命令。16 位命令如表8所示。它们与可重复性(低、中、高)和时钟拉伸(启用或禁用)不同。传感器完成测量后,主程序可以通过发送一一个START条件和一个I2C读取头来读取测量结果(RH& T对)。传感器将接收读头的接收,并发送两个字节的数据(温度),接着是一个字节的CRC校验和另外两个字节的数据(相对湿度),然后是一个字节的CRC校验和。每个字节必须承认微控制器与ACK条件传感器继续发送数据。如果传感器在任何字节的数据之后没有从主程序接收到ACK,它就不会继续发送数据。传感器将首先发送温度值,然后发送相对湿度值。在收到湿度值的校验和后,就应该发送一个NACK和停止条件。
Periodic Data Acquisition Mode:在这种模式下,一个发出的测量命令产生一对数据流。每个数据对由一个16 位温度和一个16位湿度值(按此顺序)组成。在周期模式下,可以选择不同的测量命令。相应的16位命令如表9所示。它们与重复性(低、中、高)和数据采集频率(测量每秒0.5, 1, 2, 4和10, MPS)不同。在这种模式下不能选择时钟沿伸。
测:量数据的传输可以通过表10 所示的获取数据命令来启动。如果没有测量数据,I2C 读取头会反应出一个NACK (表10 中的9位),从而停止通信。在读出命令获取数据之后,数据存储器被清除,即测量数据不会存在。
三、C代码
当树莓派的硬件完成好后,我们就可以开始编程了。c编程当然离不开ictol这个函数,它提供了强大的功能,能够让开发者调用它获取到底层的数据。设备连接好后我们就可以查找传感器的i2c通信地址。
安装好i2c库和工具
sudo apt-get install i2c-tools
之后我们就会发现出现了这个文件
然后查看传感器地址
sudo i2cdetect -y -a 1
0x44就是sht30的通信地址,之后我们就要打开上面我们看到的文件i2c-1
int sht_open(int i2c_addr, uint8_t sht_addr)
{
char i2c_filename[10];
int fd = -1;
int rv = -1;
snprintf(i2c_filename, 19, "/dev/i2c-%d", i2c_addr);
fd=open(i2c_filename, O_RDWR);
if(fd < 0)
{
printf("open %s fialeure\n", i2c_filename);
return -1;
}
return fd;
}
打开文件记得给读写的权限,然后开始往i2c设备也就是sht30传感器写入命令,我们要注意的是,sht30的通信方式是一个字节一个字节的传输的,而我们获取温度的命令地址为16位的,所以我们需要将它拆分,并且通信的时候是高地址为先传送的。我们需要将地址简单处理下send[0]=(read_model>>8) & 0xff; send[1]=read_model & 0xff;利用ioctl函数之前我们得了解下下面的结构体
struct i2c_rdwr_ioctl_data { struct i2c_msg __user *msgs; /* pointers to i2c_msgs */ __u32 nmsgs; /* number of i2c_msgs */ }; struct i2c_msg { _ _u16 addr; /* slave address */ _ _u16 flags; /* 标志(读、写) */ _ _u16 len; /* msg length */ _ _u8 *buf; /* pointer to msg data */ };这结构体就是存储着树莓派向传感器同行的所有信息,和i2c设备通信就是通过修改结构体的内容来实现的,将对应的参数写入然后掉要ioctl函数
data.nmsgs = 1;//消息的数目 msgs.len = sendsize;//写入目标的字节数 msgs.addr = sht_addr;//i2c设备地址 msgs.flags = 0;//flags为0:表示写;为1:表示读 msgs.buf = send;//发送的数据 data.msgs = &msgs; rv=ioctl(fd, I2C_RDWR, &data);获取温湿度的后,传感器将会给我们6个字节的数据,前面三个字节为温度两个字节后面跟着一个crc码,后面三个字节为湿度两个字节后面跟着一个crc码,中间可以使用定时函数延时下,等待温度获取成功
在这里插入代码data.nmsgs =1; msgs.len = readsize; msgs.addr = sht_addr; msgs.flags = 1; msgs.buf = buf; data.msgs = &msgs; rv=ioctl(fd, I2C_RDWR, &data);片温度获取成功后我们可以进行crc的校检,看看数据是否正确然后利用官方提供的说明对温湿度的值进行加工
程序运行
源代码:
程序中写入的命令可以是我们介绍传感器中的图片的那些16为地址,当然还有这其它的功能,具体可参考给出的链接
/********************************************************************************* * Copyright: (C) 2020 longyongtu<longyongtu13@qq.com> * All rights reserved. * * Filename: d_sht_temp_hmti.c * Description: This file * * Version: 1.0.0(12/07/20) * Author: longyongtu <longyongtu13@qq.com> * ChangeLog: 1, Release initial version on "12/07/20 14:45:20" * ********************************************************************************/ #include <stdio.h> #include <string.h> #include <linux/i2c-dev.h> #include <time.h> #include <sys/ioctl.h> #include <time.h> #include <stdint.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.h> #include <unistd.h> #include <linux/i2c.h> #define I2C_ADDR 1 #define SHT_ADDR 0X44 #define SHT_MEAS_MEDREP 0x240b #define SHT_MEAS_LOWREP 0x2416 void delay_ms(unsigned int time); uint8_t crc8(const uint8_t *data, int len); int sht_open(int i2c_addr, uint8_t sht_addr); int sht_read_write(int fd, int sht_addr, uint16_t read_model, uint8_t *buf, int readsize); int get_temp_hmti(int i2c_addr, uint8_t sht_addr, float *temp, float *hmti); /* delay ms*/ void delay_ms(unsigned int time) { struct timespec sleeper, temp; sleeper.tv_sec = (time_t)(time/1000); sleeper.tv_nsec = (long)(time%1000)*1000000; nanosleep(&sleeper, &temp); return ; } uint8_t crc8(const uint8_t *data, int len) { const uint8_t magic = 0x31; uint8_t crc = 0xff; int i; int j; for(j=len; j; --j) { crc ^= *data++; for(i=8; i; --i) { crc=(crc & 0x80) ? (crc << 1) ^ magic : (crc << 1); } } return crc; } int sht_open(int i2c_addr, uint8_t sht_addr) { char i2c_filename[10]; int fd = -1; int rv = -1; snprintf(i2c_filename, 19, "/dev/i2c-%d", i2c_addr); fd=open(i2c_filename, O_RDWR); if(fd < 0) { printf("open %s fialeure\n", i2c_filename); return -1; } return fd; } int sht_read_write(int fd, int sht_addr, uint16_t read_model, uint8_t *buf, int readsize) { int rv = -1; int sendsize = 2; uint8_t send[2]; struct i2c_msg msgs; struct i2c_rdwr_ioctl_data data; send[0]=(read_model>>8) & 0xff; send[1]=read_model & 0xff; data.nmsgs = 1;//消息的数目 msgs.len = sendsize;//写入目标的字节数 msgs.addr = sht_addr;//i2c设备地址 msgs.flags = 0;//flags为0:表示写;为1:表示读 msgs.buf = send;//发送的数据 data.msgs = &msgs; rv=ioctl(fd, I2C_RDWR, &data); if(rv < 0) { printf("write to sht_30 failure\n"); return -1; } delay_ms(10); data.nmsgs =1; msgs.len = readsize; msgs.addr = sht_addr; msgs.flags = 1; msgs.buf = buf; data.msgs = &msgs; rv=ioctl(fd, I2C_RDWR, &data); if(rv < 0) { printf("read to sht_30 failure\n"); return -1; } return 1; } int get_temp_hmti(int i2c_addr, uint8_t sht_addr, float *temp, float *hmti) { int rv = -1; int sht_fd = -1; uint8_t buf[10]; uint16_t tmp_t, tmp_h; memset(buf, 0, sizeof(buf)); sht_fd=sht_open(I2C_ADDR, SHT_ADDR); if(sht_fd < 0) { return -1; } rv=sht_read_write(sht_fd, sht_addr, SHT_MEAS_LOWREP, buf, 6); if(rv < 0) { return -1; } if(buf[2] != crc8(buf, 2) || buf[5] != crc8(buf+3, 2)) { printf("crc check errno\n"); return -1; } tmp_t = buf[0]; tmp_t <<= 8; tmp_t |= buf[1]; tmp_h = buf[3]; tmp_h <<= 8; tmp_h |= buf[4]; *temp = -45.0 + (175.0 * ((float) tmp_t / (float) 0xFFFF)); *hmti = 100.0 * ((float) tmp_h / (float) 0xFFFF); close(sht_fd); return 1; } int main(int argc, char *argv[]) { float temp, hmti; get_temp_hmti(I2C_ADDR, SHT_ADDR, &temp, &hmti); printf("Temperature %.2fc\n", temp); printf("Humidity %.2f%%\n", hmti); return 0; }
教程结束!