1.前言
Si7021是一款微型数字温湿度传感器,其通过IIC与外部处理器通信,常用于采集环境信息。。本文首先介绍Si7021芯片关键特性,再在树莓派基于Bcm2835库开发Si7021驱动库。树莓派安装Bcm2835库参考这篇文章。
2.Si7021
Si7021供电1.9-3.6V,湿度量程0-80%,最高精度±3%,温度量程-10-85℃,最高精度±0.4℃。其通过IIC与外部芯片通信,芯片地址0x40。
他有以下基础指令:
-
重置(0xFE),软件重置需要等待芯片15ms;
-
读(0xE7)写(0xE6)用户寄存器,用户寄存器的D7和D0可以配置温湿度ADC的位数,设置的位数不同,ADC转换耗时不同,D6可以看出供电是否正常,D2可以配置是否启用加热器。
-
读取序列号1st Bytes(0xFA 0xDF)和2nd Bytes(0xFC 0xC9),读取第一组序列号时,共需要8个字节(含4个CRC),读取第二组序列号时,需要接收6个字节(含2个CRC)。根据SNB_3的数值可以区分该模块的具体信号:0x0D=Si7013,0x14=Si7020,0x15=Si7021;
-
读取固件版本号(0x84 0xB8);
-
湿度测量,No Hold Master Mode(0xF5),测量湿度时,会先测量一次温度,即该条指令下去,会先ADC测量温度,再ADC测量湿度。ADC转换时间为两个通道耗时之和。
-
读取上个RH测量的温度值(0xE0),通常在湿度测量之后,因为湿度测量会自动进行一次温度测量,因此,无需再进行一次温度测量,只需读取就行。
两个通道的ADC转换时间,和温湿度转换公式,和其他详细情况可参考芯片手册。
3.在树莓派移植Si7021库
在树莓派中基于Bcm2835移植Si7021库,参考Adafruit_Si7021_Library,做了对序列号2st Bytes的读取的修改。
si7021.h
#ifndef SI7021_H
#define SI7021_H
#include <bcm2835.h>
#include <cstdio>
#include <cstdint>
/*!
* I2C ADDRESS/BITS
*/
#define SI7021_DEFAULT_ADDRESS 0x40
#define SI7021_MEASRH_HOLD_CMD \
0xE5 /**< Measure Relative Humidity, Hold Master Mode */
#define SI7021_MEASRH_NOHOLD_CMD \
0xF5 /**< Measure Relative Humidity, No Hold Master Mode */
#define SI7021_MEASTEMP_HOLD_CMD \
0xE3 /**< Measure Temperature, Hold Master Mode */
#define SI7021_MEASTEMP_NOHOLD_CMD \
0xF3 /**< Measure Temperature, No Hold Master Mode */
#define SI7021_READPREVTEMP_CMD \
0xE0 /**< Read Temperature Value from Previous RH Measurement */
#define SI7021_RESET_CMD 0xFE /**< Reset Command */
#define SI7021_WRITERHT_REG_CMD 0xE6 /**< Write RH/T User Register 1 */
#define SI7021_READRHT_REG_CMD 0xE7 /**< Read RH/T User Register 1 */
#define SI7021_WRITEHEATER_REG_CMD 0x51 /**< Write Heater Control Register */
#define SI7021_READHEATER_REG_CMD 0x11 /**< Read Heater Control Register */
#define SI7021_REG_HTRE_BIT 0x02 /**< Control Register Heater Bit */
#define SI7021_ID1_CMD 0xFA0F /**< Read Electronic ID 1st Byte */
#define SI7021_ID2_CMD 0xFCC9 /**< Read Electronic ID 2nd Byte */
#define SI7021_FIRMVERS_CMD 0x84B8 /**< Read Firmware Revision */
#define SI7021_REV_1 0xff /**< Sensor revision 1 */
#define SI7021_REV_2 0x20 /**< Sensor revision 2 */
/** An enum to represent sensor types **/
enum si_sensorType {
SI_Engineering_Samples,
SI_7013,
SI_7020,
SI_7021,
SI_UNKNOWN,
};
/*!
* @brief Class that stores state and functions for interacting with
* Si7021 Sensor
*/
class SI7021{
public:
SI7021(uint8_t addr = SI7021_DEFAULT_ADDRESS);
bool begin();
float readHumi();
float readTemp();
void reset();
/*!
* @brief Returns sensor revision established during init
* @return model value
*/
uint8_t getRevision() { return _revision; };
/*!
* @brief Returns sensor sensor types
* @return sensor types
*/
si_sensorType getModel(){ return _model; };
private:
uint8_t _addr;
si_sensorType _model;
uint8_t _revision;
uint32_t sernum_a; /**< Serialnum A */
uint32_t sernum_b; /**< Serialnum B */
void _readRevision();
uint8_t _readRegister8(uint8_t reg);
void _readSerialNumber();
uint8_t errCode;
char sendBuf[3];
};
#endif
si7021.cpp
#include "si7021.h"
SI7021::SI7021(uint8_t addr)
{
_addr = addr;
sernum_a = sernum_b = 0;
_model = SI_7021;
_revision = 0;
}
/**
* @brief Before SI7021::init(), please bcm2835_init() in main.c!
**/
bool SI7021::begin()
{
printf("SI7021 Init...\n");
if(!bcm2835_i2c_begin())
{
printf("bcm2835_i2c_begin failed at %s:%d\n",__FILE__ ,__LINE__);
return false;
}
bcm2835_i2c_setSlaveAddress(_addr);
bcm2835_i2c_set_baudrate(100000);
if (_readRegister8(SI7021_READRHT_REG_CMD) != 0x3A)
return false;
_readSerialNumber();
_readRevision();
return true;
}
uint8_t SI7021::_readRegister8(uint8_t reg)
{
char value;
sendBuf[0] = reg;
if((errCode = bcm2835_i2c_write_read_rs(sendBuf,1,&value,1)))
{
printf("bcm2835_i2c_write_read_rs failed at%s:%d, errCode = 0x%x\n",__FILE__ ,__LINE__, errCode);
return 0;
}
//printf("value = %d\n", value);
return static_cast<uint8_t>(value);
}
void SI7021::_readSerialNumber()
{
sendBuf[0] = static_cast<uint8_t>(SI7021_ID1_CMD >> 8);
sendBuf[1] = static_cast<uint8_t>(SI7021_ID1_CMD & 0xFF);
char recvBuf[8];
if((errCode = bcm2835_i2c_write_read_rs(sendBuf,2,recvBuf,8)))
{
printf("bcm2835_i2c_write_read_rs failed at %s:%d, errCode = 0x%x\n", __FILE__, __LINE__, errCode);
return;
}
sernum_a = (recvBuf[0]<<24)|(recvBuf[2]<<16)|(recvBuf[4]<<8)|(recvBuf[6]);
//printf("sernum_a = %d\n", sernum_a);
sendBuf[0] = static_cast<uint8_t>(SI7021_ID2_CMD >> 8);
sendBuf[1] = static_cast<uint8_t>(SI7021_ID2_CMD & 0xFF);
if((errCode = bcm2835_i2c_write_read_rs(sendBuf,2,recvBuf,6)))
{
printf("bcm2835_i2c_write_read_rs failed at %s:%d, errCode = 0x%x\n", __FILE__, __LINE__, errCode);
return;
}
sernum_b = (recvBuf[0]<<24)|(recvBuf[1]<<16)|(recvBuf[3]<<8)|(recvBuf[4]);
//printf("sernum_b = %d\n", sernum_b);
switch (sernum_b >> 24) {
case 0:
case 0xff:
_model = SI_Engineering_Samples;
break;
case 0x0D:
_model = SI_7013;
break;
case 0x14:
_model = SI_7020;
break;
case 0x15:
_model = SI_7021;
break;
default:
_model = SI_UNKNOWN;
break;
}
//printf("_model = %d\n",_model);
}
void SI7021::_readRevision()
{
sendBuf[0] = static_cast<uint8_t>(SI7021_FIRMVERS_CMD >> 8);
sendBuf[1] = static_cast<uint8_t>(SI7021_FIRMVERS_CMD & 0xFF);
char recvBuf[2];
if((errCode = bcm2835_i2c_write_read_rs(sendBuf,2,recvBuf,2)))
{
printf("bcm2835_i2c_write_read_rs failed at %s:%d, errCode = 0x%x\n",__FILE__ ,__LINE__, errCode);
return;
}
if(recvBuf[0] == SI7021_REV_1)
_revision = 1;
else if(recvBuf[0] == SI7021_REV_2)
_revision = 2;
else
_revision = 0;
//printf("_revision = %d\n",_revision);
}
/**
* @brief Reset the SI7021 device, it takes almost 50 ms
*/
void SI7021::reset()
{
bcm2835_i2c_setSlaveAddress(_addr);
sendBuf[0] = SI7021_RESET_CMD;
if((errCode = bcm2835_i2c_write(sendBuf,1)))
{
printf("bcm2835_i2c_write failed at %s:%d, errCode = 0x%x\n", __FILE__, __LINE__ ,errCode);
return;
}
bcm2835_delay(20);
}
/**
* @brief Reads the humidity value from Si7021 (No Master hold), the function takes almost 20ms.
* @return Returns humidity as float value or -1 when error
*/
float SI7021::readHumi()
{
bcm2835_i2c_setSlaveAddress(_addr);
sendBuf[0] = SI7021_MEASRH_NOHOLD_CMD;
if((errCode = bcm2835_i2c_write(sendBuf,1)))
{
printf("bcm2835_i2c_write failed at %s:%d, errCode = 0x%x\n", __FILE__, __LINE__ ,errCode);
return -1;
}
bcm2835_delay(20);
char recvBuf[3];
if((errCode = bcm2835_i2c_read(recvBuf,3)))
{
printf("bcm2835_i2c_read failed at %s:%d, errCode = 0x%x\n", __FILE__, __LINE__ ,errCode);
return -1;
}
float humi = recvBuf[0]<<8 | recvBuf[1];
humi *= 125;
humi /= 65536;
humi -= 6;
return humi > 100.0 ? 100.0 : humi;
}
/**
* @brief Reads the temperature value from Si7021 (Read temperature value from previous RH measurement)
* @return Returns temperature as float value or -1 when there is error
*/
float SI7021::readTemp()
{
bcm2835_i2c_setSlaveAddress(_addr);
sendBuf[0] = SI7021_READPREVTEMP_CMD;
char recvBuf[2];
if((errCode = bcm2835_i2c_write_read_rs(sendBuf,1,recvBuf,2)))
{
printf("bcm2835_i2c_write_read_rs failed at %s:%d, errCode = 0x%x\n", __FILE__, __LINE__ ,errCode);
return -1;
}
float temp = recvBuf[0]<<8 | recvBuf[1];
temp *= 175.72;
temp /= 65536;
temp -= 46.85;
return temp;
}
testMain.cpp
#include "si7021.h"
int main()
{
bcm2835_init();
SI7021 si7021;
if(!si7021.begin())
{
printf("SI7021 init failed\n");
exit(-1);
}
float humi = 0;
float temp = 0;
while(1)
{
humi = si7021.readHumi();
temp = si7021.readTemp();
printf("Humi = %f, temp = %f\n",humi,temp);
bcm2835_delay(500);
}
return 0;
}
//g++ si7021.cpp testMain.cpp -lbcm2835 -o testMain
源码可见github。