自制工业控制系统:平台管理实战——电源控制、风扇调速与内存信息获取
Contents
起因
远程运维的机床,晚上加工完要能自己关机省电;风扇卡了转速掉零,报警得比冒烟早;客户报修,第一句先问"机器里是什么内存条"。这三件事落到系统上就是三个问题:电源怎么控制(软关机/重启到底写了什么寄存器)、风扇转速怎么读怎么控、内存容量和条目信息从哪来。教材不讲、教程跳过,这篇把账一次算清。
这三样东西在 PC 产业里都有年头:ACPI 是 1996 年 Intel、微软、东芝联手定的,接的是 BIOS 时代的烂摊子——那会儿节能靠 APM 黑盒、特性靠主板跳线;SMBIOS 早一年由 Intel 起草、现归 DMTF 维护,厂商把内存板卡信息写死在表里,省得每台机器都得拆开看;4 线 PWM 风扇规范最晚,2000 年代 Intel 才定下来,在那之前只有 3 线测速加电压调速的粗办法。
一、内存容量:三个信息源,各管一层
| 信息源 | 回答的问题 | 获取方式 |
|---|---|---|
| UEFI 内存映射 | 系统实际可用多少内存 | 移交清单里现成(UEFI 篇已取) |
| SMBIOS/DMI 表 | 装了几根条、每根多大、频率、厂商、序列号 | UEFI Configuration Table 按 GUID 取表指针 |
| SPD(DIMM 上的 EEPROM) | 内存条的原始出厂数据 | SMBus 逐字节读,SMBIOS 够用时可跳过 |
可用容量:遍历 UEFI 内存映射,把 type == 7(EFI_CONVENTIONAL_MEMORY)区间的页数 ×4KB 累加——这是物理页分配器能看到的全部家底。
SMBIOS 操控:UEFI 系统表 Configuration Table 里按 GUID eb9d2d31-2d88-11d3-9a16-0090273fc14d 拿到入口(32 位入口签名 _SM_,64 位是 _SM3_,现代主板给哪个都能用)。入口结构里最要紧的是 Table Address 和 Structure Max Size——从 Table Address 开始就是一条结构链,遍历算法固定三步:
|
|
跟内存相关的是 Type 16(物理内存阵列)和 Type 17(内存器件,每根条一条记录)。Type 17 常用字段的偏移(SMBIOS 3.x,定长部分):
| 偏移 | 字段 | 说明 |
|---|---|---|
| 0x0C | Size(u16) | 单位 KB;0x7FFF 表示真实值看 0x1A 的 ExtendedSize(MB) |
| 0x12 | MemoryType(u8) | DDR4=0x1A、DDR5=0x22(SMBIOS 3.3+) |
| 0x13 | Speed(u16) | MT/s |
| 0x15 / 0x16 / 0x18 | 厂商 / 序列号 / 部件号 | 都是字符串表索引(1 起,0=空),按上面的字符串区数过去 |
工控自检页打印"2×16GB DDR4-3200 Samsung"就靠它;序列号记进资产台账,换件对账——二级经销商的机器档案从这条记录开始建。
二、电源控制:ACPI 的最小可用集
工控系统对电源的真实需求就三个动作:软关机(S5)、重启、以及"自己挂了能被拉起来"(看门狗)。前两个走 ACPI,最后一个走芯片组。
ACPI 的入口还是那套老路:移交的 RSDP → XSDT → FADT(签名 “FACP”)。FADT 里跟电源相关的字段(偏移按 ACPI 2.0+):
| 偏移 | 字段 | 用途 |
|---|---|---|
| 0x40 | PM1a_EVT_BLK | 电源事件寄存器块基址 |
| 0x48 | PM1a_CNT_BLK | 电源控制寄存器块基址 |
| 0x7C | RESET_REG | 复位寄存器地址(GAS 格式) |
| 0x84 | RESET_VALUE | 往复位寄存器写的值 |
RESET_REG 那个"GAS 格式"值得展开一句,后面到处遇到:Generic Address Structure,12 字节 = 1B 地址空间(0=系统 IO、1=系统内存)+ 1B 位宽 + 1B 位偏移 + 1B 保留 + 8B 64 位地址。解析函数写一次,ACPI 里所有 GAS 字段(RESET_REG、PM1 寄存器的长度信息配合 PM1*_LEN 字段)都复用。
软关机:往 PM1a_CNT 写 SLP_TYP | SLP_EN——SLP_EN 是 bit13,SLP_TYP 是 bits10-12,最终写值 (s5_typ << 10) | (1 << 13)。S5 对应的 SLP_TYP 编码存在 DSDT 的 \_S5_ 对象里。完整解析 DSDT 得做 AML 虚拟机,没有这个必要,经典捷径是:DSDT 的 AML 字节流里直接搜 _S5_ 签名,后面跟的 Package 数据里取 SLP_TYP——几十行代码的事,早期的 Linux/开源引导都这么干。写完这个寄存器,主板电源时序切到 S5,整机下电。注意写完要Delay几毫秒再halt,让电源时序走完,别在时序中途去摸外设。
重启优先走 FADT 的 RESET_REG/RESET_VALUE(现代主板它就是 0xCF9 端口);兜底直接操作 0xCF9:先写 0x02(温复位)或 0x06→0x0E(完整冷复位序列),写完 CPU 当场重置。最后一道保险是故意触发三重故障让 CPU 复位——不优雅但永远有效。
硬件看门狗:Intel 芯片组内置 TCO 看门狗,入口链路:LPC/eSPI 桥(bus0:dev31:fn0)PCI 配置空间偏移 0x40 是 PMBASE,TCO 寄存器块就挂在 PMBASE+0x60 起的那片 I/O 区——喂狗就是往重载寄存器写一下,超时不喂由芯片组硬复位整机,CPU 软件死透了也拦不住它。工控价值在这:控制软件万一死循环,看门狗拉起机器回到安全态。基础篇先在 coop_scheduler 的主循环里喂狗(主循环卡死 = 不喂 = 复位),ISR 侧不喂——刻意让"协作层挂了就重启"成为系统行为。TCO 块内各子寄存器的位定义按芯片组 EDS 对,做成选配模块。
三、风扇与温度:Super I/O 加 CPU 数字传感器
风扇和主板温度的真相:它们不在 CPU 里、不在 ACPI 的简单寄存器里,而在主板的 Super I/O 芯片(Nuvoton NCT6775 系列、ITE IT87xx 系列是工控板两大常客)里。这类芯片管风扇转速计、PWM 输出、板载温度传感器,通过 ISA 风格的"索引/数据"端口对访问(如 0x2E/0x2F)。
以 NCT6775 为例的操控套路(寄存器细节照 Nuvoton 数据手册和 Linux nct6775 驱动对):
|
|
CPU 自身温度不用 Super I/O,走数字温度传感器(DTS):rdmsr(0x19C)(IA32_THERM_STATUS)的 bits16-22 是"距 TjMax 的温差",TjMax 从 rdmsr(0x1A2) 读——两者相减得真实核心温度。比任何主板探针都准。
控温策略(协作层一个 step 任务):温度-转速曲线用带迟滞的分段表(60℃ 以下低速、60-75℃ 中速、75℃ 以上全速,回落要低 5℃ 才降档,防振荡);过温联动安全:CPU 或功率器件超阈值 → 事件进状态回传队列 → 界面报警;超危急阈值 → 走安全联锁路径减速/停止加工——风扇策略在这里第一次和安全体系挂上钩。日志(exFAT)记录温度曲线,售后排障的硬证据。
四、汇总
| 你想知道/想做什么 | 这篇给的答案 |
|---|---|
| 实际可用内存 | UEFI 内存映射 type7 累加 |
| 内存条明细 | SMBIOS Type 17(厂商/序列号/频率/容量) |
| 软关机 | FADT PM1a_CNT 写 SLP_TYP |
| 重启 | FADT RESET_REG 或 0xCF9 序列,三重故障兜底 |
| 死机自愈 | TCO 看门狗,协作层主循环喂狗 |
| 风扇转速/PWM | Super I/O(NCT6775 类)索引/数据端口 |
| CPU 温度 | MSR 0x19C + 0x1A2 |
| 控温 | 迟滞分段曲线 + 过温安全联动 |
总结
平台管理这块的"难"其实是"散":内存三个信息源各答一层(映射管可用、SMBIOS 管明细、SPD 兜底);电源是 ACPI 的最小集(FADT 三个字段 + _S5_ 提取捷径 + 0xCF9 复位序列)加 TCO 看门狗兜命;风扇温度在 Super I/O(NCT6775 的扩展模式/LDN/转速换算)和 CPU 的 DTS(两个 MSR 相减)。每一样单独都不大,攒起来就是一张工控整机的"硬件管理面"。跑通标志:自检页打印内存条明细和 CPU 温度,风扇随温升变速,远程指令能让整机软关机和重启。
参考链接
- ACPI Specification——FADT 字段、电源寄存器、S 状态定义
- SMBIOS Specification——Type 16/17 结构定义
- Linux hwmon nct6775 驱动——Super I/O 风扇操控的现成参考实现
- Intel SDM Vol.3A §16(数字温度传感器)
- 本系列 UEFI 篇(内存映射来源):UEFI固件层实战;安全联动见调度篇
- 本系列第一篇:自制工业控制系统:从UEFI到内核
Author 软件开发大郭
LastMod 2026-09-11