Showing posts with label i2c. Show all posts
Showing posts with label i2c. Show all posts

Monday, June 29, 2009

PIC 18f14k50 USB Interface Board: Part 1

Introduction
This is the first blog regarding a PIC18f14k50 USB IO board. This first session will focus on the design and building of the board, future sessions will take about software application that are being developed for this board.

At the start of the development of the board, I had the following “bullet spec” in mind:

  • Development “starter” board for Jal(lib) USB developement
  • It must have a USB 2.0 full speed connection
  • Must be able to use it as a USB<->RS-232 interface converter, including 2 HW handshake signal, operate as a true RS-232 interface as well have the ability to use it for 5v signals (RS-232c, so it can be used to interface board towards other test boards that don’t have a MAX-232 level converter).
  • Must be able to use it as a USB<->I2C interface board, either act a master or slave device
  • Be able to add (small) daughter boards, in order use to the board as a HW/SW development platform (e.g. Remote control receiver, remote control transmitter etc)
  • Homebrew friendly (easy to build, single sided PCB, no SMD component)
  • Cheap (~10 Euro)


PIC selection
The first choice is what PIC to use, since I want to use it for JAL development (which restricts me to the PIC18F series), onboard USB Serial Interface Engine (SIE), should be a thru hole device and should run with 5v IO, I got the following list from the Micrcochip website:



Product Fam Price Fl EE RAM I/O

PIC18F13K50 $1.39 8 256 512 15
PIC18F14K50 $1.53 16 256 768 15
PIC18F2450 $2.23 16 0 768 23
PIC18F2455 $3.30 24 256 2048 24
PIC18F2458 $3.58 24 256 2048 24
PIC18F2550 $3.44 32 256 2048 24
PIC18F2553 $4.12 32 256 2048 24
PIC18F4450 $2.39 16 0 768 34
PIC18F4455 $3.51 24 256 2048 35
PIC18F4458 $3.79 24 256 2048 35
PIC18F4550 $3.65 32 256 2048 35
PIC18F4553 $4.33 32 256 2048 35



In the past I’ve used the 18f4550, which is a nice chip, but it is a bit expensive. I also have used the 18f2450 in a couple of projects; however, the main drawback is the lack of onboard eeprom (always handy for device specific settings). The recently introduced PIC 18f14k50 appears to be a nice compromise (almost similar to the 18f2450, but it is a bit cheaper and has onboard eeprom). Even though it is a low pin count device, I think it has enough IO pins. I have considered the 13k50, which is even a dime cheaper, but the flash and memory size is quite limited and the availability is a problem. However, the board can be used for both chips.



Schematic & PCB:

Once the PIC was selected, the design is straightforward:




I won’t go into the details of the “standard” PIC logic, consisting of the crystal (X1) + caps (C3/C4), ISCP connector (J1) and decoupling capacitor (C1).
There are two switches connected to the PIC, SW1 is can be used as a reset switch, SW2 is a generic switch with pull resistor. The DB-9 connector is connected via a MAX-232 level converter with the PIC (towards the internal UART PINs). The non-level shifted converted TX/RS signals are also connected to J3, in addition to the UART signals pin-5 is connected with the PWM/CCP unit of the PIC.

The 5 volt supply of the USB connector is used to power the board; the differential USB signal lines d+/d- are directly coupled from the PIC to the USB connector. Capacitor C5 is required for the internal 3.3v regulator.

The I2C header signals are also directly coupled with the MSSP unit, it can be used as a master or slave depending on the firmware, besides the 3 standard wires, also a 5v is available on the connector. The pull-ups can be enabled by placing the jumper (JP1).
Potmeter P1 is connector to AN5 of the PIC, this can be used for ADC experiments.


OK enough about the schematic. Richard Zengerink was so kind to draw the schematic and do the board layout. The board layout is depicted below, a PDF file of the layour is available in the SVN repository ( 18F14K50+EXTRA_lay_out.pdf
), this has the mirrored, 1:1 scale, so when printed on glossy paper with a laser printer, it can can be ironed into a copper cladded board, then etch it and drill it.








Building the board should not be too difficult; it took me about an hour to do the component assembly and solder it and perform some quick tests (check power connections, and check shortcuts)
A picture of the assembled board is depicted below:



Testing the board;
The first test is to connect a PIC programmer to the ICSP connector. Program the 18f14k50_led_switch_test.hex file on in the PIC and reset the board by doing a power off/on cycle (external reset is disabled in order to test SW1). The source code and Hex file are available in the jallib SVN repository


The application will turn on both LEDs for about 5 seconds, afterwards it starts to blink both LEDs every second. When SW1 is pressed, LED1 will stop blinking, when SW2 is pressed, LED2 will stop blinking.

Once this is working, we can confirm that ICSP, Power, oscillator, SW1, SW2, LED1 and LED2 are properly mounted.

The next step is to download the bootloader. Select the 18f14k50_usb_bootloader.hex in the ICSP programmer, and program the hex file. Remove the ICSP connector (since there shared with the USB lines), connect the USB connector with a USB cable with the Host computer, when plugging in the USB connector, hold down SW2 (program button)

If everything went ok, after a couple of seconds the Host computer will detect the USB bootloader device, it will ask for a driver. Navigate to the directory where the USB driver bootloader driver files are located, ignore the certificate warning by selecting the continue button. MCHPUSB Custom Driver.zip contains required drivers. Unzip the package to a local drive, the drivers are located in the MCHPUSB Driver\Release directory

Once the drivers are installed, you can use the PDFSUSB.exe applicatio, contained in Pdfsusb.zip archive. After you unzip the file and launch the application, select the dropdown arrow of the combo box.



You should see the PICDEM FS USB 0 (BOOT) option, select it (see also screenshot). After selection, it should update the status bar, it will depict the version number (1.20) of the firmware.

When using the bootloader, you need to add the following setting when compiling a JAL file in cooperation with the bootloader:

-loader18 –no-fuse

The JAL compiler will generate special startup and interrupt code if the loader18 option is specified, the no-fuse is used to avoid overwriting wrong fuses which can mess up the bootloader (although the PDFSUSB will also emit a warning, and ask for confirmation when overwriting the fuses)


So far for part1, next blog(s) will discuss how to use the board as a USB<->RS-232 convert, use it as a USB driven I2C master or slave device and show how to make a small daughter board to capture and generate remote control infrared signals.

Hope you like it, feeback is always welcome…

Albert Faber & Richard Zengerink



Tuesday, January 20, 2009

Step by Step: building an i2c slave with jallib (part 3)

Part 3 : implementing an i2c slave ISR


In previous parts of this tutorial, we've seen a little of theory, we've also seen how to check if the i2c bus is operational, now the time has come to finally build our i2c slave. But what will slave will do ? For this example, slave is going to do something amazing: it'll echo received chars. Oh, I'm thinking about something more exciting: it will "almost" echo chars:
  • if you send "a", it sends "b"
  • if you send "b", it sends "c",
  • if you send "z", it sends "{"
  • ...
(why "{" ? According to ASCII, "z" is the character for position 122. 123 is... "{")


Building the i2c master

Let's start with the easy part. What will master do ? Just collect characters from a serial link, and convert them to i2c commands. So you'll need a PIC to which you can send data via serial. I mean you'll need a board with serial com. capabilities. I mean we won't do this on a breadboard... There are plenty out there on the Internet, pick your choice. If you're interested, you can find one on my SirBot site: dedicated to 16f88, serial com. available, and i2c ready (pull-ups resistors).

It looks like this:



Two connectors are used for earch port, PORTA and PORTB, to plug daughter boards, or a breadboard in our case.

We'll use a 16f88 as a i2c master. 16f88 only own a SSP module, not MSSP, this means it can't handle i2c in hardware (no built-in i2c master). Not a problem, we'll use i2c software library.

The i2c initialization part is quite straight forward. SCL and SDA pins are declared, we'll use a standard speed, 400KHz,


-- I2C io definition
var volatile bit i2c_scl is pin_b4
var volatile bit i2c_scl_direction is pin_b4_direction
var volatile bit i2c_sda is pin_b1
var volatile bit i2c_sda_direction is pin_b1_direction
-- i2c setup
const word _i2c_bus_speed = 4 ; 400kHz
const bit _i2c_level = true ; i2c levels (not SMB)
include i2c_software
i2c_initialize()

We'll also use the level 1 i2c library. The principle is easy: you declare two buffers, one for receiving and one for sending bytes, and then you call procedure specifying how many bytes you want to send, and how many are expected to be returned. Joep has written a nice post about this, if you want to read more about this. We'll send one byte at a time, and receive one byte at a time, so buffers should be one byte long.
const single_byte_tx_buffer = 1 -- only needed when length is 1
var byte i2c_tx_buffer[1]
var byte i2c_rx_buffer[1]
include i2c_level1

What's next ? Well, master also has to read chars from a serial line. Again, easy:

const usart_hw_serial = true
const serial_hw_baudrate = 57_600
include serial_hardware
serial_hw_init()
-- Tell the world we're ready !
serial_hw_write("!")

So when the master is up, it should at least send the "!" char.


Then we need to specify the slave's address. This is a 8-bits long address, the 8th bits being the bit specifying if operation is a read or write one (see part 1 for more). We then need to collect those chars coming from the PC and sends them to the slave.

The following should do the trick (believe me, it does :))


var byte icaddress = 0x5C -- slave address

forever loop
if serial_hw_read(pc_char)
then
serial_hw_write(pc_char) -- echo
-- transmit to slave
-- we want to send 1 byte, and receive 1 from the slave
i2c_tx_buffer[0] = pc_char
var bit _trash = i2c_send_receive(icaddress, 1, 1)
-- receive buffer should contain our result
ic_char = i2c_rx_buffer[0]
serial_hw_write(ic_char)
end if
end loop


The whole program is available on jallib SVN repository here (subject to change, since not released yet).


Building the i2c slave

So this is the main part ! As exposed on first post, we're going to implement a finite state machine. jallib comes with a library where all the logic is already coded, in a ISR. You just have to define what to do for each state encountered during the program execution. To do this, we'll have to define several callbacks, that is procedures that will be called on appropriate state.

Before this, we need to setup and initialize our slave. i2c address should exactly be the same as the one defined in the master section. This time, we won't use interrrupts on Start/Stop signals; we'll just let the SSP module triggers an interrupts when the i2c address is recognized (no interrupts means address issue, or hardware problems, or...). Finally, since slave is expected to receive a char, and send char + 1, we need a global variable to store the results. This gives:

include i2c_hw_slave

const byte SLAVE_ADDRESS = 0x5C
i2c_hw_slave_init(SLAVE_ADDRESS)

-- will store what to send back to master
-- so if we get "a", we need to store "a" + 1
var byte data

Before this, let's try to understand how master will talk to the slave (red) and what the slave should do (green), according to each state (with code following):
  • state 1: master initiates a write operation (but does not send data yet). Since no data is sent, slave should just do... nothing (slave just knows someone wants to send data).
procedure i2c_hw_slave_on_state_1(byte in _trash) is
pragma inline
-- _trash is read from master, but it's a dummy data
-- usually (always ?) ignored
end procedure

  • state 2: master actually sends data, that is one character. Slave should get this char, and process it (char + 1) for further sending.
procedure i2c_hw_slave_on_state_2(byte in rcv) is
pragma inline
-- ultimate data processing... :)
data = rcv + 1
end procedure

  • state 3: master initiates a read operation, it wants to get the echo back. Slave should send its processed char.
procedure i2c_hw_slave_on_state_3() is
pragma inline
i2c_hw_slave_write_i2c(data)
end procedure

  • state 4: master still wants to read some information. This should never occur, since one char is sent and read at a time. Slave should thus produce an error.
procedure i2c_hw_slave_on_state_4() is
pragma inline
-- This shouldn't occur in our i2c echo example
i2c_hw_slave_on_error()
end procedure

  • state 5: master hangs up the connection. Slave should reset its state.
procedure i2c_hw_slave_on_state_5() is
pragma inline
data = 0
end procedure

Finally, we need to define a callback in case of error. You could do anything, like resetting the PIC, and sending log/debug data, etc... In our example, we'll blink forever:

procedure i2c_hw_slave_on_error() is
pragma inline
-- Just tell user user something's got wrong
forever loop
led = on
_usec_delay(200000)
led = off
_usec_delay(200000)
end loop
end procedure

Once callbacks are defined, we can include the famous ISR library.
include i2c_hw_slave_isr

So the sequence is:
  1. include i2c_hw_slave, and setup your slave
  2. define your callbacks,
  3. include the ISR


The full code is available from jallib's SVN repository (caution, not released yet, could be modified):


You'll also need jallib-pack from here. Copy the two libraries to the "lib" directory, compile the two samples and program two 16f88.

(Edit on 01/30: libraries and samples are now include in jallib's version > 0.1, no extra download needed !)

Connecting and testing the whole thing...

As previously said, the board I use is ready to be used with a serial link. It's also i2c ready, I've put the two pull-ups resistors. If your board doesn't have those resistors, you'll have to add them on the breadboard, or it won't work (read part 2 to know and see why...).

I use a connector adapted with a PCB to connect my main board with my breadboard. Connector's wires provide power supply, 5V-regulated, so no other powered wires it required.

Connector, with power wires


Everything is ready...


Crime scene: main board, breadboard
and battery pack


Once connected, power the whole and use a terminal to test it. When pressing "a", you'll get a "a" as an echo from the master, then "b" as result from the slave.



What now ?

We've seen how to implement a simple i2c hardware slave. The ISR library provides all the logic about the finite state machine. You just have to define callbacks, according to your need.

i2c is a widely used protocol. Most of the time, you access i2c devices, acting as a master. We've seen how to be on the other side, on the slave side. Being on the slave side means you can build modular boards, accessible with a standard protocol. For instance, I've build a DC motor controller daughter board using this. It's a module, a unit on its own, just plug, and send/receive data, with just two wires.

And I also plan to build a LCD controller board, but that's for another "Step by Step" post :)


Reading:




Sébastien Lelong

Saturday, January 17, 2009

Step by Step: building an i2c slave with jallib (part 2)

Part 2 : checking the hardware and the i2c bus...


Last time, we saw a basic overview of how to implement an i2c slave, using a finite state machine implementation. Today, we're going to get our hands a little dirty, and starts connecting our master/slave together.

First of all, i2c is quite hard to debug, especially if you don't own an oscilloscope (like me). So you have to be accurate and rigorous. That's why, in this second part of this tutorial, we're going to setup the hardware, and just make sure the i2c bus is properly operational.

Connecting two PIC together through i2c is quite easy from a hardware point of view. Just connect SDA and SCL together, and don't forget pull-ups resistors. There are many differents values for these resistors, depending on how long the bus is, or the speed you want to reach. Most people use 2.2K resistors, so let's do the same ! The following schematics is here to help:


In this circuit, both PIC have a LED connected, which will help us understand what's going on. On a breadboard, this looks like that:


The master is on the right side, the slave on the left. I've put the two pull-ups resistors near the master:


Green and orange wires connect the two PICs together:




The goal of this test is simple: check if the i2c bus is properly built and operational. How ? PIC 16F88 and its SSP peripheral is able to be configured so it triggers an interrupts when a Start or Stop signal is detected. Read this page (part of an nice article on i2c, from last post's recommandations).

How are we gonna test this ? The idea of this test is simple:

  1. On power, master will blink a LED a little, just to inform you it's alive
  2. On the same time, slave is doing the same
  3. Once master has done blinking, it sends a i2c frame through the bus
  4. If the bus is properly built and configured, slave will infinitely blink its LED, at high speed

Note master will send its i2c frame to a specific address, which don't necessarily need to be the same as the slave one (and I recommand to use different addresses, just to make sure you understand what's going on).

What about the sources ? Download last jallib pack, then go get some other libraries and samples (those files aren't release yet, API may change):


(Edit on 01/30: those libraries and samples are now included in jallib's version > 0.1. No extra download needed)


The main part of the slave code is the way the initialization is done. A constant is declared, telling the library to enable Start/Stop interrupts [edit on 01/30, API has changed, previously init was taking a true/false parameter]:


const SLAVE_ADDRESS = 0x23 -- whatever, it's not important, and can be
-- different from the address the master wants
-- to talk to
-- with Start/Stop interrupts
const bit i2c_enable_start_stop_interrupts = true
-- this init automatically sets global/peripherals interrupts
i2c_hw_slave_init(SLAVE_ADDRESS)


And, of course, the Interrupt Service Routine (ISR):

procedure i2c_isr() is
pragma interrupt
if ! PIR1_SSPIF then
return
end if
-- reset flag
PIR1_SSPIF = false
-- tmp store SSPSTAT
var byte tmpstat
tmpstat = SSPSTAT
-- check start signals
if (tmpstat == 0b_1000) then
-- If we get there, this means this is an SSP/I2C interrupts
-- and this means i2c bus is properly operational !!!
while true loop
led = on
_usec_delay(100000)
led = off
_usec_delay(100000)
end loop
end if
end procedure



The important thing is to:
  • check if interrupt is currently a SSP interrupts (I2C)
  • reset the interrupt flag,
  • analyze SSPSTAT to see if Start bit is detected
  • if so, blinks 'til the end of time (or your battery)


Now, go compile both samples, and program two PICs with them. With a correct i2c bus setting, you should see the following:



On this next video, I've removed the pull-ups resistors, and it doesn't work anymore (slave doesn't high speed blink its LED).





Next time (and last time on this topic), we'll see how to implement the state machine using jallib, defining callback for each states.

Reading:



Sébastien Lelong

Wednesday, January 14, 2009

Step by Step: building an i2c slave with jallib (part 1)

Part 1 : a few words before getting our hands dirty...

i2c is a nice protocol: it is quite fast, reliable, and most importantly, it's addressable. This means that on a single 2-wire bus, you'll be able to plug up to 128 devices using 7bits addresses, and even 1024 using 10bits address. Far enough for most usage... I won't cover i2c in depth, as there are plenty resources on the Web (and I personally like this page).

i2c is found in many chips and many modules. Most of the time, you create a master, like when accessing an EEPROM. This time, in this tutorial, we're going to build a slave, which will thus respond to master's requests.

The slave side is somewhat more difficult because, as it does not initiate the talk, it has to listen to "events", and be as responsive as possible. You've guessed, we'll use interrupts. I'll only cover i2c hardware slave, that is using SSP peripheral (note: some PICs have MSSP, this means they can also be used as i2c hardware Master). Implementing an i2c software slave may be very difficult (and I even wonder if it's reasonable...).

There are different way implementing an i2c slave, but one seems to be quite common: defining a finite state machine. This implementation is well described in Microchip AppNote AN734 . It is highly recommended that you read this appnote, and the i2c sections of your favorite PIC datasheet as well (I swear it's quite easy to read, and well explained).

Basically, during an i2c communication, there can be 5 distinct states:
  1. Master writes, and last byte was an address : to sum up, master wants to talk to a specific slave, identified by the address, it wants to send data (write)
  2. Master writes, and last byte was data : this time, master sends data to the slave
  3. Master read, and last byte was an address : almost the same as 1., but this time, master wants to read something from the salve
  4. Master read, and last byte was data : just the continuation of state 3., master has started to read data, and still wants to read more data
  5. Master sends a NACK : basically, master doesn't want to talk to the slave anymore, it hangs up...

Note: in i2c protocol, one slave has actually two distinct addresses. One is for read operations, and it ends with bit 1. Another is for write operations, and it ends with bit 0.

Ex: consider the following address (8-bits long, last bit is for operation type)

0x5C => 0b_0101_1100 => write operation

The same address for read operation will be:

0x93 => 0b_0101_1101 = read operation


[EDIT: jallib currently supports up to 128 devices on a i2c bus, using 7-bits long addresses (without the 8th R/W bits). There's currently no support for 10-bits addresses, which would give 1024 devices on the same bus. If you need it, please let us know, we'll modify libraries as needed !]

OK, enough for today. Next time, we'll see how two PICs must be connected for i2c communication, and we'll check the i2c bus is fully working, before diving into the implementation.


Reading:



Sébastien Lelong

Friday, December 12, 2008

i2c master

Do you want to read an i2c compass? Store data into an i2c eeprom? Jallib let your PIC act as an i2c master and provides a powerfull and clean interface. And it's easy to use.
After the blink example, follow the steps:

Hardware setup.
First you have to setup the i2c bus hardware.
The first thing to concider is if you can and want to use the MSSP to handle i2c (i2c hardware) or handle i2c in software.
The first option - i2c hardware - is possible for most (but not all) PICs with MSSP. The advantage is that this option is generally faster then software i2c. If you choose i2c hardware, you will use the pre-defined i2c clock and i2c data pins. (scl and sda).
With i2c in software, pick any free io pin for i2c clock and i2c data.

Now you have choosen the pins, you need to setup the hardware. Connect clock, data and ground from the PIC to the i2c slave, place pull-up resistors (e.g. 1k5) from both clock and data to the pic power supply and power the slave device. For more details on this, check the i2c slave datasheet or look on the Internet.

Software setup
Now you have the hardware ready, you need to configure the software.

First, define the pins used, for example on an 16F877a:
var volatile bit i2c_scl is pin_c3
var volatile bit i2c_scl_direction is pin_c3_direction
var volatile bit i2c_sda is pin_c4
var volatile bit i2c_sda_direction is pin_c4_direction

Second, define the next two constants:
const word _i2c_bus_speed = 1 ; * 100kHz
const bit _i2c_level = true ; i2c levels (not SMB)

And now you can include the 'level0' i2c library. This is the library that creates the i2c signals and comes in two flavors:

include i2c_software
i2c_initialize()

or

include i2c_hardware
i2c_initialize()

Both libraries have the same interface to send and receive bytes. You can build complex messages with this interface but in most cases, it is easier to use the level1 layer. To use this, define an array for transmit and an array for receive and include the library:

var byte i2c_tx_buffer[6]
var byte i2c_rx_buffer[10]
include i2c_level1


The size of the two buffers are the maximum size of a message you will send or receive. When a buffer is too short, you might get compile errors or - worse - unexpected behavior. When the buffers are too long, memory will be exhausted faster. When in doubt: enlarge the buffer a few bytes!

Read from an i2c eeprom
Now we are ready to communicate with the i2c slave. The format of the messages depend on the device, so you should consult the slave's documentation. There is an example below of communicating with an i2c eeprom (24lc256).
First thing to know of a slave is it's address. An i2c address is 7 bits and is stored in the higher 7 bits of a byte. The lowest bit is set to zero. The i2c eeprom address is 0xA0 (160 decimal).
Now we want to read data from the eeprom. Let's assume we want to start at internal (eeprom) location 1234 and need 3 bytes. From the 24lc256 datasheet, we learn that we have to write a 2-byte (= one word) location. Subsequently, we can read data. Or in JAL:

-- Send word location to device 0xA0 and read 3 bytes data
r = i2c_receive_wordaddr(0xA0, 1234, 3)

print_byte_hex(serial_hw_data, i2c_rx_buffer[0]);
serial_hw_data = " "
print_byte_hex(serial_hw_data, i2c_rx_buffer[1]);
serial_hw_data = " "
print_byte_hex(serial_hw_data, i2c_rx_buffer[2]);
print_crlf(serial_hw_data)

So only one call to i2c_receive_wordaddr to do the i2c write and read! This function takes 3 params: the i2c slave device address, the value of the 2-byte location code and the number of byte to read after the location code is sent.
The result is stored in i2c_rx_buffer[] and is printed on the serial port by the example. (Have a look at 'print_serial_numbers.jal' for more details on the use of serial comms.)
You might have notices the return value r in the sample above. This bit value is true if the operation was succesfull. If false, communication has failed. Add code to check the return value and handle errors in your program!

i2c_receive_byteaddr is a similar function which uses a 1-byte location code.

Arbitrary location code lengths can be sent by using i2c_send_receive. This function is used in the example below.

Write to an i2c eeprom
In most cases, you also want to write to the eeprom. This is how you write value 99 to location 2 of the eeprom:

-- write part (increment 3rd byte at 0x0002)
i2c_tx_buffer[0] = 0 -- high byte location in i2c eeprom
i2c_tx_buffer[1] = 2 -- low byte location in i2c eeprom
i2c_tx_buffer[2] = 99 -- data
r = i2c_send_receive(0xA0, 3, 0)

Function i2c_send_receive takes the slave address as the first parameter. The second one defines the number of bytes to be sent to the slave from i2c_tx_buffer. The third param defines the number of bytes to be received from the slave (and stored in i2c_rx_buffer). In the example above, we don't want to receive any information so the third param is 0 and i2c_rx_buffer is not used.

How to continue.
We've seen how to setup an i2c master, either using hardware or software. And we read data from an i2c eeprom and wrote a byte to it. The test-program test_i2c_sw_l1.jal, preceded by a board file like board_16f877a_dwarf.jal, provides a working example for the code shown. And in the near future, we intend to add samples of i2c code to jallib that are ready to compile. Adapt it to your specific slave and off you go!
And if you can't get it to work, spent 2 euro on an 24lc256 as a 'i2c reference device', so you can check if your PIC and it's program are working like they should.


Joep Suijs