Adding i2c to the router

I append below a copy of various notes by dl4huf for my backup - they explain how to use the toolchain to load i2c - they show how to use gpio lines other than the default lines 1 and 2 (these have to be cut at the CPU - but any led line can be used)

This is how to load a USB memory stick with the i2c files.
(see the "distro" - menu item 2 - for ready made stick containing all of this.)

1) create a directory
/etc/modules.d/
place in it a linux text file named 96-i2c
containing the red text
i2c-core
i2c-algo-bit
i2c-adm5120
i2c-dev
i2c-proc <-see below *

2) create a Linux text file named S99start_i2c
containing the red text
#!/bin/sh
insmod i2c-core
insmod i2c-algo-bit
insmod i2c-adm5120
insmod i2c-dev
and
insmod i2c-proc if you need it - <-*not used by me yet
place the file in /etc/init.d/

this will make the Sweex add the five i2c modules below to the kernel at boot time

create a directory /lib/modules/2.4.32 and place these files in it

i2c-core.o
i2c-dev.o
i2c-algo-bit.o
i2c-adm5120.o
i2c-proc.o

(the links are to my backup copies. the originals are here


I have written/adapted some simple programs to drive various i2c chips

(the c code works but is simplistic - if you improve it please send me a copy!)

PCF8574_addr_byteout . . . .PCF8574_addr_byteout.c
send an 8 bit byte to switch the 8 lines on the port expander

PCF8574_addr_read . . . .PCF8574_addr_read.c
read the byte of data on the 8 bit port acting as input

PCF8591_addr_line_V . . . . PCF8591_addr_line_V.c
read the byte representing the voltage on one of 4 lines, send a byte to set the D/A voltage on the output pin
print it out as "BYTE = " decimal 0 to 255

PCF8591_addr_line_V_raw . . . . PCF8591_addr_line_V_raw.c
read the byte representing the voltage on one of 4 lines, send a byte to set the D/A voltage on the output pin
print it out as decimal 0 to 255

PCF8591-ramdisk-save_addr_line_V . . . .PCF8591-ramdisk-save_addr_line_V.c
read the byte representing the voltage on one of 4 lines, send a byte to set the D/A voltage on the output pin
then save the value read from the chosen input line in file /var/www/ramdisk/AtoD.dat as decimal 0 to 255
(then ash or Blassic can read the value stored in AtoD.dat and control devices)

lm75_address . . . . . lm75_address.c
read the temperature to 0.5 degrees C

The LM75 is a surface mount device but it can be connected to standard 0.1 inch strip board.
(Split one circuit trace with a model-makers hand held router (e.g. Dremmel))
My layout is here - note the three address lines - eight devices can be addressed

The AD5602 (Analog Devices) is a fast D to A chip. It is a very small surface mount chip (0.6mm pin distance) and was a challenge to solder to. I used a piece of copper clad board like this and wrote this simple driver AD5602_test1.c compiled (on the router!) as AD5602_test1
With ADDR set to be floating the bus address is Hex 0E (Dec 14) (not as you would calculate from the data sheet - the Linux i2c drivers ignore the R/W bit and shift the pattern 1 bit to the left as 00001110 not 00011100 as you might expect)
You send 2 bytes after the address and the 8bit analog value is cut into two. 4 bits at the end of the first data byte and 4 bits at the start of the second data byte.
I had hoped to play 8KHz 8bit wav files by speeding up the I2C bus but have not yet managed that.
Please email me if you do it!


Driving a 5volt i2c line from the 3.3volt router gpio lines. dl4huf sent me this circuit :-


This works for me but at present my i2c chips are all running on 3.3volts
(the black band on the zener body goes to the gpio line. My R2 is 220 ohms because the leds on the board are connected to +3.3 with 220 ohms - I am concerned thart the gpio can get into output mode and connect to a line by accident)


Driving a 5volt i2c line from the 3.3volt router gpio/led lines
- improved circuit as per Philips specification AN97055

The 2N7000 FETs are general purpose N channel drivers (60V 400mA).

SDA5 and SCL5 can be connected to I2C chips close to the Router.

The 4.7Kohm pull up resistors on SDA5 and SCL5 are not needed if the 82B715 long line driver is in use - but it works with them still in circuit.

There must be a second 82B715 at the far end of the line but no 270ohm resistors.

I now use gpio(11) and gpio(8) - no need to cut the CPU wires - the LEDS are still in place in the router.

/etc/init.d/S99start_i2c

#!/bin/sh
insmod i2c-core
insmod i2c-algo-bit

# use gpio(11) for SCL and gpio (8) for SDA
# 11 is marked 1 and 8 is marked LINKACT on Sweex bottom row
insmod i2c-adm5120 scl=11 sda=8

insmod i2c-dev

Connecting an LM75 thermometer


An assembly of various notes by d14huf -(many thanks!) with a few edits
- see

midge forum


I have added the I2C-driver to the Kamikaze-kernel from
"http://midge.vlad.org.ua/wiki/Download"
With this mod you can make the necessary kernel-modules.
You can also download the modules from my site:
http://www.ipb-halle.de/~ronald/ADM5120/i2c/
To make the modules :
- you need a working toolchain with finished "make menuconfig" and "make"
- copy the i2c-adm5120.c from "ftp://61.219.230.93/adm5120" to build_mipsel/linux/drivers/i2c of the toolchain
- edit build_mipsel/linux/drivers/i2c/Config.in :
insert after "dep_tristate 'NatSemi SCx200......" the 5 lines :
dep_tristate 'ADM5120-GPIO-Interface' CONFIG_I2C_ADM5120 $CONFIG_I2C
if [ "$CONFIG_I2C_ADM5120" != "n" ]; then
int ' GPIO pin used for SCL' CONFIG_I2C_ADM5120_SCL 1
int ' GPIO pin used for SDA' CONFIG_I2C_ADM5120_SDA 3
fi

ALSO!!

Be sure you also you have the line


"obj-$(CONFIG_I2C_ADM5120) += i2c-adm5120.o"
in your "linux-2.4.32/drivers/i2c/Makefile" after
"obj-$(CONFIG_I2C_ALGO_AU1550) += i2c-algo-au1550.o i2c-au1550.o".


Then go to "build_mipsel/linux" and run "make menuconfig".
Enable the I2C-ADM5120, I2C bit-banging and I2C device as module <-you can change the gpio lines here
(in Character devices / I2C support)
You can enable "I2C proc" but i dont know how to use it.
Then run "make modules"
You can then find the files in drivers/i2c : i2c-core.o, i2c-dev.o, i2c-algo-bit.o and i2c-adm5120.o (+ i2c-proc.o).
Copy the files to your router to /lib/modules/2.4.32 and insmod the files like
insmod i2c-core
insmod i2c-dev
insmod i2c-algo-bit
insmod i2c-adm5120
Check the log with logread.
If all ok, you have an i2c-device "/dev/i2c/0" !
Note : many tools expect "/dev/i2c-0", you can make a symlink: "ln -s /dev/i2c/0 /dev/i2c-0" for that.
I have only test the i2c with the GPIO1 and GPIO3 !
You need to solder the pins like the ttyS1-Mod.

With this method you can build any other modules
like Watchdog (softdog.o) or for HamRadio (6pack.o, bpqether.o) .
You can use the i2c-interface with lcd4linux (see other thread) or your own programs.
I have compiled the tools "i2cdetect" and "eeprom" from
the lm-sensors-projekt and a small sample for LM75-sensor from
"http://www.linux-magazin.de/Artikel/ausgabe/1997/06/I2C/i2c.html"
( the LM75-address is wrong, for A0-A3=low you need 72 !
the driver expects the address without the R/W-bit )


The complete i2c doc you can find in your linux kernel doc
( openwrt-midge/build_mipsel/linux/Documentation/i2c ) or
on the lm_sensors homepage : http://www.lm-sensors.org/wiki/Documentation


For scripts you need the /proc interface (i2c-proc and chip driver from lm_sensors).
For chips without a lm_sensors driver you must write your own program like the
lm75.c or pcf8574.c example.
You can also use the universal tools i2cget, i2cset, i2cdump, eeprog etc.
(without the /proc driver is needed )
I use eeprog for a PCF8583 RTC chip. With i2cset I have set the RTC before.
A small program to read/set the rtc are nice but with this ready tools I am faster :-)
With this script I read the RTC and set the clock on the router at boot time.
---------------------------------------------------------------
#!/bin/sh
#set -x
# read pcf8583 rtc and set linux clock, wait for 00 seconds,
# busybox date ignore seconds on set command
eeprom=`eeprog /dev/i2c/0 0x50 -x -q -f -r 0:0xf | grep 0000`
sec=`echo $eeprom | cut -c 13-14`
while [ "$sec" != "00" ] ; do
# sleep 1
eeprom=`eeprog /dev/i2c/0 0x50 -x -q -f -r 0:0xf | grep 0000`
sec=`echo $eeprom | cut -c 13-14`
# echo $sec
done
min=`echo $eeprom | cut -c 16-17`
stu=`echo $eeprom | cut -c 19-20`
day=`echo $eeprom | cut -c 22-23`
mon=`echo $eeprom | cut -c 25-26`
#echo "$stu:$min:$sec $day/$mon"
dstr=$mon$day$stu$min'2007'
#echo $dstr
erg=`date -s $dstr`
#echo $erg
------------------------------------------------------------------
The script runs up to 1min !


About the kernel modules. They are from the lm_sensors package. You can use they
also with "insmod pcf8591" if you copy the files to "/lib/modules/2.4.32".
You need first load the "i2c-proc".
Also its a different between "lm75.c" with the binary "lm75" and the "lm75.o" find
on my site. The "lm75.o" is also a kernel module from lm_sensors.
The "lm75.c" with "lm75" are small example how to use the i2c-bus.
After load the pcf8591.o and a PCF8591 chip is on the bus you have a new
folder on "/proc/sys/dev/sensors/".
I have added the original doc on http://www.ipb-halle.de/~ronald/ADM5120/i2c/i2c_v2.10.1/doc/
Please note the passage for chips detection and load parameters. Also for the PCF8574(A).


> Does your i2c driver all reset the gpio1 and 3 busses when run? Suppose some
> other program reset them?
>
The i2c driver reset only the used lines (default gpio1 and gpio3) without any messages.
You can see this with the led driver with "cat /dev/gpio1" or "cat /proc/driver/led".
But please note, you can furthermore change the mode and state with the led driver.
This will confuse the i2c bus but the i2c driver will always reset the gpios to his mode
if any i2c transfer is performed. If the i2c bus is idle the gpios are in input mode.


You can build a kernel with the driver, in the same way your old kernel, but ONLY with
<*> i2c-support
<*> I2C bit-banging interfaces
<*> ADM5120-GPIO-Interface
(1) GPIO pin used for SCL
(3) GPIO pin used for SDA
<*> I2C device interface
<*> I2C /proc interface (required for hardware sensors)
All other in i2c are < >!
If you have no "ADM5120-GPIO-Interface" entry then you have not patched your config.in and Makefile !


Testing to see if the i2c is present OK
- reboot the router
- "lsmod | grep i2c"
- if any line come back then do "rmmod ...." for any line
for any "rmmod ..." only the prompt come back ! if a line like "rmmod: i2c-dev: Success"
come back "Success" is a bug and must read as "error" !
- verification : "lsmod | grep i2c" returns only the prompt.
- "mount" , check for a line like "none on /dev type devfs (rw)" or "devfs on /dev type devfs (rw)"
important is the "/dev" folder not "/rom/dev" or anything.
if not do "mount -t devfs none /dev"
- ls "/dev/i2c" results in "No such file or directory"The "version 2.10.1" can you use from http://www.ipb-halle.de/~ronald/ADM5120/i2c/i2c_v2.10.1/ or
if you own build how I described is a mail before.
The "version 2.6.1 (20010830)" should also work.
- its helpful if you have the syslog to the console, otherwise to a "logread" after each command:
- "insmod i2c-core"
in the log "i2c-core.o: i2c core module version 2.10.1 (20060924)"
- "insmod i2c-algo-bit"
- "insmod i2c-adm5120" see below ****
in the log "i2c-adm5120.o: ADM5120 I2C Driver V1.1"
"i2c-adm5120.o: SCL=GPIO-01, SDA=GPIO-03"
- "insmod i2c-dev"
in the log "i2c-dev.o: i2c /dev entries driver module version 2.10.1 (20060924)"
"i2c-dev.o: Registered 'ADM5120 I2C' as minor 0"
- "ls /dev/i2c" results in "0" , the folder "/dev/i2c" and the entry "/dev/i2c/0" exist.
- "cat /proc/driver/led" -> GPIO1 and GPIO3 are INPUT
- "i2cdetect -y 0" you should see a table with any XX except 3 place, the discovered devices.
One entry should be 48, the LM75. Then the lm75 tool should work.
------------------------------------------------------
The i2c bus are now ready.- "insmod i2c-proc"
in the log "i2c-proc.o version 2.10.1 (20060924)"
"ls /proc/sys/dev/sensors" -> "chips"
- "insmod lm75" (I think only with the 2.10.1 version possible)
in the log "lm75.o version 2.10.1 (20060924)"
- "ls /proc/sys/dev/sensors" returns "chips lm75-i2c-0-48"
- "ls /proc/sys/dev/sensors/lm75-i2c-0-48" returns "temp"
- "cat /proc/sys/dev/sensors/lm75-i2c-0-48/temp" returns like "80.0 75.0 39.5"
with 80.0 = high, 75.0 = hyst and 39.5 = temperature

------------------------------------------------------------

Is there any easy way to change the speed of the I2C bus?

I do not know a easy way to do this. The only way I know to slow down the i2c speed is
to set up new values in the driver.
Please see in i2c-adm5120.c:

static struct i2c_algo_bit_data bit_adm5120_data = {
NULL,
bit_adm5120_setsda,
bit_adm5120_setscl,
bit_adm5120_getsda,
bit_adm5120_getscl,
5, 5, 100, /* waits, timeout, around 62,5KHz SCL-Clock */
};

Change this settings for lower speed and recompile the module

------------------------------------------------------------

**** Driving other lines

You do not need to recompile the module for other gpio.
You only have to load the module with parameter like "insmod i2c-adm5120 scl=14 sda=17".

Backup data for my system