一、环境搭建
参考文章:https://blog.csdn.net/weixin_38426553/article/details/132107199 以及b站视频:https://www.bilibili.com/video/BV1Ps61BNERR/
导入LED闪烁官方案例:

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

证明环境搭建没有问题;下面进行温湿度计的开发。
二、温湿度传感器驱动
本次采用的温湿度传感器是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);
#endif2.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 实现效果

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