Blue Bird Serial EEPROM Board

Interfacing Serial EEPROM Board into the Blue Bird Board

I received an email telling me that one of the boards that I had built was on its way to me.  I was wondering which one it was as I have two out to be fabbed right now.  It didn’t take long to find out as the boards were in the mail when I got home.  I guess the email was a little slow in getting to me.

The board was the Serial EEPROM board for the Blue Bird boards I have been working on.  This board contains an I2C serial EEPROM as well as an I2C temperature sensor.  Even though the board has these two parts on it, it only takes 2 pins the SDA and SCL pins on the processor.  This is a very cool board and I went about building one up to test it.

My first challenge with the board was how to set the address resistors.  I added these resistors so that I could stack multiple boards and have them addressed at different locations.  Once I had that figured out the rest of the board built pretty quickly once I got the two chips soldered down.  The board only took me about 10 minutes to build even figuring out the addressing registers.  With those figured out I think I could build the board in about 6-7 minutes.  I also soldered on the Arduino type pins so that I could stack them.

Once I had the board built I stacked it on a Blue Bird board and started looking at the code to drive the EEPROM.  I thought it would be good to start with the code from one of the library tutorials here.  Looking at this code the biggest problem I see is I wrote it to use the UART.  I wanted to use the USB port.  That was the first order of business to convert all the communication over to use the USB port.  I did but missed a part where the string in the EEPROM weren’t printed.  I found this out later.  I got the communication working and tried to write something to the EEPROM.  It seemed to go fine but then I couldn’t read the values written to the EEPROM.  I found out that I hadn’t converted the read routine to use the USB rather than the UART for its output.  I fixed that and wow the board started to work.

I was pretty impressed that the board worked without any cuts or jumpers.  I did check the board thoroughly before sending it off.  Here is a picture of the board.

Blue Bird Serial EEPROM Board

Blue Bird Serial EEPROM Board

You can see the addressing resister below the EEPROM and above the temperature sensor.  The addressing resisters are set for 0 on both the EEPROM and temperature sensor.  I thought that would be a good place to start.  You can also see the female connectors from this angle.

The schematic looks like the following:

Blue Bird Serial EEPROM board schema

Blue Bird Serial EEPROM board schema

Here you can see how the address resistors are wired.  I used 0 ohm resistors to ground the address lines on both chips.

The code is pretty simple.  It is very similar to the code from the library tutorial.  Like I said above I just changed the user interface from the UART to the USB.  That worked surprisingly well and it was fairly easy to change.  With the interface in place I was able to test the EEPROM board and verify that the EEPROM was working.  I didn’t have a chance to test the temperature sensor but have I believe that it will work also as I have used this chip before and have hooked it up in the same manner as before.

/**************************************************************
 * Copyright (c) 2014 KMI Technology, Inc. All rights reserved.
 *
 * File Name: main.c
 *
 * Project: PIC24 Library Tests with Blue Bird
 *
 * Module: EEPROM/USB interface
 *
 * Owner: Kim Mansfield
 *
 * Description: This module demonstrates using the USB CDC interface
 *  to drive an I2C serial EEPROM to store and retrieve values from
 *  the EEPROM.
 **************************************************************/
#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

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

unsigned int on = 0;
unsigned int address;

// Global external variables
extern int numBytesRead;
extern char USB_In_Buffer[256];
extern char USB_Out_Buffer[64];
extern unsigned char NextUSBOut;

// routine prototypes
void handChars(int numChars,char *chars);

/******************************************************************************
 * Function void writeUSB(char *str)
 *
 * This function sends the ascii string to the USB port.
 *
 * Input:           str pointer to an null terminated ASCII string.
 *
 * Output:          ASCII string sent out USB port
 *
 *****************************************************************************/
void
writeUSB(char *str)
{
   strcpy(USB_Out_Buffer,str);
   NextUSBOut = strlen(USB_Out_Buffer);
}

/******************************************************************************
 * Function void handUSB(void)
 *
 * This routine is called by the RTOS to handle the output of the USB port.
 *
 * Input:           none
 *
 * Output:          none
 *
 *****************************************************************************/
void
handUSB(void)
{
  char charToSend;

  if (numBytesRead > 0)
  {
    handChars(numBytesRead,USB_In_Buffer);
    numBytesRead = 0;
  }
  queueUSB(handUSB);
}

/******************************************************************************
 * Function void writeStr(void)
 *
 * This routine writes a global variable str out to the USB port.
 *
 * Input:           none
 *
 * Output:          none
 *
 *****************************************************************************/
void
writeStr(void)
{
  if (error)
  {
    writeUSB("\r\nDevice not found \r\nCMD> ");
  }
  else
  {
    sprintf(line,"\r\n %s\r\nCMD>",str);
    writeUSB(line);
  }
}

/******************************************************************************
 * Function void writeCmd(void)
 *
 * This routine is called by the OS after the I2C command is completed.
 *
 * Input:           none
 *
 * Output:          none
 *
 *****************************************************************************/
void
writeCmd(void)
{
  if (error)
  {
    writeUSB("\r\nDevice not found");
  }
  writeUSB("\r\nCMD> ");
}

/******************************************************************************
 * Function void handChars(int numChars,char *chars)
 *
 * This routine is called by the handUSB routine to handle any 
 * characters received by the USB port.
 *
 * Input:           numChars is the number of characters to process
 *                  chars is the characters to process
 *
 * Output:          none
 *
 *****************************************************************************/
void
handChars(int numChars,char *chars)
{
  int sChar;
  int index;
  char echo[2];

  index = 0;
  while (numChars > 0)
  {
    sChar = chars[index++];
    numChars--;
    echo[0] = sChar;
    echo[1] = 0;
    writeUSB(echo);
    switch(serState)
    {
      case 0:
        switch (sChar)
        {
          case '\r':
            writeUSB("\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;
            writeUSB("\r\nString to write> ");
            break;
          case 'a':
          case 'A':
            serState = 2;
            address = 0;
            writeUSB("\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;
            writeUSB("\r\nCMD> ");
            break;
          default:
            sChar = sChar - 0x30;
            if (sChar > 0x09) sChar -= 7;
            address <<= 4;
            address += sChar;
            break;
        }
        break;
    }
  }
}

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

  i2cInit(SPEED100K);

  queueUSB(handUSB);

  writeUSB("\r\nBlue Bird I2C test Ver 0.1\r\n");
  writeUSB("\r\nCMD> ");
  while(1)
  {
    runNext();
  }//end while
}//end main


One thing you will note in the code is the call to initializeSystem() and the call right after that of InitializeSystem().  These calls look very similar and are only different by the lower and uppeer case I.  These are different routines but, I agree the names are too close to being the same and one of them needs to change.  I’ll work on that.  After the two initialization routines the I2C initialization is called to setup the I2C interface.  After that I setup the routine to handle communications with the USB port with some strings output to the USB port then into the main loop.

At the top of the source code are some helper routines for the USB port.  The first one outputs ASCII text to the USB port.  The second one is the routine that is called by the USB system code to handle characters from the USB port.  There is the writeStr routine which is a callback function used by the I2C routines at the end of an I2C transaction.  This routine outputs the text returned from reading the EEPROM.  The writeCmd is another callback function used by the I2C write routines at the end of a write to the I2C system.  The handChars routine takes all the input characters from the USB port and using a state machine handles the characters and does the appropriate things with each character.

It might be a good idea to take a look at the handChars routine as it is a good example of the use of a state machine to interpret user input via a serial interface.  You will see this technique used in many places.

Well that is it for now.  Have fun.