Funpack5-4 - 用/基于FRDM-MCXN236开发板制作一个温湿度计
该项目使用了FRDM-MCXN236开发板,实现了SHT20 温湿度传感器的设计,它的主要功能为:通过板载的加速度计感知自身的姿态变化,实现一个 SHT20 温湿度传感器。
标签
嵌入式系统
Funpack活动
开发板
cubeline
更新2026-10-20
5

一、项目介绍:

本项目基于 NXP FRDM-MCXN236 开发板,通过 I2C 总线连接 SHT20 温湿度传感器,实现周期性环境数据采集,并通过调试串口输出采集结果,同时用板载 RGB LED指示系统运行状态,采集刷新周期为 1 秒(可调整)。

项目完整实现了活动要求的四项功能:

  1. 通过 I2C 读取传感器数据:使用 LPI2C2 主机,按 SHT2x 协议(保持模式 + CRC‑8 校验)读取温度与湿度;
  2. 周期采集环境信息:主循环以固定 1000ms 节拍采集;
  3. 通过串口输出采集结果:LPUART4 @115200,逐行打印 [T=..C RH=..%];
  4. 使用 LED 指示系统运行状态:绿色 LED 每次成功采集翻转一次(心跳),红色 LED 在采集失败时闪烁报警,且数据刷新周期不大于 1 秒。

实测运行效果(串口原文):

[BOOT] FRDM-MCXN236 SHT20 environmental monitor
[T=28.01C RH=61.02%] seq=67
[T=28.01C RH=61.05%] seq=68
[T=28.02C RH=60.99%] seq=70

即采集到约 28.0 ℃、61 %RH 的室内环境数据,稳定运行、刷新周期 1 秒。

二、使用到的硬件介绍:

类别

型号 / 说明

控制器(开发板)

NXP FRDM-MCXN236

传感器

SHT20 温湿度传感器模块(I2C 接口,3.3V 供电,7 位地址 0x40)

调试器

板载 MCU‑Link

线材

USB Type‑C 数据线;杜邦线若干



开发板用到的资源

  • LPI2C2(LP_FLEXCOMM2):I2C 主机,100 kHz,PIO4_0 = SDA、PIO4_1 = SCL,时钟源 FRO12M(12 MHz);
  • LPUART4(LP_FLEXCOMM4):调试串口,115200‑8‑N‑1,PIO1_8 = TX / PIO1_9 = RX;
  • GPIO4:板载 RGB LED,PIO4_18 = 红、PIO4_19 = 绿(低电平点亮,可用 PIO4_17 蓝);
  • SysTick:1 ms 定时中断,作为软件毫秒时基;
  • MCU‑Link:下载调试 + 虚拟串口。

 硬件接线(mikroBUS J5)

FRDM‑MCXN236 的 mikroBUS 插座把板载传感器 I2C 直接引出,接线非常方便:

SHT20 模块

mikroBUS

MCU 引脚 / 说明

VCC

J6‑7(+3.3V)

3.3V

GND

J5‑8 / J6‑8

GN

SDA

J5‑6

PIO4_0(LPI2C2_SDA)

SCL

J5‑5

PIO4_1(LPI2C2_SCL)

三、方案框图和设计思路介绍

设计思路

整体采用“分层 + 轮询”的裸机方案,共分三层:

  1. 驱动层(sht20.c/.h):只关心 SHT20 协议,对上层提供 SHT20_Init()、
    SHT20_ReadTemperature()、SHT20_ReadHumidity()、SHT20_ReadBoth() 四个接口。
  2. 板级初始化层(pin_mux.c、peripherals.c/.h、hardware_init.c):负责引脚复用、
    时钟(FRO12M → FLEXCOMM2/4)、LPI2C2 主机初始化、SysTick 时基、调试串口初始化。
  3. 应用层(led_blinky.c 主程序):以 1 秒为节拍调用驱动,组织“采集 → 串口输出 → LED 指示”。

关键设计取舍:

  • 协议模式:SHT2x 支持“保持模式(hold master,0xE3/0xE5)”与“非保持模式(0xF3/0xF5)”。
    本项目采用保持模式:主机一次传输即可完成“发命令 + 重复起始 + 读回 3 字节”,
    测量期间由传感器拉低 SCL 自动等待,实现最简洁,也便于 CRC 校验。
  • 数据完整性:每次测量读回 MSB, LSB, CRC 三字节,用 CRC‑8(多项式 0x31)校验,避免误读。
  • 周期节拍:用 SysTick 累加毫秒计数 g_ms,主循环把下一次采样对齐到固定节拍,
    保证实际输出周期稳定为 1 秒(满足“不大于 1 秒”)。
  • 状态指示:绿=心跳/正常,红=错误,语义清晰,便于无串口时快速判断系统是否在跑。
  • 健壮性:加入 I2C 总线恢复(详见第六节),解决“总线被从机占用卡死”的经典问题。

四、软件流程图

五、关键代码介绍

(1)SHT20 读取(保持模式 + CRC‑8 校验)——sht20.c

/* 发送命令并紧接重复起始读取:START + addr(W) + cmd + RESTART + addr(R) + data */
static status_t SHT20_ReadWithCommand(uint8_t cmd, uint8_t *data, uint32_t dataSize)
{
lpi2c_master_transfer_t xfer = {0};
xfer.slaveAddress = SHT20_I2C_ADDR; /* 0x40 */
xfer.direction = kLPI2C_Read;
xfer.subaddress = cmd; /* 0xE3/0xE5(保持模式) */
xfer.subaddressSize = 1U;
xfer.data = data;
xfer.dataSize = dataSize; /* 3 字节:MSB, LSB, CRC */
xfer.flags = kLPI2C_TransferDefaultFlag;
return LPI2C_MasterTransferBlocking(SHT20_I2C_BASE, &xfer);
}

/* CRC-8:多项式 0x31(x^8+x^5+x^4+1),初值 0x00(SHT2x 数据手册) */
static uint8_t SHT20_Crc8(const uint8_t *data, uint32_t len)
{
uint8_t crc = 0x00U, bit; uint32_t i;
for (i = 0U; i < len; i++) {
crc ^= data[i];
for (bit = 0U; bit < 8U; bit++)
crc = (0u != (crc & 0x80U)) ? (uint8_t)((crc << 1U) ^ 0x31U) : (uint8_t)(crc << 1U);
}
return crc;
}

温湿度换算(数据手册公式):

/* T(°C)  = -46.85 + 175.72 * S / 2^16 */
*temperature = -46.85f + 175.72f * ((float)raw / 65536.0f);
/* RH(%) = -6.0 + 125.0 * S / 2^16(限幅 0~100) */
rh = -6.0f + 125.0f * ((float)raw / 65536.0f);

(2)周期采集 + 串口输出 + LED 指示——led_blinky.c

int main(void)
{
BOARD_InitHardware(); /* 引脚->时钟->LPI2C2/SysTick->调试串口 */
PRINTF("\r\n[BOOT] FRDM-MCXN236 SHT20 environmental monitor\r\n");
LED_RED_OFF(); LED_GREEN_OFF();
if (SHT20_Init() != kStatus_Success) { PRINTF("[ERR] SHT20 init failed\r\n"); LED_RED_ON(); }

while (1) {
status = SHT20_ReadBoth(&temperature, &humidity);
if (status == kStatus_Success) {
PRINTF("[T=%.2fC RH=%.2f%%] seq=%u\r\n", (double)temperature, (double)humidity, seq);
LED_RED_OFF(); LED_GREEN_TOGGLE(); /* 绿灯心跳 */
} else {
PRINTF("[ERR] sensor read failed (status=%d)\r\n", (int)status);
LED_GREEN_OFF(); LED_RED_TOGGLE(); /* 红灯闪烁 */
if (++failCount >= 5U) { BOARD_I2C_Recover(); failCount = 0U; } /* 总线恢复 */
}
/* 对齐到固定 1s 节拍 */
nextTick += 1000U;
while ((int32_t)(g_ms - nextTick) < 0) { }
}
}

(3)I2C 总线恢复(稳健性关键)——peripherals.c

/* 9 个 SCL 时钟 + 一个 STOP,使占用总线的从机释放 SDA */
static void LPI2C2_ReleaseBus(void) {
/* P4_0/P4_1 临时切 GPIO:SCL 输出、SDA 输入(保留上拉) */
...
for (i = 0U; i < 9U; i++) { GPIO_PinWrite(GPIO4,1,0); delay; GPIO_PinWrite(GPIO4,1,1); delay; }
/* STOP:SCL 高时 SDA 由低变高 */
...
/* 恢复 I2C 复用 */
}
void BOARD_I2C_Recover(void) { LPI2C2_ReleaseBus(); LPI2C_MasterInit(LPI2C2, &cfg, 12000000U); }


六、功能展示图及说明

  1. 串口输出
  2. LED 指示:正常运行时绿色 LED 每秒翻转(心跳);读取失败时红色 LED 闪烁。
  3. 实测数据:室内环境下温度约 28.0 ℃、相对湿度约 61 %RH,数据连续稳定。

七、项目中遇到的难题及解决方法

难题 1:I2C 总线“卡死”,所有传输返回 900(BUSY)——最耗时的一个坑

  • 现象:程序一上电,读写 SHT20 始终返回 status=900(kStatus_LPI2C_Busy)。为定位问题,我把工程里 NXP 官方的板载加速度计例程(读 FXLS8974,同一 LPI2C2 总线)也烧进去,同样失败;说明总线硬件本身完好。
  • 排查:用软件回读寄存器确认引脚配置正确(PCR=Alt2+上拉、开漏正确),并把 SDA 临时切成 GPIO 输入观察,发现 SDA 一直被拉低(关掉主机、切成输入仍为低,推挽驱动高也拉不起来),因此判断是总线被某个从机占用。
  • 根因:I2C 传输是“起始→地址→数据→停止”的完整序列;当主机在一次传输未完成时被复位
    (调试时反复烧录/复位极易发生),从机收不到停止条件,便继续占用总线、把 SDA 拉低,之后所有传输都判“总线忙”。这是 I2C 的经典“总线挂死”,与传感器和接线无关。
  • 解决:加入总线恢复——把 SCL 临时切为 GPIO,发送 9 个时钟脉冲 + 一个 STOP强制从机释放总线,再重新初始化 I2C 主机;并在连续 5 次读取失败时自动重做一次。加入后立即恢复正常读数。
  • 预防:任何可能中途复位主机的操作后先做总线恢复;对 I2C 读写加超时与重试。

难题 2:SysTick 时基与实际时钟不符

  • 现象:配置工具按 48 MHz 生成的 SysTick 是 10 ms,但工程实际用 FRO12M(12 MHz)启动,实际中断约 40 ms,时基不准。
  • 解决:按实际时钟重算——SYSTICK0_CLK_FREQ = 12000000、SYSTICK0_TICKS = 12000(1 ms)。

难题 3:串口完全没有输出

  • 现象:固件已运行,但串口无任何输出。
  • 根因:原始 led_blinky 示例不使用串口,pin_mux.c 里没有配置 LPUART4 的引脚。
  • 解决:在引脚配置中补上 PIO1_8/PIO1_9(FC4,Alt2),并确保初始化调试串口。

难题 4:新增引脚组不生效

  • 现象:I2C 引脚配好了却不工作。
  • 根因:hardware_init.c 调用的是 BOARD_InitPins(),而新增的引脚组挂在BOARD_InitBootPins() 下。
  • 解决:改为调用 BOARD_InitBootPins()。

难题 5:浮点不打印小数

  • 现象:PRINTF("%.2f", x) 输出不正确。
  • 根因:SDK 调试串口默认不开启浮点打印。
  • 解决:在 CMakeLists.txt 中加入 -DPRINTF_FLOAT_ENABLE=1(及 PRINTF_ADVANCED_ENABLE=1)。

八、心得体会

这次活动让我完整走了一遍“从需求到真机跑通”的嵌入式开发流程,收获主要体现在:

第一,“现象 ≠ 根因”。一开始 I2C 一直报“总线忙”,我第一反应是接线或传感器坏了,但通过“换一个已知良好的例程(板载加速度计)对比”这一关键实验,排除了硬件与自身代码,才把方向锁定到“总线状态被破坏”。这让我体会到:遇到问题要用实验去二分、去隔离变量,而不是凭直觉猜。

第二,协议细节决定成败。SHT20 的保持/非保持模式、CRC 多项式、状态位清零、温湿度换算式等,任何一个弄错都会得到错误数据;我通过对照数据手册逐条实现并校验最终读数稳定合理。这说明“对着手册写驱动”比“抄一段能用就行”可靠得多。

第三,稳健性同样重要。能读到数据只是第一步,还要考虑异常(拔线、复位、总线卡死)时系统不崩、能自愈、能提示。因此我加上了总线恢复与失败计数重试,让系统更“抗造”。接下来我打算继续完善:把采集数据接入显示屏做实时曲线、增加按键交互与参数配置、并把 I2C 访问改为带超时/中断的方式,进一步降低 CPU 占用。感谢 NXP 与电子森林提供这次机会,也感谢 DigiKey 的支持,让我在真实硬件上完成了第一个完整的 I2C 传感器项目。



附件下载
Funpack5-4_FRDM-MCXN236_SHT20_源码.zip
源码文件
团队介绍
我是一个个人开发者
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号