The 'secret' i2c interface

There is a reference to an i2c interface in the source-code and on the PC board of the PT112 the system. This means that many interesting devices can be connected with a minimum amount of hardware -- no need for all the counters and shift-registers used for expansion of the GPIO bits. Some resistors on the PT112 board, and a reference to a memory chip that is not actually present, also suggests that there has been or is a less-than-well documented configuration of an I2C bus.

In the following are what I have found out about this. My example shows connecting a DS1621 thermometer circuit, but hooking up IO-expanders or NVRAMs like the 24LC128 would be very similar. Since there is plenty of non-volatile storage in the /usr filesystem, there isn't so much use for an NVRAM here, but all kinds of other sensor or activator devices may be of interest. None of this has been documented anywhere outside of the Linux sources and configurations, and there is use of one of the signals that are marked as "reserved" with no further info, both in the Picotux documentation, the NetARM documentation or the NS7520 documentation.

Hardware.

There is an indication of an I2C-bus usage in the picotux 112 hardware documentation. There is a space for a non-connected i2c chip, where SCL is connected to /INIT (pin 20 and port C5), and SDA is connected to "RFG0", (pin 17) whatever that is. All documentation I have seen indicates this as "reserved"; both the Kleinhenz and NetARM documents say this. So did someone at Kleinhenz do something out of the ordinary, and is this something that actually can be made to work? There is a reference to a 24LC128 serial memory, with SCL and SDA and power and ground readily identified, but nothing actually connected on the PT112 board.

Software

The Linux kernel also has an i2c character driver and device configured. The information in /proc/bus/i2c reveals there is a device known as

    i2c-0 NetSilicon NetARM GPIO

and the corresponding character device for this exists under /dev, at

    crw------ 1 0 0 89,0 Jan 1 1970 /dev/i2c/0

So, where can we find out where the kernel thinks these two wires for SCL and SDA are? The standard /usr/src/linux/Documentation/i2c/dev-interface tells about some ioctl calls that can be made. And this simple C program,

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <linux/i2c.h>

int main()
{
    int fd, s;
    unsigned long funcs;

    fd = open("/dev/i2c/0", O_RDWR);
    if(fd == -1)
    {
        perror("/dev/i2c/0");
        exit(1);
    }

    s = ioctl(fd, I2C_FUNCS, &funcs);

    printf("Functions=0x%lx status=%d\n", funcs, s);

    close(fd);
    return(0);
}

gave the result

Functions=0xcfffc007 status=0

which means that the i2c driver can do most everything of interest.

Now for the wires? Which ports have been allocated, and is this something that we can use? Some more digging around in the driver code for the NetARM GPIO, in the file i2c-adap-netarm.c, revealed where the string in /proc/bus/i2c came from. There is also a sign-on message in a printk() call there.

Looking at dmesg:

# dmesg | grep -i i2c
i2c-core.o: i2c core module version 2.9.2 (20050906)
i2c-dev.o: i2c /dev entries driver module version 2.9.2 (20050906)
i2c-adap-netarm_gpio.c: NetSilicon NetARM GPIO I2C adapter
i2c-adap-netarm_gpio.c: SCL on PC5, SDA on PC4
i2c-dev.o: Registered 'NetSilicon NetARM GPIO' as minor 0
i2c-proc.o version 2.9.2 (20050906)
#

The SCL assignment is consistent with observed hardware, and the SDA apparently has been either re-defined away, or it is just not there, or some secred reserved signal has been found and appropriated. Too bad, if it had used one of the orthodox port A outputs, that might have been a lot more obviously useful.

The hunt for the PC4 line

Back to docs. Where is port C4 at, and what is it normally being used for? The doc for the NS7520 unit which is the brain inside the Picotux units, says this may be one of several other things, amongst these are "Reset_Out_" or "RIB_"(an input), or another GPIO line.

Next up: Hook up a Picotux, make a small program for touching the port C4 using GPIO and see what happens, if the thing stops functioning partially or entirely, if the IO-line changes states etc.


#include <stdio.h>
#include <picotux/gpio100.h>

main()
{
    struct gpio_context *gid;

    int gv, gs, gdirx;
    int j;
    int *pcs;
    gpio_ports_t gpx;

    /* Port C config. register. */
    pcs = (int *) 0xffb00028;

    printf("GPIO_OK = 0x%x ", GPIO_OK);
    printf("GPIO_FAILED = 0x%x ", GPIO_FAILED);
    printf("GPIO_PARTIAL = 0x%x\n", GPIO_PARTIAL);
    printf("GPIO_IN = 0x%x ", GPIO_IN);
    printf("GPIO_OUT = 0x%x ", GPIO_OUT);
    printf("GPIO_UNKNOWN = 0x%x\n", GPIO_UNKNOWN);

    /* gpx = GPIO_PORTA0 | GPIO_PORTA1 | GPIO_PORTA2 | GPIO_PORTA5 | GPIO_PORTA6 ; */
    gpx = GPIO_PORTC4 | GPIO_PORTC5;

    printf("PCCR = 0x%lx\n", *pcs);

    gid = (struct gpio_context * ) gpio_open();
    printf("Open gid = 0x%x\n", gid);
    if(gid == NULL)
    {
        perror("gpio");
        exit(2);
    }

    gs = gpio_alloc(gid, gpx);
    printf("Alloc ports 0x%x status 0x%x\n", gpx, gs);

    gs = gpio_set_dir_out(gid, gpx);
    printf("Set ports 0x%x to output status 0x%x\n", gpx, 0);

    for(j = 0; j<5; j++)
    {
        gv = 0;
        gs = gpio_write(gid, gpx, gv);
        printf("Set port 0x%x to 0x%x status 0x%x\n", gpx, gv, gs);
        sleep(1);

        gv = GPIO_PORTC4;
        gs = gpio_write(gid, gpx, gv);
        printf("Set port 0x%x to 0x%x status 0x%x\n", gpx, gv, gs);
        sleep(1);

        /* gv = GPIO_PORTC4 | GPIO_PORTC5;
        gs = gpio_write(gid, gpx, gv);
        printf("Set port 0x%x to 0x%x status 0x%x\n", gpx, gv, gs);
        sleep(1); */

        /* gv = GPIO_PORTC5;
        gs = gpio_write(gid, gpx, gv);
        printf("Set port 0x%x to 0x%x status 0x%x\n", gpx, gv, gs);
        sleep(1); */
    }

    gpio_free(gid, gpx);
    gpio_close(gid);

    return(0);
}

I made the above program for trying out the C4 GPIO lines, and inspect the value of the internal Port C control/status register at memory address 0xffb00028.

Power on. Observation 1: the "RFG0" line sits at 3.27V in powerup. Shorting it to ground draws 19 mA, suggesting this works as an output at this time.

Running the above GPIO program that toggles C4 does indeed reveal that this line is going up and down accordingly. So why is this being advertised as "reserved" instead of another useful output? The picotux 112 schematic calls this line the "RFGO", but everywhere else there are dire warnings about this being reserved and do not use.

Investigations of the register at CMODE, address 0xFFB00028, reveals that this initially reads 0x1000ff later on 0x3000df. CMODE=0, meaning mode 0 for all 7 bits of register C CDIR=00010000 then 00110000 meaning direction of C4 and later C5 are output. CSF=0, meaning all 7 bits in register C are plain GPIO and not special mode CDATA=11111111 then 11011111 meaning the lines C4 and C5 are H, C5 is later L.

But C5 is the known init/ line that we already have been allowed to work with.

So that means we should be able to hook up some i2c-device as per the PT112 board circuitry, and the kernel seems to support this. So Why has this not been included in the documentation? Is it that this line used to be re-purposed for something else eventually? Seems such a waste of a good bit otherwise...

The DS1621 Experiment

I'll try connecting one of the DS1621s that I bought the other day. Hook up its SCL, SDA, VCC, set slave address to 0. So the 7-bit device address is

100 1000 = 0x48 

The chip may run from 2.7 to 5.5 V so the 3.3 V supply of the picotux is just about right. I'll try hooking it up to the board.

PinNamePT112 Note
1SDAJP2(2) SDA PortC4
2SCLJP4(1) SCL PortC5
3TOUTNC Thermostat output
4GNDJP2(4)
5A2JP2(4) Address lines to GND
6A1JP2(4)
7A0JP2(4)
8VCCJ2(6) +3.3V on the POE socket

The DS1621 recognizes the following commands and addresses:

Start Running or Perform Conversion Write ByteData=0xEE
Stop Running Write ByteData =x22
Write Config Byte Write Byte DataComm=0xAC Data=CBYTE DONE THF TLF NVH 1 0 POL 1SHOT
Write TH setting Write Word DataComm=0xA1 Data=MS LS
Write TL setting Write Word DataComm=0xA2 Data=MS LS
Read Status Byte Read Byte DataComm=0xAC DONE THF TLF NVB 1 0 POL 1SHOT
Read counter Byte Read Byte DataComm=0xA8 Data=CBYTE
Read slope Byte Read Byte DataComm=0xA9 Data=CBYTE
Read TH Read Word DataComm=0xA1 Data=MS LS TH MSB, then TH LSB
Read TL Read Word DataComm=0xA2 Data=MS LS TL MSB, then TL LSB
Read Measured temperature, TA Read Word DataComm=0xAA Data=MS LS TA MSB, then TA LSB

Of all this, the last one is the most interesting one, the rest are configuration-time settings mostly. All the temperature values are 9-bits units in 1/2 C resolution. The whole degrees are in the MSB as a signed 8-bit integer from -55 to +125 degrees, and the highest bit in the LSB indicates the 1/2-degrees.

The config and status byte is as follows:

Bit Name Description Logic levels
7 DONE Done-flag 1=done 0=progress, read-only
6 THF Temperature-above-TH flag 1 means set, Write 0 to reset
5 TLF Temperature-below-TL flag 1 means set, Write 0 to reset
4 NVB Busy-flag 1=writing to a cell is in progress, read-only
3 1
2 0
1 POL Polarity of TOUT 0=active L, 1=active H, nonvolatile
0 1SHOTMode One-shot or contiunal 0=continual; 1=one-shot, with one measurement per "Perform Conversion" action. nonvolatile

All of this is mostly a summary of the interface that the DS1621 provides. Slightly more exciting than just a serial memory. I made this example C program, ds16.c, that can be compiled and run and which shows some of the settings.

Further experience with PCF8574P

During start or restart, about 20 seconds after power-up, or about 35 seconds after the reboot command is run, all PCF8574Ps connected to this i2c bus sets the output pattern 0x48 (01001000) that is, bits 3 and 6 eventually go to 1 and the others go to 0. This happens no matter what the bus-address of the PCF8574Ps is set to.

Hooking up a digital counter to each of the outputs on a PCF8574P at bus address 0x23, setting the outputs all to H, then rebooting, shows that bit 6 stays at H, bits 7,5, and 4 simply change from H to L, while bit 3 actually pulses H-L-H.

If started at L, bit 3 will go directly to H. Bit 3 therefore seems to be well suited for use as an active low or a rising edge reset pulse. This happens on bit 3 whether the system is powered up or it is rebooted via the reboot command.

As the dmesg output indicates, there is some driver code for LM78 loaded, and this may be causing this behavior on the PCF8574Ps by some of its probing activity.

Other chips such as PCF8591, DS1621, MCP9801, MT41T81S, PIC16F690 programmed as an I2C slave unit, or even the PCF8574AP, do not seem to be affected by this however. The first three have bus-addresses in the range 0x48-0x4f, while the MT41T81S, an RTC, has bus-address 0x68, and the PCF8574AP has bus-addresses in the range 0x38-0x3f. The 16F690 can have any bus address. The PCF8574AP starts out all lines H and remains this way until programming forces otherwise. This is unlike the "twitch" seen with the PCF8574P (no A in part number).

It has been found very useful to have the RTC for time-keeping when power is off, so that the system can start out at something other than January 1 1970.

Maybe this funny behavior of the PCF8574Ps is what made the i2c-bus inadvisable to use; however, careful design here can exploit the pulse on bit 3 as a re-start signal for other connected electronics.

The pin 14 on the Picotux also works as an open-collector, active-low, power-on-reset signal. However, the level here does not change on software initiated reboots (the reboot being issued in a shell), while the bit 3 pulses (and level changes on the other bits) will happen in all cases.

Trying a memory chip

Though it doesn't seem to make that much sense to just connect an EEPROM of the 24LC16 variety, I have been thinking of a scheme where this EEPROM serves as a program storage area for another microprocessor, and thus should be readable and writeable by both the Picotux and this other device.

On trying out reading from this memory device, I got "Device Busy" from the system when wanting to set the slave address, so looking at the source of the i2c-dev there was another ioctl I2C_SLAVE_FORCE that worked. This device busy error appeared on slave addresses 0x50-0x57, where the EEPROM lives. Some other driver has dibs on these addresses it seems, there is a reference to "eeprom.o" during startup that appears in dmesg, now there are several source files named eeprom.c but none of them seem to be relevant.