Programming Static RAM with a PIC16F877A

I’m currently working on my own Z80 computer and one crucial part that I don’t have is ROM, or more precisely, an EPROM programmer. I found that they are very expensive and heard that programming an EPROM chip takes ages, so I needed another solution.

Since I have SRAM chips lying around, I thought I’d use one of them to store the program. I would need to load a program into it somehow, and since it’s volatile memory I would need to  repeat this process every single time after I cut off power from the board.

Luckily I also happen to have a couple of Microchip PIC microcontrollers lying around. One of them is the PIC16F877A, a 40-pin 8-bit microcontroller that has a lot of I/O pins and also a USART port. So I used a MAX232 chip and hooked it up to the serial port of my computer. In the end I built this (don’t mind my horrible schematic drawing skills):

Schematic to upload data to SRAM

 

Address and data busses are connected as follows:

  • A0 is connected to RE2.
  • A1-A7 is connected to PORTB<1:7>.
  • A8-A13 is connected to PORTA<0:5>.
  • A14-A15 is connected to PORTE<0:1>.
  • D0-D7 is connected to PORTD<0:7>.

The reason why I didn’t connect A0 to PORTB<0> is that it can also be used as an external interrupt (which I plan on using later).

I used the HI-TECH PIC C compiler to write code that reads 32768 bytes from the serial port and writes it to the RAM chip. Here is the code for those who would like to do something similar:

#include 

// Unsigned char typedef
typedef unsigned char  uchar;
typedef unsigned short ushort;

// Define crystal oscillator frequency
#define _XTAL_FREQ 20000000

// Define baud rate
#define BAUD_RATE 57600

// Define memory size
#define MEM_SIZE 4 * 1024

// Define some useful functions
#define HIBYTE( x ) ( ( uchar )( x >> 8 ) )
#define LOBYTE( x ) ( ( uchar )( x & 0xFF ) )

// Sets up the serial port in asynchronous mode
void SetupSerialPort( void )
{
	// Setup TXSTA register
	TX9	  = 0;	// 8-bit transmission
	TXEN  = 0;	// Transmit disabled
	TXEN  = 1;	// Transmit enabled (effectively resetting it)
	SYNC  = 0;	// Asynchronous mode
	BRGH  = 1;	// Select High Baud Rate

	// Setup RCSTA register
	SPEN  = 1;	// Enable Serial Port
	RX9	  = 0;	// 8-bit transmission
	CREN  = 1;	// Enable continous receive
	ADDEN = 0;	// Disable address detection

	// Setup SPBRG register
	// Divider is calculated as:
	// BAUD_RATE = Fosc / ( 16 * ( DIVIDER + 1 ) )
	// which gives:
	// DIVIDER = ( Fosc / ( 16 * BAUD_RATE ) ) - 1
	// which in the case of a 20MHz Fosc and baud
	// rate of DIVIDER is 20;
	#define DIVIDER ( _XTAL_FREQ / ( 16 * BAUD_RATE ) ) - 1
	#assert DIVIDER == 20
	SPBRG = DIVIDER;

	// Disable RX/TX interrupts
	TXIE = 0;
	RCIE = 0;
}

// Resets the Usart in case of errors
void ClearUsartErrors( void )
{
	// Overrun error
	if( OERR )
	{
		TXEN = 0;
		TXEN = 1;
		CREN = 0;
		CREN = 1;
	}
	// Framing error
	if( FERR )
	{
		uchar dummy = RCREG;
		TXEN = 0;
		TXEN = 1;
	}
}

// Writes a character to the serial port
void PutChar( uchar c )
{
	while( !TXIF )
	{
		ClearUsartErrors();
		CLRWDT();
	}

	TXREG = c;
	__delay_us( 60 );
}

// Reads character from the serial port without timeout
uchar GetChar( void )
{
	while( !RCIF )
	{
		CLRWDT();
		ClearUsartErrors();
	}

	return RCREG;
}

// Configures the ports
void SetupIO( void )
{
	// Set Port A and E to digitil I/O pins
	ADON   = 0;
	ADCON1 = 0b00000110;

	// Set Ports as output
	TRISA = 0x00;
	TRISB = 0x00;
	TRISC = TRISC & 0b11110001;
	TRISD = 0x00;
	TRISE = 0x00;

	// Disable !CS
	RC1 = 1;

	// Disable !WE
	RC2 = 1;

	// Disable !OE
	RC3 = 1;
}

// Writes contents of memory to serial port
void MemoryToSerial( void )
{
	uchar  data;
	ushort address;

	// Set port D as input
	TRISD = 0xFF;

	// Enable !CS
	RC1 = 0;

	// Enable !OE
	RC3 = 0;

	for( address = 0; address < MEM_SIZE; address++ )
	{
		// Set address
		uchar lowAddr  = LOBYTE( address );
 		uchar highAddr = HIBYTE( address );

 		PORTB = lowAddr;
 		RE2	  = lowAddr & 0x01;
 		PORTA = highAddr;
 		RE0	  = ( highAddr & 0x40 ) >> 6;
		RE1   = ( highAddr & 0x80 ) >> 7;

		// Read data
		data = PORTD;

		PutChar( data );
	}
}

// Writes data from serial port to memory
void SerialToMemory( void )
{
	uchar  data;
	ushort address;

	for( address = 0; address < MEM_SIZE; address++ )
 	{
 		// Read data byte
 		data = GetChar();

 		// Set address
 		uchar lowAddr  = LOBYTE( address );
 		uchar highAddr = HIBYTE( address );

 		PORTB = lowAddr;
 		RE2	  = lowAddr & 0x01;
 		PORTA = highAddr;
 		RE0	  = ( highAddr & 0x40 ) >> 6;
		RE1   = ( highAddr & 0x80 ) >> 7;

		// Enable !CS
		RC1 = 0;

		// Enable !WE
		RC2 = 0;

		__delay_us( 1 );

		// Set data
		PORTD = data;

		// Disable !WE
		RC2 = 1;

		// Disable !CS
		RC1 = 1;
	}
}

void main( void )
{
	OPTION_REG = 0xFF;
	CLRWDT();

	SetupSerialPort();
	SetupIO();

	SerialToMemory();
	MemoryToSerial();	

	while( 1 )
	{
		if( GetChar() == 'd' )
		{
			MemoryToSerial();
		}
	}

	return;
}

Here’s a short video to see it in action:

    • Moacir Jr.
    • January 12th, 2012

    Hi,

    Me also was making a computer z80. I am make the same, testing in RAM memory, and now, I am just programing in flash memory 29010 (AMIC 128K x 8), and yesterday I received my AT28C256 (EEPROM ATMEL) :) .

    I am working now in a serial programer. My older programer is a paralel programer, that uses a paralel port, but I have one PC with paralel port, and all other have USB only. I am just communicate with my PIC (16F628A), and his work very fine. Today and tomorrow, I think that my test’s will end and I will can begin to write in my 28C256.

    I Thiking in do a blog like this, but I dont have time. :o

    Moacir Jr.

  1. No trackbacks yet.