标签 GPIO 下的文章

“”

On different operation system, have different method to read/write I/O port. I
know WinIO on windows, IoWrite8/16/32 on Linux.

There is a good open source software “Open Hardware Monitor”, which develop by
C# language. It support almost all the motherboard.

  1. Datasheet of chipsLook the access channels on file NCT6776F_NCT6776D_Datasheet_V1_2
    page 275

19.2 ACCESS CHANNELS
There are two different channels to set up/access the GPIO ports. The first one is the indirect access via register
2E/2F (4E/4F, it depends by HEFRAS trapping). The registers can be read / written only when the respective
logical device ID and port number are selected.
known that rigister index is 0x2E, and data Input/Output port is 0x2F

On page 54

To program the NCT6776F / NCT6776D configuration registers, the following configuration procedures must be
followed in sequence:
(1). Enter the Extended Function Mode.
(2). Configure the configuration registers.
(3). Exit the Extended Function Mode.

7.1.1 Enter the Extended Function Mode
To place the chip into the Extended Function Mode, two successive writes of 0x87 must be applied to Extended
Function Enable Registers (EFERs, i.e. 2Eh or 4Eh).

7.1.2 Configure the Configuration Registers
The chip selects the Logical Device and activates the desired Logical Devices through Extended Function Index
Register (EFIR) and Extended Function Data Register (EFDR). The EFIR is located at the same address as the
EFER, and the EFDR is located at address (EFIR+1).
First, write the Logical Device Number (i.e. 0x07) to the EFIR and then write the number of the desired Logical
Device to the EFDR. If accessing the Chip (Global) Control Registers, this step is not required.

Secondly, write the address of the desired configuration register within the Logical Device to the EFIR and then
write (or read) the desired configuration register through the EFDR.

7.1.3 Exit the Extended Function Mode
To exit the Extended Function Mode, writing 0xAA to the EFER is required. Once the chip exits the Extended
Function Mode, it is in the normal running mode and is ready to enter the configuration mode.
Software Programming Example
The following example is written in Intel 8086 assembly language. It assumes that the EFER is located at 2Eh, so
the EFIR is located at 2Eh and the EFDR is located at 2Fh. If the HEFRAS (CR[26h] bit 6 showing the value of
the strap pin at power on) is set, 2Eh can be directly replaced by 4Eh and 2Fh replaced by 4Fh.
This example programs the configuration register F0h (clock source) of logical device 1 (UART A) to the value of
3Ch (24MHz). First, one must enter the Extended Function Mode, then setting the Logical Device Number (Index
07h) to 01h. Then program index F0h to 3Ch. Finally, exit the Extended Function Mode.

;-----------------------------------------------------
; Enter the Extended Function Mode
;-----------------------------------------------------
MOV DX, 2EH
MOV AL, 87H
OUT DX, AL
OUT DX, AL
;-----------------------------------------------------------------------------
; Configure Logical Device 1, Configuration Register CRF0
;-----------------------------------------------------------------------------
MOV DX, 2EH
MOV AL, 07H
OUT DX, AL
; point to Logical Device Number Reg.
MOV DX, 2FH
MOV AL, 01H
OUT DX, AL
; select Logical Device 1
;
MOV DX, 2EH
MOV AL, F0H
OUT DX, AL
; select CRF0
MOV DX, 2FH
MOV AL, 3CH
OUT DX, AL
; update CRF0 with value 3CH
;-----------------------------------------------
; Exit the Extended Function Mode
;----------------------------------------------
MOV DX, 2EH
MOV AL, AAH
OUT DX, AL

  1. Open Hardware MonitorHomepage
  2. code

The Open Hardware Monitor is a free open source software that monitors
temperature sensors, fan speeds, voltages, load and clock speeds of a computer.

As below, introduct how to use VC++ to monitor HW status on windows:

1). modify computer.cs,add some interface like GetFanSpeed(). if have problem , look report function .
2). set the project properity to output class library.
3). Reference VC++ use C# DLL

1.1 Analyze open-hardware-monitorUse Winbond W83627DHG-P chip as example.

A description about how to monitor hardware, see this
http://www.supermicro.com/support/faqs/faq.cfm?faq=12015

Question
I'm trying to access the Winbond 83627DHG-P IO chip on the motherboard X8SIL.
We want to use the LMsensor. Do you have the offset codes for the smbus?

Answer
Bus Type = ISAIO/SMBus
One W83627DHG-P

Windbond W83627DHG-P, Slave Address=0x2d (0x5a in 8-Bit format)

OR IndexReg=A15, DataReg=A16

Fan1 Fan Speed, Bank 0, Offset 0x29 RPM = 1350000/8/Data
Fan2 Fan Speed, Bank 5, Offset 0x53 RPM = 1350000/8/Data
Fan3 Fan Speed, Bank 0, Offset 0x28 RPM = 1350000/8/Data
Fan4 Fan Speed, Bank 0, Offset 0x3f RPM = 1350000/8/Data
Fan5 Fan Speed, Bank 0, Offset 0x2a RPM = 1350000/8/Data

CPU Voltage, Bank 0, Offset 0x20 Voltage = Data* 0.008
-12V Voltage, Bank 0, Offset 0x26 Voltage = ((Data*0.008-2.048)/(10./242.))+2.048
+12V Voltage, Bank 0, Offset 0x21 Voltage = Data* 0.008/ (10./66.2)

AVCC Voltage, Bank 0, Offset 0x22 Voltage = Data* 0.016
3.3Vcc Voltage, Bank 0, Offset 0x23 Voltage = Data* 0.016

DIMM Voltage, Bank 0, Offset 0x24 Voltage = Data* 0.008
+5V Voltage, Bank 0, Offset 0x25 Voltage = Data* 0.008/ (10./40.)
+3.3VSb Voltage, Bank 5, Offset 0x50 Voltage = Data* 0.016

VBAT Voltage, Bank 5, Offset 0x51 Voltage = Data* 0.016

CPU Temperature, Bank 1, Offset 0x50 Temperature = Data

System Temperature, Bank 2, Offset 0x50 Temperature = Data

Chassis Intrusion, Bank 0, Offset 0x42, BitMask 0x10 1 = Bad, 0 = Good
(Clear Bit: Bank 0, Offset 0x46, BitMask 0x80)

Power Supply Failure, GP23(From W83627DHG-P) 1 = Good, 0 = Bad

1.1.1 File: Hardware/LPC/LPCIO.csIO Ports

// I/O Ports
private readonly ushort[] REGISTER_PORTS = new ushort[] { 0x2E, 0x4E };
private readonly ushort[] VALUE_PORTS = new ushort[] { 0x2F, 0x4F };
Winbond, Nuvoton, Fintek

private const byte FINTEK_VENDOR_ID_REGISTER = 0x23;
private const ushort FINTEK_VENDOR_ID = 0x1934;

private const byte WINBOND_NUVOTON_HARDWARE_MONITOR_LDN = 0x0B;

private const byte F71858_HARDWARE_MONITOR_LDN = 0x02;
private const byte FINTEK_HARDWARE_MONITOR_LDN = 0x04;

private void WinbondNuvotonFintekEnter() {
Ring0.WriteIoPort(registerPort, 0x87);
Ring0.WriteIoPort(registerPort, 0x87);
}

private void WinbondNuvotonFintekExit() {
Ring0.WriteIoPort(registerPort, 0xAA);
}
ITE

private const byte IT87_ENVIRONMENT_CONTROLLER_LDN = 0x04;
private const byte IT8705_GPIO_LDN = 0x05;
private const byte IT87XX_GPIO_LDN = 0x07;
private const byte IT87_CHIP_VERSION_REGISTER = 0x22;

private void IT87Enter() {
Ring0.WriteIoPort(registerPort, 0x87);
Ring0.WriteIoPort(registerPort, 0x01);
Ring0.WriteIoPort(registerPort, 0x55);
Ring0.WriteIoPort(registerPort, 0x55);
}

private void IT87Exit() {
Ring0.WriteIoPort(registerPort, CONFIGURATION_CONTROL_REGISTER);
Ring0.WriteIoPort(valuePort, 0x02);
}
SMSC

private void SMSCEnter() {
Ring0.WriteIoPort(registerPort, 0x55);
}

private void SMSCExit() {
Ring0.WriteIoPort(registerPort, 0xAA);
}
How to get value:call ReadByte to get value:

byte id = ReadByte(CHIP_ID_REGISTER);
byte revision = ReadByte(CHIP_REVISION_REGISTER);
look at function ReadByte:

private byte ReadByte(byte register) {
Ring0.WriteIoPort(registerPort, register);
return Ring0.ReadIoPort(valuePort);
}
first, write register value to registerPort;
second, read value from valueport.

1.1.2 File: Hardware/LPC/W836XX.csHow to get value:// Hardware Monitor
private const byte ADDRESS_REGISTER_OFFSET = 0x05;
private const byte DATA_REGISTER_OFFSET = 0x06;

// Hardware Monitor Registers
private const byte BANK_SELECT_REGISTER = 0x4E;

private byte ReadByte(byte bank, byte register) {
Ring0.WriteIoPort(

 (ushort)(address + ADDRESS_REGISTER_OFFSET), BANK_SELECT_REGISTER);

Ring0.WriteIoPort(

 (ushort)(address + DATA_REGISTER_OFFSET), bank);

Ring0.WriteIoPort(

 (ushort)(address + ADDRESS_REGISTER_OFFSET), register);

return Ring0.ReadIoPort(

(ushort)(address + DATA_REGISTER_OFFSET));

}
What is the value of “address”:set value by construction

public W836XX(Chip chip, byte revision, ushort address) {
this.address = address;
this.revision = revision;
this.chip = chip;
...
}
on LPCIO.cs, found the calling code:

private const byte BASE_ADDRESS_REGISTER = 0x60;

ushort address = ReadWord(BASE_ADDRESS_REGISTER);

switch (chip) {
case Chip.W83627DHG:
case Chip.W83627DHGP:
case Chip.W83627EHF:
case Chip.W83627HF:
case Chip.W83627THF:
case Chip.W83667HG:
case Chip.W83667HGB:
case Chip.W83687THF:

superIOs.Add(new W836XX(chip, revision, address));
break;

...
}
Get temperature:private readonly byte[] TEMPERATURE_REG = new byte[] { 0x50, 0x50, 0x27 };
private readonly byte[] TEMPERATURE_BANK = new byte[] { 1, 2, 0 };

for (int i = 0; i < temperatures.Length; i++) {
int value = ((sbyte)ReadByte(TEMPERATURE_BANK[i],

TEMPERATURE_REG[i]))  0) 
value |= ReadByte(TEMPERATURE_BANK[i],
  (byte)(TEMPERATURE_REG[i] + 1)) >> 7;

float temperature = value / 2.0f;
if (temperature = -55 && !peciTemperature[i]) {

temperatures[i] = temperature;

} else {

temperatures[i] = null;

}
}
Get fan speed:call ReadByte(FAN_TACHO_BANK[i], FAN_TACHO_REG[i]) to get speed value to “count”
, then set real fan spee to array variable “fans[]“.

private readonly byte[] FAN_TACHO_REG =
new byte[] { 0x28, 0x29, 0x2A, 0x3F, 0x53 };
private readonly byte[] FAN_TACHO_BANK =
new byte[] { 0, 0, 0, 0, 5 };
private readonly byte[] FAN_BIT_REG =
new byte[] { 0x47, 0x4B, 0x4C, 0x59, 0x5D };
private readonly byte[] FAN_DIV_BIT0 = new byte[] { 36, 38, 30, 8, 10 };
private readonly byte[] FAN_DIV_BIT1 = new byte[] { 37, 39, 31, 9, 11 };
private readonly byte[] FAN_DIV_BIT2 = new byte[] { 5, 6, 7, 23, 15 };

ulong bits = 0;
for (int i = 0; i < FAN_BIT_REG.Length; i++)

bits = (bits > FAN_DIV_BIT2[i]) & 1) > FAN_DIV_BIT1[i]) & 1) > FAN_DIV_BIT0[i]) & 1));
int divisor = 1  192 && divisorBits < 7) 
    divisorBits++;
if (count < 96 && divisorBits > 0)
    divisorBits--;

newBits = SetBit(newBits, FAN_DIV_BIT2[i], (divisorBits >> 2) & 1);
newBits = SetBit(newBits, FAN_DIV_BIT1[i], (divisorBits >> 1) & 1);
newBits = SetBit(newBits, FAN_DIV_BIT0[i], divisorBits & 1);

}

Referenced from:https://matrix207.github.io/2012/12/17/monitor-hardware-status/

'Re: Implement a watchdog' - MARC
update:2021-11-9

i have a ENDAT 7703 motherboard near my desk,
the manual point out some instruction to configure the
watchdog :

//init, select the destination of command...

outportb(0x2e,0x87);
outportb(0x2e,0x87);
outportb(0x2e,0x07);
outportb(0x2f,0x08);

//com

outportb(0x2e,0x30); //main

outportb(0x2f,0x01); //0 to inactive

outportb(0x2e,0xf2); //reset param

outportb(0x2f,0x00); // bit 6 keyboard, bit 7 mouse

outportb(0x2e,0xf0); //time unit conf
outportb(0x2f,0x04); // bit3 = 1 minutes

outportb(0x2e,0xf1); // how much time
outportb(0x2f,1); // 1 minute

I suppose only the kernel can write to the 0x2e and 0x2f address ?

Usually watchdog are detected and shown in dmesg, have i to ask more
detail to the supplier ?

Looks like a semi-standard "motherboard plug and play" SuperIO unlock
sequence to me. If you can find out what chip they use on the
motherboard, you might be able to find a data sheet for it.

Referenced from:https://marc.info/?l=openbsd-tech&m=141821845915978&w=2

linux - How to identify GPIO pin numbers? Is GPIO driver loaded? - Unix & Linux Stack Exchange
update:2021-11-9
My system: CPU Version: Intel(R) Celeron(R) CPU J1900 @ 1.99GHz

At some point I figured that this implements the IT8786 Chip.

If you check your Kernel Configuration, you might find support for the IT87x family of drivers (under GPIO Drivers of course). In my case this was only enable as a Module.

Then, I loaded the module with:

sudo insmod /lib/modules/5.8.0-55-generic/kernel/drivers/gpio/gpio-it87.ko
dmesg returned the following item:

gpio_it87: Found Chip IT8786 rev 2. 64 GPIO lines starting at 0a00h
By checking /dev, gpiochip0 appeared and from there on you can use the already mentioned sysfs method if you wish so (although in retirement).

If you don't know how to relate the physical pins to the hardware, then try this:

apt-get install gpiod
Running gpioinfo returns in my case the following:

gpiochip0 - 64 lines:

    line   0:  "it87_gp10"       unused   input  active-high
    line   1:  "it87_gp11"       unused   input  active-high
    line   2:  "it87_gp12"      "sysfs"   input  active-high [used]
    line   3:  "it87_gp13"       unused   input  active-high
    line   4:  "it87_gp14"       unused   input  active-high
    line   5:  "it87_gp15"       unused   input  active-high
    line   6:  "it87_gp16"       unused   input  active-high
    line   7:  "it87_gp17"       unused   input  active-high
    line   8:  "it87_gp20"       unused   input  active-high
    line   9:  "it87_gp21"       unused   input  active-high
    line  10:  "it87_gp22"       unused   input  active-high
    line  11:  "it87_gp23"       unused   input  active-high
    line  12:  "it87_gp24"      "sysfs"   input  active-high [used]
    line  13:  "it87_gp25"       unused   input  active-high
    line  14:  "it87_gp26"       unused   input  active-high
    line  15:  "it87_gp27"       unused   input  active-high
    line  16:  "it87_gp30"       unused   input  active-high
    line  17:  "it87_gp31"       unused   input  active-high
    line  18:  "it87_gp32"       unused   input  active-high
    line  19:  "it87_gp33"       unused   input  active-high
    line  20:  "it87_gp34"       unused   input  active-high
    line  21:  "it87_gp35"       unused   input  active-high
    line  22:  "it87_gp36"      "sysfs"  output  active-high [used]
    line  23:  "it87_gp37"       unused   input  active-high
    line  24:  "it87_gp40"       unused   input  active-high
    line  25:  "it87_gp41"       unused   input  active-high
    line  26:  "it87_gp42"       unused   input  active-high
    line  27:  "it87_gp43"       unused   input  active-high
    line  28:  "it87_gp44"       unused   input  active-high
    line  29:  "it87_gp45"       unused   input  active-high
    line  30:  "it87_gp46"       unused   input  active-high
    line  31:  "it87_gp47"       unused   input  active-high
    line  32:  "it87_gp50"       unused   input  active-high
    line  33:  "it87_gp51"       unused   input  active-high
    line  34:  "it87_gp52"       unused   input  active-high
    line  35:  "it87_gp53"       unused   input  active-high
    line  36:  "it87_gp54"       unused   input  active-high
    line  37:  "it87_gp55"       unused   input  active-high
    line  38:  "it87_gp56"       unused   input  active-high
    line  39:  "it87_gp57"       unused   input  active-high
    line  40:  "it87_gp60"       unused   input  active-high
    line  41:  "it87_gp61"       unused   input  active-high
    line  42:  "it87_gp62"       unused   input  active-high
    line  43:  "it87_gp63"       unused   input  active-high
    line  44:  "it87_gp64"       unused   input  active-high
    line  45:  "it87_gp65"       unused   input  active-high
    line  46:  "it87_gp66"       unused   input  active-high
    line  47:  "it87_gp67"       unused   input  active-high
    line  48:  "it87_gp70"       unused   input  active-high
    line  49:  "it87_gp71"       unused   input  active-high
    line  50:  "it87_gp72"       unused   input  active-high
    line  51:  "it87_gp73"      "sysfs"   input  active-high [used]
    line  52:  "it87_gp74"      "sysfs"  output  active-high [used]
    line  53:  "it87_gp75"      "sysfs"   input  active-high [used]
    line  54:  "it87_gp76"      "sysfs"   input  active-high [used]
    line  55:  "it87_gp77"       unused   input  active-high
    line  56:  "it87_gp80"       unused   input  active-high
    line  57:  "it87_gp81"       unused  output  active-high
    line  58:  "it87_gp82"       unused   input  active-high
    line  59:  "it87_gp83"       unused   input  active-high
    line  60:  "it87_gp84"       unused   input  active-high
    line  61:  "it87_gp85"       unused   input  active-high
    line  62:  "it87_gp86"       unused   input  active-high
    line  63:  "it87_gp87"       unused   input  active-high

You can use the utilities of gpiod to work with the different pins. If your manufacturer has in some way indicated which GPI/O you can use, then you can related the "line" to the it87_gpXX" part, being XX the pins probably provided by the manufacturer.

A quick check can be done on the old way.

Go to /sys/class/gpio and see what gpiochip you find. The base number inside the gpiochip is the start position and the line points returned by gpioinfo the offset.

In my case I had 448 as base. If I want to reach GP81, that is line 57, so I would do this:

echo 505 > /sys/class/gpio/export
I hope this helps! It saved me from writing the Driver (By the way, the manufacturer sent me a driver but it didn't work)
Referenced from:https://unix.stackexchange.com/questions/485324/how-to-identify-gpio-pin-numbers-is-gpio-driver-loaded

linux下在用户空间访问I/O端口的ioperm和iopl函数
    Linux下设置端口权限的系统调用有两个:ioperm和iopl函数。
1.ioperm函数
     功能描述:
为调用进程设置I/O端口访问权能。ioperm的使用需要具有超级用户的权限,只有低端的[0-0x3ff] I/O端口可被设置,要想指定更多端口的权能,可使用iopl函数。这一调用只可用于i386平台。

用法:

include  / for libc5 /

include  / for glibc /

int ioperm(unsigned long from, unsigned long num,
int turn_on);
      
参数:
from:起始端口地址。
num:需要修改权能的端口数。
turn_on:端口的新权能位。1为开启,0为关闭。
返回说明: 
成功执行时,返回0。失败返回-1,errno被设为以下的某个值 
EINVAL:参数无效
EIO:这一调用不被支持
EPERM:调用进程权能不足。
   
2.iopl函数
功能描述:该调用用于修改当前进程的操作端口的权限。可以用于所有65536个端口的权限。因此,ioperm相当于该调用的子集。和ioperm一样,这一调用仅适用于i386平台。

用法:

include 

   int
iopl(int level);
参数:
level: 端口的权限级别。为3时可以读写端口。默认权能级别为0,用户空间不可读写。
返回说明:成功执行时,返回0。失败返回-1,errno被设为以下的某个值
EINVAL:level值大于3
ENOSYS:未实现该调用
EPERM:调用进程权能不足。
Referenced from:https://blog.csdn.net/chdhust/article/details/8142141

GPIO的基础知识
       GPIO(General Purpose I/O Ports)意思为通用输入/输出端口,通俗地说,就是一些引脚,可以通过它们输出高低电平或者通过它们读入引脚的状态-是高电平或是低电平。GPIO口一是个比较重要的概念,用户可以通过GPIO口和硬件进行数据交互(如UART),控制硬件工作(如LED、蜂鸣器等),读取硬件的工作状态信号(如中断信号)等。几乎所有的CPU、MCU都会具GPIO功能,以下是作者统计的GPIO的种类,几乎囊括了所有的MCU的GPIO类型。
gpio-input.png
输入浮空(Floating):即输入端口既不上拉(Pull up)也不下拉(Pull down),电平由外部输入决定;这种模式较少,一般模拟量输入、按键扫描输入使用该模式。
输入上拉(Pull up):即输入端口配置一个电阻接到电源(Power)端,该电阻可以使用芯片内置,也可以是外部电阻。
输入下拉(Pull down):即输入端口配置一个电阻到地(Ground),该电阻可以使用芯片内置,也可以是外部电阻。
模拟输入:模拟量信号输入,需要芯片内部支持AD转换功能才可以。
开漏输出: 漏极开路输出(OD) 和集电极开路输出(OC)十分相似,都是无法输出高电平,只能输出低电平,需要高电平时需要外接上拉电阻。
推挽式输出:推挽输出既可以输出高电平也可以输出低电平,无需外配置电阻。
推挽式复用功能:即推挽式输出和输入功能,使用时需要配置具体使用哪一种(雙方向端口,需要首先配置方向)。
开漏复用功能:即开漏输出和输入功能,使用时需要配置具体使用哪一种。
 
上下拉电阻作用
上拉(Pull up)就是将不确定的信号(X態)通过一个电阻嵌位在高电平(High),电阻同时起限流作用;下拉同理,下拉(Pull down)就是将不确定的信号(X態)通过一个电阻嵌位在低电平(Low),电阻同时起限流作用。
上拉(Pull Up)是对器件注入电流,下拉(Pull down)是输出电流。
弱强只是上拉电阻的阻值不同,没有什么严格区分。
对于非集电极(或漏极)开路输出型电路(如普通门电路)提升电流和电压的能力是有限的,上拉电阻(Pull-up resistor)的功能主要是为集电极开路输出(OC)型电路输出电流通道。
一般作单键触发使用时,如果IC本身没有内接电阻,为了使单键维持在不被触发的状态或是触发后回到原状态,必须在IC外部另接一电阻。
数字电路有三种状态:高电平、低电平、和高阻状态,有些应用场合不希望出现高阻状态,可以通过上拉电阻或下拉电阻的方式使处于稳定状态,具体视设计要求而定!
一般说的是I/O端口,有的可以设置,有的不可以设置,有的是内置,有的是需要外接。
上拉电阻是用来解决总线驱动能力不足时提供电流的。一般说法是拉电流,下拉电阻是用来吸收电流的,也就是我们通常所说的灌电流。
在I/O引脚悬空时,接电阻就是为了防止输入端悬空,从而提高系统的抗干扰能力。减弱外部电流对芯片产生的干扰。
通过上拉或下拉来增加或减小驱动电流。
上下拉电阻改变电平的电位,常用在TTL-CMOS匹配。
上拉电阻可以为OC门或者OD门提供电流。
Referenced from:https://blog.csdn.net/gsjthxy/article/details/106551565