I2C Driver in Psudocode

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 :

  1. / $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ /
  2. / **** I2C Driver V1.1 Written by V.Himpe. Released as Public Domain **** /
  3. / $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ /
  4. DECLARE N,SIZE,BUFFER,X Byte
  5. DECLARE DATA() Array of SIZE elements
  6. SUBroutine I2C_INIT / call this immediately after power-on /
  7.       SDA=1
  8.       SCK=0
  9.       FOR n = 0 to 3
  10.             CALL STOP
  11.       NEXT n
  12. ENDsub
  13. SUBroutine START
  14.       SCK=1 / BUGFIX !/
  15.       SDA=1 / Improvement /
  16.       SDA=0
  17.       SCK=0
  18.       SDA=1
  19. ENDsub
  20. SUBroutine STOP
  21.       SDA=0
  22.       SCK=1
  23.       SDA=1
  24. ENDsub
  25. SUBroutine PUTBYTE(BUFFER)
  26.       FOR n = 7 TO 0
  27.             SDA= BIT(n) of BUFFER
  28.             SCK=1
  29.             SCK=0
  30.       NEXT n
  31.       SDA=1
  32. ENDsub
  33. SUBroutine GETBYTE
  34.       FOR n = 7 to 0
  35.             SCK=1
  36.             BIT(n) OF BUFFER = SDA
  37.             SCK=0
  38.       NEXT n
  39.       SDA=1
  40. ENDsub
  41. SUBroutine GIVEACK
  42.       SDA=0
  43.       SCK=1
  44.       SCK=0
  45.       SDA=1
  46. ENDsub
  47. SUBroutine GETACK
  48.       SDA=1
  49.       SCK=1
  50.       WAITFOR SDA=0
  51.       SCK=0
  52. ENDSUB
  53. / this concludes the low-level set of instructions for the I2C driver
  54. The next functions will handle the telegram formatting on a higher level /
  55. SUBroutine READ(Device_address,Number_of_bytes)
  56.       Device_adress=Device_adress OR (0000.0001)b /This sets the READ FLAG/
  57.       CALL START
  58.       CALL PUTBYTE(Device_adress)
  59.       CALL GETACK
  60.       FOR x = 0 to Number_of_bytes
  61.             CALL GETBYTE DATA(x)=BUFFER /Copy received BYTE to DATA array /
  62.             IF X< Number_of_bytes THEN /Not ack the last byte/
  63.                   CALL GIVEACK
  64.             END IF
  65.       NEXT x
  66.       CALL STOP
  67. ENDsub
  68. SUBroutine WRITE(Device_address,Number_of_bytes)
  69.       Device_adress=Device_adress AND (1111.1110)b / This clears READ flag /
  70.       CALL START
  71.       CALL PUTBYTE(Device_adress)
  72.       CALL GETACK
  73.       FOR x = 0 to Number_of_bytes
  74.             CALL PUTBYTE (DATA(x))
  75.             CALL GETACK
  76.       NEXT x
  77.       CALL STOP
  78. ENDsub
  79. SUBroutine RANDOMREAD(Device_adress,Start_adress,Number_of_bytes)
  80.       Device_adress=Device_adress AND (1111.1110)b / This clears READ flag /
  81.       CALL START
  82.       CALL PUTBYTE(Device_adress)
  83.       CALL GETACK
  84.       CALL PUTBYTE(Start_adress)
  85.       CALL GETACK
  86.       CALL START /create a repeated start condition/
  87.       Device_adress=Device_adress OR (0000.0001)b /This sets the READ FLAG/
  88.       CALL PUTBYTE(Device_adress)
  89.       CALL GETACK
  90.       FOR x = 0 to Number_of_bytes
  91.             CALL GETBYTE
  92.             DATA(x)=BUFFER
  93.             CALL GIVEACK
  94.       NEXT x
  95.       CALL STOP
  96. ENDsub
  97. SUBroutine RANDOMWRITE(Device_adress,Start_adress,Number_of_bytes)
  98.       Device_adress=Device_adress AND (1111.1110)b / This clears READ flag /
  99.       CALL START
  100.       CALL PUTBYTE(Device_adress)
  101.       CALL GETACK
  102.       CALL PUTBYTE(Start_adress)
  103.       CALL GETACK
  104.       FOR x = 0 to Number_of_bytes
  105.             CALL PUTBYTE (DATA(x))
  106.             CALL GETACK
  107.       NEXT x
  108.       CALL STOP
  109. ENDsub
  110. / $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ /
  111. / **** I2C Driver . (c)95-97 V.Himpe . Public Domain release *** /
  112. / $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ /

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:

  1. PCD8574=(0100.0000)b
  2. CALL READ(PCD8574,1)
  3. result = DATA(0)
  4. / will read the status of the 8 bit input port of a PCD8574. /
  5. DATA(0)=(0110.01010)b
  6. CALL WRITE(PCD8574,1)
  7. / 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.

  1. PCF8582=(1010.0000)b
  2. 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:

  1. DATA(0)=(1010.0011)b
  2. DATA(1)=(1110.0000)b
  3. DATA(2)=(0000.1100)b
  4. DATA(3)=(1111.0000)b
  5. CALL RANDOMWRITE (PCF8582,(20)h,3)

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

  1. 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.

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 /

  •  

      CALL STOP

  • SDA=1

    SCK=0

    FOR n = 0 to 3

    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)

  •  

      SDA= BIT(n) of BUFFER

      SCK=1

      SCK=0

  • FOR n = 7 TO 0

    NEXT n

    SDA=1

ENDsub

SUBroutine GETBYTE

  •  

      SCK=1

      BIT(n) OF BUFFER = SDA

      SCK=0

  • FOR n = 7 to 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)

  •  
    •  

        CALL GIVEACK

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

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

      END IF

  • 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

    NEXT x

    CALL STOP

ENDsub

SUBroutine WRITE(Device_address,Number_of_bytes)

  •  

      CALL PUTBYTE (DATA(x))

      CALL GETACK

  • 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

    NEXT x

    CALL STOP

ENDsub

SUBroutine RANDOMREAD(Device_adress,Start_adress,Number_of_bytes)

  •  

      CALL GETBYTE

      DATA(x)=BUFFER

      CALL GIVEACK

  • 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

    NEXT x

    CALL STOP

ENDsub

SUBroutine RANDOMWRITE(Device_adress,Start_adress,Number_of_bytes)

  •  

      CALL PUTBYTE (DATA(x))

      CALL GETACK

  • 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

    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.