这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » 低功耗蓝牙温湿度计-过程贴

共1条 1/1 1 跳转至

低功耗蓝牙温湿度计-过程贴

菜鸟
2026-06-14 20:35:02     打赏

一、环境搭建

参考文章:https://blog.csdn.net/weixin_38426553/article/details/132107199   以及b站视频:https://www.bilibili.com/video/BV1Ps61BNERR/  


导入LED闪烁官方案例:

image.png

编译并下载至开发板,效果如下:

LED闪烁效果.gif

证明环境搭建没有问题;下面进行温湿度计的开发。

二、温湿度传感器驱动

本次采用的温湿度传感器是SHTC3, 是 Sensirion 的一款 数字式温湿度传感器,内部集成了湿度敏感单元、温度测量单元和数字信号处理电路。它通过 I2C 接口与 MCU 通信,输出的不是模拟电压,而是已经校准后的数字温度和湿度数据。它的典型精度为 ±2 %RH、±0.2 ℃,湿度测量范围为 0~100 %RH,温度测量范围为 -40~125 ℃,供电范围为 1.62~3.6 V

SHTC3 的供电范围覆盖 1.62~3.6 V,并且官方说明其每次测量能量预算低于 1 μA,面向移动设备和电池供电的无线应用。 这点和“支持低功耗运行”高度匹配:MCXW71 可以定时唤醒,启动 SHTC3 采样,读取完温湿度后让传感器进入 sleep,再通过 BLE 广播或 notify 发送数据。

SHTC3 固定 I2C 地址通常为 0x70,典型流程就是“唤醒 → 发送测量命令 → 延时等待 → 读取 6 字节 → CRC 校验 → 数据换算 → 休眠”,这种流程很容易移植。

温湿度传感的驱动程序在官方案例“cmsis_lpi2c_edma_b2b_transfer_master”中修改:

2.1 SHTC3驱动程序:

SHTC3.c

#include "shtc3.h"

#include "fsl_common.h"
#include <string.h>

/*
 * SHTC3 固定 7-bit I2C 地址
 */
#define SHTC3_I2C_ADDRESS              0x70U

/*
 * SHTC3 命令
 */
#define SHTC3_CMD_WAKEUP               0x3517U
#define SHTC3_CMD_SLEEP                0xB098U
#define SHTC3_CMD_SOFT_RESET           0x805DU
#define SHTC3_CMD_READ_ID              0xEFC8U

/*
 * Normal Mode, Clock Stretching Disabled, Temperature First
 * 读取结果顺序:
 * Byte0 Temperature MSB
 * Byte1 Temperature LSB
 * Byte2 Temperature CRC
 * Byte3 Humidity MSB
 * Byte4 Humidity LSB
 * Byte5 Humidity CRC
 */
#define SHTC3_CMD_MEASURE_T_FIRST      0x7866U

#define SHTC3_TIMEOUT_COUNT            1000000UL

/*
 * 因为官方例程使用 EDMA,I2C 收发缓冲区建议放在 non-cacheable 区域。
 * 数据量很小,所以用静态全局缓冲区。
 */
AT_NONCACHEABLE_SECTION(static uint8_t s_i2c_tx_buf[2]);
AT_NONCACHEABLE_SECTION(static uint8_t s_i2c_rx_buf[6]);

static ARM_DRIVER_I2C *s_shtc3_i2c = NULL;

static volatile bool s_i2c_done = false;
static volatile bool s_i2c_error = false;

static void SHTC3_I2C_Callback(uint32_t event)
{
    if (event & ARM_I2C_EVENT_TRANSFER_DONE)
    {
        s_i2c_done = true;
    }

    if (event & (ARM_I2C_EVENT_TRANSFER_INCOMPLETE |
                 ARM_I2C_EVENT_ADDRESS_NACK |
                 ARM_I2C_EVENT_BUS_ERROR |
                 ARM_I2C_EVENT_ARBITRATION_LOST))
    {
        s_i2c_error = true;
        s_i2c_done = true;
    }
}

static void SHTC3_DelayUs(uint32_t us)
{
    SDK_DelayAtLeastUs(us, SystemCoreClock);
}

static void SHTC3_DelayMs(uint32_t ms)
{
    while (ms--)
    {
        SHTC3_DelayUs(1000U);
    }
}

static int SHTC3_WaitTransferDone(void)
{
    uint32_t timeout = SHTC3_TIMEOUT_COUNT;

    while (!s_i2c_done)
    {
        if (timeout-- == 0U)
        {
            return SHTC3_ERROR_TIMEOUT;
        }
    }

    if (s_i2c_error)
    {
        return SHTC3_ERROR_I2C;
    }

    return SHTC3_OK;
}

static int SHTC3_WriteCommand(uint16_t cmd)
{
    int32_t status;

    if (s_shtc3_i2c == NULL)
    {
        return SHTC3_ERROR_PARAM;
    }

    s_i2c_tx_buf[0] = (uint8_t)(cmd >> 8);
    s_i2c_tx_buf[1] = (uint8_t)(cmd & 0xFFU);

    s_i2c_done = false;
    s_i2c_error = false;

    status = s_shtc3_i2c->MasterTransmit(SHTC3_I2C_ADDRESS,
                                         s_i2c_tx_buf,
                                         2U,
                                         false);
    if (status != ARM_DRIVER_OK)
    {
        return SHTC3_ERROR_I2C;
    }

    return SHTC3_WaitTransferDone();
}

static int SHTC3_ReadBytes(uint8_t *rx_buf, uint32_t len)
{
    int32_t status;
    int ret;

    if ((s_shtc3_i2c == NULL) || (rx_buf == NULL) || (len == 0U) || (len > sizeof(s_i2c_rx_buf)))
    {
        return SHTC3_ERROR_PARAM;
    }

    memset(s_i2c_rx_buf, 0, sizeof(s_i2c_rx_buf));

    s_i2c_done = false;
    s_i2c_error = false;

    status = s_shtc3_i2c->MasterReceive(SHTC3_I2C_ADDRESS,
                                        s_i2c_rx_buf,
                                        len,
                                        false);
    if (status != ARM_DRIVER_OK)
    {
        return SHTC3_ERROR_I2C;
    }

    ret = SHTC3_WaitTransferDone();
    if (ret != SHTC3_OK)
    {
        return ret;
    }

    memcpy(rx_buf, s_i2c_rx_buf, len);

    return SHTC3_OK;
}

/*
 * SHTC3 CRC-8
 * Polynomial: 0x31
 * Init: 0xFF
 */
static uint8_t SHTC3_CalcCrc8(const uint8_t *data, uint32_t len)
{
    uint8_t crc = 0xFFU;

    for (uint32_t i = 0U; i < len; i++)
    {
        crc ^= data[i];

        for (uint8_t bit = 0U; bit < 8U; bit++)
        {
            if (crc & 0x80U)
            {
                crc = (uint8_t)((crc << 1U) ^ 0x31U);
            }
            else
            {
                crc <<= 1U;
            }
        }
    }

    return crc;
}

int SHTC3_Init(ARM_DRIVER_I2C *i2c)
{
    int32_t status;
    int ret;

    if (i2c == NULL)
    {
        return SHTC3_ERROR_PARAM;
    }

    s_shtc3_i2c = i2c;

    status = s_shtc3_i2c->Initialize(SHTC3_I2C_Callback);
    if (status != ARM_DRIVER_OK)
    {
        return SHTC3_ERROR_I2C;
    }

    status = s_shtc3_i2c->PowerControl(ARM_POWER_FULL);
    if (status != ARM_DRIVER_OK)
    {
        return SHTC3_ERROR_I2C;
    }

    status = s_shtc3_i2c->Control(ARM_I2C_BUS_SPEED, ARM_I2C_BUS_SPEED_STANDARD);
    if (status != ARM_DRIVER_OK)
    {
        return SHTC3_ERROR_I2C;
    }

    SHTC3_DelayMs(2U);

    ret = SHTC3_WriteCommand(SHTC3_CMD_WAKEUP);
    if (ret != SHTC3_OK)
    {
        return ret;
    }

    SHTC3_DelayMs(1U);

    ret = SHTC3_WriteCommand(SHTC3_CMD_SOFT_RESET);
    if (ret != SHTC3_OK)
    {
        return ret;
    }

    SHTC3_DelayMs(2U);

    ret = SHTC3_WriteCommand(SHTC3_CMD_SLEEP);
    if (ret != SHTC3_OK)
    {
        return ret;
    }

    SHTC3_DelayMs(1U);

    return SHTC3_OK;
}

int SHTC3_ReadId(uint16_t *id)
{
    uint8_t rx_buf[3];
    uint16_t raw_id;
    int ret;

    if (id == NULL)
    {
        return SHTC3_ERROR_PARAM;
    }

    ret = SHTC3_WriteCommand(SHTC3_CMD_WAKEUP);
    if (ret != SHTC3_OK)
    {
        return ret;
    }

    SHTC3_DelayMs(1U);

    ret = SHTC3_WriteCommand(SHTC3_CMD_READ_ID);
    if (ret != SHTC3_OK)
    {
        return ret;
    }

    ret = SHTC3_ReadBytes(rx_buf, sizeof(rx_buf));
    if (ret != SHTC3_OK)
    {
        return ret;
    }

    if (SHTC3_CalcCrc8(rx_buf, 2U) != rx_buf[2])
    {
        (void)SHTC3_WriteCommand(SHTC3_CMD_SLEEP);
        return SHTC3_ERROR_CRC;
    }

    raw_id = (uint16_t)(((uint16_t)rx_buf[0] << 8U) | rx_buf[1]);

    /*
     * SHTC3 ID 判断:
     * bit11 = 1
     * bit5:0 = 0x07
     */
    if ((raw_id & 0x083FU) != 0x0807U)
    {
        (void)SHTC3_WriteCommand(SHTC3_CMD_SLEEP);
        return SHTC3_ERROR_ID;
    }

    *id = raw_id;

    ret = SHTC3_WriteCommand(SHTC3_CMD_SLEEP);
    if (ret != SHTC3_OK)
    {
        return ret;
    }

    return SHTC3_OK;
}

int SHTC3_ReadTemperatureHumidity(shtc3_data_t *data)
{
    uint8_t rx_buf[6];
    uint16_t raw_temp;
    uint16_t raw_humi;
    int32_t temp_x100;
    uint32_t humi_x100;
    int ret;

    if (data == NULL)
    {
        return SHTC3_ERROR_PARAM;
    }

    ret = SHTC3_WriteCommand(SHTC3_CMD_WAKEUP);
    if (ret != SHTC3_OK)
    {
        return ret;
    }

    SHTC3_DelayMs(1U);

    ret = SHTC3_WriteCommand(SHTC3_CMD_MEASURE_T_FIRST);
    if (ret != SHTC3_OK)
    {
        return ret;
    }

    /*
     * 使用 Clock Stretching Disabled 命令时,主机需要主动等待测量完成。
     * 给 15 ms 比较稳。
     */
    SHTC3_DelayMs(15U);

    ret = SHTC3_ReadBytes(rx_buf, sizeof(rx_buf));
    if (ret != SHTC3_OK)
    {
        return ret;
    }

    if (SHTC3_CalcCrc8(&rx_buf[0], 2U) != rx_buf[2])
    {
        (void)SHTC3_WriteCommand(SHTC3_CMD_SLEEP);
        return SHTC3_ERROR_CRC;
    }

    if (SHTC3_CalcCrc8(&rx_buf[3], 2U) != rx_buf[5])
    {
        (void)SHTC3_WriteCommand(SHTC3_CMD_SLEEP);
        return SHTC3_ERROR_CRC;
    }

    raw_temp = (uint16_t)(((uint16_t)rx_buf[0] << 8U) | rx_buf[1]);
    raw_humi = (uint16_t)(((uint16_t)rx_buf[3] << 8U) | rx_buf[4]);

    /*
     * SHTC3 换算公式:
     * T  = -45 + 175 * raw_temp / 65536
     * RH = 100 * raw_humi / 65536
     *
     * 这里转成 ×100 的整数,避免 printf 浮点配置问题。
     */
    temp_x100 = -4500 + ((int32_t)17500 * (int32_t)raw_temp) / 65536;
    humi_x100 = ((uint32_t)10000 * (uint32_t)raw_humi) / 65536U;

    if (humi_x100 > 10000U)
    {
        humi_x100 = 10000U;
    }

    data->temperature_x100 = (int16_t)temp_x100;
    data->humidity_x100 = (uint16_t)humi_x100;

    ret = SHTC3_WriteCommand(SHTC3_CMD_SLEEP);
    if (ret != SHTC3_OK)
    {
        return ret;
    }

    return SHTC3_OK;
}

shtc3.h

#ifndef SHTC3_H_
#define SHTC3_H_

#include <stdint.h>
#include <stdbool.h>
#include "fsl_lpi2c_cmsis.h"

#define SHTC3_OK                 0
#define SHTC3_ERROR_I2C         -1
#define SHTC3_ERROR_TIMEOUT     -2
#define SHTC3_ERROR_CRC         -3
#define SHTC3_ERROR_ID          -4
#define SHTC3_ERROR_PARAM       -5

typedef struct
{
    int16_t temperature_x100;   /* 温度 ×100,例如 2536 = 25.36 ℃ */
    uint16_t humidity_x100;     /* 湿度 ×100,例如 5862 = 58.62 %RH */
} shtc3_data_t;

int SHTC3_Init(ARM_DRIVER_I2C *i2c);
int SHTC3_ReadId(uint16_t *id);
int SHTC3_ReadTemperatureHumidity(shtc3_data_t *data);

#endif

2.2 主函数

/*
 * Copyright 2017 NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <stdio.h>
#include <stdint.h>

#include "board.h"
#include "fsl_debug_console.h"
#include "fsl_lpi2c.h"
#include "app.h"
#include "fsl_lpi2c_cmsis.h"

#include "shtc3.h"

static void App_DelayUs(uint32_t us)
{
    SDK_DelayAtLeastUs(us, SystemCoreClock);
}

static void App_DelayMs(uint32_t ms)
{
    while (ms--)
    {
        App_DelayUs(1000U);
    }
}

static void PrintSignedX100(const char *name, int16_t value_x100, const char *unit)
{
    int32_t value = value_x100;

    if (value < 0)
    {
        value = -value;
        PRINTF("%s: -%d.%02d %s", name, (int)(value / 100), (int)(value % 100), unit);
    }
    else
    {
        PRINTF("%s: %d.%02d %s", name, (int)(value / 100), (int)(value % 100), unit);
    }
}

static void PrintUnsignedX100(const char *name, uint16_t value_x100, const char *unit)
{
    PRINTF("%s: %d.%02d %s", name, (int)(value_x100 / 100U), (int)(value_x100 % 100U), unit);
}

static void PrintShtc3Error(int ret)
{
    switch (ret)
    {
        case SHTC3_ERROR_I2C:
            PRINTF("SHTC3 error: I2C transfer failed. Check wiring, address, pull-up resistors.\r\n");
            break;

        case SHTC3_ERROR_TIMEOUT:
            PRINTF("SHTC3 error: I2C transfer timeout. Check CMSIS callback and EDMA configuration.\r\n");
            break;

        case SHTC3_ERROR_CRC:
            PRINTF("SHTC3 error: CRC check failed. Check I2C signal quality.\r\n");
            break;

        case SHTC3_ERROR_ID:
            PRINTF("SHTC3 error: ID mismatch. The device may not be SHTC3.\r\n");
            break;

        case SHTC3_ERROR_PARAM:
            PRINTF("SHTC3 error: parameter error.\r\n");
            break;

        default:
            PRINTF("SHTC3 error: unknown ret = %d\r\n", ret);
            break;
    }
}

int main(void)
{
    shtc3_data_t th;
    uint16_t shtc3_id = 0U;
    int ret;

    /*
     * 官方例程原有初始化:包含时钟、引脚、Debug Console 等。
     */
    BOARD_InitHardware();

    /*
     * 官方例程原有 EDMA / DMAMUX 初始化。
     * 这一段必须保留,因为当前工程使用 CMSIS LPI2C EDMA 驱动。
     */
#if (defined(FSL_FEATURE_SOC_DMAMUX_COUNT) && FSL_FEATURE_SOC_DMAMUX_COUNT)
    DMAMUX_Init(EXAMPLE_LPI2C_DMAMUX_BASEADDR);
#endif

    edma_config_t edmaConfig = {0};
    EDMA_GetDefaultConfig(&edmaConfig);

#if defined(BOARD_GetEDMAConfig)
    BOARD_GetEDMAConfig(edmaConfig);
#endif

    EDMA_Init(EXAMPLE_LPI2C_DMA_BASEADDR, &edmaConfig);

    PRINTF("\r\nFRDM-MCXW71 SHTC3 Temperature & Humidity Test\r\n");
    PRINTF("Base project: cmsis_lpi2c_edma_b2b_transfer_master\r\n");
    PRINTF("Sensor: SHTC3, I2C address: 0x70\r\n\r\n");

    /*
     * 使用官方例程中的 EXAMPLE_I2C_MASTER。
     * 注意:I2C 初始化放在 SHTC3_Init() 内部完成。
     */
    ret = SHTC3_Init(&EXAMPLE_I2C_MASTER);
    if (ret != SHTC3_OK)
    {
        PRINTF("SHTC3 init failed, ret = %d\r\n", ret);
        PrintShtc3Error(ret);

        while (1)
        {
        }
    }

    ret = SHTC3_ReadId(&shtc3_id);
    if (ret == SHTC3_OK)
    {
        PRINTF("SHTC3 detected, ID = 0x%04X\r\n\r\n", shtc3_id);
    }
    else
    {
        PRINTF("SHTC3 ID read failed, ret = %d\r\n", ret);
        PrintShtc3Error(ret);
        PRINTF("The program will continue to try reading temperature and humidity.\r\n\r\n");
    }

    while (1)
    {
        ret = SHTC3_ReadTemperatureHumidity(&th);

        if (ret == SHTC3_OK)
        {
            PrintSignedX100("Temperature", th.temperature_x100, "C");
            PRINTF(", ");
            PrintUnsignedX100("Humidity", th.humidity_x100, "%RH");
            PRINTF("\r\n");
        }
        else
        {
            PRINTF("SHTC3 read failed, ret = %d\r\n", ret);
            PrintShtc3Error(ret);
        }

        App_DelayMs(1000U);
    }
}

2.3 实现效果

image.png

目前室温为29度,将手指按到传感器上,看到温度明显上升,证明程序成功驱动。




关键词: 湿度计     环境搭建          湿度计    

共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]