Proton BASIC Compiler - Software (Bit-Banged) I2C library for Proton


  • PicŪ Basic


  • Software (Bit-Banged) I2C library for Proton

    Here is a standard I2C library that uses bit-bashing. i.e. Software. It can easily be changed if LSB is required instead of MSB for the I2C interface by changing the bit examined and the bit written in the I2C_ReadByte and I2C_WriteByte procedures:

    The library code is here. Make a file named "I2C_Software.inc" from it:

    Code:
    $ifndef _I2C_INC_
    $define _I2C_INC_
    '
    '   /\\\\\\\\\
    '  /\\\///////\\\
    '  \/\\\     \/\\\                                                 /\\\          /\\\
    '   \/\\\\\\\\\\\/        /\\\\\     /\\\\\\\\\\     /\\\\\\\\   /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
    '    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
    '     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\     \/\\\         \/\\\        /\\\\\\\\\\
    '      \/\\\     \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\     \/\\\ /\\   /\\\/////\\\
    '       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\   \//\\\\\\\\/\\
    '        \///        \///     \/////     \//////////    \//////////      \/////        \/////     \////////\//
    '                                  Let's find out together what makes a PIC Tick!
    '
    ' Software I2C writing and reading routines
    ' Written for the Proton BASIC compiler by Les Johnson
    '
    ' Setup the default pins for SDA and SCL
    '
    $ifndef I2C_SDA_Pin
        $define I2C_SDA_Pin PORTC.4
    $endif
    
    $ifndef I2C_SCL_Pin
        $define I2C_SCL_Pin PORTC.3
    $endif
    
    '----------------------------------------------------------------------------------------------
    ' Delay a fixed number of microseconds
    '
    $define I2C_hDelay() DelayUS 2
    
    '----------------------------------------------------------------------------------------------
    ' Send a single byte to the I2C bus
    ' Input     : pData holds the byte to write
    ' Output    : None
    ' Notes     : MSB first
    '
    Proc I2C_WriteByte(pData As Byte)
    
        Dim bIndex As Byte = 8
        Repeat                              ' Create a loop for the 8-bits
            If pData.7 = 1 Then
                Input I2C_SDA_Pin           ' Make the SDA pin an Input (High)
            Else
                Output I2C_SDA_Pin          ' Make the SDA pin an Output (Low)
            EndIf
            I2C_hDelay()
            Input I2C_SCL_Pin               ' Make the SCL pin an Input (High)
            I2C_hDelay()
            Output I2C_SCL_Pin              ' Make the SCL pin an Output (Low)
            I2C_hDelay()
            pData = pData << 1
            Dec bIndex
        Until bIndex = 0
    '
    ' Close up
    '
        Input I2C_SDA_Pin                   ' Make the SDA pin an Input (High)
        Input I2C_SCL_Pin                   ' Make the SCL pin an Input (High)
        I2C_hDelay()
        Output I2C_SCL_Pin                  ' Make the SCL pin an Output (Low)
        I2C_hDelay()
    EndProc
    
    '----------------------------------------------------------------------------------------------
    ' Read a single byte from the I2C bus
    ' Input     : None
    ' Output    : Returns the byte received
    ' Notes     : MSB first, sample before clock
    '
    Proc I2C_ReadByte(), Byte
        
        Dim bIndex As Byte = 8
        Result = 0
        Input I2C_SDA_Pin                   ' Make the SDA pin an Input (High)
        Repeat                              ' Create a loop for the 8-bits
            Result = Result << 1
            '
            ' Wait for any clock stretching (This may benefit from a timeout being added)
            '
            Repeat                          ' Create a loop
                Input I2C_SCL_Pin           ' Make the SCL pin an Input
            Until I2C_SCL_Pin = 1           ' Until the SCL pin is high
            '
            ' Read the data
            '
            I2C_hDelay()
            Result.0 = I2C_SDA_Pin
            Output I2C_SCL_Pin              ' Make the SCL pin an Output (Low)
            I2C_hDelay()
            Dec bIndex
        Until bIndex = 0
    EndProc
    
    '----------------------------------------------------------------------------------------------
    ' Send an I2C bus start condition
    ' Input     : None
    ' Output    : None
    ' Notes     : A Start condition is high to low of SDA when SCL is high
    '
    Proc I2C_Start()
        Input I2C_SDA_Pin                   ' Make the SDA pin an Input (High)
        I2C_hDelay()
        Input I2C_SCL_Pin                   ' Make the SCL pin an Input (High)
        I2C_hDelay()
        Output I2C_SDA_Pin                  ' Make the SDA pin an Output (Low)
        I2C_hDelay()
        Output I2C_SCL_Pin                  ' Make the SCL pin an Output (Low)
        I2C_hDelay()
    EndProc
    
    '----------------------------------------------------------------------------------------------
    ' Send an I2C bus restart condition
    ' Input     : None
    ' Output    : None
    ' Notes     : None
    '
    $define I2C_Restart() I2C_Start()
    
    '----------------------------------------------------------------------------------------------
    ' Send an I2C bus stop condition
    ' Input     : None
    ' Output    : None
    ' Notes     : A Stop condition is low to high of SDA when SCL is high
    '
    Proc I2C_Stop()
        Output I2C_SDA_Pin                  ' Make the SDA pin an Output (Low)
        I2C_hDelay()
        Input I2C_SCL_Pin                   ' Make the SCL pin an Input (High)
        I2C_hDelay()
        Input I2C_SDA_Pin                   ' Make the SDA pin an Input (High)
        I2C_hDelay()
    EndProc
    
    '----------------------------------------------------------------------------------------------
    ' Initiate an I2C acknowledge
    ' Input     : None
    ' Output    : None
    ' Notes     : None
    '
    Proc I2C_Ack()
        I2C_hDelay()
        Output I2C_SDA_Pin                  ' Make the SDA pin an Output (Low)
        I2C_hDelay()
        Input I2C_SCL_Pin                   ' Make the SCL pin an Input (High)
        I2C_hDelay()
        Output I2C_SCL_Pin                  ' Make the SCL pin an Output (Low)
        I2C_hDelay()
        Input I2C_SDA_Pin                   ' Make the SDA pin an Input (High)
    EndProc
    
    '----------------------------------------------------------------------------------------------
    ' Initiate an I2C Not acknowledge
    ' Input     : None
    ' Output    : None
    ' Notes     : None
    '
    Proc I2C_NAck()
        Input I2C_SDA_Pin                   ' Make the SDA pin an Input (High)
        I2C_hDelay()
        Input I2C_SCL_Pin                   ' Make the SCL pin an Input (High)
        I2C_hDelay()
        Output I2C_SCL_Pin                  ' Make the SCL pin an Output (Low)
        I2C_hDelay()
        Input I2C_SDA_Pin                   ' Make the SDA pin an Input (High)
    EndProc
    
    '----------------------------------------------------------------------------------------------
    ' Startup section
    '
    _I2C_Main_:
        Low I2C_SDA_Pin                   ' Make the I2C SDA pin an output low
        Low I2C_SCL_Pin                   ' Make the I2C SCL pin an output low
    
    $endif
    Here's a demo runing the above library to write and read to and from an I2C eeprom:
    Code:
    '
    '   /\\\\\\\\\
    '  /\\\///////\\\
    '  \/\\\     \/\\\                                                 /\\\          /\\\
    '   \/\\\\\\\\\\\/        /\\\\\     /\\\\\\\\\\     /\\\\\\\\   /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
    '    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
    '     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\     \/\\\         \/\\\        /\\\\\\\\\\
    '      \/\\\     \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\     \/\\\ /\\   /\\\/////\\\
    '       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\   \//\\\\\\\\/\\
    '        \///        \///     \/////     \//////////    \//////////      \/////        \/////     \////////\//
    '                                  Let's find out together what makes a PIC Tick!
    '
    ' Write and read an I2C eeprom using the software I2C library routines
    ' Written for the Proton BASIC compiler by Les Johnson
    '    
        Include "Amicus18.inc"                  ' Setup the program to run on an Amicus18 board(PIC18F25K20 at 64MHz)
    
    $define I2C_SDA_Pin PORTC.4                 ' Set the SDA pin for the I2C library
    $define I2C_SCL_Pin PORTC.3                 ' Set the SCL pin for the I2C library
        Include "I2C_Software.inc"              ' Load the software I2C library routines
    '
    ' Create variables for the demo
    '
        Dim MyLoop As Byte
        Dim MyByte As Byte
        
    '---------------------------------------------------------------------------------------------
    Main:  
    '
    ' Transmit bytes to the I2C eeprom
    '
        I2C_Start()                             ' Send a Start condition
        I2C_WriteByte($A0)                      ' Target an eeprom, and send a Write command
        I2C_WriteByte(0)                        ' Send the High Byte of the address
        I2C_WriteByte(0)                        ' Send the Low Byte of the address
        For MyLoop = 48 To 58                   ' Create a loop containing ASCII 0 to 9
          I2C_WriteByte(MyLoop)                 ' Send the value of Loop to the eeprom
        Next                                    ' Close the loop
        I2C_Stop()                              ' Send a Stop condition
        DelayMS 10                              ' Wait for the data to be entered into eeprom matrix
    '
    ' Receive bytes from the I2C eeprom and display them on a serial terminal
    '
        I2C_Start()                             ' Send a Start condition
        I2C_WriteByte($A0)                      ' Target an eeprom, and send a Write command
        I2C_WriteByte(0)                        ' Send the High Byte of the address
        I2C_WriteByte(0)                        ' Send the Low Byte of the address
        I2C_Restart()                           ' Send a Restart condition
        I2C_WriteByte($A1)                      ' Target an eeprom, and send a Read command
        For MyLoop = 0 To 9                     ' Create a loop
            MyByte = I2C_ReadByte()             ' Load MyByte with byte received
            If MyLoop = 9 Then                  ' Are we at the end of the reads?
                I2C_Stop()                      ' Yes. So send a stop command
            Else                                ' Otherwise...
                I2C_Ack()                       ' Send an Ack command
            EndIf
            HRSOutLn Dec MyByte                 ' Display MyByte on the serial terminal
        Next                                    ' Close the loop
    Hopefully, the above routines show how flexible and useful Procedures are in the Proton compiler. Because they are not mandatory as in other procedural languages, where everything has to be a procedure, but can be used with the standard BASIC syntax and commands, so they offer a whole load more flexability. This makes code far more easy to read and write, unlike the vast majority of procedure only languages.
    This article was originally published in forum thread: Bit bang I2C code started by RGV250 View original post
  • Recent Activity

    John Drew-26

    SMS Number Save

    Thread Starter: bravo

    Hi Folks, I need some help with a GSM problem, which seems to be to do with Cntrl Z. But may not be. I am trying to text a number to a GSM module (...

    John Drew Today, 01:22 Go to last post
    davroski-65267

    Serin baud rate / clock dependency?

    Thread Starter: davroski

    Can anyone help regarding the range of baud rates I should expect to be able to set with a 32MHz clock? I am aware of the baudmode calculation...

    davroski Yesterday, 22:05 Go to last post
    top204-15

    Problem with 3.7.2.8 and 18F25K50

    Thread Starter: towlerg

    I have not raised an anomaly report because it seems very unfair to expect someone (Les) to hunt down bugs for free. If I compile code,...

    top204 Yesterday, 20:01 Go to last post