The Engineer Tutor PIC24FJ Library – I2C Master

I would now like to work on an I2C master interface to some I2C devices.  This is a really nice interface.  It allows you to talk to all kinds of devices, temperature sensor, EEPROM, parallel ports, serial ports, codecs, audio filters, audio attenuators, and too many others to name.  This is one of my favorite interfaces.  I have been using it for a long time and have talked to many different devices.

In this tutorial I’ll show you how to talk to a serial EEPROM.  This is a very basic device and can be very useful in storing data for later retrieval when you are hooked up to a computer or other device.

This peripheral doesn’t allow you to remap the pins.  There is no pin setup for this device.  The pins we are going to use are pins 18 (SDA) and pins 17 (SCL).  The SDA pin is the data pin and it is bidirectional.  The SCL pin is the clock and when the peripheral is in the master mode this pin is an output only.  If you would like an explanation of the I2C bus google is your friend.

To run this code you have to do the normal things and build this main against the latest library in the downloads section.  Once you have that done you have to hook up the serial port at 9600 baud.  You have several commands:

a – allows you to set an address.  You can use hexadecimal characters to set a two byte address.

r – reads from the EEPROM what is at the address you selected in the a command

w – writes a string you type in at the prompt.

That’s it.  It is pretty simple.

Take a look at the schematic for how this has been hooked up.

PARTS –

SCHEMATICS –

NOTE: this has not been tested in a multiple master environment.

#include <p24fxxxx.h>
#include <string.h>
#include "rtos.h"
#include "pins.h"
#include "initializeSystem.h"
#include "uart1.h"
#include "uart1Pins.h"
#include "pwmPins.h"
#include "pwm.h"
#include "masterI2C.h"

#define FIVE_MINUTE_DELAY 5
#define TEN_MINUTE_DELAY 10
#define TWENTY_MINUTE_DELAY 20

unsigned int on = 0;

//
/******************************************************************************
 * Function void blink(void)
 *
 * This function is called by the RTOS when the delay has expired.  Interesting
 * to note that you have to call the delay routine again to get another delay.
 * The LED is flashed in this routine.
 *
 * PreCondition:    None
 *
 * Input:           None
 *
 * Output:          None
 *
 * Side Effects:    blinks the LED and also toggles the on variable between 1 and
 *                  0.
 *
 *****************************************************************************/
void
blink(void)
{
  if (on)
  {
    digitalWrite(16,HIGH);
    on = 0;
  }
  else
  {
    digitalWrite(16,LOW);
    on = 1;
  }
  if (!delayMS(blink,500))
  {
    runNext(); // next item to be run
    // wasn't able to add to the delay queue.  It must be full.
    // Try again.
    while (!delayMS(blink,500))
      runNext();
    // or you could setup an infinite loop to catch the error in
    // a debugger.
    // while (1) 
    //    ;
  }
}

unsigned int serState = 0;
char str[32];
unsigned int strPtr = 0;
unsigned char loc[2];
unsigned char error;
#define WRITE 0
#define READ 1

unsigned int address;

void
writeStr(void)
{
  if (error)
  {
    uart1ps("\r\nDevice not found ");
  }
  uart1ps("\r\n");
  uart1ps(str);
  uart1ps("\r\nCMD> ");
}

void
writeCmd(void)
{
  if (error)
  {
    uart1ps("\r\nDevice not found");
  }
  uart1ps("\r\nCMD> ");
}

void
serial1Handler(void)
{
  int sChar;

  while (uart1IsChar())
  {
    sChar = uart1GetChar();
    uart1put((unsigned char)sChar);
    switch(serState)
    {
      case 0:
        switch (sChar)
        {
          case '\r':
            uart1ps("\r\nCMD> ");
            break;
          case 'r':
          case 'R':
            i2cQueue(READ,0x50,loc,2,(unsigned char *)str,32,&error,writeStr);
            break;
          case 'w':
          case 'W':
            serState = 1;
            strPtr = 0;
            uart1ps("\r\nString to write> ");
            break;
          case 'a':
          case 'A':
            serState = 2;
            address = 0;
            uart1ps("\r\nAddress to write to> ");
            break;
        }
        break;
      case 1:
        switch (sChar)
        {
          case '\r':
            // write string to eeprom
            serState = 0;
            str[strPtr] = 0;
            i2cQueue(WRITE,0x50,loc,2,(unsigned char *)str,strlen(str),&error,writeCmd);
            break;
          default:
            str[strPtr++] = sChar;
            break;
        }
        break;
      case 2:
        switch (sChar)
        {
          case '\r':
            serState = 0;
            // save address to write to.
            loc[0] = address >> 8;
            loc[1] = address;
            uart1ps("\r\nCMD> ");
            break;
          default:
            sChar = sChar - 0x30;
            if (sChar > 0x09) sChar -= 7;
            address <<= 4;
            address += sChar;
            break;
        }
        break;
    }
  }

  // wait for next character
  while (!queueSerial1(serial1Handler))
    runNext();
}

unsigned int val = 10;
unsigned int dir = 0;
#define BLINKSPEED 10

void
pwmTest(void)
{
  if (dir)
  {
    val += 1000;
    if (val > 63000)
    {
      dir = 0;
    }
  }
  else
  {
    val -= 1000;
    if (val < 2000)
    {
      dir = 1;
    }
  }
  analogWrite(0,val);
  //analogWrite(1,val);
  //analogWrite(2,val);
  //analogWrite(3,val);
  //analogWrite(4,val);
  if (!delayMS(pwmTest,BLINKSPEED))
  {
    runNext(); // next item to be run
    // wasn't able to add to the delay queue.  It must be full.
    // Try again.
    while (!delayMS(pwmTest,BLINKSPEED))
      runNext();
    // or you could setup an infinite loop to catch the error in
    // a debugger.
    // while (1) 
    //    ;
  }
}


/******************************************************************************
 * Function:        void main(void)
 *
 * PreCondition:    None
 *
 * Input:           None
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        Main program entry point.
 *
 * Note:            None
 *****************************************************************************/
int main(void)
{   
  initializeSystem();

  pinMode(16, OUTPUT);

  // make serial pins inputs
  pinMode(11, INPUT);
  pinMode(24, INPUT);

  // map rx pin to pin 11 and tx pin to pin 24
  uart1Pins(11,24);
  uart1Init(9600);

  // map pwm0 to pin 25
  pwmOutPins(0,25);
  pwmInit();

  i2cInit(SPEED100K);

  while (!queueSerial1(serial1Handler))
    runNext();

  while (!delayMS(blink,500))
    runNext();

  while (!delayMS(pwmTest,BLINKSPEED))
    runNext();

  uart1ps("\r\nI2C test Ver 0.1\r\n");
  uart1ps("\r\nCMD> ");
  while(1)
  {
    runNext();
  }//end while
}//end main