//
// Arduino Nano 1702A reader version 1.0
// by Giuseppe Perotti, I1EPJ
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// -------- Options - #define them to enable --------
// Test code
#undef TESTRELAY // Include relay test command
#undef TESTCALC // Include calculations test code
// Features
#define BACKSPACE // Include backspace handling code
#define VT100 // Include use of some VT100 escape codes
// ------------------ End options -------------------
// For HEX INTEL dump routine
#define MAXHEXLINE 32 /* the maximum number of bytes to put in one line */
// Arduino NANO defines
#define AD0 10
#define AD1 11
#define AD2 12
#define AD3 A0
#define AD4 A1
#define AD5 A2
#define AD6 A3
#define AD7 A4
#define DB1 9
#define DB2 8
#define DB3 7
#define DB4 6
#define DB5 5
#define DB6 4
#define DB7 3
#define DB8 2
// Analog inputs to measure Vcc and Vdd/Vgg
// (On Arduino Nano A6 and A7 cannot be used as digital outputs)
#define VP5 A6
#define VM9 A7
// 1702A power relay control
#define PWRRLY 13
// The conditioning circuit gives Va6=Vcc/2, i.e. Va6=2,5V @Vcc=5V (M=512)
// (assuming resistors ideal values)
#define MINP5 486 // 5V*0,95
#define MAXP5 538 // 5V*1,05
// The conditioning circuit gives Va7=(10Vcc+5.1Vdd)/15.1, i.e. Va7= 0,2715V@Vdd=-9V, Vcc=5V (M=56)
// (assuming resistors ideal values)
#define MINM9 25 // -9V*1,05
#define MAXM9 87 // -9V*0,95
// Messages
#define BASEADDRSETTO "Base address set to %04XH"
#define CURRENTBASE "Current base address: %04XH"
#define CHECKFAILED "Check failed."
#define CHECKOK "Check OK."
#define COPY1 "1702A EPROM reader v1 - (C) I1EPJ 2021"
#define COPY2 "Distributed under the GPL v3 (or later, at your option)"
#define DONEREADING "Done reading, now checking..."
#define ERRORAT "Error at address %02XH: %02XH != %02XH."
#ifdef TESTRELAY
#define HELP "Available commands:\r\n"\
"B Base address for D and V commands (in hex, default 0000H)\r\n"\
"C Check EPROM\r\n"\
"D Dump EPROM (HEX INTEL)\r\n"\
"H Help (this command list)\r\n"\
"I Info\r\n"\
"P Check power (NOTE: power supplies are checked at every command)\r\n"\
"R Read EPROM\r\n"\
"V View HEX/ASCII listing\r\n"\
"X 0=RELAY OFF, 1=RELAY ON\r\n"\
"Commands can be issued upper or lower case and must be followed\r\nby CR and/or LF"
#else
#define HELP "Available commands:\r\n"\
"B Base address for D and V commands (in hex, default 0000H)\r\n"\
"C Check EPROM\r\n"\
"D Dump EPROM (HEX INTEL)\r\n"\
"H Help (this command list)\r\n"\
"I Info\r\n"\
"P Check power (NOTE: power supplies are checked at every command)\r\n"\
"R Read EPROM\r\n"\
"V View HEX/ASCII listing\r\n"\
"Commands can be issued upper or lower case and must be followed\r\nby CR and/or LF"
#endif
#define INVALIDCMD "Invalid command"
#define NODATA "No data available, read EPROM first"
#define PROMPT "Ready-->"
#define PSVALUES "Vcc=Vbb=%sV Vdd=Vgg=%sV"
#define PWROK "Power values OK."
#define PWROUTOFRANGE1 "WARNING! Power voltages out of range!"
#define PWROUTOFRANGE2 "Now stopping the program, please check the power supply circuits\r\nand then reset/restart"
#define TESTINGPS "Testing power supplies: "
// Global variables
static unsigned char buf[256];
static char line[80], s[15], sVp[10], sVm[10];
static bool EPROM_read = false;
static int base_addr = 0;
// Initialization code
void setup() {
// Set address lines as inputs, these will be set to outputs later
pinMode(AD0, INPUT);
pinMode(AD1, INPUT);
pinMode(AD2, INPUT);
pinMode(AD3, INPUT);
pinMode(AD4, INPUT);
pinMode(AD5, INPUT);
pinMode(AD6, INPUT);
pinMode(AD7, INPUT);
// data lines are always inputs
pinMode(DB1, INPUT);
pinMode(DB2, INPUT);
pinMode(DB3, INPUT);
pinMode(DB4, INPUT);
pinMode(DB5, INPUT);
pinMode(DB6, INPUT);
pinMode(DB7, INPUT);
pinMode(DB8, INPUT);
// 1702A power relay control
pinMode(PWRRLY, OUTPUT);
// Set EPROM power off
digitalWrite(PWRRLY, LOW);
// Initialize communications and print copyright and help
Serial.begin(115200);
#ifdef VT100
Serial.print("\x1b[2J"); // VT100 clear screen
Serial.print("\x1b[H"); // VT100 cursor home
#endif
Serial.println(COPY1);
Serial.println(COPY2);
Serial.println();
Serial.println(HELP);
Serial.println();
}
// Check power supplies voltages: returns true if OK, false otherwise
bool CheckPwr() {
int i, valueP5, valueM9;
float Vp, Vm;
valueP5 = 0;
valueM9 = 0;
// Take and sum 16 readings
for (i = 0; i < 16; i++) {
valueP5 += analogRead(VP5);
valueM9 += analogRead(VM9);
};
// Shift right values 4 places, i.e. divide by 16
valueP5 >>= 4;
valueM9 >>= 4;
// Calculate +5V and -9V measured values
// Vp = 2*5*valueP5/1024
// Vm = (15.1*5*valueM9/1024-10*Vp)/5.1
// (assuming resistors ideal values)
Vp = 10.0*(float)valueP5/1024.0;
Vm = (15.1*5*(float)valueM9/1024.0-10.0*Vp)/5.1;
#ifdef TESTCALC
Serial.print("valueM9=");
Serial.println(valueM9);
#endif
// Convert float values to string (no float support in Arduino sprintf())
dtostrf(Vp, 2, 2, sVp);
dtostrf(Vm, 2, 2, sVm);
// Check if voltages are in +-5% of nominal values
if ((valueP5 > MINP5) and (valueP5 < MAXP5) and
(valueM9 > MINM9) and (valueM9 < MAXM9))
return true;
else
return false;
}
// Set address lines to input
void SetAddressIn(void) {
pinMode(AD0, INPUT);
pinMode(AD1, INPUT);
pinMode(AD2, INPUT);
pinMode(AD3, INPUT);
pinMode(AD4, INPUT);
pinMode(AD5, INPUT);
pinMode(AD6, INPUT);
pinMode(AD7, INPUT);
}
// Set address lines to output
void SetAddressOut(void) {
pinMode(AD0, OUTPUT);
pinMode(AD1, OUTPUT);
pinMode(AD2, OUTPUT);
pinMode(AD3, OUTPUT);
pinMode(AD4, OUTPUT);
pinMode(AD5, OUTPUT);
pinMode(AD6, OUTPUT);
pinMode(AD7, OUTPUT);
}
// Set AD lines to addr
void SetAddr(unsigned char addr) {
if ((addr & 0x01) != 0) digitalWrite(AD0, HIGH); else digitalWrite(AD0, LOW);
if ((addr & 0x02) != 0) digitalWrite(AD1, HIGH); else digitalWrite(AD1, LOW);
if ((addr & 0x04) != 0) digitalWrite(AD2, HIGH); else digitalWrite(AD2, LOW);
if ((addr & 0x08) != 0) digitalWrite(AD3, HIGH); else digitalWrite(AD3, LOW);
if ((addr & 0x10) != 0) digitalWrite(AD4, HIGH); else digitalWrite(AD4, LOW);
if ((addr & 0x20) != 0) digitalWrite(AD5, HIGH); else digitalWrite(AD5, LOW);
if ((addr & 0x40) != 0) digitalWrite(AD6, HIGH); else digitalWrite(AD6, LOW);
if ((addr & 0x80) != 0) digitalWrite(AD7, HIGH); else digitalWrite(AD7, LOW);
}
// Read a byte from DB lines
unsigned char ReadByte(void) {
unsigned char val = 0;
if (digitalRead(DB1) == HIGH) val += 1;
if (digitalRead(DB2) == HIGH) val += 2;
if (digitalRead(DB3) == HIGH) val += 4;
if (digitalRead(DB4) == HIGH) val += 8;
if (digitalRead(DB5) == HIGH) val += 16;
if (digitalRead(DB6) == HIGH) val += 32;
if (digitalRead(DB7) == HIGH) val += 64;
if (digitalRead(DB8) == HIGH) val += 128;
return val;
}
// Check data read in buf against EPROM content
bool CheckEPROM(void) {
unsigned char rbyte;
int addr;
bool result = true;
if (CheckPwr()) {
// Activate EPROM power
digitalWrite(PWRRLY, HIGH);
delay(100);
// Set address lines to OUTPUT
SetAddressOut();
for (addr = 0; addr < 256; addr++) {
SetAddr(addr);
delay(1);
if (buf[addr] != (rbyte = ReadByte())) {
sprintf(s, ERRORAT, addr, rbyte, buf[addr]);
Serial.println(s);
result = false;
}
}
}
// Set address lines to inputs
SetAddressIn();
// EPROM power off
digitalWrite(PWRRLY, LOW);
return result;
}
// Read EPROM content into buf
void ReadEPROM(void) {
unsigned char rbyte;
int addr;
if (CheckPwr()) {
// Turn power on
digitalWrite(PWRRLY, HIGH);
delay(100);
// Set address lines to OUTPUT
SetAddressOut();
// Read all 256 bytes
for (addr = 0; addr < 256; addr++) {
SetAddr(addr);
delay(1);
buf[addr] = ReadByte();
}
Serial.println(DONEREADING);
EPROM_read = true;
if (CheckEPROM) {
Serial.println(CHECKOK);
} else {
Serial.println(CHECKFAILED);
}
}
// Set address lines to inputs
SetAddressIn();
// Turn power off
digitalWrite(PWRRLY, LOW);
}
// Downloaded from www.pjrc.com and adapted by I1EPJ
//
// produce intel hex file output... call this routine with
// each byte to output and it's memory location. When a line
// is complete, print it on the serial (USB) port. After
// all data is written, call with end=1 (normally set to 0)
// so it will flush the data from its static buffer
//
void hex_out(int bytein, int memory_location, bool endflag)
{
static int byte_buffer[MAXHEXLINE];
static int last_mem, buffer_pos, buffer_addr;
static int writing_in_progress = 0;
register int i, sum;
if (!writing_in_progress) {
// initial condition setup
last_mem = memory_location - 1;
buffer_pos = 0;
buffer_addr = memory_location;
writing_in_progress = 1;
}
if ( (memory_location != (last_mem + 1)) || (buffer_pos >= MAXHEXLINE) \
|| ((endflag) && (buffer_pos > 0)) ) {
// it's time to dump the buffer to a line via USB serial port
sprintf(s, ":%02X%04X00", buffer_pos, buffer_addr);
Serial.print(s);
sum = buffer_pos + ((buffer_addr >> 8) & 255) + (buffer_addr & 255);
for (i = 0; i < buffer_pos; i++) {
sprintf(s, "%02X", byte_buffer[i] & 255);
Serial.print(s);
sum += byte_buffer[i] & 255;
}
sprintf(s, "%02X\r\n", (-sum) & 255);
Serial.print(s);
buffer_addr = memory_location;
buffer_pos = 0;
}
if (endflag) {
sprintf(s, ":00000001FF\r\n"); /* end of file marker */
Serial.print(s);
writing_in_progress = 0;
}
last_mem = memory_location;
byte_buffer[buffer_pos] = bytein & 255;
buffer_pos++;
}
// Dump buffer content in HEX INTEL format
void DumpEPROM(void) {
unsigned char value;
int addr;
// Call hex_out for all 256 bytes
for (addr = 0; addr < 256; addr++) {
hex_out(buf[addr], addr+base_addr, false);
}
// End of dump
hex_out(buf[addr], addr+base_addr, true);
}
// Executing forever
void loop() {
char cmd[20];
bool result;
int addr,i;
char c;
if (!CheckPwr()) {
// At least one power supply is out of specifications...
// ... so power off EPROM to avoid damage...
digitalWrite(PWRRLY,LOW);
// ... notify user...
Serial.println(PWROUTOFRANGE1);
sprintf(line, PSVALUES, sVp, sVm);
Serial.println(line);
Serial.println(PWROUTOFRANGE2);
// ... and stop execution to avoid damaging the EPROM.
for (;;);
} else
// Power supplies OK, we are ready!
Serial.print(PROMPT);
// Get command
cmd[0]='\0';
do {
// Read characters, echo and put them in cmd until a CR or LF is received
// Stop accepting characters when command buffer full
c=Serial.read();
if (c!=-1) { // We have a new character
if (c>31) { // If it is not a control character, append and print it...
if ((strlen(cmd))<19) { // ...but only if cmd buffer is not full
strncat(cmd,&c,1);
Serial.print((char)c);
}
}
#ifdef BACKSPACE
if (c==8) {
// backspace
if ((i=strlen(cmd))>0) {
Serial.print("\x08");
Serial.print(" ");
Serial.print("\x08");
cmd[i-1]='\0';
}
}
#endif
}
} while ((c!='\r') && (c!='\n'));
Serial.println("");
// Now we have a command in cmd[]
// Command parser
switch (toupper(cmd[0])) {
case '\0':
// no command, ignore
break;
case 'B': // Read/set base address
if (strlen(cmd)==1) {
// no parameter given, show actual value
sprintf(line,CURRENTBASE,base_addr);
Serial.println(line);
} else {
base_addr = strtoul(&cmd[1],NULL,16);
sprintf(line,BASEADDRSETTO,base_addr);
Serial.println(line);
}
break;
case 'C': // Check EPROM
if (!EPROM_read) {
// No data to use
Serial.println(NODATA);
break;
}
// We have data, check them against the EPROM content
if (CheckEPROM()) {
Serial.println(CHECKOK);
// Errors, if any, are displayed by the CheckEPROM routine
}
break;
case 'D': // Dump in HEX INTEL format
if (!EPROM_read) {
// No data to use
Serial.println(NODATA);
break;
}
// Have data, dump them
DumpEPROM();
break;
case 'H': // Display help
Serial.println(HELP);
break;
case 'I': // Display program info
Serial.println(COPY1);
Serial.println(COPY2);
break;
case 'P': // Check power supplies (+5 and -9V)
Serial.print(TESTINGPS);
result = CheckPwr();
// Display results
sprintf(line, PSVALUES, sVp, sVm);
Serial.println(line);
if (result)
Serial.println(PWROK);
else
Serial.println(PWROUTOFRANGE1);
break;
case 'R': // Read EPROM
ReadEPROM();
break;
case '\r': // CR/LF only, ignore
case '\n':
// no command
break;
case 'V': // HEX/ASCII listing
if (!EPROM_read) {
// No data to use
Serial.println(NODATA);
break;
}
for (addr=0; addr<256; addr+=16) {
line[0]='\0';
sprintf(s,"%04X ",addr+base_addr);
strncat(line,s,5);
for (i=addr; i<addr+16; i++) {
sprintf(s,"%02X ", buf[i]);
strncat(line,s,3);
}
strcat(line," ");
for (i=addr; i<addr+16; i++) {
c=buf[i];
if ((c<32) or (c>126)) c='.';
strncat(line,&c,1);
}
Serial.println(line);
}
break;
#ifdef TESTRELAY
case 'X':
switch(cmd[1]) {
case '1':
digitalWrite(PWRRLY, HIGH);
break;
case '0':
digitalWrite(PWRRLY, LOW);
break;
}
break;
#endif
default:
// Not a valid command
Serial.println(INVALIDCMD);
break;
}
}