Atmega328 as an external EEPROM programmer

Atmega328 as an external EEPROM programmer
Need extra memory for an AVR project, but don't want to splash out on a expensive EEPROM chip programmer?  It's easy to build your own!

The problem:
You need additional EEPROM memory for an AVR project, for example to hold data that wouldn't otherwise fit in the native AVR's EEPROM.  But how to get that data onto the external EEPROM?  

The solution:

Well... you could use an EEPROM programmer, but they can be expensive, and besides, you probably only want to write a few chips and it feels like overkill.  Why not use your AVR to accept data from your computer and then burn it directly onto your EEPROM chip?


PC --> AVR communication with USART
The easiest way to get your AVR to talk to your PC is to use a USB USART converter.  I picked one up for next to nothing. The one I got was a CP2012 STC.  To use it, hook up the USB TX line to the RX pin on your AVR, and the RX line to TX pin on your AVR.  There are also 5V, 3.3V and GND lines as required.  

Windows should recognise the device... otherwise you might need to mess around downloading the correct driver from Silicon Laboratories.  




Next we need some code to run on our AVR (I'm using an ATMEGA328p chip clocked at 8 MHz) using the UART interface.  This is a great blog post with code here. which I've reproduced below, changing the F_CPU clocking to 8 MHz:

#define F_CPU 8000000UL
#include <avr/io.h>
#include <util/delay.h>
#define BAUDRATE 9600
#define BAUD_PRESCALLER (((F_CPU / (BAUDRATE * 16UL))) - 1)
//Declaration of our functions
void USART_init(void);
unsigned char USART_receive(void);

void USART_send( unsigned char data);
void USART_putstring(char* StringPtr);
char String[]="Hello world!!"; //String[] is in fact an array but when we put the text between the " " symbols the compiler treats it as a String and automatically puts the null termination character in the end of the text

int main(void){
USART_init();   //Call the USART initialization code
while(1)
{//Infinite loop

 USART_putstring(String); //Pass the string to the USART_putstring function and sends it over the serial
 _delay_ms(5000);  //Delay for 5 seconds so it will re-send the string every 5 seconds
}
return 0;
}

void USART_init(void){
 UBRR0H = (uint8_t)(BAUD_PRESCALLER>>8);
 UBRR0L = (uint8_t)(BAUD_PRESCALLER);
 UCSR0B = (1<<RXEN0)|(1<<TXEN0);
 UCSR0C = ((1<<UCSZ00)|(1<<UCSZ01));
}

unsigned char USART_receive(void){
 while(!(UCSR0A & (1<<RXC0))); //loops forever until byte recieved
 return UDR0;
 }

void USART_send( unsigned char data){
 while(!(UCSR0A & (1<<UDRE0)));
 UDR0 = data;
}

void USART_putstring(char* StringPtr)
{
 while(*StringPtr != 0x00){
 USART_send(*StringPtr);
 StringPtr++;
 }
}
Simple, huh?  The USART_init function informs our AVR controller of the desired baud rate (bits per second) via the 16-bit UBRR0 register and the formula in BAUD_PRESCALLER macro.  THe UCSR0B register enables the receiver (RX) and transmitter (TX).

Once this is burnt to your Atmega328 controller, you'll need something running "PC-side" (not a computer massacre...) to exchange characters back and forth as you like.  As a test of this code, open a program such as Putty, connect to the USB UART device (COM5), at a baudrate of 9600 bps, as defined in the above code.  If all is successful, you should see "Hello world!!" appearing every 5 seconds.  That's never going to get annoying.


We can check if there is a character sent from the PC (via Putty, for example)  by calling a new function, USART_waiting, derived from USART_receive.


unsigned char USART_waiting(void){
 return (UCSR0A & (1<<RXC0));
}
On our Atmega328 chip we can periodically query the USART_waiting function, and if a character is waiting, append it to a growing buffer.  We can check if it's a carriage return, and if so, take some action based on the string, i.e.:

#define COMMAND_LEN 66
.
.
.
void collate_input(char *StringPtr)
{
 char rcv=0;
 int pos=COMMAND_LEN,i;
 
 for (i=0;i<COMMAND_LEN;i++) // Find first null position in string
 {    // (we could store this in a variable I suppose...)
  if (StringPtr[i]==0)
   {pos=i;break;} 
 }
 rcv = USART_receive();
        if (rcv==10)
  { 
   StringPtr[pos] = '\0';
   //if (pos!=0) DO SOMETHING WITH StringPtr
                        //wipe StringPtr
   for (i=0;i<COMMAND_LEN;i++)
    StringPtr[i]=0;
   StringPtr[0] = '\0';
  }
 else if (pos<COMMAND_LEN)
  StringPtr[pos] = rcv;
 else USART_putstring("Command length too long!");
}
.
.
.
int main(void)
{
 char input[COMMAND_LEN]=""; 
 USART_init();

  while(1)
  {
      if (USART_waiting())
      {
      collate_input(input);
      }
  }
}

We *can* send characters from Putty to our controller and interpret them by stringing them together on the AVR controller, but we want to send whole files!  For this we'll need to write a custom Python program.  Python being Python, there are of course serial communication libraries already written.  The following code will dispatch commands to your microcontroller and wait for a reply:  
import time
import serial

# configure the serial connections (the parameters differs on the device you are connecting to)
ser = serial.Serial(
    port='\\.\COM5',
    baudrate=9600,
    bytesize = serial.EIGHTBITS, #number of bits per bytes
    parity = serial.PARITY_NONE, #set parity check: no parity
    stopbits = serial.STOPBITS_ONE, #number of stop bits
    interCharTimeout=None,
    writeTimeout=None,
    xonxoff = True
)

ser.isOpen()

print 'Enter your commands below.\r\nInsert "exit" to exit the application.'
print 'or type "leave" to return to prompt in IDLE'

input=1

out=''
while ser.inWaiting() > 0:
            out += ser.read(1)
print out

while 1 :
    # get keyboard input
    input = raw_input(">> ")
        # Python 3 users
        # input = input(">> ")
    if input == 'leave':
        ser.close()
        break
    
    if input == 'exit':
        ser.close()
        exit()
    else:
        for i in input:
            ser.write(i)
            time.sleep(0.001)
        out = ''
        # let's wait one second before reading output (let's give device time to answer)
        time.sleep(1)
        while ser.inWaiting() > 0:
            out += ser.read(1)

        if out != '':
            print ">>" + out
Just a side note here- I had problems with maintaining 100% fidelity if I sent characters too quickly - hence the time.sleep(0.001) between each character send.  Unfortunately this will have the effect of seriously slowing down sending large amounts of data by this method.

Now we can send our Atmega328 characters and strings, we need to get it to accept commands and do something useful.  In a later blog post we'll cover the actual EEPROM writing itself, then putting the whole lot together: send a file from the PC to the AVR, which then gets written to the EEPROM chip.

Now the ingredients are in place - an AVR chip that can talk to your PC and a way of sending data from the PC to the chip... For now I will leave it to you to open files and send strings of data this way to the AVR chip, which will then program your external EEPROM.  Maybe later I'll post some code for EEPROM programming by the AVR (Atmega328), and a simple command language I wrote for implementing various read/write functions by sending commands from the PC.

Comments

Popular posts from this blog

Getting started with the Pro Micro Arduino Board

Arduino and Raspberry Pi serial communciation