配置寄存器访问协议
NCT6776F / NCT6776D 使用特殊协议访问配置寄存器来设置不同类型的配置。 NCT6776F / NCT6776D 共有 17 个逻辑设备(从逻辑设备 0 到逻辑设备 17,但逻辑设备 4、C、10、11、12、13 和 15 除外,以实现向后兼容),分别对应十四个独立功能:FDC(逻辑设备 0)、并行端口(逻辑设备 1)、UART A(逻辑设备 2)、UART B 和 IR(逻辑设备 3)、键盘控制器(逻辑设备 5)、CIR(逻辑设备 6)、GPIO6、7、8 和 9(逻辑设备 7)、WDT1 和 GPIO0、1 和 A(逻辑设备 8)、GPIO1、2、3、4、5、6 和 7(逻辑设备 9)、ACPI(逻辑设备 A)、硬件监视器和前面板 LED(逻辑设备 B)、VID(逻辑设备 D)、CIRWAKEUP(逻辑设备 E)、GPIO(逻辑设备 F)、SVID(逻辑设备 14)、深度睡眠(逻辑设备 16)和 GPIOA(逻辑设备 17)。如果将所有逻辑设备配置寄存器映射到普通 PC 地址空间,则需要较大的地址空间来访问它们。然后,NCT6776F / NCT6776D 通过两个 I/O 地址(2Eh/2Fh 或 4Eh/4Fh)映射所有配置寄存器,这两个地址在通电时由带脚 2E_4E_SEL 设置。这两个 I/O 地址充当索引/数据对,用于读取或写入超级 I/O 数据。必须将索引写入指向寄存器的第一个 I/O 地址,并读取或写入作为数据寄存器的第二个地址。仅当Super I/O 处于特殊模式(称为扩展功能模式)时才允许数据更新,从而增加了额外的安全级别。通过向第一个 I/O 地址连续两次写入 87h 数据进入此模式。此特殊模式可确保在程序失控期间不会有虚假数据破坏超级 I/O 配置。索引 0h – 2Fh 处有一组全局寄存器,其中包含整个芯片的信息和配置。访问各个逻辑设备的控制寄存器的方法很简单。只需将所需的逻辑设备号写入全局寄存器 07h。索引为 30h 或更高的后续访问将直接访问逻辑设备寄存器。
要对NCT6776F/NCT6776D配置寄存器进行编程,必须按顺序遵循以下配置程序:(1)。进入扩展功能模式。2.配置配置寄存器。3.退出扩展功能模式。
- 要将芯片置于扩展功能模式,必须对扩展功能启用寄存器(EFER,即2Eh或4Eh)连续写入0x87两次。
- 芯片选择逻辑器件,并通过扩展功能索引寄存器(EFIR)和扩展功能数据寄存器(EFDR)激活所需的逻辑器件。EFIR位于与EFER相同的地址,EFDR位于地址(EFIR+1)。首先,将逻辑设备编号(即0x07)写入EFIR,然后将所需逻辑设备的编号写入EFDR。如果访问芯片(全局)控制寄存器,则不需要此步骤,其次,将逻辑设备内所需配置寄存器的地址写入EFIR,然后通过EFDR写入(或读取)所需的配置寄存器。
- 要退出扩展功能模式,需要将0xAA写入EFER。一旦芯片退出扩展功能模式,它就处于正常运行模式,并准备进入配置模式。
以下是在ubuntu下使用c语言访问编写的访问看门狗寄存器值示列程序:
#include <stdio.h>
#include <stdlib.h>
#include <sys/io.h>#define INDEX_PORT 0x2E
#define DATA_PORT 0x2Fint main() {// 设置I/O端口访问权限if (ioperm(INDEX_PORT, 2, 1)) {perror("ioperm");exit(1);}// 进入扩展功能模式outb(0x87, INDEX_PORT);outb(0x87, INDEX_PORT);outb(0x07, INDEX_PORT); // 选择逻辑设备8 outb(0x08, DATA_PORT); // 选择看门狗定时器 outb(0x30, INDEX_PORT); // 设置看门狗定时器的配置寄存器地址outb(0x0B, DATA_PORT); //设置看门狗定时器的配置寄存器的值为0x0B,启动看门狗定时器//查看看门狗定时器的配置寄存器的值printf("CRF0: 0x%02X\n", inb(DATA_PORT));outb(0xAA, INDEX_PORT); // 退出扩展功能模式// 释放I/O端口访问权限if (ioperm(INDEX_PORT, 2, 0)) {perror("ioperm");exit(1);}printf("NCT6776F configuration completed.\n");return 0;
}
编译运行结果
Watch Dog
NCT6776F的看门狗定时器由一个8位可编程超时计数器和一个控制寄存器和状态寄存器组成。GPIO0、GPIO2、GPIO3、GPIO4、GPIO5、GPIO6、GPIO8、GPIO9、GPIOA提供了可选的WDT1功能。这个功能可以通过相对的GPIO控制寄存器来配置。看门狗定时器计数器的单位可以选择在逻辑设备8,CR[F5h],位[3]。超时值设置在逻辑设备8,CR[F6h]。写零将禁用看门狗定时器功能。将任何非零值写入该寄存器将导致计数器将该值加载到看门狗计时器计数器并开始向下计数。
当Watchdog Timer 1超时事件发生时,GPIO0 bit[0]、[3]、[4]、[7]、GPIO2、GPIO3、GPIO4、GPIO5、GPIO6、GPIO8 bit[0]、[3]和GPIO9、GPIOA bit[0]触发低脉冲,频率为100mS。也就是说,当该值被计数到0时,定时器停止,NCT6776F设置WDT1状态位在逻辑设备8,CR[F7h],位[4]。写入零将清除状态位。如果LRESET#或PWROK#信号被断言,该位也将被清除。
NCT6776F的看门狗定时器2由一个8位可编程超时计数器寄存器(逻辑器件D, CR[Ebh])和一个状态寄存器(逻辑器件D, CR[F3h] bit7)组成。
模式选择配置寄存器配置信息如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/io.h>
#include <string.h>#define INDEX_PORT 0x2E //选择寄存器索引的端口
#define DATA_PORT 0x2F //读取/写入数据到选定寄存器的端口
#define NCT6776F_UNLOCK 0x87 //解锁NCT6776F芯片配置
#define NCT6776F_LOCK 0xAA //锁定NCT6776F芯片配置#define NCT6776F_DEVICE 0x07 // 逻辑设备地址选择寄存器端口
#define WDT_LDN 0x08 /// 看门狗定时器的逻辑设备号#define WDT_CR 0x30 //配置寄存器地址
#define WDT_CR_START_VALUE 0x0B // 启动看门狗定时器的值#define WDT_MINUTE_MODE 0xF5 //设置定时器倒计时模式(分钟)的寄存器
#define WDT_MINUTE_MODE_VALUE 0x0A // 设置分钟模式的值(每个计数为1分钟)
#define WDT_SECOND_MODE_VALUE 0x02 // 设置秒模式的值(每个计数为1秒)#define WDT_TIMEOUT 0xF6 // 设置超时值的寄存器,写入任何非零值开始倒计时#define WDT_FEED 0xF7 // 用于重置(喂养)看门狗定时器的寄存器
#define WDT_CR_STOP_VALUE 0x00 // 停止/重置看门狗定时器的值// NCT6776F芯片功能函数
void enter_config_mode();
void exit_config_mode();
void select_logical_device(unsigned char ld);
unsigned char read_register(unsigned char reg);
void write_register(unsigned char reg, unsigned char value);// 看门狗控制函数
void watchdog_init(int mode_select);
void watchdog_start(int timeout_minutes);
void watchdog_feed();
void watchdog_stop();/*** @brief 进入NCT6776F芯片配置模式* * @return void*/
void enter_config_mode() {outb(NCT6776F_UNLOCK, INDEX_PORT);outb(NCT6776F_UNLOCK, INDEX_PORT);//printf("DATA_PORT: %x", inb(DATA_PORT));
}/*** @brief 退出NCT6776F芯片配置模式* * @return void*/
void exit_config_mode() {outb(NCT6776F_LOCK , INDEX_PORT);
}/*** @brief 在NCT6776F中选择逻辑设备* * @param ld 逻辑设备号* @return void*/
void select_logical_device(unsigned char ld) {outb(NCT6776F_DEVICE, INDEX_PORT);outb(ld, DATA_PORT);//printf("已选择逻辑设备: 0x%02X\n", inb(DATA_PORT));
}/*** @brief 从NCT6776F读取寄存器值* * @param reg 寄存器地址* @return unsigned char 读取到的寄存器值*/
unsigned char read_register(unsigned char reg) {outb(reg, INDEX_PORT);printf("DATA_PORT: %x\n", inb(DATA_PORT));return inb(DATA_PORT);
}/*** @brief 向NCT6776F芯片的寄存器写入值* * @param reg 寄存器地址* @param value 要写入的值* @return void*/
void write_register(unsigned char reg, unsigned char value) {outb(reg, INDEX_PORT);outb(value, DATA_PORT);printf("已写入寄存器: 0x%02X, 值: 0x%02X\n", reg, inb(DATA_PORT));
}/*** @brief 初始化看门狗定时器,设置指定的超时时间* * @param timeout_minutes 超时时间(以分钟为单位)* @return void*/
void watchdog_init(int mode_select) {enter_config_mode();select_logical_device(WDT_LDN);write_register(WDT_CR, WDT_CR_START_VALUE);if(mode_select == 0){write_register(WDT_MINUTE_MODE, WDT_SECOND_MODE_VALUE);}else if (mode_select == 1){write_register(WDT_MINUTE_MODE, WDT_MINUTE_MODE_VALUE);}printf("看门狗已初始化");
}/*** @brief 启动看门狗定时器* * @return void*/
void watchdog_start(int timeout_minutes) {write_register(WDT_TIMEOUT, timeout_minutes);exit_config_mode();printf("看门狗已启动\n");
}/*** @brief 喂狗(重置看门狗计时器)* * @return void*/
void watchdog_feed() {enter_config_mode();select_logical_device(WDT_LDN);write_register(WDT_FEED, WDT_CR_STOP_VALUE);exit_config_mode();printf("看门狗已喂食\n");
}/*** @brief 停止看门狗定时器* * @return void*/
void watchdog_stop() {enter_config_mode();select_logical_device(WDT_LDN);write_register(WDT_CR, WDT_CR_STOP_VALUE);exit_config_mode();printf("看门狗已停止\n");
}int main(int argc, char *argv[]) {// 检查是否提供了正确数量的参数if (argc < 2) {printf("用法: %s [init timeout|start|feed|stop]\n", argv[0]);return 1;}// 确保程序以root权限运行if (ioperm(INDEX_PORT, 2, 1)) {perror("ioperm");exit(1);}// 解析命令行参数并调用相应的函数if (strcmp(argv[1], "init") == 0 && argc == 3) {int modeSelect = atoi(argv[2]);if(modeSelect == 0 || modeSelect == 1){watchdog_init(modeSelect);}else{printf("看门狗计时模式选择错误 -> 0:秒; 1:分钟\n");return 0;}} else if (strcmp(argv[1], "start") == 0 && argc == 3) {int timeout = atoi(argv[2]);watchdog_start(timeout);} else if (strcmp(argv[1], "feed") == 0) {watchdog_feed();} else if (strcmp(argv[1], "stop") == 0) {watchdog_stop();} else {printf("无效的命令\n");}return 0;
}
启动脚本
#!/bin/bash
gcc -o watchdog_control watchdog.c
echo "警告:此脚本将导致系统重启!"
echo "请确保您已保存所有重要的工作。"
echo "按 Ctrl+C 取消,或等待 10 秒继续..."
sleep 10#echo "初始化看门狗,设置秒为记时方式..."
#sudo ./watchdog_control init 0echo "初始化看门狗,设置分钟为计时方式..."
sudo ./watchdog_control init 1echo "启动看门狗..."#sudo ./watchdog_control start 10
#echo "系统将在大约 10 秒后重启。"sudo ./watchdog_control start 1
echo "系统将在大约 1 分钟后重启。"#这里我们等待 70 秒,略微超过设置的超时时间
sleep 70echo "如果你看到这条消息,说明看门狗可能没有正常工作。"
测试看门狗程序是否正常工作:
等待一分钟后,电脑重启,看门狗测试成功。需要注意的是,测试时,要关闭其它应用程序,否则会出现文件系统挂载不上情况,需要手动修复一下。
#手动修复文件系统
fsck /dev/sda4
硬件监控
基本介绍
NCT6776F监测PC硬件中的几个关键参数,包括电源电压、风扇速度和温度,所有这些对于高端计算机系统稳定正常工作都非常重要。此外,专有硬件减少了控制冷却风扇速度的编程和处理器干预量,最大限度地降低了环境噪声,并最大限度地提高了系统温度和可靠性。
NCT6776F可以同时监测以下所有输入:
- 九个模拟电压输入(五个内部电压CPUVCORE、VBAT、3VSB、3VCC和AVCC;四个外部电压输入)
- 四个风扇转速计输入
- 三个远程温度,使用热敏电阻或CPU热二极管(电压或电流模式测量方法)
- 一个开路检测信号
使用集成的八位模数转换器(ADC)将这些输入转换为数字值。作为对这些输入的响应,NCT6776F可以产生以下输出:
- 用于风扇速度控制的三个PWM(脉宽调制)或DC风扇输出
- 用于系统保护事件的SMI信号
- OVT信号
NCT6776F通过LPC或I2C接口提供对所有监测参数的硬件访问,并通过应用软件(如新唐的hardware DoctorTM或BIOS)提供软件访问。
LPC接口:硬件监视器块的内部寄存器可以通过LPC总线上的两个独立方法访问。第一组寄存器主要启用块并在CPU I/O地址空间中设置其地址,由第七章中描述的超级I/O协议访问,地址为2Eh/2Fh或4Eh/4Fh。这个块的大部分功能和内部寄存器是通过CPU I/O地址的索引/数据对来访问的。标准位置通常为295h/296h,由第7章描述的使用超级I/O协议访问的CR[60h]和CR[61h]设置。由于内部寄存器的数量较多,有必要将寄存器集划分为寄存器4Eh指定的“库”。内部寄存器结构如下图所示。
模拟输入
硬件监视器块的9个模拟输入连接到8位模数转换器(ADC),由4个连接到外部设备引脚的通用输入(VIN0 - VIN3)和5个连接到电源的内部信号(CPUVCORE, AVCC, VBAT, 3VSB和3VCC)组成。由于8mV LSB(256步长× 8mV = 2.048V)的内部设置,所有输入都限制在最大电压为2.048V。ADC的所有输入必须使用分压器限制最大电压。电源有内部电阻,而外部引脚需要外部限制电阻,如下所述:
电压范围
大于2.048 V的输入电压应通过外部电阻分压器降低,以保持输入电压在适当范围内。例如,输入电压V0(+12 V)在连接到VIN0之前应根据以下公式降低:
VIN0 = V0 × R2 / (R1 + R2)
R1和R2可分别设置为56 kΩ和10 kΩ,以将V0从+12 V降低到小于2.048 V。
ADC、AVCC、VBAT、3VSB和3VCC的所有内部输入都使用集成电压分压器,两个电阻均为34kΩ,产生电源电压的一半。考虑到10%的变化或3.63V的最大电压,ADC的输入将为1.815V,在最大范围内。
Vin = VCC × 34kΩ / (34kΩ + 34kΩ) ≈ 1.65V,其中VCC