News:

Let's find out together what makes a PIC Tick!

Main Menu

2 x I2C?

Started by Peter Truman, Jan 02, 2025, 02:44 AM

Previous topic - Next topic

Peter Truman

Hi all - is it possible to have 2 I2C busses in Proton?

TimB


I have some routines that enable 2 x i2c channels.

I will dig out the code a bit later

Tim

TimB



This is the i2c routine


$ifndef _Software_I2C_Fixed_Pins_inc_
$define _Software_I2C_Fixed_Pins_inc_
'
'   /\\\\\\\\\
'  /\\\///////\\\
'  \/\\\     \/\\\                                                 /\\\          /\\\
'   \/\\\\\\\\\\\/        /\\\\\     /\\\\\\\\\\     /\\\\\\\\   /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
'    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
'     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\     \/\\\         \/\\\        /\\\\\\\\\\
'      \/\\\     \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\     \/\\\ /\\   /\\\/////\\\
'       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\   \//\\\\\\\\/\\
'        \///        \///     \/////     \//////////    \//////////      \/////        \/////     \////////\//
'                                  Let's find out together what makes a PIC Tick!
'
' Software I2C writing and reading routines using fixed pins for the SDA and SCL lines.
'
' Written for the Positron8 and Positron16 BASIC compilers by Les Johnson
'
' Set the pins used for the I2C SDA and SCL lines
'
'    Symbol I2C_SDA_Pin = PORTC.3                ' Holds the pin used for the SDA line
'    Symbol I2C_SCL_Pin = PORTC.4                ' Holds the pin used for the SCL line
'
' Create some global variables for the routines
'
    Dim I2C_bBitIndex As Byte Access            ' Holds the index for the bits to send/receive on the I2C line
    Dim I2C_bData     As Byte Access            ' Holds the byte sent or received
    Dim bTimeout      As Byte Access
'----------------------------------------------------------------------------------------------
' Create a very small delay to slow down the I2C interface (if required)
' Input     : None
' Output    : None
' Notes     : None
'
$define I2C_hDelayL() Delayus 2

$define I2C_hDelayH() Delayus 3
'----------------------------------------------------------------------------------------------
' Write a byte to the I2C bus
' Input     : pData holds the byte to write
' Output    : None
' Notes     : MSB first
'

Proc I2C_WriteByte(pData As I2C_bData)

    For I2C_bBitIndex = 7 DownTo 0              ' Create a loop for the 8-bits to write
        If pData.7 = 1 Then                     ' Is bit-7 of pData set?
            PinInput I2C_SDA_Pin                ' Yes. So make the SDA pin an input (made high by the pull-up resistor)
        Else                                    ' Otherwise...
            PinLow I2C_SDA_Pin                  ' Make the SDA pin an output low
        EndIf                                   '
        I2C_hDelayLL()                            ' Create a small delay
        PinInput I2C_SCL_Pin                    ' Make the SCL pin an input (made high by the pull-up resistor)
        I2C_hDelayH()                            ' Create a small delay
        PinLow I2C_SCL_Pin                      ' Make the SCL pin an output low
        I2C_hDelayL()                            ' Create a small delay
        Rol pData                               ' Rotate pData left by 1 bit
    Next
'
' Close up the interface
'
    PinInput I2C_SCL_Pin                    ' Make the SCL pin an input (made high by the pull-up resistor)
    bTimeout = $FF                        ' \
    Repeat                                  ' / Create a loop for the clock stretching
        If I2C_SCL_Pin = 1 Then Break       ' Exit the loop when the SCL pin is high
        Dec bTimeout                        ' \ Until the timeout value reaches 0
    Until bTimeout = 0                      ' /

    PinInput I2C_SDA_Pin                        ' Make the SDA pin an input (made high by the pull-up resistor)
    PinInput I2C_SCL_Pin                        ' Make the SCL pin an input (made high by the pull-up resistor)
    I2C_hDelayH()                                ' Create a small delay
    PinLow I2C_SCL_Pin                          ' Make the SCL pin an output low
    I2C_hDelayL()                                ' Create a small delay
EndProc

'----------------------------------------------------------------------------------------------
' Read a byte from the I2C bus
' Input     : None
' Output    : Returns the byte received
' Notes     : MSB first
'
Proc I2C_ReadByte(), I2C_bData

    Result = 0                                  ' Clear the result of the procedure before entering the loop
    PinInput I2C_SDA_Pin                        ' Make the SDA pin an input (made high by the pull-up resistor)
    For I2C_bBitIndex = 7 DownTo 0              ' Create a loop for the 8-bits to read
        Result = Result << 1                    ' Shift the result value left by 1 bit
        '
        ' Wait for any clock stretching required, with a small timeout
        '
        PinInput I2C_SCL_Pin                    ' Make the SCL pin an input (made high by the pull-up resistor)
        bTimeout = $FF                        ' \
        Repeat                                  ' / Create a loop for the clock stretching
            If I2C_SCL_Pin = 1 Then Break       ' Exit the loop when the SCL pin is high
            Dec bTimeout                        ' \ Until the timeout value reaches 0
        Until BTimeout = 0                      ' /
        '
        ' Read the data
        '
        I2C_hDelayL()                            ' Create a small delay
        Result.0 = I2C_SDA_Pin                  ' Read the state of the SDA pin into bit-0 of the result
        PinLow I2C_SCL_Pin                      ' Make the SCL pin an output low
        I2C_hDelayL()                            ' Create a small delay
    Next
EndProc

'----------------------------------------------------------------------------------------------
' Send an I2C bus Start condition
' Input     : None
' Output    : None
' Notes     : None
'
Proc I2C_Start()
    PinInput I2C_SDA_Pin                        ' Make the SDA pin an input (made high by the pull-up resistor)
    I2C_hDelayH()                                ' Create a small delay
    PinInput I2C_SCL_Pin                        ' Make the SCL pin an input (made high by the pull-up resistor)
    I2C_hDelayH()                                ' Create a small delay
    PinLow I2C_SDA_Pin                          ' Make the SDA pin an output low
    I2C_hDelayL()                                ' Create a small delay
    PinLow I2C_SCL_Pin                          ' Make the SCL pin an output low
    I2C_hDelayL()
EndProc

'----------------------------------------------------------------------------------------------
' Send an I2C bus ReStart condition
' Input     : None
' Output    : None
' Notes     : None
'
Proc I2C_ReStart()
    PinInput I2C_SDA_Pin                        ' Make the SDA pin an input (made high by the pull-up resistor)
    I2C_hDelayH()                                ' Create a small delay
    PinInput I2C_SCL_Pin                        ' Make the SCL pin an input (made high by the pull-up resistor)
    I2C_hDelayH()                                ' Create a small delay
    PinLow I2C_SDA_Pin                          ' Make the SDA pin an output low
    I2C_hDelayL()                                ' Create a small delay
    PinLow I2C_SCL_Pin                          ' Make the SCL pin an output low
    I2C_hDelayL()
EndProc

'----------------------------------------------------------------------------------------------
' Send an I2C bus Stop condition
' Input     : None
' Output    : None
' Notes     : None
'
Proc I2C_Stop()
    PinLow I2C_SDA_Pin                          ' Make the SDA pin an output low
    I2C_hDelayL()                                ' Create a small delay
    PinInput I2C_SCL_Pin                        ' Make the SCL pin an input (made high by the pull-up resistor)
    I2C_hDelayH()                                ' Create a small delay
    PinInput I2C_SDA_Pin                        ' Make the SDA pin an input (made high by the pull-up resistor)
    I2C_hDelayH()
EndProc

'----------------------------------------------------------------------------------------------
' Initiate an I2C Acknowledge
' Input     : None
' Output    : None
' Notes     : None
'
Proc I2C_Ack()
    I2C_hDelayL()
    PinLow I2C_SDA_Pin                          ' Make the SDA pin an output low
    I2C_hDelayL()                                ' Create a small delay
    PinInput I2C_SCL_Pin                        ' Make the SCL pin an input (made high by the pull-up resistor)
    I2C_hDelayH()                                ' Create a small delay
    PinLow I2C_SCL_Pin                          ' Make the SCL pin an output low
    I2C_hDelayL()                                ' Create a small delay
    PinInput I2C_SDA_Pin                        ' Make the SDA pin an input (made high by the pull-up resistor)
EndProc

'----------------------------------------------------------------------------------------------
' Initiate an I2C Not Acknowledge
' Input     : None
' Output    : None
' Notes     : None
'
Proc I2C_NAck()
    PinInput I2C_SDA_Pin                        ' Make the SDA pin an input (made high by the pull-up resistor)
    I2C_hDelayH()                                ' Create a small delay
    PinInput I2C_SCL_Pin                        ' Make the SCL pin an input (made high by the pull-up resistor)
    I2C_hDelayH()                                ' Create a small delay
    PinLow I2C_SCL_Pin                          ' Make the SCL pin an output low
    I2C_hDelayL()                                ' Create a small delay
    PinInput I2C_SDA_Pin                        ' Make the SDA pin an input (made high by the pull-up resistor)
EndProc

$endif      ' _Software_I2C_Fixed_Pins_inc_


And this is it in use on 2 channels


Sorry it's not really obvious but I'm just cut/pasting my code

'*******************************************************************************
'* Title  :  ReadPressures
'* Input  :  None
'* Notes  :  Reads both pressure sensors and places the result in a ring buffer
'*******************************************************************************
    Proc ReadPressures()

        Dim wTemp1 As Word


        xP1Error = cFalse
        xP2Error = cFalse

        EnablePressurei2c1()

        If bPressureBufferIndex >= cPressureBufferSize Then                       ' Check the pointer is in bounds
          bPressureBufferIndex = 0
        EndIf


        ' Read Pressure sensor 1
        I2C_Start()
        I2C_WriteByte(cPSensorI2CAddress)
        delayus 2
        wTemp1.Byte1 = I2C_ReadByte()
        I2C_Ack()
        wTemp1.Byte0 = I2C_ReadByte()
        I2C_NAck()
        I2C_Stop()

        ' Test result to see if there was any responce and flag an error if so
        if wTemp1 = $FFFF then
            xP1Error = cTrue
            wTemp1 = 0
        Endif

        Wtemp1.15 = 0                                                           ' There are error flags that have to be masked
        Wtemp1.14 = 0

        awPressureBuffer1[bPressureBufferIndex] = wTemp1                    ' Save to the ring buffer

        DisablePressurei2c1()


        EnablePressurei2c2()                                                    ' Swap over to the other pressure sensor

        ' Read Pressure sensor 2
        I2C_Start()
        I2C_WriteByte(cPSensorI2CAddress)
        delayus 2
        wTemp1.Byte1 = I2C_ReadByte()
        I2C_Ack()
        wTemp1.Byte0 = I2C_ReadByte()
        I2C_NAck()
        I2C_Stop()

        ' Test result to see if there was any responce and flag an error if so
        if wTemp1 = $FFFF then
            xP2Error = cTrue
            wTemp1 = 0
        Endif

        Wtemp1.15 = 0                                                           ' There are error flags that have to be masked
        Wtemp1.14 = 0

        awPressureBuffer2[bPressureBufferIndex] = wTemp1                        ' Save to the ring buffer

        DisablePressurei2c2()


        Inc bPressureBufferIndex                                                ' Move to the next place in the ring buffer


    EndProc
                 

TimB



Looking at my code and remembering what I did its come back to me that the way I handled it in this code was to have hardware buffers on the i2c lines and enabled and disabled them to get the 2 i2c ports

I do have a 2 channel code I will dig it out

It was written as you see by Les

 

Frizie

If it can be a software variant then why not use I2CIn and I2Cout?
I have I²C components on various ports and that works fine with these Positron commands.
Ohm sweet Ohm | www.picbasic.nl

trastikata

Quote from: Peter Truman on Jan 02, 2025, 02:44 AMHi all - is it possible to have 2 I2C busses in Proton?

Hi Peter, are you talking about Hardware or Software I2C? Both are possible.

Peter Truman

Thanks everyone - I2Cin and I2C out look like the simplest way of doing this - It looks like the software implementation is the easiest (I need simple - bit thick!) - I haven't designed the hardware yet so I'm not clear what I need, just as long as I know it's possible, I guess I can use any standard I/O pin for either hardware or software? - the application doesn't need to run a lightening speed or anything.

You guys rock and I very much appreciate the support from the forum generally!