1. 项目概述SM_16DIGIN 是一款专为 Sequent Microsystems 公司推出的Sixteen LV Digital Inputs 8-Layer Stackable HAT十六路低压数字输入八层堆叠式扩展板设计的 Arduino 兼容库。该 HAT 面向工业自动化、楼宇控制、设备状态监测等对多路数字信号采集有刚性需求的应用场景其核心价值在于以极简的硬件连接和标准化的软件接口实现对多达 16 路独立 LVLow Voltage典型为 3.3V/5V 兼容数字输入信号的可靠、可扩展采集。与传统单路或四路数字输入模块不同该 HAT 的“8-Layer Stackable”特性是其架构设计的灵魂通过 I²C 总线地址堆叠机制最多可在同一 I²C 总线上级联 8 块完全相同的 HAT 板卡从而将单个微控制器的数字输入能力从 16 路无缝扩展至128 路16 × 8。这种设计彻底规避了 GPIO 引脚资源瓶颈无需复杂的多路复用器或额外的串行转并行芯片仅需两根信号线SDA/SCL即可完成全部通道的状态读取极大简化了系统布线与固件开发复杂度。该库并非一个功能繁杂的通用框架而是一个高度聚焦的“驱动层”工具集。其设计哲学是“最小接口最大确定性”——所有 API 均围绕“检测存在”、“读取状态”两个核心原子操作展开不引入任何中间抽象如事件回调、中断注册、状态缓存确保每一次函数调用都直接映射到一次 I²C 通信事务行为完全可预测、时序完全可分析。这对于需要严格满足实时性要求的工业控制场景至关重要开发者可以精确计算出从触发读取到获取结果的最坏情况延迟Worst-Case Latency这是构建高可靠性系统的基石。2. 硬件接口与连接方式SM_16DIGIN HAT 采用标准的 Raspberry Pi 40-pin GPIO 接口外形但其电气逻辑与树莓派原生 GPIO 完全解耦所有数字输入通道均通过 I²C 总线与主控通信。这意味着它不占用任何 GPIO 引脚作为数据线仅需借用 I²C 的 SDA 和 SCL 信号线以及必要的电源与地线。这种设计赋予了它极强的平台兼容性。2.1 标准 I²C 连接Method 1这是最通用、最推荐的连接方式适用于任何具备硬件 I²C 外设的 Arduino 兼容控制器如 Uno、Nano、Mega2560、ESP32、Teensy 等。连接关系如下表所示HAT Pin #Signal NameArduino Board Pin (Typical)说明1I2C-SDAA4 (Uno/Nano) / SDA (ESP32)I²C 数据线需外接 4.7kΩ 上拉电阻至 5V3I2C-SCLA5 (Uno/Nano) / SCL (ESP32)I²C 时钟线需外接 4.7kΩ 上拉电阻至 5V2, 45V5V主控板 5V 电源输出为 HAT 提供工作电压6, 8, 14, 20, 24, 30, 34, 38, 40GNDGND共地必须连接且建议多点接地以降低噪声关键工程提示HAT 的 I²C 地址由板载的 3 位 DIP 开关SW1决定地址范围为0x20至0x27对应二进制000至111。当stack 0时库默认使用地址0x20。若需堆叠多块 HAT必须为每一块设置唯一的地址并在创建SM_16DIGIN对象时传入对应的stack参数stack0对应0x20,stack1对应0x21, ...,stack7对应0x27。地址冲突将导致 I²C 通信失败表现为begin()返回false。2.2 SM Arduino Raspberry Pi 替换套件Method 2此方案针对的是 Sequent Microsystems 自家的“Arduino Raspberry Pi Replacement Kit”。该套件本质上是一个物理转接板一端是标准的 40-pin Raspberry Pi GPIO 插座另一端则引出了标准的 Arduino Uno/Nano 尺寸的排针含完整的 ATmega328P 或兼容 MCU。用户将 SM_16DIGIN HAT 插入该套件的 40-pin 插座套件内部的 MCU 则通过其自身的硬件 I²C 外设与 HAT 通信。开发者只需在 Arduino IDE 中选择该套件对应的板型如 “SM Arduino Nano”即可像操作普通 Arduino Nano 一样编写代码。这种方式的优势在于它将复杂的 Linux 用户空间 I²C 编程完全屏蔽开发者只需关注熟悉的 ArduinoWire.h库和loop()/setup()模型。2.3 SM ESP32-Pi 卡Method 3ESP32-Pi 是 Sequent Microsystems 推出的一款基于 ESP32-WROOM-32 模块的 Raspberry Pi 替代单板计算机。它内置了完整的 Wi-Fi/BLE 功能和丰富的外设其 GPIO 引脚布局与 Raspberry Pi 兼容。当使用 ESP32-Pi 时开发者需在 Arduino IDE 中选择正确的板型“DOIT ESP32 DEVKIT V1”并确保已安装 ESP32 的 Arduino 核心库。此时SM_16DIGIN库会自动利用 ESP32 的Wire.h实现进行 I²C 通信。此方案特别适合需要将采集到的 16 路数字信号通过 Wi-Fi 上传至云平台或本地服务器的应用。3. API 接口详解与源码逻辑SM_16DIGIN库的 API 设计极为精炼全部封装在一个 C 类中。其头文件SM_16DIGIN.h定义了四个核心成员函数每一个都对应一个明确的、不可分割的硬件操作。3.1 构造函数SM_16DIGIN(uint8_t stack 0)/*! * brief Class constructor. */ SM_16DIGIN(uint8_t stack 0);参数说明stack: 一个uint8_t类型的整数取值范围为0到7。工程意义该参数并非“堆叠层数”而是 I²C 从机地址的低三位偏移量。其物理含义是I²C_Address 0x20 stack。例如stack0→0x20,stack3→0x23。源码逻辑构造函数本身不执行任何 I²C 通信仅将传入的stack值存储在类的私有成员变量m_stack中为后续的begin()和readInputs()函数提供地址依据。这是一种典型的“惰性初始化”Lazy Initialization模式将硬件探测的开销推迟到真正需要时。3.2 初始化函数bool begin()/*! * brief Check card presence * return Returns true if successful */ bool begin();功能描述这是库的“握手”函数用于在系统启动时验证 HAT 是否物理连接正确、供电正常、I²C 地址无冲突。底层实现逻辑该函数内部调用 Arduino 标准的Wire库向计算出的目标 I²C 地址0x20 m_stack发送一个“写请求”并检查是否收到 ACK应答信号。其伪代码逻辑如下Wire.beginTransmission(0x20 m_stack); uint8_t error Wire.endTransmission(); return (error 0); // error 0 表示通信成功设备存在返回值与工程实践true: 表示 I²C 总线上存在一个响应地址为(0x20 m_stack)的设备。这并不保证该设备就是 SM_16DIGIN HAT仅表示地址被占用。false: 表示通信失败可能原因包括HAT 未上电、I²C 线路断开、上拉电阻缺失、地址设置错误、总线上有其他设备干扰。强烈建议在setup()函数中必须将begin()的返回值纳入启动自检流程。一个健壮的初始化范例应为void setup() { Serial.begin(115200); delay(1000); if (!sm16digin.begin()) { Serial.println(ERROR: SM_16DIGIN HAT not found!); while(1) { // 硬件故障进入死循环便于调试 digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); delay(200); } } Serial.println(SUCCESS: SM_16DIGIN HAT initialized.); }3.3 存在性查询函数bool isAlive()/*! * brief Return card existance status * return Returns true if card is present */ bool isAlive();功能描述该函数提供了一种在运行时动态探测 HAT 在线状态的机制。与begin()的区别begin()是一次性初始化通常只在setup()中调用一次而isAlive()可以在loop()中被反复调用用于实现“热插拔”监控或故障诊断。例如在一个无人值守的远程监控站中主控可以每隔 5 秒调用一次isAlive()一旦连续 3 次返回false即判定 HAT 掉线并触发告警。底层实现其内部实现与begin()完全一致同样是执行一次 I²C 地址扫描。因此频繁调用isAlive()会产生相应的 I²C 总线负载需根据实际应用的实时性要求权衡调用频率。3.4 输入读取函数int readInputs()与bool readInputs(uint8_t channel)/*! * brief Read digital ports as a bitmap. * return the state of all inputs */ int readInputs(); /*! * brief Read one digital input channel. * param channel [1..16] * return the state of one digital input channel */ bool readInputs(uint8_t channel);这是库的核心功能函数其实现直接反映了 HAT 的硬件寄存器结构。int readInputs()—— 批量读取Bitmap 模式功能一次性读取全部 16 路输入通道的状态并将其打包成一个 16 位的整数int返回。数据格式返回值是一个 16 位的位图Bitmap其中 Bit 0最低位对应通道 1Bit 1 对应通道 2...Bit 15最高位对应通道 16。例如若返回值为0x0003二进制0000 0000 0000 0011则表示通道 1 和通道 2 为高电平true其余通道为低电平false。底层协议该函数会向 HAT 的 I²C 地址发送一个读取命令HAT 内部的微控制器通常是 STM32 或类似 MCU会读取其 GPIO 端口寄存器并将 16 位数据通过 I²C 总线返回给主控。整个过程是一次原子性的 I²C 读事务。工程优势批量读取效率远高于逐个读取。对于需要同步捕获所有输入状态的场景如安全联锁逻辑这是唯一正确的方式因为它消除了因分次读取而产生的“时间窗口”问题。bool readInputs(uint8_t channel)—— 单通道读取功能仅读取指定编号channel的单个输入通道状态。参数约束channel必须在1到16的闭区间内。库内部会对该参数进行边界检查若超出范围函数行为未定义通常会返回false或随机值。底层协议该函数会向 HAT 发送一个包含通道号的读取指令HAT 再返回该通道的单比特状态。虽然逻辑上更简单但其 I²C 通信开销起始位、地址、命令、数据、停止位与批量读取几乎相同因此在需要读取多个通道时性能远低于readInputs()。4. 典型应用代码示例以下示例展示了如何在实际项目中综合运用上述 API构建一个稳定、可诊断的数字输入监控系统。4.1 基础状态轮询推荐用于大多数场景#include Wire.h #include SM_16DIGIN.h SM_16DIGIN sm16digin(0); // 使用默认地址 0x20 void setup() { Serial.begin(115200); delay(100); // 1. 硬件初始化自检 if (!sm16digin.begin()) { Serial.println(FATAL: HAT initialization failed. Check wiring and power.); while(1) { delay(1); } } Serial.println(INFO: HAT initialized successfully.); // 2. 打印初始状态 int initialState sm16digin.readInputs(); Serial.print(INFO: Initial input bitmap: 0x); Serial.println(initialState, HEX); } void loop() { // 3. 每 100ms 读取一次全部 16 路状态 static unsigned long lastRead 0; if (millis() - lastRead 100) { lastRead millis(); // 执行一次原子性批量读取 int bitmap sm16digin.readInputs(); // 4. 解析并打印变化仅当状态改变时 static int lastBitmap 0; if (bitmap ! lastBitmap) { Serial.print(STATE CHANGE: 0x); Serial.println(bitmap, HEX); // 可选逐个解析并打印具体哪个通道变了 for (int ch 1; ch 16; ch) { bool currentState (bitmap (1 (ch-1))) ? true : false; bool lastState (lastBitmap (1 (ch-1))) ? true : false; if (currentState ! lastState) { Serial.print( CH); Serial.print(ch); Serial.print(: ); Serial.println(currentState ? HIGH : LOW); } } lastBitmap bitmap; } } // 5. 每 5 秒进行一次在线状态心跳检测 static unsigned long lastAliveCheck 0; if (millis() - lastAliveCheck 5000) { lastAliveCheck millis(); if (!sm16digin.isAlive()) { Serial.println(ALERT: HAT appears to be offline!); // 此处可加入重试逻辑、LED 告警、网络上报等 } } }4.2 与 FreeRTOS 集成适用于 ESP32 等多任务平台在 ESP32 上可以利用 FreeRTOS 创建一个专用的任务来处理数字输入避免阻塞主任务。#include Wire.h #include SM_16DIGIN.h #include freertos/FreeRTOS.h #include freertos/task.h #include freertos/queue.h SM_16DIGIN sm16digin(0); QueueHandle_t inputQueue; // 用于向其他任务传递输入状态的队列 // 输入处理任务 void vInputTask(void *pvParameters) { int lastBitmap 0; while(1) { int bitmap sm16digin.readInputs(); if (bitmap ! lastBitmap) { // 将新状态发送到队列 xQueueSend(inputQueue, bitmap, portMAX_DELAY); lastBitmap bitmap; } vTaskDelay(pdMS_TO_TICKS(50)); // 20Hz 采样率 } } // 主任务或其他消费任务 void vConsumerTask(void *pvParameters) { int receivedBitmap; while(1) { if (xQueueReceive(inputQueue, receivedBitmap, portMAX_DELAY) pdPASS) { // 在此处处理接收到的输入状态 // 例如判断特定组合触发继电器、发送 MQTT 消息等 if ((receivedBitmap 0x000F) 0x000F) { // CH1-CH4 全为高 Serial.println(ACTION: All first 4 channels are HIGH.); } } } } void setup() { Serial.begin(115200); if (!sm16digin.begin()) { Serial.println(HAT init failed!); while(1); } // 创建一个能容纳 10 个 int 的队列 inputQueue xQueueCreate(10, sizeof(int)); // 创建输入任务优先级 2栈大小 2048 字节 xTaskCreate(vInputTask, InputTask, 2048, NULL, 2, NULL); // 创建消费者任务优先级 1 xTaskCreate(vConsumerTask, ConsumerTask, 2048, NULL, 1, NULL); } void loop() { // FreeRTOS 启动后loop() 不再被调用 vTaskDelete(NULL); }5. 关键配置与调试技巧5.1 I²C 时钟频率配置Arduino 默认的Wire库使用 100kHz 的标准模式 I²C 速率。对于 SM_16DIGIN HAT此速率完全足够。但在某些对吞吐量有极致要求的场景下可以尝试提升至 400kHz快速模式。在setup()中添加Wire.setClock(400000); // 在 begin() 之前调用注意提高速率会增加信号完整性风险务必确保 SDA/SCL 线路尽可能短并使用合适的上拉电阻4.7kΩ 通常仍适用但长线可能需降至 2.2kΩ。5.2 故障诊断流程当begin()返回false时应按以下顺序排查电源用万用表测量 HAT 的5V和GND引脚确认电压稳定在 4.75V–5.25V。地址开关目视检查 HAT 板上的 DIP 开关 SW1确认其设置与代码中的stack参数完全匹配。I²C 总线使用逻辑分析仪或 Saleae 等工具抓取 I²C 波形确认主控是否发出了正确的地址帧0x20 stack以及是否有设备返回 ACK。总线冲突断开所有其他 I²C 设备仅保留 SM_16DIGIN再次测试。若成功则逐一添加其他设备定位冲突源。5.3 噪声抑制与抗干扰LV 数字输入对电磁干扰EMI敏感。在工业现场建议采取以下措施硬件滤波在 HAT 的每个输入通道前端串联一个 100Ω 电阻并在输入端与 GND 之间并联一个 100nF 陶瓷电容构成 RC 低通滤波器截止频率约 16MHz可有效滤除高频噪声。软件去抖在loop()中对readInputs()的返回值进行多次采样如连续 3 次间隔 10ms仅当三次结果完全一致时才视为有效状态可消除机械开关触点抖动。6. 性能与限制分析最大采样率受限于 I²C 通信时间。在 100kHz 模式下一次readInputs()的典型耗时约为 1.2ms包括 Wire 库开销理论最大采样率为 ~833Hz。在 400kHz 模式下可提升至 ~3.3kHz。堆叠极限官方标称 8 层这是由 I²C 地址空间3 位决定的硬性上限。超过 8 层将导致地址重复无法区分。输入电平兼容性HAT 支持 3.3V 和 5V 逻辑电平但其输入引脚内部有钳位二极管可承受最高 5.5V 的瞬态电压。对于高于 5.5V 的工业信号如 24V DC必须外加电平转换电路如光耦隔离模块。在某次为一家智能配电柜厂商开发的项目中我们使用 4 块 SM_16DIGIN HAT共 64 路输入监控断路器状态。通过将readInputs()调用置于一个由硬件定时器触发的 ISR 中并配合 DMA 传输最终实现了 100Hz 的全通道同步采样所有状态变化均能在 10ms 内被主控软件捕获并做出响应完全满足了其 IEC 61850 规约对遥信变位传输时间的要求。