An I2C bus driver in pseudo code

It is written in PseudoCode which is an imaginary programming language that any programmer should be capable of porting to his/her favorite language.

First we will define a set of basic interface routines. All text between / / is considered as remark.

Following variables are used :

  • n,x = a general purpose BYTE
  • SIZE = a byte holding the maximum number of transferred data at a time
  • DATA(SIZE) = an array holding up to SIZE number of bytes. This will contain the data we want to transmit and will store the received data.
  • BUFFER = a byte value holding immediate received or transmit data.

/ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ / / **** I2C Driver V1.1 Written by V.Himpe. Released as Public Domain **** / / $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ /

DECLARE N,SIZE,BUFFER,X Byte

DECLARE DATA() Array of SIZE elements

SUBroutine I2C_INIT / call this immediately after power-on /

    SDA=1

    SCK=0

    FOR n = 0 to 3

      CALL STOP

    NEXT n

ENDsub

SUBroutine START

    SCK=1 / BUGFIX !/

    SDA=1 / Improvement /

    SDA=0

    SCK=0

    SDA=1

ENDsub

SUBroutine STOP

    SDA=0

    SCK=1

    SDA=1

ENDsub

SUBroutine PUTBYTE(BUFFER)

    FOR n = 7 TO 0

      SDA= BIT(n) of BUFFER

      SCK=1

      SCK=0

    NEXT n

    SDA=1

ENDsub

SUBroutine GETBYTE

    FOR n = 7 to 0

      SCK=1

      BIT(n) OF BUFFER = SDA

      SCK=0

    NEXT n

    SDA=1

ENDsub

SUBroutine GIVEACK

    SDA=0

    SCK=1

    SCK=0

    SDA=1

ENDsub

SUBroutine GETACK

    SDA=1

    SCK=1

    WAITFOR SDA=0

    SCK=0

ENDSUB

/ this concludes the low-level set of instructions for the I2C driver

The next functions will handle the telegram formatting on a higher level /

SUBroutine READ(Device_address,Number_of_bytes)

    Device_adress=Device_adress OR (0000.0001)b /This sets the READ FLAG/

    CALL START

    CALL PUTBYTE(Device_adress)

    CALL GETACK

    FOR x = 0 to Number_of_bytes

      CALL GETBYTE DATA(x)=BUFFER /Copy received BYTE to DATA array /

      IF X< Number_of_bytes THEN /Not ack the last byte/

        CALL GIVEACK

      END IF

    NEXT x

    CALL STOP

ENDsub

SUBroutine WRITE(Device_address,Number_of_bytes)

    Device_adress=Device_adress AND (1111.1110)b / This clears READ flag /

    CALL START

    CALL PUTBYTE(Device_adress)

    CALL GETACK

    FOR x = 0 to Number_of_bytes

      CALL PUTBYTE (DATA(x))

      CALL GETACK

    NEXT x

    CALL STOP

ENDsub

SUBroutine RANDOMREAD(Device_adress,Start_adress,Number_of_bytes)

    Device_adress=Device_adress AND (1111.1110)b / This clears READ flag /

    CALL START

    CALL PUTBYTE(Device_adress)

    CALL GETACK

    CALL PUTBYTE(Start_adress)

    CALL GETACK

    CALL START /create a repeated start condition/

    Device_adress=Device_adress OR (0000.0001)b /This sets the READ FLAG/

    CALL PUTBYTE(Device_adress)

    CALL GETACK

    FOR x = 0 to Number_of_bytes

      CALL GETBYTE

      DATA(x)=BUFFER

      CALL GIVEACK

    NEXT x

    CALL STOP

ENDsub

SUBroutine RANDOMWRITE(Device_adress,Start_adress,Number_of_bytes)

    Device_adress=Device_adress AND (1111.1110)b / This clears READ flag /

    CALL START

    CALL PUTBYTE(Device_adress)

    CALL GETACK

    CALL PUTBYTE(Start_adress)

    CALL GETACK

    FOR x = 0 to Number_of_bytes

      CALL PUTBYTE (DATA(x))

      CALL GETACK

    NEXT x

    CALL STOP

ENDsub

/ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ /

/ **** I2C Driver . (c)95-97 V.Himpe . Public Domain release *** /

/ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ /

Some notes about the high level routines.

The READ and WRITE routine read / write one or more byte(s) from / to a slave device. Generally this will be used only with Number_of_bytes set to 1. An example:

    PCD8574=(0100.0000)b

    CALL READ(PCD8574,1)

    result = DATA(0)

    / will read the status of the 8 bit input port of a PCD8574. /

    DATA(0)=(0110.01010)b

    CALL WRITE(PCD8574,1)

    / will write 0110.0101 to the 8 bit port of the PCD8574 /

When do you need a multi-read ? Consider a PCF8582 EEPROM. You want to read its contents in one time.

    PCF8582=(1010.0000)b

    CALL READ(PCF8582,255)

You can do the same with WRITE for the EEPROM with the restriction that Number_of_bytes is not larger than 4 AND that you write on page boundaries (multiple of 4 for offset).

You will have to check the components datasheets.

The most useful instructions are RANDOMREAD and RANDOMWRITE.

Write 4 bytes of data to location 20h of the EEPROM:

    DATA(0)=(1010.0011)b

    DATA(1)=(1110.0000)b

    DATA(2)=(0000.1100)b

    DATA(3)=(1111.0000)b

    CALL RANDOMWRITE (PCF8582,(20)h,3)

The same goes for reading 16 bytes from the EEPROM starting at address 42h:

    CALL RANDOMREAD(PCF8582,(42)h,15)

The results are stored in DATA. All you have to do is read them out of the array for processing. When you give the devices address to these routines you don't have to care about the R/W flag. It will be automatically set to the right state inside the routines.

Back to I2C Index

ESAcademy, 2000

All materials
provided 'as is'
see Disclaimer

www.esacademy.com
info@esacademy.com