News:

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

Main Menu

Options for BUSOUT to two busses

Started by SCV, Dec 19, 2023, 04:36 PM

Previous topic - Next topic

SCV

Hi everyone,

Is there a way to use Busin etc on two seperate I2c busses (Busin2?) or are the SDA/SCL pins set by the defines at compile?
I'm struggling with the hardware modules (I2c1) & I2c2) on dsPIC33EP512MC806 locking up occasionally holding SDA low. Software Busin works fine, but I can only use one bus!


Thanks in advance,
Tim.

trastikata

Hi,

I've been using both hardware I2C modules and had no problem. Look in the manual for HSDA2_Pin, HSCL2_Pin, HbStart2,  HbStop2 etc.

SCV

I am directly manipulating the hardware modules. After a while (hours or days), SDA gets held low by the master (dsPIC). Hoping I can capture the activity for analysis and in the meantime, looking for options until I can trace the problem.

JonW

#3
Les did a dynamic I2C library where you can change the I2C pins at runtime.  I cant find the post but have the library here.  You pass the pins in the I2C_Start routine and release them in the I2C_Stop Proc.  May help you out short term

$ifndef _Software_I2C_inc_
$define _Software_I2C_inc_
'
'   /\\\\\\\\\
'  /\\\///////\\\
'  \/\\\     \/\\\                                                 /\\\          /\\\
'   \/\\\\\\\\\\\/        /\\\\\     /\\\\\\\\\\     /\\\\\\\\   /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
'    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
'     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\     \/\\\         \/\\\        /\\\\\\\\\\
'      \/\\\     \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\     \/\\\ /\\   /\\\/////\\\
'       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\   \//\\\\\\\\/\\
'        \///        \///     \/////     \//////////    \//////////      \/////        \/////     \////////\//
'                                  Let's find out together what makes a PIC Tick!
'
' Software I2C writing and reading routines with 'dynamic' Pins. The I2C routines use pins that can be changed at run time.
' This means other I2C units on other pins can be used by the library's procedures. Much the same as the compiler's I2Cin and I2Cout commands.
'
' Written for the Positron8 and Positron16 BASIC compilers by Les Johnson'
'-------------------------------------------------------------------------------------------------------------------------------------------------
'NOTES

(*With the library, the only procedure that needs the pins designated is I2C_Start,
then the other procedures use the same pins for SDA and SCL.
It was created this way so that it saves on flash memory and time,
because the pins do not need to be designated for every I2C function,
and every I2C function starts with a START, so the pins remain the same until STOP. :-)

However, the library could easily be changed.
To make it more efficient and faster to operate,
designate two fixed pins by making the I2C_bSDA_Pin and I2C_bSCL_Pin symbols
actual Port.Pin designations using either $define or Symbol, and remove the parameters from the I2C_Start procedure.*)






'
' 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 I2C_bSDA_Pin As Pin Access         ' Holds the value of the pin used for the SDA line
    Dim I2C_bSCL_Pin As Pin Access         ' Holds the value of the pin used for the SCL line

'----------------------------------------------------------------------------------------------
' Create a small delay to slow down the I2C interface (if required)
' Input     : None
' Output    : None
' Notes     : An actual delay may not be required here.
'           : If the I2C slave device shows innaccuracies with long leads to it, increase the DelayUs command's value to slow down the I2C interface more
'
Proc I2C_hDelay()
    DelayUS 1
EndProc

'----------------------------------------------------------------------------------------------
' 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 To 0 Step -1      ' Create a loop for the 8-bits to write
        If pData.7 = 1 Then                 ' Is bit-7 of pData set?
            PinInput I2C_bSDA_Pin           ' Yes. So make the SDA pin an input (made high by the pull-up resistor)
        Else                                ' Otherwise...
            PinLow I2C_bSDA_Pin             ' Make the SDA pin an output low
        EndIf                               '
        I2C_hDelay()                        ' Create a small delay
        PinInput I2C_bSCL_Pin               ' Make the SCL pin an input (made high by the pull-up resistor)
        I2C_hDelay()                        ' Create a small delay
        PinLow I2C_bSCL_Pin                 ' Make the SCL pin an output low
        I2C_hDelay()                        ' Create a small delay
        Rol pData                           ' Rotate pData left by 1 bit
    Next
'
' Close up the interface
'
    PinInput I2C_bSDA_Pin                   ' Make the SDA pin an input (made high by the pull-up resistor)
    PinInput I2C_bSCL_Pin                   ' Make the SCL pin an input (made high by the pull-up resistor)
    I2C_hDelay()                            ' Create a small delay
    PinLow I2C_bSCL_Pin                     ' Make the SCL pin an output low
    I2C_hDelay()                            ' 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
    Dim wTimeout As Word = $03FF

    Result = 0                              ' Clear the result of the procedure before entering the loop
    PinInput I2C_bSDA_Pin                   ' Make the SDA pin an input (made high by the pull-up resistor)
    For I2C_bBitIndex = 7 To 0 Step -1      ' 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
        '
        For wTimeout = $FF To 0 Step -1     ' Create a loop for the clock stretching
            PinInput I2C_bSCL_Pin           ' Make the SCL pin an input
            If GetPin I2C_bSCL_Pin = 1 Then Break  ' Exit the loop when the SCL pin is high
        Next                                ' Until the timeout value times out
        '
        ' Read the data
        '
        I2C_hDelay()                        ' Create a small delay
        Result.0 = GetPin I2C_bSDA_Pin      ' Read the state of the SDA pin into bit-0 of the result
        PinLow I2C_bSCL_Pin                 ' Make the SCL pin an output low
        I2C_hDelay()                        ' Create a small delay
    Next
EndProc

'----------------------------------------------------------------------------------------------
' Send an I2C bus Start condition
' Input     : pSDA holds the pin number used for the SDA line
'           : pSCL holds the pin number used for the SCL line
' Output    : None
' Notes     : Because all I2C coms start with a "Start" command being sent
'           : It is the only procedure that requires the SDA/SCL pins, because the other I2C procedures will use the same pins.
'
Proc I2C_Start(pSDA As Pin, pSCL As Pin)
    I2C_bSDA_Pin = pSDA                     ' Transfer the SDA pin to the global variable, so the other I2C procedures can share and use it
    I2C_bSCL_Pin = pSCL                     ' Transfer the SCL pin to the global variable, so the other I2C procedures can share and use it
    PinInput pSDA                           ' Make the SDA pin an input (made high by the pull-up resistor)
    I2C_hDelay()                            ' Create a small delay
    PinInput pSCL                           ' Make the SCL pin an input (made high by the pull-up resistor)
    I2C_hDelay()                            ' Create a small delay
    PinLow pSDA                             ' Make the SDA pin an output low
    I2C_hDelay()                            ' Create a small delay
    PinLow pSCL                             ' Make the SCL pin an output low
    I2C_hDelay()
EndProc

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

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

'----------------------------------------------------------------------------------------------
' Initiate an I2C Acknowledge
' Input     : None
' Output    : None
' Notes     : None
'
Proc I2C_Ack()
    I2C_hDelay()
    PinLow I2C_bSDA_Pin                     ' Make the SDA pin an output low
    I2C_hDelay()                            ' Create a small delay
    PinInput I2C_bSCL_Pin                   ' Make the SCL pin an input (made high by the pull-up resistor)
    I2C_hDelay()                            ' Create a small delay
    PinLow I2C_bSCL_Pin                     ' Make the SCL pin an output low
    I2C_hDelay()                            ' Create a small delay
    PinInput I2C_bSDA_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_bSDA_Pin                   ' Make the SDA pin an input (made high by the pull-up resistor)
    I2C_hDelay()                            ' Create a small delay
    PinInput I2C_bSCL_Pin                   ' Make the SCL pin an input (made high by the pull-up resistor)
    I2C_hDelay()                            ' Create a small delay
    PinLow I2C_bSCL_Pin                     ' Make the SCL pin an output low
    I2C_hDelay()                            ' Create a small delay
    PinInput I2C_bSDA_Pin                   ' Make the SDA pin an input (made high by the pull-up resistor)
EndProc

$endif


EDIT: found this post https://protoncompiler.com/index.php/topic,1533.0.html

TimB

#4
I used Les's software variable pin i2c routines but found them slow due to all the pin checking changing etc

It was easier to just have 2 hard coded i2c routines just rename the routines so you can make 2 versions

In the end I just used i2c driver chips and enabled them to select the end hardware

SCV

Thanks for the replies, I shall look into this.

top204

#6
Tim...

If you change the delay in the I2C_hDelay() procedure, the I2C bus becomes faster or slower. If a delay less than 1us is required, the DelayCs command can be used, so the delay can be clock cycle delays that can be down to a few 10s of nanoseconds between pin state changes with a device running at 64MHz. Or for even faster, replace the I2C_hDelay() calls with a DelayCs 1 command, which will delay for 1 clock cycle. Or comment out the I2C_hDelay() calls, making the interface very fast indeed!

I added the delay procedure because a lot of I2C peripherals do not like a fast bus speed, and when prototyping a serially interfaced peripheral device it is always better to err on the side of caution before speeding things up.

SVC...

You also use the I2CIn and I2COut commands because these have the SDA and SCL pins as parameters, so any valid pair of pins can be used for multiple I2C interfaces. These are software based routines just like the BusIn and BusOut commands and operate at approximately the same speed.