
简介这份资源围绕24LE1单片机与LIS3DH、LIS3DSH三轴加速度计的INT1中断唤醒方案展开面向嵌入式开发初学者与需要低功耗运动检测设计的工程师。内容涵盖SPI通信配置、INT1中断初始化、中断服务程序、加速度数据读取及CRC错误与超时处理等关键环节可用于运动检测、物联网终端与消费电子等场景的参考实现。压缩包共248个文件约701KB以48个c源码、40个h头文件为核心辅以lst、obj、m51、hex等编译中间与烧录文件以及uvproj、uvopt等Keil工程配置便于直接打开工程编译验证。目前已有1364人学习下载。通过研读源码与工程结构读者可掌握24LE1外部中断唤醒机制、LIS3DH/LIS3DSH寄存器配置与SPI时序控制并借鉴其错误处理与电源管理思路快速迁移到自己的低功耗采集项目中。1. 从 24LE1 3DH.zip 说起一个 Nordic 老芯片的三轴姿态方案到底值不值得做如果你手里正好有一批 nRF24LE1 模组或者你在做低功耗无线姿态采集的选型那你大概率搜到过 24LE1 3DH.zip 这个名字。它不是一个新东西核心是一颗 Nordic 早期的 2.4G 无线 SoC——nRF24LE1片上带 8051 内核、2.4GHz 射频收发器和一套增强型外设配合一颗三轴加速度传感器把姿态数据通过私有 2.4G 协议发出去。听起来很朴素但它的价值恰恰在于单芯片搞定采集和无线待机电流能做到微安级成本压到极低适合那种「一节纽扣电池撑半年、只需要传几个字节姿态数据」的场景。我见过太多人一上来就想用 BLE 或 Wi-Fi 方案结果功耗和成本双双超标回头才发现这类老芯片才是正解。这篇笔记就围绕 24LE1 3DH 这个方向把硬件连接、寄存器配置、数据打包、无线收发和调试排错一步步拆开让你能照着复现一套最小可用的三轴姿态采集节点。新手能跟着跑通熟手能看到参数边界和踩坑点。2. 先搞懂 nRF24LE1 的硬件底子为什么它适合做三轴姿态节点2.1 芯片内部有什么决定了你能怎么用nRF24LE1 不是一颗单纯的射频芯片它把 8051 MCU、2.4GHz 射频、Flash、RAM、ADC、PWM、I2C、SPI、UART 全部塞进一个 4x4mm 的 QFN 封装里。对三轴姿态采集来说关键资源是这几个增强型 8051 内核跑在 16MHz够你跑简单的滤波和打包逻辑16KB Flash 存固件1KB RAM 做缓冲对于只传 XYZ 三轴数据的场景绰绰有余最核心的是它自带 2.4GHz 射频支持 GFSK 调制空中速率可配 250kbps、1Mbps、2Mbps配合 Nordic 自家的 Enhanced ShockBurst 协议硬件自动处理前导码、地址、CRC 和自动应答你只需要往 FIFO 里塞数据就行。三轴加速度传感器一般通过 I2C 或 SPI 接进来。常见做法是选一颗 LIS3DH 或类似的小封装加速度计量程 ±2g 到 ±16g 可配输出数据率从 1Hz 到 5kHz 可调。为什么强调这个因为 nRF24LE1 的 8051 没有硬件 I2C你得用 GPIO 模拟时序控制全靠软件延时所以传感器不能选太娇贵的LIS3DH 这类容忍度高的比较合适。2.2 最小系统连接电源、晶振、天线、传感器先把最小系统搭起来。nRF24LE1 的工作电压是 1.9V 到 3.6V典型 3V。如果你用纽扣电池 CR2032电压 3V但内阻偏大射频发射瞬间电流冲到十几毫安时会有压降所以建议在电源脚旁边并一颗 10uF 钽电容加一颗 100nF 陶瓷电容位置尽量靠近芯片。晶振用 16MHz 无源晶振负载电容按晶振规格配一般 12pF 左右。天线部分如果模组自带 PCB 天线就直接用自己画板的话2.4GHz 的匹配网络和天线布局是玄学重灾区建议直接抄模组厂商的参考设计别自己发挥。传感器连接以 LIS3DH 为例I2C 地址由 SA0 引脚决定接 GND 是 0x18接 VDD 是 0x19。SCL 和 SDA 分别接 nRF24LE1 的两个 GPIO我一般选 P0.0 和 P0.1这两个脚复用功能少不容易冲突。中断脚可以不接用轮询方式读数据简单可靠。下面是一个典型的连接表nRF24LE1 引脚连接对象备注VDD3V 电源并 10uF 100nF 去耦GND地单点接地P0.0LIS3DH SCL软件 I2C 时钟P0.1LIS3DH SDA软件 I2C 数据P0.2LIS3DH SA0接地则地址 0x18XC1/XC216MHz 晶振负载电容 12pFANT2.4G 天线用参考设计匹配2.3 时钟和电源管理低功耗的关键在这里nRF24LE1 的低功耗不是自动的你得主动配置。它有几个时钟源16MHz 晶振、16MHz RC 振荡器、32.768kHz 晶振或 RC。射频工作时必须用 16MHz 晶振精度够平时采集和休眠可以切到 32.768kHz 或者 RC省电。电源管理寄存器里有个 PWRDWN 位置位后芯片进入掉电模式电流降到微安级但 RAM 数据保留唤醒后继续跑。我一般的工作流程是上电初始化配置传感器为 10Hz 输出率然后进主循环。主循环里读一次三轴数据打包通过射频发出去然后进掉电模式等定时器或外部中断唤醒。定时器用 32.768kHz 时钟源设一个 100ms 的周期这样每秒发 10 次数据平均电流能压到几百微安。如果你用 CR2032容量 200mAh 左右算下来能撑几个月具体看发射功率和占空比。提示掉电模式唤醒后射频部分需要重新校准别直接发数据先等晶振稳定一般延时 1ms 左右。3. 三轴数据采集从寄存器配置到软件 I2C 读写3.1 LIS3DH 初始化四个必调参数LIS3DH 的初始化不复杂但有几个参数必须配对否则数据要么不动要么跳得厉害。第一个是 CTRL_REG1 里的 ODR输出数据率我一般设 10Hz 或 50Hz对应姿态采集够用。第二个是 LPen 位低功耗模式使能开了之后功耗降但噪声略增看你对精度的要求。第三个是 CTRL_REG4 里的 FS满量程±2g 适合静态姿态±4g 以上适合有振动的场景。第四个是 BDU块数据更新置 1 后保证你读高低字节时数据不会在中间被刷新这个坑我踩过不设的话偶尔会读到错位的数据。初始化代码用软件 I2C 写下面是一个典型的配置序列// 软件 I2C 写一个字节到 LIS3DH void lis3dh_write(uint8_t reg, uint8_t val) { i2c_start(); i2c_write_byte(0x18 1); // 设备地址 0x18写操作 i2c_write_byte(reg); // 寄存器地址 i2c_write_byte(val); // 写入的值 i2c_stop(); } // 初始化 LIS3DH void lis3dh_init(void) { // CTRL_REG1: ODR10Hz, 使能 XYZ 轴 lis3dh_write(0x20, 0x27); // CTRL_REG4: 满量程 ±2g, BDU1 lis3dh_write(0x23, 0x88); // CTRL_REG5: 不启用 FIFO lis3dh_write(0x24, 0x00); }逻辑说明0x20 是 CTRL_REG10x27 拆开看是 0010 0111低三位 XEN/YEN/ZEN 置 1 使能三轴高四位 0010 对应 ODR10Hz。0x23 是 CTRL_REG40x88 是 1000 1000BDU 位是 bit7置 1FS 位是 bit5:401 对应 ±2g。参数怎么改如果你要 50Hz把 0x27 改成 0x57要 ±4g把 0x88 改成 0x98。3.2 读三轴数据高低字节顺序和符号处理LIS3DH 的输出寄存器是 0x28 到 0x2D依次是 OUT_X_L、OUT_X_H、OUT_Y_L、OUT_Y_H、OUT_Z_L、OUT_Z_H。读的时候要连续读先读低字节再读高字节拼成 16 位有符号数。注意LIS3DH 输出的是左对齐还是右对齐取决于 CTRL_REG4 的 BLE 位默认是右对齐直接拼就行。拼完之后要转成实际加速度值公式是加速度 原始值 / 32768 * 满量程 * 2。比如 ±2g 满量程原始值 16384 对应 1g。// 读三轴原始数据 void lis3dh_read_raw(int16_t *x, int16_t *y, int16_t *z) { uint8_t buf[6]; i2c_start(); i2c_write_byte(0x18 1); i2c_write_byte(0x28 | 0x80); // 0x80 表示地址自动递增 i2c_start(); i2c_write_byte((0x18 1) | 1); // 读操作 for (int i 0; i 6; i) { buf[i] i2c_read_byte(i 5 ? 0 : 1); // 最后一个字节发 NACK } i2c_stop(); *x (int16_t)((buf[1] 8) | buf[0]); *y (int16_t)((buf[3] 8) | buf[2]); *z (int16_t)((buf[5] 8) | buf[4]); }逻辑说明0x28 | 0x80 是告诉 LIS3DH 从 0x28 开始连续读地址自动递增这样一次 I2C 事务就能读完六个字节。最后一个字节发 NACK 是 I2C 协议要求表示主机接收完毕。符号处理用 int16_t 强转C 语言里这是实现定义行为但 8051 的 Keil 编译器支持放心用。3.3 数据打包怎么把 XYZ 塞进射频 payloadnRF24LE1 的 Enhanced ShockBurst 协议payload 长度可配从 1 到 32 字节。三轴数据每个轴 2 字节一共 6 字节再加一个帧头或序号8 字节足够。我一般这样打包byte0 是帧头 0xAAbyte1 是序号循环 0 到 255byte2 到 byte7 是 XYZ 的高低字节。接收端先找 0xAA再对序号做丢包统计。payload 长度设 8固定长度接收端不用处理变长简单。// 打包三轴数据到 payload void pack_payload(uint8_t *payload, int16_t x, int16_t y, int16_t z, uint8_t seq) { payload[0] 0xAA; // 帧头 payload[1] seq; // 序号 payload[2] x 0xFF; // X 低字节 payload[3] (x 8) 0xFF; // X 高字节 payload[4] y 0xFF; payload[5] (y 8) 0xFF; payload[6] z 0xFF; payload[7] (z 8) 0xFF; }逻辑说明小端模式低字节在前接收端拼的时候反过来就行。序号的作用是丢包检测接收端发现序号跳变就知道中间丢了包。参数怎么改如果你还想传电池电压或温度把 payload 长度加到 10 或 12在 pack 函数里追加就行但注意别超过 32 字节。4. 2.4G 无线收发Enhanced ShockBurst 配置与收发流程4.1 射频寄存器配置频道、速率、地址nRF24LE1 的射频配置和 nRF24L01 很像但寄存器地址和位定义有差异别直接抄 24L01 的代码。关键寄存器是 RF_CH、RF_SETUP、RX_ADDR_P0、TX_ADDR、EN_AA、EN_RXADDR、SETUP_RETR、RF_SETUP。频道选 2.400GHz 到 2.525GHz步进 1MHz我一般选 76 频道2.476GHz避开 Wi-Fi 常用的 1、6、11 信道。速率选 250kbps灵敏度最高距离最远虽然空中时间长一点但姿态数据量小无所谓。地址用 5 字节发射和接收地址要一致我一般用 0xE7E7E7E7E7好记。// 射频初始化 void rf_init(void) { // 进入待机模式 RFCE 0; // 频道 76 RF_CH 76; // 速率 250kbps, 发射功率 0dBm RF_SETUP 0x27; // 使能自动应答和接收通道 0 EN_AA 0x01; EN_RXADDR 0x01; // 地址宽度 5 字节 SETUP_AW 0x03; // 自动重发等待 250us重发 3 次 SETUP_RETR 0x13; // 接收地址 RX_ADDR_P0 0xE7E7E7E7E7; // 发射地址 TX_ADDR 0xE7E7E7E7E7; // payload 长度 8 字节 RX_PW_P0 8; // 配置为发射模式 CONFIG 0x0E; // 上电 RFCE 1; }逻辑说明RF_SETUP 的 0x27 拆开是 0010 0111bit5 是 RF_DR_LOW置 1 配合 bit3 的 RF_DR_HIGH 为 0 就是 250kbpsbit2:1 是 RF_PWR11 对应 0dBm。SETUP_RETR 的 0x13 是 0001 0011高四位 0001 对应 250us 等待低四位 0011 对应重发 3 次。CONFIG 的 0x0E 是 0000 1110bit1 是 PWR_UP置 1 上电bit0 是 PRIM_RX0 表示发射模式。参数怎么改要 1Mbps 就把 RF_SETUP 改成 0x07要接收模式就把 CONFIG 改成 0x0F。4.2 发射流程写 FIFO 和等待发送完成发射流程很简单拉低 RFCE写 payload 到 TX FIFO拉高 RFCE 触发发射然后等 STATUS 寄存器的 TX_DS 位置 1表示发送成功。如果 MAX_RT 置 1说明重发次数用完还没收到应答丢包了。发射完成后要清 STATUS 标志位写 1 清除。// 发送一包数据 uint8_t rf_send(uint8_t *payload) { uint8_t status; RFCE 0; // 写 payload 到 TX FIFO for (int i 0; i 8; i) { RF_TX_PAYLOAD payload[i]; } RFCE 1; // 等待发送完成超时 10ms for (int t 0; t 100; t) { status STATUS; if (status 0x20) { // TX_DS STATUS 0x20; // 清标志 return 1; } if (status 0x10) { // MAX_RT STATUS 0x10; return 0; } delay_us(100); } return 0; }逻辑说明TX_DS 是 bit5MAX_RT 是 bit4写 1 清除。超时 10ms 是经验值250kbps 下 8 字节 payload 空中时间不到 1ms加上重发 3 次10ms 足够。参数怎么改如果你要发更长的 payload改循环次数和 RX_PW_P0 一致。4.3 接收端中断方式还是轮询接收端可以轮询 STATUS 的 RX_DR 位也可以用中断。nRF24LE1 的射频中断可以映射到 P0.3 或 P1.0 等引脚我一般用轮询简单反正主循环也没别的事。收到数据后读 RX_FIFO清 RX_DR 标志然后处理。注意如果开了自动应答接收端收到数据后硬件自动发 ACK你不用管。// 接收一包数据返回 1 表示收到 uint8_t rf_receive(uint8_t *payload) { uint8_t status STATUS; if (status 0x40) { // RX_DR RFCE 0; for (int i 0; i 8; i) { payload[i] RF_RX_PAYLOAD; } RFCE 1; STATUS 0x40; // 清标志 return 1; } return 0; }逻辑说明RX_DR 是 bit6读 RX_FIFO 时 RFCE 要拉低读完再拉高。参数怎么改如果你用中断把 RX_DR 映射到中断引脚在中断服务函数里读 FIFO。5. 避坑与排查调试 24LE1 3DH 时最容易翻车的五个地方5.1 现象射频发不出去STATUS 一直没反应原因最常见的是 RFCE 时序不对。nRF24LE1 的 RFCE 是射频使能拉高后要等至少 130us 才能发数据如果你拉高后立刻写 FIFO射频还没稳定数据就丢了。另一个原因是 CONFIG 寄存器的 PWR_UP 没置 1芯片还在掉电模式。解决在 rf_init 里RFCE 拉高后加 1ms 延时再发第一包。检查 CONFIG 是否写了 0x0E 或 0x0F。用示波器看 RFCE 引脚确认拉高时间够。5.2 现象接收端偶尔收到错位的数据原因I2C 读传感器时高低字节之间被刷新了。LIS3DH 的输出寄存器在 ODR 周期内会更新如果你读低字节后传感器更新了数据再读高字节就是新数据的拼起来就错了。解决在 CTRL_REG4 里把 BDU 位置 1这样读低字节后输出寄存器锁定直到你读完高字节才更新。代码里就是 lis3dh_write(0x23, 0x88) 这一句别漏。5.3 现象功耗降不下来纽扣电池几天就没电原因掉电模式没进对或者射频没关。nRF24LE1 进掉电模式要置 PWRDWN 位但射频部分如果还在待机电流下不来。另外GPIO 如果悬空输入缓冲器会振荡耗电。解决进掉电前把 RFCE 拉低CONFIG 的 PWR_UP 清 0然后把所有未用的 GPIO 配成输出低或输入上拉别悬空。用万用表测掉电电流正常应该在 1uA 以下。5.4 现象通信距离短隔堵墙就丢包原因天线匹配没做好或者发射功率设太低。nRF24LE1 的发射功率最大 0dBm不算大天线效率差一点距离就砍半。另外电源去耦不够发射瞬间电压跌落射频性能下降。解决先用模组自带天线验证确认是板子问题还是设计问题。发射功率设 0dBm速率降到 250kbps。电源脚并 10uF 钽电容位置离芯片越近越好。如果还不行检查天线匹配网络的电容电感值用矢网测一下 S11。5.5 现象程序跑飞看门狗复位原因8051 的堆栈溢出或者中断向量没处理。nRF24LE1 的 RAM 只有 1KB堆栈设太大留给变量的空间就小递归或大数组一用就溢出。解决在 Keil 里把堆栈大小设成 0x40 左右别超过 0x80。检查中断服务函数没用的中断也要放个空函数别让 PC 跳到未定义区域。开看门狗但喂狗周期要算好别在掉电模式里被狗咬。6. 进阶技巧用序号和 RSSI 做链路质量评估6.1 丢包率统计序号跳变怎么算发射端 payload 里的序号从 0 到 255 循环接收端维护一个期望序号每收到一包就对比。如果收到的序号等于期望值说明没丢包如果大于期望值差值就是丢包数如果小于说明序号回绕了要加 256 再算。丢包率 丢包数 / (丢包数 收到包数)。这个指标比单纯看 RSSI 更直接因为 RSSI 只反映信号强度不反映干扰和冲突。// 接收端丢包统计 uint8_t expected_seq 0; uint32_t lost_cnt 0; uint32_t recv_cnt 0; void update_loss_stats(uint8_t seq) { if (seq expected_seq) { recv_cnt; } else { uint8_t diff seq - expected_seq; lost_cnt diff; recv_cnt; } expected_seq seq 1; }逻辑说明diff 是 uint8_t自动处理回绕。比如期望 255收到 2diff 2 - 255 3模 256丢包 3 个。参数怎么改如果你要统计窗口丢包率每 100 包清零一次计数器。6.2 RSSI 读取寄存器在哪怎么换算nRF24LE1 的 RSSI 在 RPD 寄存器里但它是粗略的只表示信号强度是否大于 -64dBm。要精确 RSSI得用 nRF24LE1 的 RSSI 寄存器读出来是一个 8 位值换算公式看数据手册。我一般用 RPD 做快速判断RPD1 表示信号强RPD0 表示弱。如果你要做链路质量评估把 RPD 和丢包率结合起来看RPD1 且丢包率高说明有干扰RPD0 且丢包率高说明距离太远。6.3 低功耗姿态节点的完整工作流把前面所有东西串起来一个完整的工作流是这样的上电初始化 GPIO、I2C、LIS3DH、射频进主循环读三轴数据打包发射频统计发送结果然后进掉电模式等定时器唤醒唤醒后重复。定时器用 32.768kHz 时钟源设 100ms 周期这样每秒 10 包数据。平均电流 工作电流 × 占空比 掉电电流 × (1 - 占空比)。工作电流大概 12mA工作时间 2ms占空比 2%平均电流 12 × 0.02 0.001 × 0.98 ≈ 0.24mACR2032 能撑 200 / 0.24 ≈ 833 小时约 34 天。如果你把发射间隔拉到 1 秒能撑将近一年。注意掉电模式唤醒后I2C 和射频都要重新初始化别以为它们还在工作。我自己的习惯是每次改完射频参数或传感器配置先用序号丢包率跑 1000 包看丢包率是否低于 1%再测功耗最后测距离。这三步过了方案才算稳。希望帮到你。本文还有配套的精品资源点击获取