program i2c_tester; // Timing settings #pragma CLOCK_FREQ 4000000 // Copyright(c)2000 David L. Jones Tronnort Technology // http://www.ozemail.com.au/~dljones // email:dljones@ozemail.com.au // converted to Pascal by M.Ursum // email: m.ursum@hetnet.nl // // - No bus arbitration ability, simple PIC=Master/Multiple Slave interface only // - No multi-master capability // - No timing ability, so I2C speed will depend on PIC clock rate // - The pull-up resistors on the IC2 pins are not enabled, you must do // it yourself if you want to avoid using external resistors // - make sure the I2C_init routine is run at least once before anything else // - These routines default to using RA0=SCL and RA1=SDA, change the define // statements below and the I2C_init routine to use other pins // - code is generic and can be compiled to any target PIC device without // modification // // ROUTINES: // I2C_start - Sets the I2C bus to the START condition // I2C_stop - Sets the I2C bus to the STOP condition // I2C_stable - Sets the I2C bus to the STABLE (IDLE) condition // char I2C_readbyte - returns a byte from the I2C bus // I2C_sendbyte - sends a byte down the I2C bus // // HOW TO TALK TO AN I2C DEVICE: // (generically, please refer to the chip datasheet as not all I2C chips work // this way) // remember to run I2C_init first!) // // To set a byte within the register of an I2C device: // I2C_stable // I2C_start // I2C_sendbyte(device address with WRITE bit set) // I2C_sendbyte(register address) // I2C_sendbyte(byte to write to register) // ...write subsequent sequential bytes (if any) // I2C_stop // To read the current register value from an I2C device: // I2C_stable // I2C_start // I2C_sendbyte(device address with READ bit set) // byte=I2C_readbyte // I2C_stop // To change the current register pointer in an I2C device: // I2C_stable // I2C_start // I2C_sendbyte(device address with WRITE bit set) // I2C_sendbyte(new pointer address) // I2C_stop // ******************************************************************* function set_scl; //RA0=1 SCL pin HIGH definition. Change to your own pin! begin output_high_port_a(0); end; // ******************************************************************* function clr_scl; // RA1=0 SCL pin LOW definition. Change to your own pin! begin output_low_port_a(0); end; // ******************************************************************* function set_sda; // TRISA1=1 SDA pin HIGH definition. begin set_bit( STATUS, 'RP0' ); // bank #1 set_bit(trisa,1); // bit Ra1 input clear_bit( STATUS, 'RP0' ); // bank #0 end; // Change to your own pin! // pin must be set to INPUT via TRIS because I2C data line is open collector // ******************************************************************* function clr_sda; // TRISA1=0 //SDA pin HIGH definition. begin set_bit( STATUS, 'RP0' ); // bank #1 clear_bit(trisa,1); // bit Ra1 output clear_bit( STATUS, 'RP0' ); // bank #0 output_low_port_a(1); end; // This macro only sets the SDA pin to an output, you still have to // manually ensure that the SDA pin PORT register is set LOW // This is doen by running the init_I2C routine */ // ******************************************************************** // The "start" routine sets the I2C bus to the START condition */ function I2C_start; begin set_scl; set_sda; clr_sda; clr_scl; end; // ******************************************************************* // The "stop" routine sets the I2C bus to the STOP condition function I2C_stop; begin clr_sda; set_scl; set_sda; set_scl; end; // ******************************************************************* // The "stable" routine sets the I2C bus to the STABLE condition */ function I2C_stable; begin set_sda; set_scl; end; // ******************************************************************* // "sendbyte" sends a byte down the I2C bus // The byte to be sent is passed to the routine via "sendbuf" */ procedure I2C_sendbyte(sendbuf:char); begin clr_scl; // set scl low initially */ // note:I2C requires MSBit to be sent first */ if ((sendbuf&128)=0) then clr_sda; else set_sda; // ouput bit7 */ set_scl; // toggle clock line */ clr_scl; if ((sendbuf&64)=0) then clr_sda; else set_sda; // ouput bit6 */ set_scl; // toggle clock line */ clr_scl; if ((sendbuf&32)=0) then clr_sda; else set_sda; // ouput bit5 */ set_scl; // toggle clock line */ clr_scl; if ((sendbuf&16)=0) then clr_sda; else set_sda; // ouput bit4 */ set_scl; // toggle clock line */ clr_scl; if ((sendbuf&8)=0) then clr_sda; else set_sda; // ouput bit3 */ set_scl; // toggle clock line */ clr_scl; if ((sendbuf&4)=0) then clr_sda; else set_sda; // ouput bit2 */ set_scl; // toggle clock line */ clr_scl; if ((sendbuf&2)=0) then clr_sda; else set_sda; // ouput bit1 */ set_scl; // toggle clock line */ clr_scl; if ((sendbuf&1)=0) then clr_sda; else set_sda; // ouput bit0 */ set_scl; // toggle clock line */ clr_scl; set_sda; set_scl; // allow SLAVE to return ACK but just ignore it */ clr_scl; end; // ******************************************************************* // "readbyte" reads a byte from the I2C bus // Function returns the byte received // NOTE: This routine has no timeout, so may get stuck in an infinite // loop if it does not get the correct data from the I2C port */ function I2C_readbyte:char; var byteread:char; begin byteread:=0; // assume the data will be 0 clr_scl; set_sda; // set SDA line to INPUT set_scl; if input_pin_port_a(1) then byteread:=byteread | 128; // get bit7 */ clr_scl; set_scl; if input_pin_port_a(1) then byteread:=byteread |64; // get bit6 */ clr_scl; set_scl; if input_pin_port_a(1) then byteread:=byteread |32; // get bit5 */ clr_scl; set_scl; if input_pin_port_a(1) then byteread:=byteread |16; // get bit4 */ clr_scl; set_scl; if input_pin_port_a(1) then byteread:=byteread |8; // get bit3 */ clr_scl; set_scl; if input_pin_port_a(1) then byteread:=byteread |4; // get bit2 */ clr_scl; set_scl; if input_pin_port_a(1) then byteread:=byteread |2; // get bit1 */ clr_scl; set_scl; if input_pin_port_a(1) then byteread:=byteread |1; // get bit0 */ clr_scl; I2C_readbyte:= byteread; // return the byte read from the bus */ end; // ******************************************************************* begin //main I2C_init set_bit( STATUS, 'RP0' ); // bank #1 set_tris_a( 0x00 ); // all outputs set_tris_b( 0x00 ); // all outputs clear_bit( STATUS, 'RP0' ); // bank #0 output_port_a(0); output_port_b(0); I2C_stable; I2C_start; I2C_sendbyte( 01000000b); // write to PCF8574P I2C_sendbyte( 0xFF); // all outputs high to use it for inputs I2C_stop; while 1 do begin I2C_start; I2C_sendbyte( 01000001b); // 8574P address with read bit set) output_port_b(I2C_readbyte); // copy to port B I2C_stop; end; end. // testroutine for output, configure PortB inputs! // I2C_start; // I2C_sendbyte( 01000000b); // 8574P address with write bit set) // I2C_sendbyte(0xFF ^ input_port_b); // copy port B to 8574 // I2C_stop;