R/W NVRam

The following is description of adaptation of EEPROM programmer written initially by K. Adcock. .. for a MEGA type Arduino board. Few mods have been made to the board   .. and the software   .. to be able to use it with DS1250 and DS1270 memory chips. This software allows to read and write in blocks of 16bytes at any address in the first 32K space from x0000  ... or read and write (binary files) in blocks of 1K, 2K, 4K, 8K, 16K or 32K bytes.

As shown this software (below) ... has the higher address lines 15,16,17 and 18 set to LOW .. so you can use it to manipulate data in the first 32K bytes of the RAM chip. For manipulation of memory in higher areas of the memory map .. adjust values of address lines in Arduino program (HIGH or LOW) to position the start of the data at that point. E.g. to access the top half of DS1250 .. set A18 HIGH and 15,16 and 17 to LOW.

Completed board ...

LED Indicator mods

There is 3 extra indicator LEDS used on these pins:

Steps in using the program:

Here is snapshot of Byte type data manipulation using Arduino COMM port:

NOTE: RED is sent text via COMM port

Image below is the resulting output after each command ..

Hence you can easily write small type of code in any part of the ram just using the comm port.

For larger code use the Windows app.

The app allows you to: (in binary format)

Sample Data View screen .. (in blocks of 1,2,4,8,16,or 32K bytes ... as selected).

Arduino Code

NRAM R/W program on Mega

// NVRam Programmer - code for an Arduino Mega 2560

//

// Written by K Adcock.

//       Jan 2016 - Initial release

//       Dec 2017 - Slide code tartups, to remove compiler errors for new Arduino IDE (1.8.5).

//   7th Dec 2017 - Updates from Dave Curran of Tynemouth Software, adding commands to enable/disable SDP.

//  10th Dec 2017 - Fixed one-byte EEPROM corruption (always byte 0) when unprotecting an EEPROM

//                  (doesn't matter if you write a ROM immediately after, but does matter if you use -unprotect in isolation)

//                - refactored code a bit (split loop() into different functions)

//                - properly looked at timings on the Atmel datasheet, and worked out that my delays

//                  during reads and writes were about 10,000 times too big!

//                  Reading and writing is now orders-of-magnitude quicker.

//  April 2018    - modified for NVRam application by Michael Cvetanovski, Australia

// Distributed under an acknowledgement licence, because I'm a shallow, attention-seeking tart. :)

//

// http://danceswithferrets.org/geekblog/?page_id=903

//

// This software presents a 9600-8N1 serial port. <CR> and <LF> enabled using arduino comms

//

// R[hex address]                         - reads 16 bytes of data from the NVRam

// W[hex address]:[data in two-char hex]  - writes up to 16 bytes of data to the NVRam

// P                                      - set write-protection bit (Atmels only, AFAIK)

// U                                      - clear write-protection bit (ditto)

// V                                      - prints the version string

//

// Any data read from the NVRam will have a CRC checksum appended to it (separated by a comma).

// If a string of data is sent with an optional checksum, then this will be checked

// before anything is written.

//

//This include seems to be part of Arduino IDE

#include <avr/pgmspace.h>

const char hex[] =

{

  '0', '1', '2', '3', '4', '5', '6', '7',

  '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'

};

//This is NVRam version

const char version_string[] = {"NVRam Version=0.01"};

//Note these pin assignments are different to original eeprom assignments

//Addreess pins 15,16,17,18 added but not used ..

//Assigned later as LOW or HIGH to suit

static const int kPin_Addr18  = 40;

static const int kPin_Addr17  = 39;

static const int kPin_Addr16  = 38;

static const int kPin_Addr15  = 37;

//All pins that are used

static const int kPin_Addr14  = 36;

static const int kPin_Addr12  = 34;

static const int kPin_Addr7   = 29;

static const int kPin_Addr6   = 28;

static const int kPin_Addr5   = 27;

static const int kPin_Addr4   = 26;

static const int kPin_Addr3   = 25;

static const int kPin_Addr2   = 24;

static const int kPin_Addr1   = 23;

static const int kPin_Addr0   = 22;

static const int kPin_Data0   = 5;

static const int kPin_Data1   = 6;

static const int kPin_Data2   = 7;

static const int kPin_nWE     = 2;

static const int kPin_Addr13  = 35;

static const int kPin_Addr8   = 30;

static const int kPin_Addr9   = 31;

static const int kPin_Addr11  = 33;

static const int kPin_nOE     = 4;

static const int kPin_Addr10  = 32;

static const int kPin_nCE     = 3;

static const int kPin_Data7   = 12;

static const int kPin_Data6   = 11;

static const int kPin_Data5   = 10;

static const int kPin_Data4   = 9;

static const int kPin_Data3   = 8;

static const int kPin_WaitingForInput  = 43;

static const int kPin_LED_Red = 41;

static const int kPin_LED_Grn = 42;

byte g_cmd[80]; // strings received from the controller will go in here

static const int kMaxBufferSize = 16;

byte buffer[kMaxBufferSize];

static const long int k_uTime_WritePulse_uS = 1;

static const long int k_uTime_ReadPulse_uS = 1;

// (to be honest, both of the above are about ten times too big - but the Arduino won't reliably

// delay down at the nanosecond level, so this is the best we can do.)

// the setup function runs once when you press reset or power the board

void setup()

{

  Serial.begin(9600); //setup comms @9600 N81

 

  // LED indicators

  pinMode(kPin_WaitingForInput, OUTPUT); digitalWrite(kPin_WaitingForInput, HIGH);

  pinMode(kPin_LED_Red, OUTPUT); digitalWrite(kPin_LED_Red, LOW);

  pinMode(kPin_LED_Grn, OUTPUT); digitalWrite(kPin_LED_Grn, LOW);

  // address lines are ALWAYS outputs

  pinMode(kPin_Addr0,  OUTPUT);

  pinMode(kPin_Addr1,  OUTPUT);

  pinMode(kPin_Addr2,  OUTPUT);

  pinMode(kPin_Addr3,  OUTPUT);

  pinMode(kPin_Addr4,  OUTPUT);

  pinMode(kPin_Addr5,  OUTPUT);

  pinMode(kPin_Addr6,  OUTPUT);

  pinMode(kPin_Addr7,  OUTPUT);

  pinMode(kPin_Addr8,  OUTPUT);

  pinMode(kPin_Addr9,  OUTPUT);

  pinMode(kPin_Addr10, OUTPUT);

  pinMode(kPin_Addr11, OUTPUT);

  pinMode(kPin_Addr12, OUTPUT);

  pinMode(kPin_Addr13, OUTPUT);

  pinMode(kPin_Addr14, OUTPUT);

  pinMode(kPin_Addr15, OUTPUT);

  pinMode(kPin_Addr16, OUTPUT);

  pinMode(kPin_Addr17, OUTPUT);

  pinMode(kPin_Addr18, OUTPUT);

 

  // set unused higher adresses low

  digitalWrite(kPin_Addr15, LOW);

  digitalWrite(kPin_Addr16, LOW);

  digitalWrite(kPin_Addr17, LOW);

  digitalWrite(kPin_Addr18, LOW); 

  // control lines are ALWAYS outputs

  pinMode(kPin_nCE, OUTPUT); digitalWrite(kPin_nCE, LOW); // might as well keep the chip enabled ALL the time

  pinMode(kPin_nOE, OUTPUT); digitalWrite(kPin_nOE, HIGH);

  pinMode(kPin_nWE, OUTPUT); digitalWrite(kPin_nWE, HIGH); // not writing

  SetDataLinesAsInputs();

  SetAddress(0);

}

void loop()

{

  while (true)

  {

    digitalWrite(kPin_WaitingForInput, HIGH);

    ReadString();

    digitalWrite(kPin_WaitingForInput, LOW);

   

    switch (g_cmd[0])

    {

      case 'V': Serial.println(version_string); break;

      case 'P': SetSDPState(true); break;

      case 'U': SetSDPState(false); break;

      case 'R': ReadEEPROM(); break;

      case 'W': WriteEEPROM(); break;

      case 0: break; // empty string. Don't mind ignoring this.

      default: Serial.println("ERR Unrecognised command"); break;

    }

  }

}

void ReadEEPROM() // R<address>  - read kMaxBufferSize bytes from NVRam, beginning at <address> (in hex)

{

  if (g_cmd[1] == 0)

  {

    Serial.println("ERR");

    return;

  }

  // decode ASCII representation of address (in hex) into an actual value

  int addr = 0;

  int x = 1;

  while (x < 5 && g_cmd[x] != 0)

  {

    addr = addr << 4;

    addr |= HexToVal(g_cmd[x++]);

  }    

  digitalWrite(kPin_nWE, HIGH); // disables write

  SetDataLinesAsInputs();

  digitalWrite(kPin_nOE, LOW); // makes the NVRam output the byte

  delayMicroseconds(1);

     

  ReadEEPROMIntoBuffer(addr, kMaxBufferSize);

  // now print the results, starting with the address as hex ...

  Serial.print(hex[ (addr & 0xF000) >> 12 ]);

  Serial.print(hex[ (addr & 0x0F00) >> 8  ]);

  Serial.print(hex[ (addr & 0x00F0) >> 4  ]);

  Serial.print(hex[ (addr & 0x000F)       ]);

  Serial.print(":");

  PrintBuffer(kMaxBufferSize);

  Serial.println("OK");

  digitalWrite(kPin_nOE, HIGH); // stops the NVRam outputting the byte

}

void WriteEEPROM() // W<four byte hex address>:<data in hex, two characters per byte, max of 16 bytes per line>

{

  if (g_cmd[1] == 0)

  {

    Serial.println("ERR");

    return;

  }

  int addr = 0;

  int x = 1;

  while (g_cmd[x] != ':' && g_cmd[x] != 0)

  {

    addr = addr << 4;

    addr |= HexToVal(g_cmd[x]);

    ++x;

  }

  // g_cmd[x] should now be a :

  if (g_cmd[x] != ':')

  {

    Serial.println("ERR");

    return;

  }

 

  x++; // now points to beginning of data

  uint8_t iBufferUsed = 0;

  while (g_cmd[x] && g_cmd[x+1] && iBufferUsed < kMaxBufferSize && g_cmd[x] != ',')

  {

    uint8_t c = (HexToVal(g_cmd[x]) << 4) | HexToVal(g_cmd[x+1]);

    buffer[iBufferUsed++] = c;

    x += 2;

  }

  // if we're pointing to a comma, then the optional checksum has been provided!

  if (g_cmd[x] == ',' && g_cmd[x+1] && g_cmd[x+2])

  {

    byte checksum = (HexToVal(g_cmd[x+1]) << 4) | HexToVal(g_cmd[x+2]);

    byte our_checksum = CalcBufferChecksum(iBufferUsed);

    if (our_checksum != checksum)

    {

      // checksum fail!

      iBufferUsed = -1;

      Serial.print("ERR ");

      Serial.print(checksum, HEX);

      Serial.print(" ");

      Serial.print(our_checksum, HEX);

      Serial.println("");

      return;

    }

  }

  // buffer should now contains some data

  if (iBufferUsed > 0)

  {

    WriteBufferToEEPROM(addr, iBufferUsed);

  }

  if (iBufferUsed > -1)

  {

    Serial.println("OK");

  }

}

// This part not relevant for NVRam

// Important note: the EEPROM needs to have data written to it immediately after sending the "unprotect" command, so that the buffer is flushed.

// So we read byte 0 from the EEPROM first, then use that as the dummy write afterwards.

// It wouldn't matter if this facility was used immediately before writing an EEPROM anyway ... but it DOES matter if you use this option

// in isolation (unprotecting the EEPROM but not changing it).

void SetSDPState(bool bWriteProtect)

{

  digitalWrite(kPin_LED_Red, HIGH);

  digitalWrite(kPin_nWE, HIGH); // disables write

  digitalWrite(kPin_nOE, LOW); // makes the EEPROM output the byte

  SetDataLinesAsInputs();

 

  byte bytezero = ReadByteFrom(0);

 

  digitalWrite(kPin_nOE, HIGH); // stop EEPROM from outputting byte

  digitalWrite(kPin_nCE, HIGH);

  SetDataLinesAsOutputs();

  if (bWriteProtect)

  {

    WriteByteTo(0x1555, 0xAA);

    WriteByteTo(0x0AAA, 0x55);

    WriteByteTo(0x1555, 0xA0);

  }

  else

  {

    WriteByteTo(0x1555, 0xAA);

    WriteByteTo(0x0AAA, 0x55);

    WriteByteTo(0x1555, 0x80);

    WriteByteTo(0x1555, 0xAA);

    WriteByteTo(0x0AAA, 0x55);

    WriteByteTo(0x1555, 0x20);

  }

 

  WriteByteTo(0x0000, bytezero); // this "dummy" write is required so that the EEPROM will flush its buffer of commands.

  digitalWrite(kPin_nCE, LOW); // return to on by default for the rest of the code

  digitalWrite(kPin_LED_Red, LOW);

  Serial.print("OK SDP ");

  if (bWriteProtect)

  {

    Serial.println("enabled");

  }

  else

  {

    Serial.println("disabled");

  }

}

// ----------------------------------------------------------------------------------------

void ReadEEPROMIntoBuffer(int addr, int size)

{

  digitalWrite(kPin_LED_Grn, HIGH);

  digitalWrite(kPin_nWE, HIGH);

  SetDataLinesAsInputs();

  digitalWrite(kPin_nOE, LOW);

 

  for (int x = 0; x < size; ++x)

  {

    buffer[x] = ReadByteFrom(addr + x);

  }

  digitalWrite(kPin_nOE, HIGH);

  digitalWrite(kPin_LED_Grn, LOW);

}

void WriteBufferToEEPROM(int addr, int size)

{

  digitalWrite(kPin_LED_Red, HIGH);

  digitalWrite(kPin_nOE, HIGH); // stop NVRam from outputting byte

  digitalWrite(kPin_nWE, HIGH); // disables write

  SetDataLinesAsOutputs();

  for (uint8_t x = 0; x < size; ++x)

  {

    WriteByteTo(addr + x, buffer[x]);

  }

 

  digitalWrite(kPin_LED_Red, LOW);

}

// ----------------------------------------------------------------------------------------

// this function assumes that data lines have already been set as INPUTS, and that

// nOE is set LOW.

byte ReadByteFrom(int addr)

{

  SetAddress(addr);

  digitalWrite(kPin_nCE, LOW);

  delayMicroseconds(k_uTime_ReadPulse_uS);

  byte b = ReadData();

  digitalWrite(kPin_nCE, HIGH);

  return b;

}

// this function assumes that data lines have already been set as OUTPUTS, and that

// nOE is set HIGH.

void WriteByteTo(int addr, byte b)

{

  SetAddress(addr);

  SetData(b);

  digitalWrite(kPin_nCE, LOW);

  digitalWrite(kPin_nWE, LOW); // enable write 

  delayMicroseconds(k_uTime_WritePulse_uS);

 

  digitalWrite(kPin_nWE, HIGH); // disable write

  digitalWrite(kPin_nCE, HIGH);

}

// ----------------------------------------------------------------------------------------

void SetDataLinesAsInputs()

{

  pinMode(kPin_Data0, INPUT);

  pinMode(kPin_Data1, INPUT);

  pinMode(kPin_Data2, INPUT);

  pinMode(kPin_Data3, INPUT);

  pinMode(kPin_Data4, INPUT);

  pinMode(kPin_Data5, INPUT);

  pinMode(kPin_Data6, INPUT);

  pinMode(kPin_Data7, INPUT);

}

void SetDataLinesAsOutputs()

{

  pinMode(kPin_Data0, OUTPUT);

  pinMode(kPin_Data1, OUTPUT);

  pinMode(kPin_Data2, OUTPUT);

  pinMode(kPin_Data3, OUTPUT);

  pinMode(kPin_Data4, OUTPUT);

  pinMode(kPin_Data5, OUTPUT);

  pinMode(kPin_Data6, OUTPUT);

  pinMode(kPin_Data7, OUTPUT);

}

void SetAddress(int a)

{

  digitalWrite(kPin_Addr0,  (a&1)?HIGH:LOW    );

  digitalWrite(kPin_Addr1,  (a&2)?HIGH:LOW    );

  digitalWrite(kPin_Addr2,  (a&4)?HIGH:LOW    );

  digitalWrite(kPin_Addr3,  (a&8)?HIGH:LOW    );

  digitalWrite(kPin_Addr4,  (a&16)?HIGH:LOW   );

  digitalWrite(kPin_Addr5,  (a&32)?HIGH:LOW   );

  digitalWrite(kPin_Addr6,  (a&64)?HIGH:LOW   );

  digitalWrite(kPin_Addr7,  (a&128)?HIGH:LOW  );

  digitalWrite(kPin_Addr8,  (a&256)?HIGH:LOW  );

  digitalWrite(kPin_Addr9,  (a&512)?HIGH:LOW  );

  digitalWrite(kPin_Addr10, (a&1024)?HIGH:LOW );

  digitalWrite(kPin_Addr11, (a&2048)?HIGH:LOW );

  digitalWrite(kPin_Addr12, (a&4096)?HIGH:LOW );

  digitalWrite(kPin_Addr13, (a&8192)?HIGH:LOW );

  digitalWrite(kPin_Addr14, (a&16384)?HIGH:LOW);

}

// this function assumes that data lines have already been set as OUTPUTS.

void SetData(byte b)

{

  digitalWrite(kPin_Data0, (b&1)?HIGH:LOW  );

  digitalWrite(kPin_Data1, (b&2)?HIGH:LOW  );

  digitalWrite(kPin_Data2, (b&4)?HIGH:LOW  );

  digitalWrite(kPin_Data3, (b&8)?HIGH:LOW  );

  digitalWrite(kPin_Data4, (b&16)?HIGH:LOW );

  digitalWrite(kPin_Data5, (b&32)?HIGH:LOW );

  digitalWrite(kPin_Data6, (b&64)?HIGH:LOW );

  digitalWrite(kPin_Data7, (b&128)?HIGH:LOW);

}

// this function assumes that data lines have already been set as INPUTS.

byte ReadData()

{

  byte b = 0;

  if (digitalRead(kPin_Data0) == HIGH) b |= 1;

  if (digitalRead(kPin_Data1) == HIGH) b |= 2;

  if (digitalRead(kPin_Data2) == HIGH) b |= 4;

  if (digitalRead(kPin_Data3) == HIGH) b |= 8;

  if (digitalRead(kPin_Data4) == HIGH) b |= 16;

  if (digitalRead(kPin_Data5) == HIGH) b |= 32;

  if (digitalRead(kPin_Data6) == HIGH) b |= 64;

  if (digitalRead(kPin_Data7) == HIGH) b |= 128;

  return(b);

}

// ----------------------------------------------------------------------------------------

void PrintBuffer(int size)

{

  uint8_t chk = 0;

  for (uint8_t x = 0; x < size; ++x)

  {

    Serial.print(hex[ (buffer[x] & 0xF0) >> 4 ]);

    Serial.print(hex[ (buffer[x] & 0x0F)      ]);

    chk = chk ^ buffer[x];

  }

  Serial.print(",");

  Serial.print(hex[ (chk & 0xF0) >> 4 ]);

  Serial.print(hex[ (chk & 0x0F)      ]);

  Serial.println("");

}

void ReadString()

{

  int i = 0;

  byte c;

  g_cmd[0] = 0;

  do

  {

    if (Serial.available())

    {

      c = Serial.read();

      if (c > 31)

      {

        g_cmd[i++] = c;

        g_cmd[i] = 0;

      }

    }

  }

  while (c != 10);

}

uint8_t CalcBufferChecksum(uint8_t size)

{

  uint8_t chk = 0;

  for (uint8_t x = 0; x < size; ++x)

  {

    chk = chk ^  buffer[x];

  }

  return(chk);

}

// converts one character of a HEX value into its absolute value (nibble)

byte HexToVal(byte b)

{

  if (b >= '0' && b <= '9') return(b - '0');

  if (b >= 'A' && b <= 'F') return((b - 'A') + 10);

  if (b >= 'a' && b <= 'f') return((b - 'a') + 10);

  return(0);

}