• AVR Freaks

Helpful ReplyHelp configuring the I2C Bit-Banging example code

Author
Dadavic
Starting Member
  • Total Posts : 33
  • Reward points : 0
  • Joined: 2017/03/30 00:09:59
  • Location: Spain
  • Status: offline
2017/03/31 06:20:58 (permalink)
0

Help configuring the I2C Bit-Banging example code

Hello everyone,
 
I recently downloaded an example code from the Microchip web to configure my PIC32MZ0512EFE144 for a bit-bang I2C application due to the silicon errata of this PIC which makes it difficult to implement the I2C communication via hardware. I already have generated the clock and data signals changing the corresponding PORTx, TRISx, LATx, etc. but I'm having difficulties finding where I can write my own data I want to transmit, as well as the slave address, Tx/Rx bufflen... In short, customize it for my specific needs.
 
The code example in cuestion is in this link: http://ww1.microchip.com/...oc/i2c_bitbang_isr.zip
 
I basically need to move 5 bytes of data to the direction 0x20 of an I/O's expander and a buffer for tx/rx of 10.
 
If you need any adicional information just let me know.
 
Appreciate any help. Best regards.
post edited by Dadavic - 2017/03/31 07:57:59
#1
ravic
think_do_repeat
  • Total Posts : 137
  • Reward points : 0
  • Joined: 2016/02/04 03:02:20
  • Location: Pune, MH, India
  • Status: offline
Re: Help configuring the I2C Bit-Banging example code 2017/04/03 23:41:05 (permalink)
3 (1)
Hi Dadavic, You said you are able to generate data and clock signal, So next is you have to use this clock and data signal in conjunction with I2C protocol. That means you have to toggle your clock at particular rate and with its edge( whatever you select) toggle your Data line as per data. I think there are already many sample drivers for bit banged SPI,I2C, UART etc. Better you modify them , you just need to replace there pins with your selected pins .

Always carry latest and best tools, whether it is WAR or PROGRAMMING
#2
Dadavic
Starting Member
  • Total Posts : 33
  • Reward points : 0
  • Joined: 2017/03/30 00:09:59
  • Location: Spain
  • Status: offline
Re: Help configuring the I2C Bit-Banging example code 2017/04/04 04:23:10 (permalink)
0
Hi Ravic, thank you for your answer.


I am a beginner in PIC programming, so for now I rely on example codes which I use to learn how the codes work so I can create my own based on the examples. Now I'm working on I2C communications so I need to set up a bit-banged I2C for my PIC32MZ + I/O Expander.
I downloaded the example code Microchip has for my specific PIC in the "example codes" section. The problem is, although I'm able to generate CLK and SDA by changing the default PORTx, TRISx, ANSELx... for the ones corresponding to my micro, I can't send the data I want. So my questions are:

1) In this specific example code (I left the link in the first post), where goes the data, Tx/Rx buffer, byte length, etc., so I can change it?
2) Are there any example codes out there for PIC32MZ about I2C bit-bang? I only have found this and it's very difficult to understand for me. A "simpler" one will be very appreciated.

If you need any additional information I will provide it for you.

Thank you for your time.
post edited by Dadavic - 2017/04/04 04:29:47
#3
ravic
think_do_repeat
  • Total Posts : 137
  • Reward points : 0
  • Joined: 2016/02/04 03:02:20
  • Location: Pune, MH, India
  • Status: offline
Re: Help configuring the I2C Bit-Banging example code 2017/04/04 05:04:06 (permalink)
0
////////////////////////////////////////////////////////////////////////////////
/// @file
/// @brief Inter-Integrated Circuit (I2C) driver.
////////////////////////////////////////////////////////////////////////////////

// *****************************************************************************
// ************************** System Include Files *****************************
// *****************************************************************************

#include <plib.h>

// *****************************************************************************
// ************************** User Include Files *******************************
// *****************************************************************************

#include "system.h"
#include "i2c.h"

// *****************************************************************************
// ************************** Defines ******************************************
// *****************************************************************************

// A write to a LAT register latches data to corresponding port I/O pins.
// Those I/O port pin(s) configured as outputs are updated.

// A read from LAT register reads the data held in PORT data latch, not
// from the port I/O pins.

#define mGetI2CClk() (PORTBbits.RB3) // Get Clock.
#define mGetI2CData() (PORTBbits.RB4) // Get Data.

#define mGetI2CClkLat() (LATBbits.LATB3) // Get state of Clock latch.
#define mGetI2CDataLat() (LATBbits.LATB4) // Get state of Data latch.

#define mSetI2CClkHigh() do {LATBSET = _LATB_LATB3_MASK;} while(0)
#define mSetI2CClkLow() do {LATBCLR = _LATB_LATB3_MASK;} while(0)

#define mSetI2CDataHigh() do {LATBSET = _LATB_LATB4_MASK;} while(0)
#define mSetI2CDataLow() do {LATBCLR = _LATB_LATB4_MASK;} while(0)

// *****************************************************************************
// ************************** Definitions **************************************
// *****************************************************************************

static volatile I2C_TRANSFER *ptr;
static volatile int error; // 0 = normal, 1 = error condition

// *****************************************************************************
// ************************** Function Prototypes ******************************
// *****************************************************************************

// *****************************************************************************
// ************************** Global Functions *********************************
// *****************************************************************************

int16_t I2CXfer(I2C_TRANSFER *xfer)
{
if ((xfer->buf == 0) || (xfer->status == 0))
{
// Null pointer error.
return -1;
}

// Set I2C driver status as waiting for bus.
*xfer->status = I2C_DRV_STATUS_WAIT;

// Take control of software I2C bus if it's available. ----------

asm("di"); // Entering critical section: disable interrupts.
if (IEC0bits.T4IE == 1)
{
asm("ei"); // Exiting critical section: enable interrupts.
return 0; // Software I2C bus is busy.
}

ptr = xfer;

// Configure the software I2C timer for desired clock frequency.
// 4 interrupts per clock cycle.
if ((GetPeripheralClock() % (xfer->freq * 4)) != 0)
{
// No perfect frequency divisor possible.
// Round up to next period number to be conservative.
WritePeriod4((GetPeripheralClock() / (xfer->freq * 4)) + 1);
}
else
{
// Perfect frequency match.
WritePeriod4(GetPeripheralClock() / (xfer->freq * 4));
}

// Reset error.
error = 0;

// Set I2C driver status as busy.
*xfer->status = I2C_DRV_STATUS_BUSY;

TMR4 = 0;
mT4ClearIntFlag();
mT4IntEnable(1); // Initiate software I2C transfer.
asm("ei"); // Exiting critical section: enable interrupts.

return 1;
}

// *****************************************************************************
// ************************** Static Functions *********************************
// *****************************************************************************

////////////////////////////////////////////////////////////////////////////////
/// @brief I2C bit-bang processing task.
///
/// The I2C data is bit-banged on the communication channel.
////////////////////////////////////////////////////////////////////////////////
void __ISR (_TIMER_4_VECTOR, IPL6SOFT) Timer4ISR(void)
{
static enum {
START = 0, // <-- Start of I2C transfer.
ADDRESS_W = 1,
CHECK_ACK1 = 2,
REGISTER_W = 3,
CHECK_ACK2 = 4,

RESTART = 5, // <-- Beginning of read sequence.
ADDRESS_R = 6,
CHECK_ACK3 = 7,
READ_BYTE = 8,
WRITE_ACK = 9,
NACK = 10,

WRITE_BYTE = 12, // <-- Beginning of write sequence.
CHECK_ACK4 = 13,

STOP = 11 // <-- End of I2C transfer.
} i2cState = START;

static int iState = 0;
static int ack = 0;
static uint8_t value = 0;
static uint8_t i2cBytesRead = 0;
static uint8_t i2cBytesWritten = 0;

// Delay in case of clock stretching.
if (mGetI2CClkLat() && !mGetI2CClk())
{
mT4ClearIntFlag();
return;
}

// Each case has discrete states (iState):
// (iState % 4) == 0: (Clock Low) Toggle Data
// (iState % 4) == 1: Set Clock High
// (iState % 4) == 2: (Clock High) Commit Data
// (iState % 4) == 3: Set Clock Low

switch (i2cState)
{
//-----------------------------------------------------------
case START:
case RESTART:
{
// Start condition: Data transitions high-to-low
// while clock is high.
switch (iState)
{
case 0:
{
mSetI2CDataHigh();
iState++;
break;
}
case 1:
{
mSetI2CClkHigh();
iState++;
break;
}
case 2:
{
mSetI2CDataLow();
iState++;
break;
}
case 3:
{
mSetI2CClkLow();
iState = 0;
i2cState++;
break;
}
}
break;
}
//-----------------------------------------------------------
// Write 8 bits.
case ADDRESS_W:
case REGISTER_W:
case ADDRESS_R:
{
if ((iState % 4) == 0)
{
// Load the proper value to transmit.
if (iState == 0)
{
if (i2cState == ADDRESS_W)
{
value = (ptr->dev << 1);
}
else if (i2cState == REGISTER_W)
{
value = ptr->addr;
}
else if (i2cState == ADDRESS_R)
{
value = (ptr->dev << 1) | 0x01;
}
}

// Set data line appropriately.
if ((value >> (8 - (iState / 4) - 1)) & 0x0001)
{
mSetI2CDataHigh();
}
else
{
mSetI2CDataLow();
}
}
else if ((iState % 4) == 1)
{
// Clock high.
mSetI2CClkHigh();
}
else if ((iState % 4) == 2)
{
// Freeze data.
}
else if ((iState % 4) == 3)
{
// Clock low.
mSetI2CClkLow();
if (iState == 31)
{
// Ready to check ACK.
iState = 0;
i2cState++;
break;
}
}
iState++;
break;
}
//-----------------------------------------------------------
// Check for ACK.
case CHECK_ACK1:
case CHECK_ACK2:
case CHECK_ACK3:
case CHECK_ACK4:
{
switch (iState)
{
case 0:
{
mSetI2CDataHigh();
iState++;
break;
}
case 1:
{
mSetI2CClkHigh();
iState++;
break;
}
case 2:
{
if (mGetI2CData())
{
ack = 0;
}
else
{
ack = 1;
}
iState++;
break;
}
case 3:
{
mSetI2CClkLow();
iState = 0;
if (!ack)
{
i2cState = STOP;
error = 1;
}
else
{
if ((ptr->rw == 0) && (i2cState != CHECK_ACK1))
{
if (i2cBytesWritten < ptr->numBytes)
{
i2cState = WRITE_BYTE;
}
else
{
i2cState = STOP;
}
}
else
{
i2cState++;
}
}
break;
}
}
break;
}
//-----------------------------------------------------------
case READ_BYTE:
{
if ((iState % 4) == 0)
{
if (iState == 0)
{
value = 0;
mSetI2CDataHigh();
}
}
else if ((iState % 4) == 1)
{
// Clock high.
mSetI2CClkHigh();
}
else if ((iState % 4) == 2)
{
// Read data.
value |= (mGetI2CData() << (8 - (iState / 4) - 1));
}
else if ((iState % 4) == 3)
{
// Clock low.
mSetI2CClkLow();
if (iState == 31)
{
// Transfer value.
ptr->buf[i2cBytesRead] = value;
i2cBytesRead++;

// Ready to ACK unless done reading bytes.
if (i2cBytesRead >= ptr->numBytes)
{
i2cState = NACK;
i2cBytesRead = 0;
}
else
{
i2cState = WRITE_ACK;
}

iState = 0;
break;
}
}
iState++;
break;
}
//-----------------------------------------------------------
case WRITE_ACK:
{
switch (iState)
{
case 0:
{
mSetI2CDataLow();
iState++;
break;
}
case 1:
{
mSetI2CClkHigh();
iState++;
break;
}
case 2:
{
// Freeze data.
iState++;
break;
}
case 3:
{
mSetI2CClkLow();
iState = 0;
i2cState = READ_BYTE;
break;
}
}
break;
}
//-----------------------------------------------------------
case NACK:
{
switch (iState)
{
case 0:
{
mSetI2CDataHigh();
iState++;
break;
}
case 1:
{
mSetI2CClkHigh();
iState++;
break;
}
case 2:
{
if (mGetI2CData())
{
ack = 0;
}
else
{
ack = 1;
}
iState++;
break;
}
case 3:
{
mSetI2CClkLow();
iState = 0;
// Look for NACK.
if (!ack)
{
i2cState++;
}
else
{
// Error occurred.
i2cState = STOP;
error = 1;
}
break;
}
}
break;
}
//-----------------------------------------------------------
case WRITE_BYTE:
{
if ((iState % 4) == 0)
{
// Load the proper value to transmit.
if (iState == 0)
{
value = ptr->buf[i2cBytesWritten];
}

// Set data line appropriately.
if ((value >> (8 - (iState / 4) - 1)) & 0x0001)
{
mSetI2CDataHigh();
}
else
{
mSetI2CDataLow();
}
}
else if ((iState % 4) == 1)
{
// Clock high.
mSetI2CClkHigh();
}
else if ((iState % 4) == 2)
{
// Freeze data.
}
else if ((iState % 4) == 3)
{
// Clock low.
mSetI2CClkLow();
if (iState == 31)
{
i2cBytesWritten++;

// Ready to check ACK.
iState = 0;
i2cState++;
break;
}
}
iState++;
break;
}
//-----------------------------------------------------------
case STOP:
{
// Stop condition: Data transitions low-to-high
// while clock is high.
switch (iState)
{
case 0:
{
mSetI2CDataLow();
iState++;
break;
}
case 1:
{
mSetI2CClkHigh();
iState++;
break;
}
case 2:
{
mSetI2CDataHigh();
iState++;
break;
}
case 3:
{
// Leave the clock high.
iState = 0;
i2cBytesRead = 0;
i2cBytesWritten = 0;
i2cState = 0;

// Disable interrupts, set completed flag.
mT4IntEnable(0);
if (error != 0)
{
*ptr->status = I2C_DRV_STATUS_ERROR;
}
else
{
*ptr->status = I2C_DRV_STATUS_DONE;
}
break;
}
}
break;
}
}

mT4ClearIntFlag();
return;
}

Always carry latest and best tools, whether it is WAR or PROGRAMMING
#4
Mysil
Super Member
  • Total Posts : 3324
  • Reward points : 0
  • Joined: 2012/07/01 04:19:50
  • Location: Norway
  • Status: offline
Re: Help configuring the I2C Bit-Banging example code 2017/04/04 10:20:02 (permalink)
0
Hi,
The link given in message #1 do not work,
it is incomplete because it have been truncated, possibly by copy and paste from a webpage display.
I think this is the link: http://ww1.microchip.com/downloads/en/DeviceDoc/i2c_bitbang_isr.zip
 
The main function call to use from application code is:
DRV_I2C_MasterBufferWrite(...);
You may try something like this:
#include <stdint.h>
#include <xc.h>
#include "i2c_bitbang_isr.h"
#include "i2c_queue.h"
    I2C_OPERATION_STATUS  Status;
    uint16_t deviceaddress;     /* I2C address of MCP23018 I/O Expander. */
                                /* For MCP23018, I2C address is called: Control Byte. */
                                /* The deviceaddress argument is a 8 bit I2C address. */
                                /* complete value of this byte is: 
                                 * = 0x40 + (Address from control pin)*2 + R/W bit.
                                 * Leave the value of R/W bit = 0, it will be set by driver functions when needed. */
    uint8_t  txBuffer[10];      /* Array to contain bytes to transfer. */
    uint16_t  txbuflen = 10;    /* Number of bytes to transfer from txBuffer, 10 is maximum. */
 
    deviceaddress = 0x40;
    txBuffer[0] = ...; /* Register number within port expander. */
    txBuffer[1] = ...; /* Fill in txBuffer with values for slave device. */
    txbuflen = 2;      /* Set actual number of bytes to be transferred. */
 
    Status = DRV_I2C_MasterBufferWrite( deviceaddress, txBuffer, txbuflen);
                /* Status is result of the initiation call.
                 * Final status of the transfer will not be available, until the transfer is completed,
                 * and must be retrieved in some other way. */

 
Code contributed in message #4 is based on Peripheral Library (PLIB),
which exists for PIC32MX devices, but have been declared obsolete by Microchip, and have not been ported to PIC32MZ devices.
Macro functions like: mT4ClearIntFlag();
are defined in int_5xx_6xx_7xx_legacy.h,
which is in the legacy part of the legacy library collection.
Although this software may be downloaded from microchip Download Archive and installed for use with PIC32MX devices, it is not likely to work for PIC32MZ without some tricky porting work.
 
Regards,
   Mysil
#5
Dadavic
Starting Member
  • Total Posts : 33
  • Reward points : 0
  • Joined: 2017/03/30 00:09:59
  • Location: Spain
  • Status: offline
Re: Help configuring the I2C Bit-Banging example code 2017/04/05 01:25:26 (permalink)
3 (1)
EDIT: images don't show up. I leave the raw link. Sorry for the inconvenience.
 
Thank you Ravic and Mysil for your answers.
 
Sorry, I didn't notice the link was broken. Indeed that is the code I'm refering to.

As Mysil stated, the code Ravic posted uses libraries no longer supported, although I thank you so much for the answer.
 
Mysil, this is the code I created using your indications, not sure if it's correct.
 
This is the default code in i2c_bitbang_isr.c for the write function
http://imgur.com/a/lwlGI
 
Accordingly I wrote this in main:
http://imgur.com/a/CamXW
 
Being this lines my slave's address and my txBuffer (this last function in i2c_bitbang_isr.c is declared as a pointer):
http://imgur.com/a/8Oy4d
 
When I start the debugging, I read this values in the watch option:
http://imgur.com/a/u1uxo
 
What did I miss?
 
Besides, I didn't understand what did you refer to with this
   
"txBuffer[0] = ...; /* Register number within port expander. */
 
 txBuffer[1] = ...; /* Fill in txBuffer with values for slave device. */"
 
Why you call twice that same array? And one is for writting the "register number within port expander", that's not the same as the port expander direction (0x20)?
 
I didn't understand either why you have to add 0x40 to the slave direction and multiply it by 2 ("* = 0x40 + (Address from control pin)*2 + R/W bit.").
 
I skipped those lines and tried to adapt your code to the default one. Maybe by missing those things which I don't understand the code doesn't work.
 
Anyway I appreciate your help, it's helping me to understand how this works.
Regards.
 
#6
qhb
Superb Member
  • Total Posts : 9998
  • Reward points : 0
  • Joined: 2016/06/05 14:55:32
  • Location: One step ahead...
  • Status: offline
Re: Help configuring the I2C Bit-Banging example code 2017/04/05 02:08:41 (permalink)
3 (1)
Dadavic
...
Besides, I didn't understand what did you refer to with this
   
"txBuffer[0] = ...; /* Register number within port expander. */
 
 txBuffer[1] = ...; /* Fill in txBuffer with values for slave device. */"
 
Why you call twice that same array? And one is for writting the "register number within port expander", that's not the same as the port expander direction (0x20)?

You don't "call" arrays.
That code is simply writing bytes to the first and second locations in the array, and then calling
DRV_I2C_MasterBufferWrite() to send those two bytes to the I2C slave.
The first byte is the register address, and the second byte is the data to write to that register.
That is how you write data to the registers in a MCP23018, which is what that example code was written for.
 
 
I didn't understand either why you have to add 0x40 to the slave direction and multiply it by 2 ("* = 0x40 + (Address from control pin)*2 + R/W bit.").

That is how the slave address in the MCP23018 is encoded.
#7
Dadavic
Starting Member
  • Total Posts : 33
  • Reward points : 0
  • Joined: 2017/03/30 00:09:59
  • Location: Spain
  • Status: offline
Re: Help configuring the I2C Bit-Banging example code 2017/04/05 02:23:49 (permalink)
0
qhb
Dadavic
...
Besides, I didn't understand what did you refer to with this
   
"txBuffer[0] = ...; /* Register number within port expander. */
 
 txBuffer[1] = ...; /* Fill in txBuffer with values for slave device. */"
 
Why you call twice that same array? And one is for writting the "register number within port expander", that's not the same as the port expander direction (0x20)?

You don't "call" arrays.
That code is simply writing bytes to the first and second locations in the array, and then calling
DRV_I2C_MasterBufferWrite() to send those two bytes to the I2C slave.
The first byte is the register address, and the second byte is the data to write to that register.
That is how you write data to the registers in a MCP23018, which is what that example code was written for.
 
 
I didn't understand either why you have to add 0x40 to the slave direction and multiply it by 2 ("* = 0x40 + (Address from control pin)*2 + R/W bit.").

That is how the slave address in the MCP23018 is encoded.




Okay, I understand now. My expander is the PCA9505/06, I should have said before, sorry. Its address is 0x20 if I'm not mistaken. This expander is enocoded the same way?
#8
qhb
Superb Member
  • Total Posts : 9998
  • Reward points : 0
  • Joined: 2016/06/05 14:55:32
  • Location: One step ahead...
  • Status: offline
Re: Help configuring the I2C Bit-Banging example code 2017/04/05 02:31:40 (permalink)
3 (1)
Dadavic
 
...
Okay, I understand now. My expander is the PCA9505/06, I should have said before, sorry. Its address is 0x20 if I'm not mistaken. This expander is enocoded the same way?

You are mistaken. It is 0x20 before shifting left one bit to add the R/W bit, so it's actually 0x40 in the byte sent, the same as the MCP23018.
The PCA9505 also then expects a register number, then one or more bytes of data.
That means it is very similar to an MCP23018.
 
#9
Dadavic
Starting Member
  • Total Posts : 33
  • Reward points : 0
  • Joined: 2017/03/30 00:09:59
  • Location: Spain
  • Status: offline
Re: Help configuring the I2C Bit-Banging example code 2017/04/05 03:09:13 (permalink)
0
Ok, noted. I corrected it in my code. Now my last question is about the code I posted photos of on my answer to Mysil. I think it's incorrect.
 
#10
Mysil
Super Member
  • Total Posts : 3324
  • Reward points : 0
  • Joined: 2012/07/01 04:19:50
  • Location: Norway
  • Status: offline
Re: Help configuring the I2C Bit-Banging example code 2017/04/05 07:16:29 (permalink) ☄ Helpfulby Dadavic 2017/04/19 07:55:18
5 (1)
Hi,
You are quite right, there are some mistakes in the code shown on Imgur.
 
You may put values into an array, as a list of initial values, as you have done in the snippet on Imgur:
uint8_t  txmsg1[10] = {0x90, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
 
Note, that arrays in C are indexed starting from 0,
thus an array with 10 locations are indexed from txmsg1[0] to txmsg1[9] (inclusive).
Be aware that txmsg1[10] is outside of the array that have been declared.
 
Then, in the program code, you may change values in each individual position in the array, like this :
    txmsg1[5] = 0x55;   /* Change value in the 6.th location in the array. */
    txmsg1[6] = 0xAA;   /* New value in the 7.th location in the array. */
 
When using an array as argument in a function call, you normally should use just the array name.
No cast operator, no star, and no ampersand, unless you want to do something special.
The construct: ... ,(uint8_t *)&txmsg1[10], ... will point to a location outside the end of the array.
 
The compiler understand both hexadecimal numbers and decimal numbers as constant values,
all integer values less than 10 is the same in decimal notation as in hex, so 0x06 is the same as 6
Since you have defined a macro symbol for the I2C address of the expander you are using, you may write like this:
    Status = DRV_I2C_MasterBufferWrite( SLAVE_ADDRESS_WRITE, txmsg1, 6); 
 
The operation started by calling  DRV_I2C_MasterBufferWrite(...)
will proceed in it's own timing together with your ordinary main program.
The Transfer is not queued, so you must call
    Status = DRV_I2C_MASTEROpStatus();
in suitable intervals, to know when the transfer is completed.
 
When the transfer is completed, you should call and test:
    if (DRV_I2C_MasterACKStatus())
    {            /* I have not tried this driver, but this is possibly where program go when there is a problem. */
    }
    else
    {            /* If above was a problem, then this is success. */
    }
 
Regards,
   Mysil
 
 
 
 
#11
Dadavic
Starting Member
  • Total Posts : 33
  • Reward points : 0
  • Joined: 2017/03/30 00:09:59
  • Location: Spain
  • Status: offline
Re: Help configuring the I2C Bit-Banging example code 2017/04/19 03:02:15 (permalink)
0
EDIT: posted old version of code. Updated with my last one.
 
Hi guys.
 
Thank you so much for your answers.
 
With your suggestions and with some code I found on Internet I created this new code, which is easier to understand. I am however unable to read data. I post the code here so you can help me to find the problems.
 
main.c

 
#include <xc.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include "driverUart1.h"
#include "HardwareControls.h"
 


/*Buffer for I2C transmision*/
 uint8_t RX_BUFFER_I2C2[6];
 uint8_t TX_BUFFER_I2C3[5];
 

void main(void)
{   
    DRIVERUART1__P_startingModule(); // UART for debugging using Docklight Scripting
    
    TX_BUFFER_I2C3[0]=0xFF;
    TX_BUFFER_I2C3[1]=0xFF;
    TX_BUFFER_I2C3[2]=0xFF;
    TX_BUFFER_I2C3[3]=0xFF;
    TX_BUFFER_I2C3[4]=0xFF;
    
    I2C__P_writeI2C3(0x20,0x90,(5),(uint8_t *)&TX_BUFFER_I2C3[0]); // 0x20 is the I/O Expander address, 0x90 the polarity inversion command and 5 the number of bytes
    
    while(1)
    {

        RX_BUFFER_I2C2[5]=0xAE;
        I2C__P_ReadRoutine(0x20,0x80, (6), (uint8_t *)&RX_BUFFER_I2C2[0]); // 0x20 is the I/O Expander address, 0x80 the data bank and 5 the number of bytes
        
        DRIVERUART1__F_copyRAM2Com((uint8_t*)&RX_BUFFER_I2C2[0], 6); // UART for debugging using Docklight Scripting
        
                       
    }
    
}

 

 
I2C_ROUTINES.c

 
#include <xc.h>
#include <stdint.h>
#include <stdlib.h>
#include "delays.h"
#include "HardwareControls.h"
 
/*Buffer for I2C transmision*/
 uint8_t RX_BUFFER_I2C2[5];
 uint8_t TX_BUFFER_I2C3[5];
 
#define SCLDir              TRISFbits.TRISF8
#define SDADir              TRISFbits.TRISF2
#define PUSH1_INPUT         PORTAbits.RA0
#define SDA_INPUT           PORTFbits.RF2
#define SCL_INPUT           PORTFbits.RF8

#define I2CLOW  0         //-- Puts pin into output/low mode
#define I2CHIGH 1         //-- Puts pin into Input/high mode

//*************************************************************************
//                          Local functions
//*************************************************************************
void I2C__P_StartRoutine(void);
uint8_t I2C__P_GetACK(void);
void I2C__P_StopRoutine(void);
void I2C__P_Clock(void);
uint8_t I2C__P_SendByte(uint8_t Byte);
extern uint8_t I2C__P_writeI2C3(uint8_t lAddressI2C,uint8_t lMemAdd, uint8_t lBytesNumber, uint8_t * pStartBytes);
uint8_t I2C__P_ReadBit(void);
uint8_t I2C__P_GetByte(void);
void I2C__P_SendACK(void);
extern uint8_t I2C__P_ReadRoutine(uint8_t lAddressI2C,uint8_t lMemAdd, uint8_t lBytesNumber, uint8_t * pStartBytes);
extern uint8_t IOEXPANDER__P_Write(void);
extern uint8_t IOEXPANDER__F_ReadIOExpander(tInputs *lFilterStruct);
//*************************************************************************
//                           I2C__P_StartRoutine
//*************************************************************************
void I2C__P_StartRoutine(void)
{
 // -- Ensure that the pins are in high impedance mode
 SDADir=I2CHIGH;
 SCLDir=I2CHIGH;
 SCL_INPUT=I2CLOW;
 SDA_INPUT=I2CLOW;
 __delay_us(50);

 //-- Generate the start condition
 SDADir=I2CLOW;
 SDA_INPUT=I2CLOW;
 __delay_us(50);
 SCLDir=I2CLOW;
 __delay_us(100);

}

//*************************************************************************
//                           I2C_Init
//*************************************************************************
uint8_t I2C__P_Init(void)
{
    SDADir = I2CHIGH;
    
    SCLDir = I2CLOW;
    
    SCL_INPUT = I2CHIGH;
    
    SDA_INPUT = I2CHIGH;
}
//************** END OF i2cgetack

//*************************************************************************
//                           I2C__P_StopRoutine
//*************************************************************************
void I2C__P_StopRoutine(void)
{
  //-- Generate Stop Condition --
 SDADir=I2CLOW;
 SCLDir=I2CHIGH;
__delay_us(50);
 SDADir=I2CHIGH;

}

//*************************************************************************
//                          I2C__P_Clock
//*************************************************************************
void I2C__P_Clock(void)
{
 __delay_us(20);       //-- Minimum Clock Low Time
 SCLDir=I2CHIGH;              //-- Release clock
 __delay_us(100);        //-- Minimum Clock High Time
 SCLDir=I2CLOW;               //-- Lower the clock
 __delay_us(100);         //-- Minimum Clock Low Time
}
//************** END OF i2cclock



//*************************************************************************
//                        I2C__P_SendByte
//*************************************************************************
uint8_t I2C__P_SendByte(uint8_t Byte)
{
 uint8_t count;
 SDA_INPUT=I2CLOW;
 SCL_INPUT=I2CLOW;

 __delay_us(100);         //-- Minimum Clock Low Time

 for(count=0;count<8;count++)   //-- Send 8 bits of data
 {
  SCL_INPUT=I2CLOW;
  __delay_us(20);
 
  if( (Byte << count)&SDA_INPUT)        //-- Get the Bit
  {
      
   SDADir=I2CHIGH;            //-- Release pin if bit = 1
    
  }
  else
  {
   
   SDA_INPUT=I2CLOW;                  //-- Ensure Port pin is low
   SDADir=I2CLOW;             //-- Lower pin if bit = 0
  __delay_us(20);
  SCL_INPUT=I2CHIGH;
  __delay_us(50);
  }
   
  I2C__P_Clock();//-- Pulse the clock
                
 }
 SDADir=I2CHIGH;              //-- Release data pin for ACK
 return(SDADir);
}



//*************************************************************************
//                          I2C__P_writeI2C3
//
// Inputs:
//         lAddressI2C  - Address to write data to
//         pStartBytes  - Pointer to buffer
//         lBytesNumber - Number of bytes to send
//         lMemAdd - Polarity inversion of I/O expander for writting
//*************************************************************************
extern uint8_t I2C__P_writeI2C3(uint8_t lAddressI2C,uint8_t lMemAdd, uint8_t lBytesNumber, uint8_t * pStartBytes)
{

I2C__P_StartRoutine();
    
I2C__P_SendByte(lAddressI2C << 0);

//if(!I2C__P_GetACK())
// {
//  I2C__P_StopRoutine();
//  return(0);
// }

I2C__P_SendByte(lMemAdd);

//if(!I2C__P_GetACK())
// {
//  I2C__P_StopRoutine();
//  return(0);
// }

while(lBytesNumber--)
 {
    I2C__P_SendByte(*pStartBytes);
 
//  if(!I2C__P_GetACK())
//  {
//   I2C__P_StopRoutine();
//   return(0);
//  }
//  pStartBytes++;
 }
 I2C__P_StopRoutine();
 return(1);


}

//*************************************************************************
//                          I2C__P_ReadBit
//*************************************************************************
uint8_t I2C__P_ReadBit(void)
{
 uint8_t Data=0;
 __delay_us(20);       //-- Minimum Clock Low Time
 SCLDir=I2CHIGH;              //-- Release clock
 __delay_us(50);        //-- 1/2 Minimum Clock High Time
 if(SDA_INPUT !=0 ) Data=1;           //-- READ in the data bit
 __delay_us(50);        //-- 1/2 Minimum Clock High Time
 SCLDir=I2CLOW;               //-- Lower the clock
 __delay_us(100);         //-- Minimum Clock Low Time
 return(Data);
}

//*************************************************************************
//                      I2C__P_GetByte
//
//  Reads in a byte from the I2C Port
//*************************************************************************
uint8_t I2C__P_GetByte(void)
{
 char count, Byte = 0;
 
 for(count=0;count<8;count++)   //-- Read 8 bits of data
 {
     SCL_INPUT = I2CLOW;
     __delay_us(50);
     
     SCL_INPUT = I2CHIGH;
     __delay_us(50);
     
     Byte = Byte |( SDADir << (7-count) );
     __delay_us(20);
     
     SDADir=I2CHIGH;            //-- Release pin so data can be recieved
     __delay_us(20);
 }
 return(Byte);

}

//*************************************************************************
//                   I2C__P_SendACK
//*************************************************************************
void I2C__P_SendACK(void)
{
 //--- Send Ack to slave except for last time ---
 SDA_INPUT=0;
 SDADir=I2CLOW;              //-- Send ACK
 __delay_us(20);      //-- Give it time to settle
 I2C__P_Clock();                   //-- Pulse the clock
 SDADir=I2CHIGH;             //-- Release ACK
 __delay_us(20);      //-- Gap between next byte
}

//*************************************************************************
//                   I2C__P_SendNACK
//*************************************************************************
void I2C__P_SendNACK(void)
{
    SDADir = I2CLOW;
    
    SDA_INPUT = I2CLOW;
    __delay_us(20);
    
    SCL_INPUT = I2CLOW;
    __delay_us(20);
    
    SDA_INPUT = I2CHIGH;
    __delay_us(20);
    
    SCL_INPUT = I2CHIGH;
    __delay_us(50);
}

//************** END OF i2csendack

//*************************************************************************
//            I2C__P_ReadRoutine
//*************************************************************************
extern uint8_t I2C__P_ReadRoutine(uint8_t lAddressI2C,uint8_t lMemAdd, uint8_t lBytesNumber, uint8_t * pStartBytes)
{
 I2C__P_StartRoutine();

 //-- Send Address
 I2C__P_SendByte((lAddressI2C << 0x01) | (0x00));   //-- Lowest bit = 1 => READ

// if(!I2C__P_GetACK())
// {
//  I2C__P_StopRoutine();
//  return(0);
// }

 I2C__P_SendByte(lMemAdd);
//if(!I2C__P_GetACK())
// {
//  I2C__P_StopRoutine();
//  return(0);
// }
 
 I2C__P_StartRoutine();
 
 I2C__P_SendByte((lAddressI2C << 0x01) | (0x01));
 
// if(!I2C__P_GetACK())
// {
//  I2C__P_StopRoutine();
//  return(0);
// }
 
 while(lBytesNumber--)
 {
 *pStartBytes = I2C__P_GetByte();
 
  if(lBytesNumber > 0)
  {
   I2C__P_SendACK();
  }
 }
 I2C__P_SendNACK();
 I2C__P_StopRoutine();
 return(1);

}

//extern uint8_t IOEXPANDER__F_ReadIOExpander(tInputs *lFilterStruct)
//        {
//            I2C__P_ReadRoutine(0x20,0x80, (5), (uint8_t *)&RX_BUFFER_I2C2[0]);
//            
//        }

//extern uint8_t IOEXPANDER__P_Write(void)
//{
//    TX_BUFFER_I2C3[0]=0xFF;
//    TX_BUFFER_I2C3[1]=0xFF;
//    TX_BUFFER_I2C3[2]=0xFF;
//    TX_BUFFER_I2C3[3]=0xFF;
//    TX_BUFFER_I2C3[4]=0xFF;
//    
//    I2C__P_writeI2C3(0x20,0x90,(5),(uint8_t *)&TX_BUFFER_I2C3[0]);
//}  

 

 
 
In these two files is the problem I suppose.
Thank you for your patience.
post edited by Dadavic - 2017/04/19 03:21:10
#12
qhb
Superb Member
  • Total Posts : 9998
  • Reward points : 0
  • Joined: 2016/06/05 14:55:32
  • Location: One step ahead...
  • Status: offline
Re: Help configuring the I2C Bit-Banging example code 2017/04/19 03:15:26 (permalink)
3 (1)
Pasting raw code into a post corrupts it.
Please either place code tags around your code
[ code ] and [ /code] without the extra spaces
 
or attach the file to your message.
 
 
#13
Dadavic
Starting Member
  • Total Posts : 33
  • Reward points : 0
  • Joined: 2017/03/30 00:09:59
  • Location: Spain
  • Status: offline
Re: Help configuring the I2C Bit-Banging example code 2017/04/19 03:18:49 (permalink)
0
Already corrected, sorry.
#14
Mysil
Super Member
  • Total Posts : 3324
  • Reward points : 0
  • Joined: 2012/07/01 04:19:50
  • Location: Norway
  • Status: offline
Re: Help configuring the I2C Bit-Banging example code 2017/04/19 07:30:13 (permalink) ☄ Helpfulby Dadavic 2017/04/19 07:55:04
3 (1)
Hi,
I think there are several mistakes in the code you show in message #12.
I did write some comments, but the forum server do not like it, so I'll try to attach the text instead.
 
Regards,
   Mysil
#15
Dadavic
Starting Member
  • Total Posts : 33
  • Reward points : 0
  • Joined: 2017/03/30 00:09:59
  • Location: Spain
  • Status: offline
Re: Help configuring the I2C Bit-Banging example code 2017/04/19 07:42:21 (permalink)
0
Hi, thanks for your time.
 
As you said there are several mistakes. Some of them were corrected after my message was posted, and some were corrected after your answer.
 
At work I have access to an oscilloscope which I use to measure the SDA and SCL lines of the I/O Expander, which is connected to the PIC32. Because the control board the micro and expander are built in have an UART driver I also use Docklight Scripting to see any change in some registers I use for debugging, in my case 4 connectors I manually (using cables) drive to ground, which should translate into a "0" in Docklight. However this doesn't happen, so my main problems are in the read functions I suppose.
 
I have deleted the lines in which SDA_INPUT/SCL_INPUT are used so I only work with TRIS. I also have changed the __delay_us() times to be more accurate, using the times NXP Semiconductors specifies for I2C in my I/O Expander datasheet.
 
I paste the corrected (I hope it is corrected hahaha) here.

#include <xc.h>
#include <stdint.h>
#include <stdlib.h>
#include "delays.h"
#include "HardwareControls.h"
 
/*Buffer for I2C transmision*/
 uint8_t RX_BUFFER_I2C2[6];
 uint8_t TX_BUFFER_I2C3[5];
 
#define SCLDir              TRISFbits.TRISF8
#define SDADir              TRISFbits.TRISF2
#define SDA_INPUT           PORTFbits.RF2
#define SCL_INPUT           PORTFbits.RF8

#define I2CLOW  0         //-- Puts pin into output/low mode
#define I2CHIGH 1         //-- Puts pin into Input/high mode

//*************************************************************************
//                          Local functions
//*************************************************************************
void I2C__P_StartRoutine(void);
uint8_t I2C__P_GetACK(void);
void I2C__P_StopRoutine(void);
void I2C__P_Clock(void);
uint8_t I2C__P_SendByte(uint8_t Byte);
extern uint8_t I2C__P_writeI2C3(uint8_t lAddressI2C,uint8_t lMemAdd, uint8_t lBytesNumber, uint8_t * pStartBytes);
uint8_t I2C__P_ReadBit(void);
uint8_t I2C__P_GetByte(void);
void I2C__P_SendACK(void);
extern uint8_t I2C__P_ReadRoutine(uint8_t lAddressI2C,uint8_t lMemAdd, uint8_t lBytesNumber, uint8_t * pStartBytes);
extern uint8_t IOEXPANDER__P_Write(void);
extern uint8_t IOEXPANDER__F_ReadIOExpander(tInputs *lFilterStruct);
//*************************************************************************
//                           I2C__P_StartRoutine
//*************************************************************************
void I2C__P_StartRoutine(void)
{
 // -- Ensure that the pins are in high impedance mode
 SDADir=I2CHIGH;
 SCLDir=I2CHIGH;
 __delay_us(2);

 //-- Generate the start condition
 SDADir=I2CLOW;
 __delay_us(1);
 SCLDir=I2CLOW;
}

//*************************************************************************
//                           I2C__P_GetACK
//*************************************************************************
uint8_t I2C__P_GetACK(void)
{
 SCLDir=I2CHIGH;                //-- raise the clock pin
 if(SDA_INPUT)                          //-- sample the ACK signal
 {
  return(0);                      //-- No ACK so return BAD
 }
 __delay_us(1);           //-- Else wait for rest of clock
 SCLDir=I2CLOW;                 //-- Finish the clock pulse
 __delay_us(1);           //-- Minimum Clock Low Time
 return(1);
}
//************** END OF i2cgetack

//*************************************************************************
//                           I2C__P_StopRoutine
//*************************************************************************
void I2C__P_StopRoutine(void)
{
  //-- Generate Stop Condition --
 SDADir=I2CLOW;
 SCLDir=I2CHIGH;
__delay_us(1);
 SDADir=I2CHIGH;

}

//*************************************************************************
//                          I2C__P_Clock
//*************************************************************************
void I2C__P_Clock(void)
{
 __delay_us(1);       //-- Minimum Clock Low Time
 SCLDir=I2CHIGH;              //-- Release clock
 __delay_us(2);        //-- Minimum Clock High Time
 SCLDir=I2CLOW;               //-- Lower the clock
 __delay_us(1);
}
//************** END OF i2cclock



//*************************************************************************
//                        I2C__P_SendByte
//*************************************************************************
uint8_t I2C__P_SendByte(uint8_t Byte)
{
 uint8_t count;

 __delay_us(2);         //-- Minimum Clock Low Time

 for(count=8;count>0;count--)   //-- Send 8 bits of data
 {
    if((Byte&0x80) == 0)        //-- Get the Bit
    {
     SDADir=I2CLOW;             //-- Lower pin if bit = 0
    }
    else
    {
     SDADir=I2CHIGH;            //-- Release pin if bit = 1
    }
    Byte=Byte<<1;                 //-- Shift next bit into position
    __delay_us(1);
    I2C__P_Clock();                   //-- Pulse the clock
 }
 SDADir=I2CHIGH;              //-- Release data pin for ACK
 return(1);
}



//*************************************************************************
//                          I2C__P_writeI2C3
//
// Inputs:
//         lAddressI2C  - Address to write data to
//         pStartBytes  - Pointer to buffer
//         lBytesNumber - Number of bytes to send
//         lMemAdd - Polarity inversion of I/O expander for writting
//*************************************************************************
extern uint8_t I2C__P_writeI2C3(uint8_t lAddressI2C,uint8_t lMemAdd, uint8_t lBytesNumber, uint8_t * pStartBytes)
{

I2C__P_StartRoutine();
    
I2C__P_SendByte(lAddressI2C << 0);

if(!I2C__P_GetACK())
 {
  I2C__P_StopRoutine();
  return(0);
 }

I2C__P_SendByte(lMemAdd);

if(!I2C__P_GetACK())
 {
  I2C__P_StopRoutine();
  return(0);
 }


while(lBytesNumber--)
 {
  I2C__P_SendByte(*pStartBytes);
 
  if(!I2C__P_GetACK())
  {
   I2C__P_StopRoutine();
   return(0);
  }
  pStartBytes++;
 }
 I2C__P_StopRoutine();
 return(1);


}

//*************************************************************************
//                          I2C__P_ReadBit
//*************************************************************************
//uint8_t I2C__P_ReadBit(void)
//{
// uint8_t Data=0;
// __delay_us(20);       //-- Minimum Clock Low Time
// SCLDir=I2CHIGH;              //-- Release clock
// __delay_us(50);        //-- 1/2 Minimum Clock High Time
// if(SDA_INPUT !=0 ) Data=1;           //-- READ in the data bit
// __delay_us(50);        //-- 1/2 Minimum Clock High Time
// SCLDir=I2CLOW;               //-- Lower the clock
// __delay_us(100);         //-- Minimum Clock Low Time
// return(Data);
//}

//*************************************************************************
//                      I2C__P_GetByte
//
//  Reads in a byte from the I2C Port
//*************************************************************************
uint8_t I2C__P_GetByte(void)
{
 char count, Byte = 0;
 
 for(count=8;count>0;count--)   //-- Read 8 bits of data
 {
 
   if((Byte == 0))        //-- Get the Bit
    {
     SDADir=I2CLOW;             //-- Lower pin if bit = 0
    }
    else
    {
     SDADir=I2CHIGH;            //-- Release pin if bit = 1
    }
    Byte = Byte | ( SDADir << (7-count) );                 //-- Shift next bit into position
    __delay_us(1);
    I2C__P_Clock();                   //-- Pulse the clock   
  }
 return(Byte);

}

//*************************************************************************
//                   I2C__P_SendACK
//*************************************************************************
void I2C__P_SendACK(void)
{
 //--- Send Ack to slave except for last time ---
 SDADir=I2CLOW;              //-- Send ACK
 __delay_us(1);      //-- Give it time to settle
 I2C__P_Clock();                   //-- Pulse the clock
 SDADir=I2CHIGH;             //-- Release ACK
 __delay_us(1);      //-- Gap between next byte
}
//************** END OF i2csendack

//*************************************************************************
//            I2C__P_ReadRoutine
//*************************************************************************
extern uint8_t I2C__P_ReadRoutine(uint8_t lAddressI2C,uint8_t lMemAdd, uint8_t lBytesNumber, uint8_t * pStartBytes)
{
 I2C__P_StartRoutine();

 //-- Send Address
 I2C__P_SendByte((lAddressI2C << 1) | (0x01));   //-- Lowest bit = 1 => READ

 if(!I2C__P_GetACK())
 {
  I2C__P_StopRoutine();
  return(0);
 }

 I2C__P_SendByte(lMemAdd);
if(!I2C__P_GetACK())
 {
  I2C__P_StopRoutine();
  return(0);
 }
 
 I2C__P_StartRoutine();
 
 I2C__P_SendByte((lAddressI2C << 0x01) | (0x01));
 
 if(!I2C__P_GetACK())
 {
  I2C__P_StopRoutine();
  return(0);
 }
 
 while(lBytesNumber--)
 {
 *pStartBytes = I2C__P_GetByte();
 
  pStartBytes++;

  if(lBytesNumber > 0)
  {
   I2C__P_SendACK();
  }
 }
 I2C__P_StopRoutine();
 return(1);

}
 
 
 
 
 
 
 

 
Again thank you for your help.
post edited by Dadavic - 2017/04/19 07:47:14
#16
Dadavic
Starting Member
  • Total Posts : 33
  • Reward points : 0
  • Joined: 2017/03/30 00:09:59
  • Location: Spain
  • Status: offline
Re: Help configuring the I2C Bit-Banging example code 2017/04/21 07:52:40 (permalink)
0
Well, after a lot of hours of work I finally achieved a functional I2C bitbang code. I appreciate so much all your help and patience.
 
I post here the resulting code, including only the main files. It might help anyone facing a similar problem. This code was implemented for PIC32MZ0512EFE144 and the PCA 9505 I/O Expander (which is the slave). Hope it is helpful.
 
MAIN.C
 
/********************************************************************************
*Module name:
* MAIN
*
*Module version:
* 1.00. Initial version.
*
*Module description:
* Main funtions of the system.
********************************************************************************/

/*Header files.*/
/********************************************************************************/
/********************************************************************************/
#include <xc.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include "driverUart1.h"
#include "I2C_Routines.h"
#include "delays.h"


/*Global variables of module.*/
/********************************************************************************/
/********************************************************************************/

/* Buffer for I2C reception */
uint8_t RX_BUFFER_I2C2[6];

/* Buffer for I2C transmission */
uint8_t TX_BUFFER_I2C3[5];


/*Prototipes of local functions at module*/
/********************************************************************************/
/********************************************************************************/

/* Generates a loop for reading */
uint32_t uax=0;



/*******************************************************************************
*Function description:
* Main function of the system.
*
*Input parameters:
* Has not input parameters.
*
*Return:
* Has not return.
*
*Notes:
* .
********************************************************************************/
void main(void)
{
/*
*Local variables.
*/


/*
*Not exist.
*/


/*
*Procedure.
*/


/*
*Start and enable System functions.
*/

/* UART for debugging using Docklight Scripting */
DRIVERUART1__P_startingModule();

/* Stores 0xFF in the buffer positions */
TX_BUFFER_I2C3[0]=0xFF;
TX_BUFFER_I2C3[1]=0xFF;
TX_BUFFER_I2C3[2]=0xFF;
TX_BUFFER_I2C3[3]=0xFF;
TX_BUFFER_I2C3[4]=0xFF;

/* Writes the bytes and the TX buffer */
/* 0x20 is the I/O Expander address, 0x90 the polarity inversion command and 5 the number of bytes */
DRIVERI2C3__F_writeI2C3(0x20,0x90,(5),(uint8_t *)&TX_BUFFER_I2C3[0]);


/* Main loop tasks */
while(1)
{
/* Auxiliar loop */
if(uax==200)
{
/* Read the incoming bytes and stores them in RX buffer */
// 0x20 is the I/O Expander address, 0x80 the data bank and 6 the number of bytes
DRIVERI2C3__F_readMemI2C3(0x20,0x80, (6), (uint8_t *)&RX_BUFFER_I2C2[0]);

/* Includes this byte in the 5th position. Used for debugging */
RX_BUFFER_I2C2[5]=0xAE;

/* Delay to reduce UART transmission speed */
__delay_us(20);

/* UART for debugging */
DRIVERUART1__F_copyRAM2Com((uint8_t*)&RX_BUFFER_I2C2[0], 6);

/* Delay to reduce UART transmission speed */
__delay_us(20);

/* Restart the counting */
uax=0;
}

/* Increases counting */
uax++;

}

}



/*******************************************************************************
*
*End of File.
*
*******************************************************************************/

 
I2C_ROUTINES.C
 
/********************************************************************************
*Module name:
* I2C_ROUTINES.
*
*Module version:
* 1.00. Initial version.
*
*Module description:
* Includes the necessary functions the I2C protocol needs for its correct
* functionality
********************************************************************************/



/*Header files.*/
/********************************************************************************/
/********************************************************************************/
#include <xc.h>
#include <stdint.h>
#include <stdlib.h>
#include "delays.h"
#include "I 2C_Routines.h"



/*Global variables of module.*/
/********************************************************************************/
/********************************************************************************/

/* Buffer for I2C reception */
uint8_t RX_BUFFER_I2C2[6];

/* Buffer for I2C transmission */
uint8_t TX_BUFFER_I2C3[5];



/*Local functions at module*/
/********************************************************************************/
/********************************************************************************/
void I2C__P_StartCondition(void);
void I2C__P_ReStartCondition(void);
uint8_t I2C__P_SendByte(uint8_t Byte);
uint8_t I2C__P_GetACK(void);
void I2C__P_StopCondition(void);
void I2C__P_Clock(void);
uint8_t I2C__P_GetByte(void);
void I2C__P_SendACK(void);
void I2C__P_NACK(void);






/*******************************************************************************
*Function description:
* Generates the start condition of the I2C protocol.
*
*Input parameters:
* Not has input parameters.
*
*Return:
* Not return.
*
*Notes:
* It will be executed before any data transmission
********************************************************************************/
void I2C__P_StartCondition(void)
{
/*
Local variables
*/


/* No variables */


/*
Procedure
*/


/*
To start communication
*/


/*Pull down de data signal*/
SDADir = I2CLOW;

/*Delay to give it time to settle*/
__delay_us(1);

/*Pull down the clock signal*/
SCLDir = I2CLOW;
}/* void I2C__P_StartCondition(void) */



/*******************************************************************************
*Function description:
* Generates the start condition of the I2C protocol in the read routine after
* relinquishing the SDA and SCL signals.
*
*Input parameters:
* Not has input parameters.
*
*Return:
* Not return.
*
*Notes:
* It will be executed after sending lMemAdd in read routine.
********************************************************************************/
void I2C__P_ReStartCondition(void)
{
/*
Local variables
*/


/* No variables */


/*
Procedure
*/


/*
To restart communication
*/


/* Pull up data line */
SDADir = I2CHIGH;

/* Give it time to settle */
__delay_us(1);

/* Pull up clock line */
SCLDir = I2CHIGH;

/* Give it time to settle */
__delay_us(1);

/* Pull down data line */
SDADir = I2CLOW;

/* Give it time to settle */
__delay_us(1);

/* Pull up clock line */
SCLDir = I2CLOW;

} /* void I2C__p_ReStartCondition(void) */


/*******************************************************************************
*Function description:
* Writes the data held in TX_BUFFER_I2C3[5] to the I/O Expander
*
*Input parameters:
* lAddressI2C: Address to write data to
* pStartBytes: Pointer to buffer
* lBytesNumber: Number of bytes to send
* lMemAdd: Command for polarity inversion of I/O expander to allow writting
*
*Return:
* Returns a 1 to ACK
*
*Notes:
*.
********************************************************************************/
extern uint8_t DRIVERI2C3__F_writeI2C3(uint8_t lAddressI2C,uint8_t lMemAdd, uint8_t lBytesNumber, uint8_t * pStartBytes)
{
/*
Local variables
*/


/* No variables */


/*
Procedure
*/


/*
To write bytes
*/


/* Generates start condition to initiate communication */
I2C__P_StartCondition();

/* Sends I/O expander address, shifting 1 bit into position */
I2C__P_SendByte(lAddressI2C<<1);

/* Checks if ACK is received */
if(!I2C__P_GetACK())
{
/* If not, stops communication and returns 0 for NACK */
I2C__P_StopCondition();
return(0);
}

/* Sends lMemAdd to allow writting */
I2C__P_SendByte(lMemAdd);

/* Checks if ACK is received */
if(!I2C__P_GetACK())
{
/* If not, stops communication and returns 0 for NACK */
I2C__P_StopCondition();
return(0);
}

/* Initiates reverse counting to send lBytesNumber bytes */
while(lBytesNumber--)
{
/* Sends the pointer to TX_BUFFER_I2C3[5] */
I2C__P_SendByte(*pStartBytes);

/* Checks if ACK is received */
if(!I2C__P_GetACK())
{
/* If not, stops communication and returns 0 for NACK */
I2C__P_StopCondition();
return(0);
}
/* Selects the next bit to be sent */
pStartBytes++;
}
/* Stops communication after the writting is complete */
I2C__P_StopCondition();

return(1);


} /* uint8_t I2C__P_WriteIOExpander(uint8_t lAddressI2C,uint8_t lMemAdd, uint8_t lBytesNumber, uint8_t * pStartBytes) */



/*******************************************************************************
*Function description:
* Sends the bytes specified in the write function
*
*Input parameters:
* Byte: Variable in which the bytes are held to be sent in write function
*
*Return:
* Returns 1 to ACK
*
*Notes:
*.
********************************************************************************/
uint8_t I2C__P_SendByte(uint8_t Byte)
{
/*
Local variables
*/


/* Variable for counting the bits sent*/
uint8_t count;


/*
Procedure
*/


/*
To send byte
*/


/* Loop used for sending eight bits, one at a time */
for(count = 0;count < 8; count++)
{
/* If the byte received is 0 holds the SDA signal down */
if((Byte&0x80) == 0)
{
SDADir = I2CLOW;
}
/* Else release the line so data can be received */
else
{
SDADir = I2CHIGH;
}
/* Saves the data in the variable and shifts next bit into position */
Byte = Byte <<1 ;
/* Delay and clock pulse to ensure the data reception */
__delay_us(1);
I2C__P_Clock();
}
/* Release data line for ACK */
SDADir = I2CHIGH;
return(1);
} /* uint8_t I2C__P_SendByte(uint8_t Byte) /*



/*******************************************************************************
*Function description:
* Checks the status of SDA LAT and sends ACK or NACK depending of that status
*
*Input parameters:
* Has not input parameters
*
*Return:
* Returns 1 to ACK
* Return 0 to NACK
*
*Notes:
*.
********************************************************************************/
uint8_t I2C__P_GetACK(void)
{
/*
Local variables
*/


/* No variables */


/*
Procedure
*/


/*
To check SDA and send ACK or NACK
*/


/* Samples SDA LAT */
if(SDAOut == 1)
{
/*If 1, return NACK*/
return(0);
}
else
{
/* Else, pulses de clock and returns ACK */
I2C__P_Clock();
__delay_us(1); //-- Minimum Clock Low Time
}

return(1);

} /* uint8_t I2C__P_GetACK(void) */



/*******************************************************************************
*Function description:
* Generates the stop condition to end communication
*
*Input parameters:
* Has not input parameters
*
*Return:
* Has no return
*
*Notes:
*.
********************************************************************************/
void I2C__P_StopCondition(void)
{
/*
Local variables
*/


/* No variables */


/*
Procedure
*/


/*
To generate stop condition
*/


/* Pull down SDA line */
SDADir = I2CLOW;
/* Pull down SCL line */
SCLDir = I2CLOW;
/* Give it time to settle */
__delay_us(1);
/* Releases SCL line */
SCLDir = I2CHIGH;
/* Give it time to settle */
__delay_us(1);
/* Releases SDA line */
SDADir = I2CHIGH;

} /* void I2C__P_StopRoutine(void) */



/*******************************************************************************
*Function description:
* Pulses SCL to generate a clock signal
*
*Input parameters:
* Has not input parameters
*
*Return:
* Has no return
*
*Notes:
*.
********************************************************************************/
void I2C__P_Clock(void)
{
/*
Local variables
*/


/* No variables */


/*
Procedure
*/


/*
To generate clock pulses
*/



/* Delay to avoid signals to bed mixed*/
__delay_us(1);
/* Pulls up SCL line */
SCLDir = I2CHIGH;
/* Give it time to settle */
__delay_us(2);
/* Pulls down SCL line */
SCLDir=I2CLOW;
/* Give it time to settle */
__delay_us(1);

} /* void I2C__P_Clock(void)*/



/*******************************************************************************
*Function description:
* Reads the data received in the I/O expander and stores it in RX_BUFFER_I2C2[5]
*
*Input parameters:
* lAddressI2C: Address to read data from
* pStartBytes: Pointer to buffer
* lBytesNumber: Number of bytes to read
* lMemAdd: Command for enabling the I/O expander data bank to be read
*Return:
* Return 1 for ACK
*
*Notes:
*.
********************************************************************************/
extern uint8_t DRIVERI2C3__F_readMemI2C3(uint8_t lAddressI2C,uint8_t lMemAdd, uint8_t lBytesNumber, uint8_t * pStartBytes)
{
/*
Local variables
*/


/* Variable used for counting the number of bits sent */
uint8_t lIndex;


/*
Procedure
*/


/*
To read data from I/O expander
*/


/* Generates start condition */
I2C__P_StartCondition();

/* Sends expander address, shifting 1 bit into position */
I2C__P_SendByte(lAddressI2C << 1);

/* Checks if ACK is received */
if(I2C__P_GetACK()==0)
{
/* If not, stops communication and return 0 for NACK */
I2C__P_StopCondition();
return(0);
}

/* Sends lMemAdd to access the data bank to be read */
I2C__P_SendByte(lMemAdd);

/* Checks if ACK is received */
if(I2C__P_GetACK()==0)
{
/* If not, stops communication and return 0 for NACK */
I2C__P_StopCondition();
return(0);
}

/*Begins reading*/


/* Restarts I2C to begin the reading */
I2C__P_ReStartCondition();

/* Sends expander addres, shifting 1 bit into position and does an OR with 1 bit to enable reading */
I2C__P_SendByte((lAddressI2C << 1) | (0x01));

/* Checks if ACK is received */
if(I2C__P_GetACK()==0)
{
/* If not, stops communication and return 0 for NACK */
I2C__P_StopCondition();
return(0);
}

/* Generates a reverse counting loop to send lBytesNumber bytes*/
for(lIndex=0;lIndex<lBytesNumber;lIndex++ )
{
/* Equals the RX_BUFFER_I2C2[5] to the bytes received in I2C__P_GetByte(); function */
pStartBytes[lIndex]=I2C__P_GetByte();

/* While the lIndex countig is below lBytesNumber - 1 sends ACK for each byte received */
if(lIndex<(lBytesNumber-1))
{
/* Forces and ACK if the condition is fulfilled */
I2C__P_SendACK();
}
/* Else sends NACK */
else
{
/* Forces NACK if else */
I2C__P_NACK();
}
}

/* Stops communitacion after the reading is completed */
I2C__P_StopCondition();

return(1);

} /* uint8_t I2C__P_ReadIOExpander(uint8_t lAddressI2C,uint8_t lMemAdd, uint8_t lBytesNumber, uint8_t * pStartBytes) */



/*******************************************************************************
*Function description:
* Gets the bytes to be read from the SDA port
*
*Input parameters:
* Has not input parameters
*
*Return:
* Byte: variable used to store the read bytes in
*
*Notes:
*.
********************************************************************************/
uint8_t I2C__P_GetByte(void)
{
/*
Local variables
*/


/* Variable used for counting the number of bytes read */
int8_t count;

/* Variable used to store the read bytes in */
int8_t Byte = 0;


/*
Procedure
*/


/*
To get the bytes held in SDA port
*/


/* Releases SDA line to allow the bytes to be read */
SDADir=I2CHIGH;

/* Initiares a loop to read 8 bits */
for(count=0;count<8;count++)
{
/* Equals the Byte variable to Byte OR data held in SDA port. ****fs the the next bits into position as the counting goes on */
Byte = Byte | ( SDA_INPUT << (7-count) ); //-- Shift next bit into position

/* Delay and clock pulse to ensure the data reception */
__delay_us(1);
I2C__P_Clock(); //-- Pulse the clock
}
/* Returns the data held in Byte variable to be used in I2C__P_ReadIOExpander function */
return(Byte);

} /* I2C__P_GetByte */



/*******************************************************************************
*Function description:
* Forces an ACK to read incoming data
*
*Input parameters:
* Has not input parameters
*
*Return:
* Has no return
*
*Notes:
*.
********************************************************************************/
void I2C__P_SendACK(void)
{
/*
Local variables
*/


/* No variables */


/*
Procedure
*/


/*
To generate ACK signal
*/

/* Pulls down SDA, generating ACK */
SDADir=I2CLOW;

/* Give it time to settle */
__delay_us(1);

/* Pulse the clock */
I2C__P_Clock();

/* Release SDA */
SDADir=I2CHIGH;

/* Give it time to settle */
__delay_us(1);

} /* void I2C__P_SendACK(void) */



/*******************************************************************************
*Function description:
* Forces a NACK to end communication
*
*Input parameters:
* Has not input parameters
*
*Return:
* Has no return
*
*Notes:
*.
********************************************************************************/
void I2C__P_NACK(void)
{
/*
Local variables
*/


/* No variables */


/*
Procedure
*/


/*
To generate NACK signal
*/


/* Release SDA*/
SDADir=I2CHIGH;

/* Give it time to settle */
__delay_us(1);

/* Pulse the clock */
I2C__P_Clock();

/* Pulls down SDA, genrating ACK */
SDADir=I2CLOW;

/* Give it time to settle */
__delay_us(1);

} /* void I2C__P_NACK(void) */



/*******************************************************************************
*Function description:
* Defines the delay in microseconds
*
*Input parameters:
* delay_us: The number of microseconds to wait during the delay
*
*Return:
* Has no return
*
*Notes:
*.
********************************************************************************/
extern void __delay_us(uint32_t delay_us)
{
/*
Local variables
*/


/* Variable to calculate the delay in microseconds */
uint32_t DelayStartTime;


/*
Procedure
*/


/*
To generate delay in microseconds
*/


/* Equals the variable to the core timer */
DelayStartTime = ReadCoreTimer();

/* Operations to transform the input number into microseconds */
while((ReadCoreTimer() - DelayStartTime) < (delay_us * CORE_TIMER_MICROSECONDS));
}


/*******************************************************************************
*
*End of File.
*
*******************************************************************************/

 
I2C_ROUTINES.H
 
/********************************************************************************
*Module name:
* I2C_ROUTINES.
*
*Module version:
* 1.00. Initial version.
*
*Module description:
* Includes the necessary functions the I2C protocol needs for its correct
* functionality
********************************************************************************/

/*
*Circular preventing errors by using macros.
*/
#ifndef I2C_ROUTINES_H
#define I2C_ROUTINES_H

/*Header files.*/
/********************************************************************************/
/********************************************************************************/
/*
*Not exist.
*/


/*Type definition.*/
/********************************************************************************/
/********************************************************************************/


/*Constant definition.*/
/********************************************************************************/
/********************************************************************************/

/* Definitions for 0 and 1 in 12C routines */

#define I2CLOW 0 /* Puts pin into output/low mode */
#define I2CHIGH 1 /* Puts pin into Input/high mode */


/*Macro definition.*/
/********************************************************************************/
/********************************************************************************/

/* SDA and SCL TRIS directions */
#define SCLDir TRISFbits.TRISF8
#define SDADir TRISFbits.TRISF2

/* SDA and SCL LAT pins */
#define SDAOut LATFbits.LATF2
#define SCLOut LATFbits.LATF8

/* SDA and SCL PORT pins */
#define SDA_INPUT PORTFbits.RF2
#define SCL_INPUT PORTFbits.RF8


/*Global functions. Extern character respect this module.*/
/********************************************************************************/
/********************************************************************************/

/* Write I/O expander */
extern uint8_t DRIVERI2C3__F_writeI2C3(uint8_t lAddressI2C,uint8_t lMemAdd, uint8_t lBytesNumber, uint8_t * pStartBytes);

/* Read from I/O Expander */
extern uint8_t DRIVERI2C3__F_readMemI2C3(uint8_t lAddressI2C,uint8_t lMemAdd, uint8_t lBytesNumber, uint8_t * pStartBytes);


/*Global variables. Extern character respect this module.*/
/********************************************************************************/
/********************************************************************************/
/*
*Not exist.
*/


#endif



/*******************************************************************************
*
*End of File.
*
*******************************************************************************/

#17
Jump to:
© 2019 APG vNext Commercial Version 4.5