Parallel Port - Programming Serial EEPROM 93C56

copyright Peter H. Anderson, Morgan State University
Baltimore, MD 21239, Dec, '96
Introduction.

This piece discusses how to program such serial EEPROM devices as the 93C46/56/66 using the parallel port. The programmed memories might then be interfaced with a STAMP, PIC, 68HC11 for such applications as storing an A/D lookup table or various messages used on an LCD panel or terminal.

There is some confusion in the industry as National Semiconductor markets a device under the same number (NM93C56A) and the protocol differs slightly from the Microchip series.

This discussion deals specifically with the Microchip 93C56 serial EEPROM. I purchased 93C56 devices from Jameco (nominally $1.00) and these are marked with the manufacturer code CGI and they appear to be like the Microchip devices. Jameco sent a data s heet from Amtel for an AT93C56 and the protocol appears to also be the same as Microchip.

All of this may sound confusing, and indeed it is. I do know that the following routines will work with the Microchip devices and it will work with the devices which are currently marketed by Jameco.

I found the Microchip data sheet to be the most readable. This may be downloaded in .pdf format from http://www.microchip.com and then read or printed using an Acrobat reader.

Hardware Connection of Parallel Port to EEPROM.

     Parallel Port            93C56

     Data_0 (term 2) ------>    CS (term 1)
     Data_1 (term 3) ------>    SK (term 2)
     Data_2 (term 4) ------>    DI (term 3)

     Status_4 (term 13) <---    DO (term 4)

     Ground (term 18)

               	    +5  ----    V_cc (term 8)
                    GRD ----    GRD (term 5)
                    GRD ----    ORG (term 6) (8 X 256 organization)

Programs.

EEPROM1.C.

The intent of program EEPROM1.C is to illustrate all feature of the protocol. However, it has direct applications in serving as a lookup table in interfacing with a STAMP, PIC, 68HC11 or similar. That is, the STAMP might obtain a quantity, perhaps a pul se count. This might then be mapped into an address in the range of 0 to 255, and then the Stamp fetches the byte at that location. In such an application, the Parallel Port may be used to program the EEPROM. All the STAMP must do is read from the EEPR OM. I have an application where this technique is used to measure temperature on my home page (http://www.access.digex.net/~pha/stamp).

Program EEPROM1.C opens a file where each line consists of an address and the byte to be programmed into that address. For example;

     2    225
     3    0
     4    80
     255  80

Each line is fetched and the data is programmed into the specified address.

When completed, the user is instructed to turn off power and then turn it on to demonstrate the data has been indeed stored in ROM. The content of the 256 locations is then displayed.

This routine contains functions to enable and disable the erase and write feature (write_enable() and disable_eeprom()), to erase all locations (erase_all()), to erase a single location (erase()), to write a fixed value to all locations (write_all()), to write data to a specific address (write_byte()) and to read from an address location (read_byte()).

Each command consists of twelve bits (three nibbles) which are shifted out, beginning with the most significant bit, using function send instruction(). In the #defines below, note that each instruction begins with a start bit, which is logic one. The ne xt two, three or four bits are used by the EEPROM to determine the nature of the operation. The lower eight bits are the address.

Note however, there is no address associated with the enable and disable and the erase all and write all commands. Thus, in these functions, the lower bits are set to logic zero. For commands dealing with a specific address, the high nibble opcode defin ing the operation is ored with the address in the lower two nibbles. Thus, all commands are 12 bits. (The National Semiconductor device uses an 11 bit format).

In writing data, this 12 bit instruction is followed with the 8 bits of data. This is implemented in function send_8().

For commands involving either an erase or a write, the CS is brought momentarily low and the EEPROM output DO is monitored until it assumes a logic one, indicating the EEPROM has completed the operation. This is read on the SELECT input on the parallel p ort. This is implemented in function check_done(). Note that this is not done for the enable, disable and read commands.

For the read command, the data is read on parallel port input Select (Term 13). This is implemented in function get_data().

I am hopeful this is a sufficient description to enable you to use and modify my code to suit your purposes. It is suggested that you also use a data sheet.

/*
** EEPROM1.C
**
** Illustrates how to program 93C56 Serial EEPROM using the Parallel
** Port. Device is first enabled for Write and Erase (EWEN).  All
** locations are erased (ERAL).
**
** A text file containing the addresses and the bytes to be programmed
** in that address is then opened.  Each line in the file is then read
** in turn and the data is programmed to the specified location using
** the write_byte function.
**
** Upon completion, the erase and write function is disabled and all
** locations are dumped to the terminal using the read_byte function.
**
** Peter Anderson, MSU, Dec 14, '96
**
*/

#include <stdio.h>
#include <dos.h>
#include <conio.h>

#define DATA 0x03bc
#define STATUS DATA+1

#define READ 0xc00  /* 1 10 A8 A7A6A5A4 A3A2A1A0 */
#define EWEN 0x980  /* 1 001 1XXX XXXX */
#define ERASE 0xe00 /* 1 11 A8 A7A6A5A4 A3A2A1A0 */
#define WRITE 0xa00 /* 1 01 A8 A7A6A5A4 A3A2A1A0 */
#define ERAL 0x900  /* 1 00 1  0A6A5A4 A3A2A1A0 */
#define WRAL 0x880  /* 1 00 0  1A6A5A4 A3A2A1A0 */
#define EWDS 0x800  /* 1 00 0  0A6A5A4 A3A2A1A0 */

#define TIME 10

int data=0x00;

void write_byte(int adr, int d);
int read_byte(int adr);
int get_data(void);

void write_enable(void);
void erase_all(void);
void write_all(int d);
void erase(int adr);
void disable_eeprom(void);

void send_instruction(int instr);
void send_data(int d);
void check_done(void);

void clock(void);
void cs_high(void);
void cs_low(void);

void t_delay(int t);

void main(void)
{
   char str[80];
   int adr, byte, n;

   FILE *f1;

   clrscr();

   write_enable();
   erase_all();

   f1=fopen("a:eeprom.dat", "rt"); /* open the data file */

   while(1)
   {
      if (fgets(str, 80, f1) == NULL) /* do until no more lines */
      {
         break;  /*end of file */
      }
      else
      {
         sscanf(str, "%d %d", &adr, &byte); /* write data to adr */
         write_byte(adr, byte);
      }
   }
   printf("Programming Completed\n");
   disable_eeprom();
   fclose(f1);

   printf("Power down and then power up the EEPROM.\
Type any key to continue.");

   while(!kbhit())  /*loop until a key is hit*/ ;
   getch();
   /* now list the content of the EEPROM */
   for(n=0; n<256; n++)
   {
      if(n==0x80)  /* if half way in displaying data */
      {
         printf("\n****MORE****\n");
         while(!kbhit())  /*loop until a key is hit*/  ;
         getch();
      }

      if(n%16 == 0)  /* start a new line */
      {
         printf("\n%.2x:", n);
      }
      byte=read_byte(n);
      printf("  %.2x", byte);
   }
}

void write_byte(int adr, int d)
/* writes byte d to location adr */
{
   int instr;
   cs_high();
   instr=WRITE + adr;
   send_instruction(instr);
   send_data(d);
   check_done();
   cs_low();
}

int read_byte(int adr)
/* returns byte at location adr */
{
   int d, instr;
   cs_high();
   instr= READ+adr;
   send_instruction(instr);
   d = get_data();
   cs_low();
   return(d);
}

int get_data(void)
/* fetch each bit in turn, ms bit first */
{
   int in, d=0, n;
   for(n=7; n>=0; n--)
   {
      clock();
      in = (((inportb(STATUS)^0x80) >> 4) &0x01);
      d = d | (in << n);
   }
   return(d);
}

void write_enable(void)
/* enable eeprom for writes and erases */
{
   int instr;
   cs_high();
   instr=EWEN;
   send_instruction(instr);
   cs_low();
}

void erase_all(void)
/* erases all bytes */
{
   int instr;
   cs_high();
   instr=ERAL;
   send_instruction(instr);
   check_done();
   cs_low();
}

void write_all(int d)
/* writes specified byte d to all locations */
{
   int instr;
   cs_high();
   instr= WRAL;
   send_instruction(instr);
   send_data(d);
   check_done();
   cs_low();
}

void erase(int adr)
/* earses specified location */
{
   int instr;
   cs_high();
   instr=ERASE + adr;
   send_instruction(instr);
   check_done();
   cs_low();
}

void disable_eeprom(void)
/* disable write and erase */
{
   int instr;
   cs_high();
   instr=EWDS;
   send_instruction(instr);
   cs_low();
}

void send_instruction(int instr)
/* sends 11 bit instruction */
{
   int n, di;
   for(n=11; n>=0; n--)
   {
      di=(instr>>n) & 0x01;
      data=(data & (~0x04)) | (di<<2);
      outportb(DATA, data);
      clock();
   }
   data=data&(~0x04);
   outportb(DATA, data);
}

void send_data(int d)
/* sends 8 bit data d */
{
   int n, di;
   for(n=7; n>=0; n--)
   {
      di=(d>>n) & 0x01;
      data=(data & (~0x04) | (di<<2));
      outportb(DATA, data);
      clock();
   }
}

void check_done(void)
/* brings CS low and then high and waits until eeprom is ready */
/* used by all erase and write commands */
{
   cs_low();
   t_delay(TIME);
   cs_high();
   while(( ((inportb(STATUS)^0x80) >> 4)&0x01) == 0)  /* wait */  ;
}

void clock(void)
{
   data=data | 0x02;
   outportb(DATA, data);
   t_delay(TIME);
   data=data & (~0x02);
   outportb(DATA, data);
   t_delay(TIME);
}

void cs_high(void)
{
   data= 0x01;
   outportb(DATA, data);
}

void cs_low(void)
{
   data=0x00;
   outportb(DATA, data);
}

void t_delay(int t)
/* for timing between clock pulses and CS 0 pulses */
{
   int n;
   for(n=0; n<t; n++)  /* just loop */  ;
}

EEPROM2.C

Program EEPROM2.C illustrates how to program strings into a serial EEPROM. This might be useful in interfacing with a STAMP or similar, in applications where messages are displayed either on a terminal or on an LCD display.

This is similar to program EEPROM1.C, except that functions write_string and read_string have been added.

Function void write_string(int adr, char *s) writes the string pointed to by s beginning at address location adr. Note that the null character which terminates the string is also written to memory. Thus, in reading the string using a STAMP or similar, t he programmer must test for zero to determine the end of the message.

/*
** EEPROM2.C
**
** Illustrates how to program 93C56 Serial EEPROM using the Parallel
** Port.
**
** Device is first enabled for Write and Erase.  All locations are
** erased.  Strings are written to addresses beginning at locations
** 0x00 and 0x50.  Device is disabled.  Strings are then immediately
** read and after power down.
**
** Peter Anderson, MSU, Dec 14, '96
*/

#include <stdio.h>
#include <dos.h>
#include <conio.h>

#define DATA 0x03bc
#define STATUS DATA+1

#define READ 0xc00  /* 1 10 A8 A7A6A5A4 A3A2A1A0 */
#define EWEN 0x980  /* 1 001 1XXX XXXX */
#define ERASE 0xe00 /* 1 11 A8 A7A6A5A4 A3A2A1A0 */
#define WRITE 0xa00 /* 1 01 A8 A7A6A5A4 A3A2A1A0 */
#define ERAL 0x900  /* 1 00 1  0A6A5A4 A3A2A1A0 */
#define WRAL 0x880  /* 1 00 0  1A6A5A4 A3A2A1A0 */
#define EWDS 0x800  /* 1 00 0  0A6A5A4 A3A2A1A0 */

#define TIME 10

int data=0x00;

void write_string(int address, char *s);
void write_byte(int adr, int d);

void read_string (int address, char *s);
int read_byte(int adr);
int get_data(void);

void write_enable(void);
void erase_all(void);
void write_all(int d);
void erase(int adr);
void disable_eeprom(void);

void send_instruction(int instr);
void send_data(int d);
void check_done(void);

void clock(void);
void cs_high(void);
void cs_low(void);

void t_delay(int t);

void main(void)
{
   char str1[80], str2[80];
   int dummy;

   clrscr();

   write_enable();
   erase_all();
   /* program one string to eeprom */
   strcpy(str1, "abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVW\
XYZ!@#$%^&*()_+\n\n");
   write_string(0x00, str1);

   /* now another */
   strcpy(str1, "!234567890\n\n");
   write_string(0x50, str1);

   erase(0x50); /* erase location 0x50 and replace with a '1'*/
   write(0x50, '1');

   disable_eeprom();
   printf("Programming Complete\n\n");

   read_string(0x00, str2);
   printf("%s", str2);
   read_string(0x50, str2);
   printf("%s", str2);

   printf("Power down and then power up the EEPROM.\
Hit any digit to continue.\n");
   while(!kbhit()) /* loop until a key is hit */

   read_string(0x00, str2);
   printf("%s", str2);
   read_string(0x50, str2);
   printf("%s", str2);
}

void write_string(int address, char *s)
/* writes string s, beginning at location specified in address */
{
   int n;
   char c;
   for(n=0; n<80; n++)
   {
      c=s[n];
      write_byte(address,(int) c);
      ++address;
      if (c == '\0') /* Note that \0 is also written to eeprom */
      {
         break;
      }
   }
}

void read_string (int address, char *s)
/* reads string s from location beginning at specified address */
/* expects NULL terminated string */
{
   int n;
   for(n=0; n<80; n++)
   {
      s[n]= read_byte(address);
      ++address;
      if (s[n] == '\0')
      {
         break;
      }
   }
}

void write_byte(int adr, int d)
/* writes byte d to location adr */
{
   int instr;
   cs_high();
   instr=WRITE + adr;
   send_instruction(instr);
   send_data(d);
   check_done();
   cs_low();
}

int read_byte(int adr)
/* returns byte at location adr */
{
   int d, instr;
   cs_high();
   instr= READ+adr;
   send_instruction(instr);
   d = get_data();
   cs_low();
   return(d);
}

int get_data(void)
/* fetch each bit in turn, ms bit first */
{
   int in, d=0, n;
   for(n=7; n>=0; n--)
   {
      clock();
      in = (((inportb(STATUS)^0x80) >> 4) &0x01);
      d = d | (in << n);
   }
   return(d);
}

void write_enable(void)
/* enable eeprom for writes and erases */
{
   int instr;
   cs_high();
   instr=EWEN;
   send_instruction(instr);
   cs_low();
}

void erase_all(void)
/* erases all bytes */
{
   int instr;
   cs_high();
   instr=ERAL;
   send_instruction(instr);
   check_done();
   cs_low();
}

void write_all(int d)
/* writes specified byte d to all locations */
{
   int instr;
   cs_high();
   instr= WRAL;
   send_instruction(instr);
   send_data(d);
   check_done();
   cs_low();
}

void erase(int adr)
/* earses specified location */
{
   int instr;
   cs_high();
   instr=ERASE + adr;
   send_instruction(instr);
   check_done();
   cs_low();
}

void disable_eeprom(void)
/* disable write and erase */
{
   int instr;
   cs_high();
   instr=EWDS;
   send_instruction(instr);
   cs_low();
}

void send_instruction(int instr)
/* sends 11 bit instruction */
{
   int n, di;
   for(n=11; n>=0; n--)
   {
      di=(instr>>n) & 0x01;
      data=(data & (~0x04)) | (di<<2);
      outportb(DATA, data);
      clock();
   }
   data=data&(~0x04);
   outportb(DATA, data);
}

void send_data(int d)
/* sends 8 bit data d */
{
   int n, di;
   for(n=7; n>=0; n--)
   {
      di=(d>>n) & 0x01;
      data=(data & (~0x04) | (di<<2));
      outportb(DATA, data);
      clock();
   }
}

void check_done(void)
/* brings CS low and then high and waits until eeprom is ready */
/* used by all erase and write commands */
{
   cs_low();
   t_delay(TIME);
   cs_high();
   while(( ((inportb(STATUS)^0x80) >> 4)&0x01) == 0)  /* wait */  ;
}

void clock(void)
{
   data=data | 0x02;
   outportb(DATA, data);
   t_delay(TIME);
   data=data & (~0x02);
   outportb(DATA, data);
   t_delay(TIME);
}

void cs_high(void)
{
   data= 0x01;
   outportb(DATA, data);
}

void cs_low(void)
{
   data=0x00;
   outportb(DATA, data);
}

void t_delay(int t)
/* for timing between clock pulses and CS 0 pulses */
{
   int n;
   for(n=0; n<t; n++)  /* just loop */  ;
}