Nordic 蓝牙芯片上开发 SHT3x系列温湿度传感器_nrf52832 sht30,从入门到精通系列物联网嵌入式开发高级工程师路线介绍

121 阅读10分钟

收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。 img img

如果你需要这些资料,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

很明显,当传感器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; }

收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。 img img

如果你需要这些资料,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!