You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
As mentioned by @TomNisbet in the documentation https://tomnisbet.github.io/TommyPROM/, by default the TommyPROM programmer does not support writing to eeprom memories that have a byte sector of 256 bytes, like at29c040. Some at29c040 eeproms that I bought had a 128 byte sector. In those it is easier, just use the default code and configure it as I show in step 1 with 128 byte sector block that will already work. You can find out the size of the sector by testing writing some file. If after writing you find from the dump that one half of the pageblock is empty and the other half has incorrect bytes, then your eeprom probably has the 256 byte sector. Tom explains this better here #44
I'll show the modifications I made to the project to work around this. I should warn you that this is just a quick fix. It is not adjustable because it changes the project's base files.
Standard 28C module is compatible with 29c040, we just need to modify device parameters to configure with eeprom size and sector size. in TommyPROM.ino file comment out or replace line 37 according to your eeprom.
The xmodem protocol only sends 128 bytes per packet. To write to a 256 byte sector eeprom then we need to handle this using buffers and flags to only write to the eeprom when a 256 byte buffer is full. The file that needs to be modified is xmodem.cpp, on ReceiveFile and ReceicePacket functions. And that's it.
xmodem.cpp code
#include"XModem.h"#include"CmdStatus.h"// The original TommyPROM code used XModem CRC protocol but this requires parameters to be// changed for the host communication program. The default is now to use basic XModem// with the 8-bit checksum instead of the 16-bit CRC error checking. This shouldn't// matter because communication errors aren't likely to happen on a 3 foot USB cable like// they did over the long distance dail-up lines that XModem was designed for. Uncomment// the XMODEM_CRC_PROTOCOL line below to restore the original XMODEM CRC support.//#define XMODEM_CRC_PROTOCOLenum {
// XMODEM control characters.XMDM_SOH=0x01,
XMDM_EOT=0x04,
XMDM_ACK=0x06,
XMDM_NAK=0x15,
XMDM_CAN=0x18,
XMDM_ESC=0x1b,
XMDM_CRC='C',
#ifdefXMODEM_CRC_PROTOCOLXMDM_TRANSFER_START=XMDM_CRC#elseXMDM_TRANSFER_START=XMDM_NAK#endif
};
enum {
// Misc constants for XMODEM.PKTLEN=128
};
enum { RX_OK,
RX_ERROR,
RX_IGNORE };
//variables to handle 256 byte sector eeprom with the 128 byte packet of xmodemboolflagFirstPacketReceived= false;
bytebufferPacket1[128];
bytebufferFull[256];
uint32_tXModem::ReceiveFile(uint32_taddress) {
uint8_tbuffer[PKTLEN];
intc;
uint8_tseq=1;
uint32_tnumBytes=0;
boolcomplete= false;
intstatus;
#ifdefXMODEM_CRC_PROTOCOLSerial.println(F("Send the image file using XMODEM CRC"));
#elseSerial.println(F("Send the image file using XMODEM"));
#endifif (!StartReceive()) {
cmdStatus.error("Timeout waiting for transfer to start.");
return0;
}
while (!complete) {
if ((c=GetChar()) <0) {
cmdStatus.error("Timeout waiting for start of next packet.");
cmdStatus.setValueDec(0, "seq", seq);
return0;
}
switch (c) {
caseXMDM_SOH:
// Start of a packetstatus=ReceivePacket(buffer, PKTLEN, seq, address);
if (status==RX_OK) {
// Good packet - advance the bufferif (flagFirstPacketReceived== false) {
address+=256;
}
numBytes+=PKTLEN;
++seq;
} elseif (status==RX_ERROR) {
return0;
}
// else ignore the duplicate packetbreak;
caseXMDM_EOT:
// End of transferSerial.write(XMDM_ACK);
complete= true;
break;
caseXMDM_CAN:
caseXMDM_ESC:
// Cancel from sendercmdStatus.error("Transfer canceled by sender.");
cmdStatus.setValueDec(0, "seq", seq);
return0;
break;
default:
// Fail the transfer on anything elsecmdStatus.error("Unexpected character received waiting for next packet.");
cmdStatus.setValueDec(0, "char", c);
cmdStatus.setValueDec(1, "seq", seq);
return0;
break;
}
}
returnnumBytes;
}
// This method it not very tolerant of communication errors. If the receiver// does not send a positive ACK for each packet or does not ACK the packet// within one second then the transfer will fail. Unlike in the dial-up// days of old, this is designed to be run on a 3 foot cable betwee two fast// hosts, so communication errors or timeouts are extremely unlikely.boolXModem::SendFile(uint32_taddress, uint32_tfileSize) {
uint8_tseq=1;
intrxChar=-1;
uint32_tbytesSent=0;
#ifdefXMODEM_CRC_PROTOCOLSerial.println(F("Set the terminal to receive XModem CRC"));
#elseSerial.println(F("Set the terminal to receive XModem"));
#endifwhile (rxChar==-1) {
rxChar=GetChar();
}
if (rxChar!=XMDM_TRANSFER_START) {
#ifdefXMODEM_CRC_PROTOCOLcmdStatus.error("Expected XModem CRC start char");
#elsecmdStatus.error("Expected XModem NAK char to start");
#endifcmdStatus.setValueDec(0, "char", rxChar);
return false;
}
while (bytesSent<fileSize) {
SendPacket(address, seq++);
address+=PKTLEN;
rxChar=GetChar(5000);
if (rxChar!=XMDM_ACK) {
cmdStatus.error("Expected XModem ACK.");
cmdStatus.setValueDec(0, "char", rxChar);
return false;
}
bytesSent+=PKTLEN;
}
// Signal end of transfer and process the ACKSerial.write(XMDM_EOT);
rxChar=GetChar(5000);
if (rxChar!=XMDM_ACK) {
cmdStatus.error("Expected XModem ACK to EOT, but received:");
cmdStatus.setValueDec(0, "char", rxChar);
return false;
}
return true;
}
voidXModem::Cancel() {
// Send a cancel and then eat input until the line is quiet for 3 seconds.Serial.write(XMDM_CAN);
while (GetChar(3000) !=-1) {}
}
// Private functionsintXModem::GetChar(intmsWaitTime) {
msWaitTime *= 4;
do {
if (Serial.available() >0) {
returnSerial.read();
}
delayMicroseconds(250);
} while (msWaitTime--);
return-1;
}
uint16_tXModem::UpdateCrc(uint16_tcrc, uint8_tdata) {
#ifdefXMODEM_CRC_PROTOCOLcrc=crc ^ ((uint16_t)data << 8);
for (intix=0; (ix<8); ix++) {
if (crc&0x8000) {
crc= (crc << 1) ^ 0x1021;
} else {
crc <<= 1;
}
}
#elsecrc= (crc+data) &0xff;
#endifreturncrc;
}
boolXModem::StartReceive() {
delay(2000);
for (intretries=20; (retries); --retries) {
// Send the XMDM_TRANSFER_START until the sender of the file responds with// something. The start character will be sent once a second for a number of// seconds. If nothing is received in that time then return false to indicate// that the transfer did not start.++promDevice.debugRxStarts;
Serial.write(XMDM_TRANSFER_START);
for (unsignedms=30000; (ms); --ms) {
delayMicroseconds(100);
if (Serial.available() >0) {
return true;
}
}
}
return false;
}
intXModem::ReceivePacket(uint8_tbuffer[], unsignedbufferSize, uint8_tseq, uint32_tdestAddr) {
intc;
uint8_trxSeq1, rxSeq2;
uint16_tcalcCrc=0;
uint16_trxCrc;
rxSeq1= (uint8_t)GetChar();
rxSeq2= (uint8_t)GetChar();
for (unsignedix=0; (ix<bufferSize); ix++) {
if ((c=GetChar()) <0) {
// If the read times out then fail this packet. Note that this check isn't// done for the sequence and CRC. If they timeout then the values won't match// so there is not point in the extra code to check for the error. The worst// that will happen is that the transfer will need to wait 3 timeouts before// realizing that something is wrong.cmdStatus.error("Timeout waiting for next packet char.");
cmdStatus.setValueDec(0, "seq", seq);
Serial.write(XMDM_CAN);
returnRX_ERROR;
}
buffer[ix] = (uint8_t)c;
calcCrc=UpdateCrc(calcCrc, buffer[ix]);
}
#ifdefXMODEM_CRC_PROTOCOLrxCrc= ((uint16_t)GetChar()) << 8;
rxCrc |= GetChar();
#elserxCrc=GetChar();
#endif// At this point in the code, there should not be anything in the receive buffer// because the sender has just sent a complete packet and is waiting on a response. If// the XModem transfer is not started quickly on the host side, TeraTerm seems to// buffer the NAK character that starts the transfer and use it as an indication that// the first packet should be sent again. That can cause the Arduino's 64 byte buffer// to overflow and gets the transfer out of sync. The normal processing will detect// the first packet and will process it correctly, but the partial packet in the// buffer will cause the processing of the next packet to fail.delay(10);
if (Serial.available()) {
++promDevice.debugRxSyncErrors;
if (seq==1) {
// If any characters are in the buffer after the first packet, quietly eat// them to get back on a packet boundary and send a NAK to cause a retransmit// of the packet.while (Serial.available() >0) {
delay(1);
Serial.read();
++promDevice.debugExtraChars;
}
delay(1);
Serial.write(XMDM_NAK);
returnRX_IGNORE;
} else {
// Sync issues shouldn't happen at any other time so fail the transfer.cmdStatus.error("RX sync error.");
cmdStatus.setValueDec(0, "seq", seq);
cmdStatus.setValueDec(1, "rxSeq1", rxSeq1);
cmdStatus.setValueDec(2, "rxSeq2", rxSeq2);
cmdStatus.setValueHex(3, "calcCrc", calcCrc);
cmdStatus.setValueHex(4, "rxCrc", rxCrc);
Serial.write(XMDM_CAN);
returnRX_ERROR;
}
}
if ((calcCrc==rxCrc) && (rxSeq1==seq-1) && ((rxSeq1 ^ rxSeq2) ==0xff)) {
// Resend of previously processed packet.delay(10);
++promDevice.debugRxDuplicates;
Serial.write(XMDM_ACK);
returnRX_IGNORE;
} elseif ((calcCrc!=rxCrc) || (rxSeq1!=seq) || ((rxSeq1 ^ rxSeq2) !=0xff)) {
// Fail if the CRC or sequence number is not correct or if the two received// sequence numbers are not the complement of one another.cmdStatus.error("Bad CRC or sequence number.");
cmdStatus.setValueDec(0, "seq", seq);
cmdStatus.setValueDec(1, "rxSeq1", rxSeq1);
cmdStatus.setValueDec(2, "rxSeq2", rxSeq2);
cmdStatus.setValueHex(3, "calcCrc", calcCrc);
cmdStatus.setValueHex(4, "rxCrc", rxCrc);
Serial.write(XMDM_CAN);
returnRX_ERROR;
} else {
////////////// 256 byte buffer treatmentif (flagFirstPacketReceived== false) {
for (inti=0; i<128; i++) { //keep the first packet bytes in bufferPacket1bufferPacket1[i] =buffer[i];
}
flagFirstPacketReceived= true;
} else {
for (inti=0; i<128; i++) {
bufferFull[i] =bufferPacket1[i]; //pass the first packet bytes to beginning of bufferFullbufferFull[i+128] =buffer[i]; //copy the second packet bytes to second half of bufferFull
}
if (!promDevice.writeData(bufferFull, 256, destAddr)) {
cmdStatus.error("Write failed");
cmdStatus.setValueHex(0, "address", destAddr);
returnRX_ERROR;
}
flagFirstPacketReceived= false;
}
///////////////Serial.write(XMDM_ACK);
}
returnRX_OK;
}
voidXModem::SendPacket(uint32_taddress, uint8_tseq) {
uint16_tcrc=0;
Serial.write(XMDM_SOH);
Serial.write(seq);
Serial.write(~seq);
for (intix=0; (ix<PKTLEN); ix++) {
bytec=promDevice.readData(address++);
Serial.write(c);
crc=UpdateCrc(crc, c);
}
#ifdefXMODEM_CRC_PROTOCOLSerial.write(crc >> 8);
#endifSerial.write(crc&0xff);
}
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
-
As mentioned by @TomNisbet in the documentation https://tomnisbet.github.io/TommyPROM/, by default the TommyPROM programmer does not support writing to eeprom memories that have a byte sector of 256 bytes, like at29c040. Some at29c040 eeproms that I bought had a 128 byte sector. In those it is easier, just use the default code and configure it as I show in step 1 with 128 byte sector block that will already work. You can find out the size of the sector by testing writing some file. If after writing you find from the dump that one half of the pageblock is empty and the other half has incorrect bytes, then your eeprom probably has the 256 byte sector. Tom explains this better here #44
I'll show the modifications I made to the project to work around this. I should warn you that this is just a quick fix. It is not adjustable because it changes the project's base files.
PromDevice28C prom(512 * 1024L, 256, 10, true); // 29C040 512kb with 256 byte sector
xmodem.cpp code
Beta Was this translation helpful? Give feedback.
All reactions