使用SHT3x-DIS温湿度传感器的I2C案例

频道:行业资讯 日期: 浏览:978
??想了解更多关于开源的内容,请访问:??

??51CTO?开源基础软件社区??

??https://ost.51cto.com??

前言

本文将介绍I2C总线、SHT3x DIS温度传感器的相关知识以及OpenHarmony的HDF驱动和NAPI框架的使用方法。

一、I2C总线原理

I2C总线是飞利浦公司开发的一种双向二线制同步串行总线。只需要两根线便可在连接于总线上的器件之间进行传输信息。I2C通信为点对点通信,存在主设备和从设备之分。主从设备通过两根线进行通信,其中两根线分别是SDA和SCL,其中SDA为数据线,SCL为时钟线。

主器件用于启动总线传送数据,并产生时钟以开放传送的器件,此时任何被寻址的器件均被认为是从器件.在总线上主和从、发和收的关系不是恒定的,而取决于此时数据传送方向。如果主机要发送数据给从器件,则主机首先寻址从器件,然后主动发送数据至从器件,最后由主机终止数据传送;如果主机要接收从器件的数据,首先由主器件寻址从器件.然后主机接收从器件发送的数据,最后由主机终止接收过程。在这种情况下,主机负责产生定时时钟和终止数据传送。

通信过程包含应答响应,时钟同步。传输的数据字节格式有一定要求,每个字节必须为8位,每次发送的字节字数不受限制,每个字节后面必须跟一位校验位。应答响应,数据传输必须有响应,由主机产生,在响应中发送器将时钟线电平被拉高,接收器将电平拉低,保持稳定的电压差;时钟同步,数据传输只发生在时钟信号的高电平期间,所以需要同步双方时钟信号以确保数据的准确性;

二、传感器SHT3X DIS

Sensirion SHT3x-DIS湿度和温度传感器基于CMOSens?传感器芯片,更加智能、可靠,精度更高。SHT3x-DIS具有增强的信号处理能力、两个独特的用户可选I2C地址,通信速度高达1MHz。SHT35-DIS的典型相对湿度 (RH) 精度为±1.5%,典型温度精度为±0.1°C。SHT3x-DIS具有2.5mm x 2.5mm x 0.9mm(长x宽x高)占位面积,电源电压范围为2.4V至5.5V。

(1)特性

完全校准、线性化和温度补偿的数字输出

I2C接口,通信速度高达1MHz,具有两个用户可选地址

SHT35的典型精度为+/-1.5% RH和+/-0.1°C

启动和测量速度极快

2.15V到5.5V的宽电源电压范围

小型8引脚DFN封装

(2)引脚介绍

主要引脚SDA,SCL,VCC,GND。

(3) 通信过程

开始测量

在开始测量前,主设备必须先把开始测量的信号发送到传感器。发送的信号被称为I2C写入标头,由7比特的I2C设备地址和一个·0(0表示写入,1表示读取),再加上16比特的测量命令构成。当传感器接收到信号时,将会把SDA信号先拉低,响应信号ACK,在第八个时钟信号下降沿时表示传感器接收到了主设备的信号,开始测量。

模式

传感器数据采集模式多种多样,我们可以选择不同的方式进行测量以满足不同的应用场景,这便是以上提到写入表头最后两位字节表示的是测量命令,大类分为两种采集模式。

单次数据采集模式

周期性数据采集模式

其它命令

除此之外,传感器里还设置了其它命令,可在传感器说明文档中查看。

数据

当测量开始时,主设备便可以接收到信号,而此时标头要使用读取标头,将0改为1。传感器返回的后六位字节便是测量到的温度和相对湿度的数据。其中六个字节,高三位为两位温度和一位校验位,低三位为两位相对湿度和一位校验位,采用CRC校验。

数据转换公式如下:

三、简单实现

以下代码只是简单地演示如何使用传感器,没有过多的规范要求。

(1)接口定义

复制

int SendCMD(char *devName, char addr, uint16_t command)

{

int fd = -1;

uint8_t cmdBuf[2L] = {0};

struct i2c_rdwr_ioctl_data i2c_data;

fd = open(devName, O_RDWR); //获取I2C设备句柄

i2c_data.nmsgs = 1;

i2c_data.msgs = (struct i2c_msg *)malloc(i2c_data.nmsgs * sizeof(struct

i2c_msg));

ioctl(fd, I2C_TIMEOUT, 1);

ioctl(fd, I2C_RETRIES, 2L);

cmdBuf[0] = command >> 8L; //对指令数据进行处理 高八位和低八位

cmdBuf[1] = command & 0xFF;

i2c_data.msgs[0].len = 2L;

i2c_data.msgs[0].addr = addr;

i2c_data.msgs[0].flags = 0;

i2c_data.msgs[0].buf = cmdBuf;

ioctl(fd, I2C_RDWR, (unsigned long)&i2c_data); //将数据写入进行传输

free(i2c_data.msgs);

close(fd);

return 0;

}

// 再定义一些数据转换函数和校验函数 简单的数据转换 忽略

int ConvertTH(uint8_t tempRH, float *rawTemp, float *rawHum);

...

1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

11.

12.

13.

14.

15.

16.

17.

18.

19.

20.

21.

22.

23.

24.

25.

(2)主函数

复制

int main(int argc, char *argv[])

{

char *dev_name = "/dev/i2c-5";

SendCMD(dev_name,ADDR,0x3093) //重启

usleep(50L * 1000L);

SendCMD(dev_name,ADDR,0x202F) //开始测量

usleep(50L * 1000L);

int fd = -1;

struct i2c_rdwr_ioctl_data i2c_data;

uint8_t rawData[6L] = {0};

float rawTemp = 0, rawHum = 0;

fd = open(devName, O_RDWR);

i2c_data.nmsgs = 1;

i2c_data.msgs = (struct i2c_msg *)malloc(i2c_data.nmsgs * sizeof(struct

i2c_msg));

i2c_data.msgs[0].len = 6L;

i2c_data.msgs[0].addr = addr;

i2c_data.msgs[0].flags = 1;

i2c_data.msgs[0].buf = rawData;

ioctl(fd, I2C_RDWR, (unsigned long)&i2c_data);

free(i2c_data.msgs);

close(fd);

ConvertTH(rawData, &rawTemp, &rawHum);

printf("Temp: %.2f°C\nHum: %.2f°F",rawTemp,rawHum);

return 0;

}

1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

11.

12.

13.

14.

15.

16.

17.

18.

19.

20.

21.

22.

23.

24.

25.

26.

四、采用标准系统HDF驱动实现

使用到:九联科技unionpi_tiger开发板,SHT3x-DIS温湿度传感器,OpenHarmony源码。

(1)配置产品驱动(一般厂商都会配置好,若没配置可以跳转至官方文档查看详细教程)

实例化驱动入口:

实例化HdfDriverEntry结构体成员。

调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。

配置属性文件:

在device_info.hcs文件中添加deviceNode描述。

复制

//device_info.hcs 配置参考

root {

device_info {

match_attr = "hdf_manager";

device_i2c :: device {

device0 :: deviceNode {

policy = 2;

priority = 50;

permission = 0644;

moduleName = "HDF_PLATFORM_I2C_MANAGER";

serviceName = "HDF_PLATFORM_I2C_MANAGER";

deviceMatchAttr = "hdf_platform_i2c_manager";

}

device1 :: deviceNode {

policy = 0; // 等于0,不需要发布服务

priority = 55; // 驱动启动优先级

permission = 0644; // 驱动创建设备节点权限

moduleName = "hi35xx_i2c_driver";

//【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致;

serviceName = "HI35XX_I2C_DRIVER"; //【必要】驱动对外发布服务的名称,必须唯一

deviceMatchAttr =

"hisilicon_hi35xx_i2c";//【必要】用于配置控制器私有数据,要与i2c_config.hcs中对应控制器保持一致

// 具体的控制器信息在 i2c_config.hcs 中

}

}

}

}

1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

11.

12.

13.

14.

15.

16.

17.

18.

19.

20.

21.

22.

23.

24.

25.

26.

27.

复制

// i2c_config.hcs 配置参考 (需要根据使用的开发板配置)

root {

platform {

i2c_config {

match_attr =

"hisilicon_hi35xx_i2c";//【必要】需要和device_info.hcs中的deviceMatchAttr值一致

template i2c_controller { //模板公共参数,继承该模板的节点如果使用模板中的默认值,则节点字段可以缺省

bus = 0; //【必要】i2c 识别号

reg_pbase = 0x120b0000; //【必要】物理基地址

reg_size = 0xd1; //【必要】寄存器位宽

irq = 0; //【可选】根据厂商需要来使用

freq = 400000; //【可选】根据厂商需要来使用

clk = 50000000; //【可选】根据厂商需要来使用

}

controller_0x120b0000 :: i2c_controller {

bus = 0;

}

controller_0x120b1000 :: i2c_controller {

bus = 1;

reg_pbase = 0x120b1000;

}

...

}

}

}

1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

11.

12.

13.

14.

15.

16.

17.

18.

19.

20.

21.

22.

23.

24.

25.

实例化I2C控制器对象:

初始化I2cCntlr成员。

实例化I2cCntlr成员I2cMethod和I2cLockMethod。

p.s 使用到的九联开发板已有相关配置,以上配置无需做更改或添加。

(2)一个结构三个接口

I2cMsg结构体:用于传输数据载体,由地址addr,缓存buf,缓存长度len,信号标记flags组成。

复制

struct I2cMsg {

/** Address of the I2C device */

uint16_t addr;

/** Address of the buffer for storing transferred data */

uint8_t *buf;

/** Length of the transferred data */

uint16_t len;

/**

* Transfer Mode Flag | Description

* ------------| -----------------------

* I2C_FLAG_READ | Read flag

* I2C_FLAG_ADDR_10BIT | 10-bit addressing flag

* I2C_FLAG_READ_NO_ACK | No-ACK read flag

* I2C_FLAG_IGNORE_NO_ACK | Ignoring no-ACK flag

* I2C_FLAG_NO_START | No START condition flag

* I2C_FLAG_STOP | STOP condition flag

*/

uint16_t flags;

};

1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

11.

12.

13.

14.

15.

16.

17.

18.

19.

三个接口分别为I2cOpen()、I2cClose()、I2cTransfer()。

复制

//number指I2C所挂载的总线号

DevHandle I2cOpen(int16_t number);

//handle是I2cOpen()返回的设备句柄

void I2cClose(DevHandle handle);

//msgs所要传输的数据结构体,count是传输结构体的大小

int32_t I2cTransfer(DevHandle handle, struct I2cMsg *msgs, int16_t

count);

1.

2.

3.

4.

5.

6.

7.

(3) 代码

头文件

复制

#include //标准输入输出

#include //使用到usleep()进程挂起函数

#include "i2c_if.h" //HDF i2c 接口

#include "hdf_log.h" //日志打印头文件

1.

2.

3.

4.

结构体与接口

复制

// 重新定义结构体方便使用

typedef struct

{

struct I2cMsg * i2cMsg;

uint8_t msgLen; //i2cMsg的长度

} I2cMessage;

//定义命令发送函数

int32_t SendCMD(DevHandle handle,uint16_t command)

{

int32_t ret;

I2cMessage i2cMessage;

i2cMessage.msgLen = 1;

i2cMessage.i2cMsg = new I2cMsg[1]; //申请内存

uint8_t cmdBuf[2L] = {0};

cmdBuf[0] = command >> 8L; //将命令拆分成高低位分别保存

cmdBuf[1] = command & 0xFF;

i2cMessage.i2cMsg[0].len = 2L;

i2cMessage.i2cMsg[0].addr = ADDR;

i2cMessage.i2cMsg[0].flags = WRITE_FLAGS;

i2cMessage.i2cMsg[0].buf = cmdBuf;

ret = I2cTransfer(handle,i2cMessage.i2cMsg,i2cMessage.msgLen);

if(ret < 0){

LOGE("%s: SendCommend faided",__func__);

delete i2cMessage.i2cMsg;

return -1;

}

delete i2cMessage.i2cMsg; //释放内存

usleep(50L * 1000L); //等待发送完成

return 1;

}

1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

11.

12.

13.

14.

15.

16.

17.

18.

19.

20.

21.

22.

23.

24.

25.

26.

27.

28.

29.

30.

主函数

复制

int main(int argc, char** argv)

{

/**

* 数据初始化

*/

DevHandle i2cHandle;

/**

* 获取句柄

*/

i2cHandle = I2cOpen(BUSID);

if(i2cHandle == NULL){

LOGE("%s:get handle failed",__func__);

I2cClose(i2cHandle);

return 0;

}

/**

* 发送命令

*/

SendCMD(i2cHandle,0x3093); //关闭reset命令

SendCMD(i2cHandle,0x202F); //发送命令 repeatability=Low mps=0.5

/**

* 接收数据

*/

I2cMessage i2cMessage;

i2cMessage.msgLen = 1;

i2cMessage.i2cMsg = new I2cMsg[1];

uint8_t regData[6L] = {0};

i2cMessage.i2cMsg[0].len = 6L;

i2cMessage.i2cMsg[0].addr = ADDR;

i2cMessage.i2cMsg[0].flags = READ_FLAGS;

i2cMessage.i2cMsg[0].buf = regData;

I2cTransfer(i2cHandle,i2cMessage.i2cMsg,i2cMessage.msgLen);

delete i2cMessage.i2cMsg;

/**

* 数据处理

*/

uint16_t value = 0;

value = regData[0] << 8;

value = value | regData[1];

printf("Temperature: %.2f C\n",175.0f * (float)value / 65535.0f - 45.0f);

value = 0;

value = regData[3] << 8;

value = value | regData[4];

printf("Humidity: %.2f H\n",100.0f * (float)value / 65535.0f);

/**

* 关闭设备

*/

I2cClose(i2cHandle);

return 0;

}

1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

11.

12.

13.

14.

15.

16.

17.

18.

19.

20.

21.

22.

23.

24.

25.

26.

27.

28.

29.

30.

31.

32.

33.

34.

35.

36.

37.

38.

39.

40.

41.

42.

43.

44.

45.

46.

47.

48.

49.

50.

至此,成功通过OpenHarmony的HDF驱动来获取传感器的值。

五、实现NAPI

(1)模块定义与注册

复制

/**

* 模块定义

*/

static napi_module i2cHDF_demoModule = {

.nm_version = 1,

.nm_flags = 0,

.nm_filename = nullptr,

.nm_register_func = registerI2cHDF_DemoApis,

.nm_modname = "i2chdf_demo",

.nm_priv = ((void *)0),

.reserved = {0},

};

/**

* 模块注册

*/

extern "C" __attribute__((constructor)) void RegisterI2cHDFoModule(void)

{

napi_module_register(&i2cHDF_demoModule);

}

1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

11.

12.

13.

14.

15.

16.

17.

18.

19.

(2)接口定义与注册

复制

int32_t SendCMD(DevHandle handle,uint16_t command)

{

int32_t ret;

struct I2cMsg * i2cMsg;

int msgLen = 1;

i2cMsg = new I2cMsg[msgLen];

uint8_t cmdBuf[2L] = {0};

cmdBuf[0] = command >> 8L;

cmdBuf[1] = command & 0xFF;

i2cMsg[0].len = 2L;

i2cMsg[0].addr = ADDR;

i2cMsg[0].flags = WRITE_FLAGS;

i2cMsg[0].buf = cmdBuf;

ret = I2cTransfer(handle,i2cMsg,msgLen);

delete i2cMsg;

usleep(50L * 1000L);

return 1;

}

/**

* 接口定义

*/

static napi_value readI2cBuf(napi_env env,napi_callback_info info)

{

napi_value ret;

DevHandle i2cHandle;

i2cHandle = I2cOpen(BUSID);

SendCMD(i2cHandle,0x3093);

SendCMD(i2cHandle,0x202F);

struct I2cMsg * i2cMsg;

int msgLen = 1;

i2cMsg = new I2cMsg[msgLen];

uint8_t regData[6L] = {0};

i2cMsg[0].len = 6L;

i2cMsg[0].addr = ADDR;

i2cMsg[0].flags = READ_FLAGS;

i2cMsg[0].buf = regData;

I2cTransfer(i2cHandle,i2cMsg,msgLen);

delete i2cMsg;

uint16_t value = 0;

double sHTTemp = 0;

value = regData[0] << 8;

value = value | regData[1];

sHTTemp = 175.0f * (double)value / 65535.0f - 45.0f;

//设计思路与上HDF大同小异,只不过将最后获取的值通过转换再返回

//此处只处理返回温度的值,方便演示

NAPI_CALL(env, napi_create_double(env, sHTTemp, &ret));

return ret;

}

/**

* 接口注册

*/

static napi_value registerI2cHDF_DemoApis(napi_env env, napi_value

exports)

{

napi_property_descriptor desc[] = {

DECLARE_NAPI_FUNCTION("readI2cBuf",readI2cBuf), //NAPI名字,上面的函数

};

NAPI_CALL(env,napi_define_properties(env,exports,sizeof(desc)/sizeof(desc[0]),desc));

return exports;

}

1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

11.

12.

13.

14.

15.

16.

17.

18.

19.

20.

21.

22.

23.

24.

25.

26.

27.

28.

29.

30.

31.

32.

33.

34.

35.

36.

37.

38.

39.

40.

41.

42.

43.

44.

45.

46.

47.

48.

49.

50.

51.

52.

53.

54.

55.

56.

57.

58.

59.

60.

(3)北向接口

NAPI

复制

function readI2cBuf(): number;

1.

Index.ets

复制

import i2chdf from '@ohos.i2chdf'

@Entry

@Component

struct Index {

@State message: string = 'Temperature: '+ i2chdf.readI2cBuf().toFixed(2) +

'°C';

aboutToAppear(): void{

var Id = setInterval(() =>{

this.message = 'Temperature: '+ i2chdf.readI2cBuf().toFixed(2) + '°C';

},1000)

}

build() {

Row() {

Column() {

Text(this.message)

.fontSize(50)

.fontWeight(FontWeight.Bold)

}

.width('100%')

}

.height('100%')

}

}

1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

11.

12.

13.

14.

15.

16.

17.

18.

19.

20.

21.

22.

23.

(4)效果演示

总结

整个案例整体思路都是围绕着I2C通信流程和SHT3x温度传感器工作流程展开的。而在HDF驱动的使用中,我们会发现,获取设备句柄的方式我们只用一个数字就可以,相比之前的"/dev/i2c-5"更加简易明了,这也是HDF的特性之一。NAPI的实现,将整个OpenHarmoy南北向打通,使得北向程序可通过本地的接口就可以访问传感器的温湿度。

??想了解更多关于开源的内容,请访问:??

??51CTO?开源基础软件社区??

??https://ost.51cto.com??

0 留言

评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。