Nordic 蓝牙芯片上开发 SHT3x系列温湿度传感器

第1部分 SHT31传感器介绍

1.1芯片简介

SHT3X系列是由瑞士Sensirion生产的高精度温湿度传感器,也是Sensirion公司目前主打的温湿度传感器系列。现在网上常见的相关资料调试的基本上以SHT30为主,SHT31则较少。本次开发用的是更高级的SHT35系列。

                                   

 

这次采用的SHT35传感器允许宽电压输入,支持2.15V~5.5V。采用IIC总线通信,最高可达1MHz的通信速度。并根据ADDR引脚的接法,提供两个可选的地址:0x44,0x45。传感器的精度为1.5%RH和0.1℃。传感器最大工作范围-40-125℃,0-100%RH。原装芯片有8个引脚。

还有一点要注意的是SHT3x推荐的的最佳工作环境是5℃-60℃,20%RH-80%RH。当传感器暴露在>80%RH的工作环境下超过60小时后,会出现+3%RH的偏差。

                            

SHT3x系列提供数字接口和模拟接口两种规格。

                            

 

    相关资料在文末给出。

 

1.2 引脚介绍

芯片总共有8个引脚

(1)SDA :IIC数据线引脚

(2)ADDR :地址引脚,可连接VSS或VDD,分别会有不同的地址。不能浮空。

(3)ALERT :报警引脚,如果使用,建议接到单片机的外部中断。不用的话建议浮空。

(4)SCL :IIC时钟线引脚

(5)VDD :电压输入引脚

(6)nRESET :复位引脚,低电平有效。如果不用,建议接到VDD。

(7)R :没有电气作用的“没卵用“”引脚,连接到VSS

(8) VSS :接地

                                           

 

(附上官方推荐的典型应用电路)

 

1.3命令和模式介绍

 

首先看一下地址

                                                          

 

很明显,当传感器ADDR引脚接VSS时,采用地址A;当传感器ADDR引脚接VDD时,采用地址B。本次开发的传感器 Address=0x44.

传感器支持单次数据采集模式和周期性数据采集模式。其实单次数据采集模式下,可选时钟延伸,而周期性数据采集默认开始数据延伸。这里我们默认采用周期性数据采集模式。

                                        

 

SHT3X支持12种工作模式,分别有高,中,低三档可选刷新率。mps=0.5,1,2…时,分别代表每两秒采集一次数据,每秒采集一次数据,每秒采集两次数据…

 

如希望设定高刷新率,每秒采集一次。那么向传感器写命令0x2130即可。注意当采用mps=10时,会导致传感器自发热,影响测量。

 

工作顺序为:先发送IIC通信开始标志Start后,写入左移一位的地址,并将空出来的位写0表示写数据。当收到传感器应答后,即可发送命令的高八位,再次等待应答,再发送余下的低八位。然后等待ACK应答即可。

其他命令同理,大部分都是同样的写入模式。

 

1.4 重要命令及其工作流程

那么我们看几条重要的命令及其工作流程。

                                                         

 

设置好工作模式后写入此命令,可以准备好接受数据。先发送IIC通信开始标志Start后,写入左移一位的地址,并将空出来的位写1表示读数据。然后等待ACK应答即可接受数据。注意数据传输顺序是先温度后湿度。并且都是十六位数据。并且每个数据后都附8位的CRC校验。在完成湿度的CRC校验后,即可回复NACK,传感器将停止发送数据,释放SDA线,以便于MCU发送Stop标志,结束通信。

 

第2部分 SHT3x官方参考代码简介

2.1.官方给的参考code是基于STM32平台的。文末有资料获取方式。

由于Nordic蓝牙芯片的I2C接口与STM32有稍微不同。本文将基于该参考code,通过修改,移植到nRF52832蓝牙芯片上。

                                                  

 

2.2 参考I2C 代码详解

本文档包含C语言的示例代码,用于通过与SHT3x湿度和温度传感器通信

I2C接口。代码的目的是在实现SHT3x传感器时简化用户的软件编程。除了

简单的测量湿度和温度,代码包含计算CRC校验和和计算物理湿度和温度值。这个示例代码是为STM32-Discovery板编写和优化的,但它可以很容易地应用于其他微控制器而做稍微改变。

                                             

 

至于更详细的官方参考code和说明,不是本文的重点,系列资料在文末有获取方式。

 

第3部分 Nordic蓝牙芯片 I2C(TWI)软件设计介绍:

关于nRF52832蓝牙芯片的I2C(TWI)串行总线的原理,之前的文章已有介绍。以下主要介绍软件设计部分。

nRF52832 片内集成的 TWI(两线串行总线)兼容 I2C 总线,带有 EasyDMA,可与连接到同一总线的多个从机设备通讯,主要特点如下:

(1)兼容 I2C。

(2)速率:100 kbps、250 kbps 或 400 kbps。

(3)支持时钟延伸。

(4)带 EasyDMA。

(5)TWI 的 SCL 和 SDA 信号可以通过配置寄存器连接到任何一个GPIO,这样可以灵活地实现器件引脚排列,并有效利用电路板空间和信号路由。

nRF52832 的 TWI 的原理框图如下图所示,TWI 主机通过触发STARTTX 或STARTRX 任务启动TWI 传输,通过触发 STOP 任务停止 TWI 传输。TWI 主机在挂起时无法停止,因此必须在 TWI 主机恢复后触发 STOP 任务停止 TWI。启动 TWI 主机后,在TWI 主机停止之前,即在 LASTRX,LASTTX 或 STOPPED 事件之后,不应再次触发 STARTTX 任务或STARTRX 任务。如果从机产生 NACK 输入,那么 TWI 主机将产生ERROR 事件。

                                               

 

3.1.nRF的I2C 函数库应用

TWI 的应用步骤如下图所示,首先要定义 TWI 驱动程序实例,驱动程序实例对应具体的硬件 TWI 外设(TWI0 和 TWI1),驱动程序实例决定了我们使用的是 TWI0 还是 TWI1。接着初始化配置TWI 连接的引脚和速率等参数,注册事件句柄(非堵塞模式下),初始化后使能TWI,之后就可以使用 TWI 进行传输数据。

                                     

 

3.2定义TWI 驱动程序实例

 

TWI 驱动程序实例使用 nrf_drv_twi_t 结构体定义,该结构体描述了具体的 TWI 外设, 当我们定义了nrf_drv_twi_t 类型的变量并对其赋值后,该变量就对应了一个具体的硬件 TWI 外设。

定义驱动程序实例代码如下,初始化宏 NRF_DRV_TWI_INSTANCE 的输入参数对应TWI 外设的编号,即如果我们定义 TWI0 的驱动程序实例,TWI_INSTANCE_ID 的值设置为 0,定义 TWI1 的驱动程序实例,TWI_INSTANCE_ID 的值设置为 1。驱动程序实例定义后,我们即可通过该驱动程序实例访问对应的 TWI。

                                          

 

3.3 初始化TWI(I2C)

TWI 初始化的库函数是 nrf_drv_twi_init ()函数,该函数同时也配置了 TWI 是否使用阻塞模式。

  (1)应用程序提供事件句柄:TWI 工作于非阻塞模式。

  (2)应用程序不提供事件句柄(event_handler 设置为 NULL):TWI 工作于阻塞模式。建议使用阻塞模式。

                                 

/** TWI初始化

 * @brief TWI initialization.

 */

//I2C引脚

#define TWI_SCL_M           26         //I2C SCL

#define TWI_SDA_M           25         //I2C SDA

/* TWI instance. */

static const nrf_drv_twi_t m_twi = NRF_DRV_TWI_INSTANCE(TWI_INSTANCE_ID);
void twi_init (void)
{
    ret_code_t err_code;

    const nrf_drv_twi_config_t twi_config = {
       .scl                = TWI_SCL_M,
       .sda                = TWI_SDA_M,
       .frequency          = NRF_DRV_TWI_FREQ_100K,
       .interrupt_priority = APP_IRQ_PRIORITY_HIGH,
       .clear_bus_init     = false
    };

    err_code = nrf_drv_twi_init(&m_twi, &twi_config, NULL, NULL);
    APP_ERROR_CHECK(err_code);

    nrf_drv_twi_enable(&m_twi);
		NRF_LOG_INFO("twi_master_init ok."); 
		NRF_LOG_FLUSH();
		nrf_gpio_pin_clear(20); //ledµÆÁÁ
}

 

3.5 TWI数据传输函数

TWI 驱动程序提供了两个单独的函数nrf_drv_twi_tx() 函数 和 nrf_drv_twi_ rx() 函数 , 分别用于完成数据的发送和接收,函原型如下表所示。使tx 和 rx 函数时,尤其要注意从机地址 address,函数接收的是 7位地址,函数内部会自己加上读写位。因此如果某个 I2C 接口设备提供的是 8位地址,我们需要提取出 7位地址赋值给address,也就是去掉 8位地址的最低(R/W位)。

nRF52832蓝牙芯片的所有I2C 通信都是通过这两个函数接口。不需要自己去重写I2C驱动函数。

 

3.5.1 TWI发送函数模型:nrf_drv_twi_tx()

                                   函数原型

   STATIC_INLINE ret_code_t nrf_drv_twi_tx (nrf_drv_twi_t const *                                 p_instance, uint8_t                         address,

uint8_t const *             p_data,

uint8_t                         length,

bool                             no_stop)

函数功能

向 TWI 从机发送数据,发生错误时将停止传输。 如果传输正在进行,则该函数返回错误代码 NRF_ERROR_BUSY。

参         数

[in] p_instance:指向 TWI 驱动程序实例结构体。

[in] address:指定的从机地址(7 位 LSB)。 [in] p_data:指向传输数据缓存。

[in] length:发送的字节数。

[in] no_stop:如果置位,成功传输后总线上不会生成停止条件(允许在

下一次传输中重复启动)。

返回值

NRF_SUCCESS:发送成功。

NRF_ERROR_BUSY:驱动程序尚未准备好进行新的传输。NRF_ERROR_INTERNAL:硬件检测到错误。NRF_ERROR_INVALID_ADDR:使用了 EasyDMA,但是缓存地址没有位于RAM 空间。

NRF_ERROR_DRV_TWI_ERR_ANACK:在轮询模式下发送地址后收到NAC。

NRF_ERROR_DRV_TWI_ERR_DNACK:在轮询模式下发送数据后收到

NACK。

 

 

3.5.2 TWI接收函数模型:nrf_drv_twi_rx()

 
 

函数原型

    STATIC_INLINE ret_code_t nrf_drv_twi_rx (

nrf_drv_twi_t const *    p_instance, uint8_t                         address,

uint8_t *                      p_data,

uint8_t                         length

)

函数功能

从 TWI 从机读取数据,发生错误时将停止传输。如果传输正在进行,则该函数返回错误代码 NRF_ERROR_BUSY。

参         数

[in] p_instance:指向 TWI 驱动程序实例结构体。

 

 

 

 

 

 

 

 

 

返回值

[in] address:指定的从机地址(7 位 LSB)。 [in] p_data:指向接收数据缓存。

[in] length:读取的字节数。

返回值

NRF_SUCCESS:发送成功。

NRF_ERROR_BUSY:驱动程序尚未准备好进行新的传输。NRF_ERROR_INTERNAL:硬件检测到错误。NRF_ERROR_DRV_TWI_ERR_OVERRUN:接收数据未及时读取,被新接收的数据覆盖。

NRF_ERROR_DRV_TWI_ERR_ANACK:在轮询模式下发送地址后收到NAC。

NRF_ERROR_DRV_TWI_ERR_DNACK:在轮询模式下发送数据后收到

NACK。

3.5 I2C 设备扫描

    为什么要扫描设备?通过扫描I2C 设备,可以确认设备是否正常工作,以及设备接入是否正常等,还可以通过打印信息,确认设备的 I2C 地址。

/* Number of possible TWI addresses. */

#define TWI_ADDRESSES      127



void iic_scan_address(void)

{

    ret_code_t err_code;

    uint8_t address;

    uint8_t sample_data;

bool detected_device = false;



    NRF_LOG_INFO("TWI scanner started.");

    NRF_LOG_FLUSH();

    twi_init();//twi init



    for (address = 1; address <= TWI_ADDRESSES; address++)

    {

        err_code = nrf_drv_twi_rx(&m_twi, address, &sample_data, sizeof(sample_data));

        if (err_code == NRF_SUCCESS)

        {

            detected_device = true;

            NRF_LOG_INFO("TWI-i2c device detected at address 0x%x.", address);

        }

        NRF_LOG_FLUSH();

    }



    if (!detected_device)

    {

        NRF_LOG_INFO("No device was found.");

        //NRF_LOG_FLUSH();

    }

        NRF_LOG_INFO("TWI device scan ended.");

        NRF_LOG_FLUSH();



}

 

I2C设备扫描的Log信息:

    由于我的I2C 总线上挂了两个设备:

0x3C为 0.96寸oled屏幕的I2C地址,

0x44为,SHT35温湿度传感器的I2C地址。

                      

 

 

第4部分 SHT3x代码移植到Nordic蓝牙芯片:

由于Nordic蓝牙芯片的I2C接口与STM32有稍微不同。本文将基于官方给的STM32代码,通过修改,移植到nRF52832蓝牙芯片上。

通过扫描,我知道我的SHT35温湿度传感器的I2C地址为0x44。当然也可以通过手册得知。扫描只是为了确认没错。

 

4.1 写SHT35寄存器函数

    这个函数非常重要,由于SHT35的命令都是16位,而nrf_drv_twi_tx()函数中,数据定义的是8位。所以关键点就在code中。

 

static uint8_t tx_buf[2];

tx_buf[0] = (uint8_t)(cmd>>8);  //8位放在buf0

tx_buf[1] = (uint8_t)(cmd & 0xFF);  //8位放在buf1

 

static etError  SHT3x_WriteCommand(uint16_t cmd)

{

        ret_code_t err_code;

        static uint8_t tx_buf[2];

        tx_buf[0] = (uint8_t)(cmd>>8);  //低8位

        tx_buf[1] = (uint8_t)(cmd & 0xFF);  //高8位

    //TWI传输完成标志设置为false

        //m_xfer_done = false;

        uint8_t retry_num = 20;

        do{

          err_code = nrf_drv_twi_tx(&m_twi, SHT35_ADDRESS, tx_buf, 2, false);

            APP_ERROR_CHECK(err_code);

            retry_num--;

            //等待TWI总线应答

            //UNUSED_VARIABLE(err_code);

            }

        while((NRF_SUCCESS != err_code) && (0 < retry_num));

   

        //返回写入成功

        return NO_ERROR;

}  

 

4.2 周期测量模式

//发送命令,周期测量模式,测量频率1Hz

void SHT3X_SetPeriodicMeasurement(void)

{

    SHT3x_WriteCommand(CMD_MEAS_PERI_2_H);

    NRF_LOG_INFO("cmd_meas_peri_2_h");

    NRF_LOG_FLUSH();

}

 

4.3 读取温湿度数据

//读取温湿度数据,函数中会校验数据

etError SHX3X_ReadMeasurementBuffer(float* temperature, float* humidity)

{

    etError error;

    ret_code_t err_code;

    static uint8_t  bytes[6];

    //写入命令

    SHT3x_WriteCommand(CMD_FETCH_DATA); //0xE000,readout measurements for periodic mode

    //读出温湿度数据

    //m_xfer_done = false;

    //尝试20次

    uint8_t retry_num = 20;

        do{

          err_code = nrf_drv_twi_rx(&m_twi, SHT35_ADDRESS, bytes, 6);

            APP_ERROR_CHECK(err_code);

            retry_num--;

            //等待TWI总线应答

            //UNUSED_VARIABLE(err_code);

            }

        while((NRF_SUCCESS != err_code) && (0 < retry_num));

    //while (m_xfer_done == false){};

    //校验温湿度数据

    error = SHT3X_CheckCrc(bytes, 2, bytes[2]);

    if(error == NO_ERROR)

  {

    *temperature = SHT3X_CalcTemperature((bytes[0] << 8) | bytes[1]);

    *humidity = SHT3X_CalcHumidity((bytes[3] << 8) | bytes[4]);

  }    

    return NO_ERROR;

}

 

4.4 CRC校验

//------------------------------CRC校验和验证------------------------

// Generator polynomial for CRC

#define POLYNOMIAL  0x131 // P(x) = x^8 + x^5 + x^4 + 1 = 100110001

 

//CRC校验

static uint8_t SHT3X_CalcCrc(uint8_t data[], uint8_t nbrOfBytes)

{

  uint8_t bit;        // bit mask

  uint8_t crc = 0xFF; // calculated checksum

  uint8_t byteCtr;    // byte counter

  //用给定的多项式计算8位校验和

  for(byteCtr = 0; byteCtr < nbrOfBytes; byteCtr++)

  {

    crc ^= (data[byteCtr]);

    for(bit = 8; bit > 0; --bit)

    {

      if(crc & 0x80) crc = (crc << 1) ^ POLYNOMIAL;

      else           crc = (crc << 1);

    }

  }

 

  return crc;

}

 

//检验校验是否正确

 

static etError SHT3X_CheckCrc(uint8_t data[], uint8_t nbrOfBytes, uint8_t checksum)

{

  uint8_t crc;     // calculated checksum

  //计算8位校验和

  crc = SHT3X_CalcCrc(data, nbrOfBytes);

  //验证校验

  if(crc != checksum)

        return CHECKSUM_ERROR;

  else               

        return NO_ERROR;

}

 

 

4.5 温湿度计算

//------------------------计算温湿度---------------------------------

/**

 * 描述 : 计算温度

 * 入参: rawValue:读取的温度信息

 * 返回值 : 计算出的温度

 **/

static float SHT3X_CalcTemperature(uint16_t rawValue)

{

  //T = -45 + 175 * rawValue / (2^16-1)

    float temperature = 175.0f * (float)rawValue / 65535.0f - 45.0f;

  return temperature;

}

/**

* 描述 : 计算湿度[%RH]

 * 入参: dat :读取的湿度信息

 * 返回值 : 计算出的湿度值

***/

static float SHT3X_CalcHumidity(uint16_t rawValue)

{

  // 计算相对湿度[%RH]

  // RH = rawValue / (2^16-1) * 100

    float huminity = 100.0f * (float)rawValue / 65535.0f;

     return huminity;

}

 

4.6 测试温湿度

    先设置测量模式,再读取数据。

//-----------------------温湿度测试程序---------------------------------------

void sht35_c_test(void){

 

    NRF_LOG_INFO("sht35_c_test start..,measure the temperature and humidity");

    NRF_LOG_FLUSH();

   

        float   temperature; // ??

        float   humidity;    // ?? [%RH]

 

    //周期测量模式1HZ

    SHT3X_SetPeriodicMeasurement();

    nrf_delay_ms(50);

    //while(true)

    //  {

        SHX3X_ReadMeasurementBuffer(&temperature, &humidity);

        NRF_LOG_INFO("temperature:%d `C",temperature);

        NRF_LOG_INFO("humidity   :%d RH",humidity);

        NRF_LOG_FLUSH();   

    //测量频率设置1Hz,因此读取间隔不能小于1s

        nrf_delay_ms(1200);

    //  }

}

 

测试结果:

                          

 

用4pin的I2C OLED屏幕显示温湿度:

                          

 

5.结语

    下一章,介绍nRF52832上点亮4pin 0.96寸I2C的OLED屏幕。

 

白浪介绍:

(1)关于射频、微波、天线、无线通信、智能硬件、软件编程、渗透安全、人工智能、区块链,Java、Android、C/C++、python等综合能力的培养提升。

(2)各种学习资料、学习软件分享。

1.扫码关注公众号(Geekxiaobai)

                                        

2. 在后台发送“Python高级编程”“Python Graphics”或者“2003”,即可免费获得电子书籍。仅供学习之用。

3. 扫码关注后,查看往期内容,会有更多资料惊喜等着你来拿哦  

  想要更多相关学习资料,可以在公众号留言哦。

========******=========******========******=========******==========

发布了42 篇原创文章 · 获赞 13 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/sunskyday/article/details/104159797