Firwmare updater for the Einsy external flash memory,

to be used as a storage for localization strings.

Hacked into the avrdude Arduino STK500 (not STK500v2) protocol.
This commit is contained in:
bubnikv 2018-06-14 15:03:16 +02:00
parent 5414f7379d
commit 7863412687
4 changed files with 147 additions and 57 deletions

View file

@ -353,8 +353,6 @@ add_library(semver STATIC
) )
add_subdirectory(src/avrdude)
# Generate the Slic3r Perl module (XS) typemap file. # Generate the Slic3r Perl module (XS) typemap file.
set(MyTypemap ${CMAKE_CURRENT_BINARY_DIR}/typemap) set(MyTypemap ${CMAKE_CURRENT_BINARY_DIR}/typemap)
add_custom_command( add_custom_command(
@ -517,12 +515,12 @@ if (WIN32 AND ";${PerlEmbed_CCFLAGS};" MATCHES ";[-/]Od;")
message("Old CMAKE_CXX_FLAGS_RELEASE: ${CMAKE_CXX_FLAGS_RELEASE}") message("Old CMAKE_CXX_FLAGS_RELEASE: ${CMAKE_CXX_FLAGS_RELEASE}")
message("Old CMAKE_CXX_FLAGS_RELWITHDEBINFO: ${CMAKE_CXX_FLAGS_RELEASE}") message("Old CMAKE_CXX_FLAGS_RELWITHDEBINFO: ${CMAKE_CXX_FLAGS_RELEASE}")
message("Old CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS_RELEASE}") message("Old CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS_RELEASE}")
set(CMAKE_CXX_FLAGS_RELEASE "/MD /Od /Zi /EHsc /DNDEBUG") set(CMAKE_CXX_FLAGS_RELEASE "/MD /Od /Zi /EHsc /DNDEBUG /DWIN32")
set(CMAKE_C_FLAGS_RELEASE "/MD /Od /Zi /DNDEBUG") set(CMAKE_C_FLAGS_RELEASE "/MD /Od /Zi /DNDEBUG /DWIN32")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "/MD /Od /Zi /EHsc /DNDEBUG") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "/MD /Od /Zi /EHsc /DNDEBUG /DWIN32")
set(CMAKE_C_FLAGS_RELWITHDEBINFO "/MD /Od /Zi /DNDEBUG") set(CMAKE_C_FLAGS_RELWITHDEBINFO "/MD /Od /Zi /DNDEBUG /DWIN32")
set(CMAKE_CXX_FLAGS "/MD /Od /Zi /EHsc /DNDEBUG") set(CMAKE_CXX_FLAGS "/MD /Od /Zi /EHsc /DNDEBUG /DWIN32")
set(CMAKE_C_FLAGS "/MD /Od /Zi /DNDEBUG") set(CMAKE_C_FLAGS "/MD /Od /Zi /DNDEBUG /DWIN32")
endif() endif()
# The following line will add -fPIC on Linux to make the XS.so rellocable. # The following line will add -fPIC on Linux to make the XS.so rellocable.
add_definitions(${PerlEmbed_CCCDLFLAGS}) add_definitions(${PerlEmbed_CCCDLFLAGS})
@ -530,6 +528,8 @@ if (WIN32)
target_link_libraries(XS ${PERL_LIBRARY}) target_link_libraries(XS ${PERL_LIBRARY})
endif() endif()
add_subdirectory(src/avrdude)
## REQUIRED packages ## REQUIRED packages
# Find and configure boost # Find and configure boost

View file

@ -102,6 +102,57 @@ static int arduino_open(PROGRAMMER * pgm, char * port)
*/ */
stk500_drain(pgm, 0); stk500_drain(pgm, 0);
{
//FIXME initialization sequence for programming the external FLASH.
const char entry_magic_send [] = "start\n";
const char entry_magic_receive[] = "w25x20cl_enter\n";
const char entry_magic_cfm [] = "w25x20cl_cfm\n";
const char *entry_magic_ptr = entry_magic_send;
struct timeval tv;
double tstart, tnow;
char c;
gettimeofday(&tv, NULL);
tstart = tv.tv_sec;
while (*entry_magic_ptr != 0) {
if (serial_recv(&pgm->fd, &c, 1) < 0)
goto timedout;
printf("Received: %c (%d)\n", c, (int)c);
if (c != *entry_magic_ptr ++) {
avrdude_message(MSG_INFO, "%s: stk500v2_recv(): MK3 printer emited incorrect start code\n", progname);
return -1;
}
gettimeofday(&tv, NULL);
tnow = tv.tv_sec;
if (tnow-tstart > 2.) { // wuff - signed/unsigned/overflow
timedout:
avrdude_message(MSG_INFO, "%s: stk500v2_recv(): MK3 printer did not boot up on time\n", progname);
return -1;
}
}
if (serial_send(&pgm->fd, entry_magic_receive, strlen(entry_magic_receive)) < 0) {
avrdude_message(MSG_INFO, "%s: stk500v2_send(): failed to send command to serial port\n",progname);
return -1;
}
entry_magic_ptr = entry_magic_cfm;
while (*entry_magic_ptr != 0) {
if (serial_recv(&pgm->fd, &c, 1) < 0)
goto timedout2;
printf("Received: %c (%d)\n", c, (int)c);
if (c != *entry_magic_ptr++) {
avrdude_message(MSG_INFO, "%s: stk500v2_recv(): MK3 printer emited incorrect start code\n", progname);
return -1;
}
gettimeofday(&tv, NULL);
tnow = tv.tv_sec;
if (tnow - tstart > 2.) { // wuff - signed/unsigned/overflow
timedout2:
avrdude_message(MSG_INFO, "%s: stk500v2_recv(): MK3 printer did not boot up on time\n", progname);
return -1;
}
}
}
if (stk500_getsync(pgm) < 0) if (stk500_getsync(pgm) < 0)
return -1; return -1;

View file

@ -716,11 +716,14 @@ static int stk500_loadaddr(PROGRAMMER * pgm, AVRMEM * mem, unsigned int addr)
} }
buf[0] = Cmnd_STK_LOAD_ADDRESS; buf[0] = Cmnd_STK_LOAD_ADDRESS;
buf[1] = addr & 0xff; // Workaround for the infamous ';' bug in the Prusa3D usb to serial converter.
buf[2] = (addr >> 8) & 0xff; // Send the binary data by nibbles to avoid transmitting the ';' character.
buf[3] = Sync_CRC_EOP; buf[1] = addr & 0x0f;
buf[2] = addr & 0xf0;
stk500_send(pgm, buf, 4); buf[3] = (addr >> 8) & 0x0f;
buf[4] = (addr >> 8) & 0xf0;
buf[5] = Sync_CRC_EOP;
stk500_send(pgm, buf, 6);
if (stk500_recv(pgm, buf, 1) < 0) if (stk500_recv(pgm, buf, 1) < 0)
return -1; return -1;
@ -765,7 +768,9 @@ static int stk500_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
int block_size; int block_size;
int tries; int tries;
unsigned int n; unsigned int n;
unsigned int i; unsigned int i, j;
unsigned int prusa3d_semicolon_workaround_round = 0;
bool has_semicolon = false;
if (strcmp(m->desc, "flash") == 0) { if (strcmp(m->desc, "flash") == 0) {
memtype = 'F'; memtype = 'F';
@ -806,15 +811,34 @@ static int stk500_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
tries++; tries++;
stk500_loadaddr(pgm, m, addr/a_div); stk500_loadaddr(pgm, m, addr/a_div);
for (i = 0; i < n_bytes; ++ i)
if (m->buf[addr + i] == ';') {
has_semicolon = true;
break;
}
for (prusa3d_semicolon_workaround_round = 0; prusa3d_semicolon_workaround_round < (has_semicolon ? 2 : 1); ++ prusa3d_semicolon_workaround_round) {
/* build command block and avoid multiple send commands as it leads to a crash /* build command block and avoid multiple send commands as it leads to a crash
of the silabs usb serial driver on mac os x */ of the silabs usb serial driver on mac os x */
i = 0; i = 0;
buf[i++] = Cmnd_STK_PROG_PAGE; buf[i++] = Cmnd_STK_PROG_PAGE;
buf[i++] = (block_size >> 8) & 0xff; // Workaround for the infamous ';' bug in the Prusa3D usb to serial converter.
buf[i++] = block_size & 0xff; // Send the binary data by nibbles to avoid transmitting the ';' character.
buf[i++] = (block_size >> 8) & 0xf0;
buf[i++] = (block_size >> 8) & 0x0f;
buf[i++] = block_size & 0xf0;
buf[i++] = block_size & 0x0f;
buf[i++] = memtype; buf[i++] = memtype;
if (has_semicolon) {
for (j = 0; j < block_size; ++i, ++ j) {
buf[i] = m->buf[addr + j];
if (buf[i] == ';')
buf[i] |= (prusa3d_semicolon_workaround_round ? 0xf0 : 0x0f);
}
} else {
memcpy(&buf[i], &m->buf[addr], block_size); memcpy(&buf[i], &m->buf[addr], block_size);
i += block_size; i += block_size;
}
buf[i++] = Sync_CRC_EOP; buf[i++] = Sync_CRC_EOP;
stk500_send( pgm, buf, i); stk500_send( pgm, buf, i);
@ -846,6 +870,7 @@ static int stk500_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
return -5; return -5;
} }
} }
}
return n_bytes; return n_bytes;
} }
@ -893,11 +918,15 @@ static int stk500_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
tries++; tries++;
stk500_loadaddr(pgm, m, addr/a_div); stk500_loadaddr(pgm, m, addr/a_div);
buf[0] = Cmnd_STK_READ_PAGE; buf[0] = Cmnd_STK_READ_PAGE;
buf[1] = (block_size >> 8) & 0xff; // Workaround for the infamous ';' bug in the Prusa3D usb to serial converter.
buf[2] = block_size & 0xff; // Send the binary data by nibbles to avoid transmitting the ';' character.
buf[3] = memtype; buf[1] = (block_size >> 8) & 0xf0;
buf[4] = Sync_CRC_EOP; buf[2] = (block_size >> 8) & 0x0f;
stk500_send(pgm, buf, 5); buf[3] = block_size & 0xf0;
buf[4] = block_size & 0x0f;
buf[5] = memtype;
buf[6] = Sync_CRC_EOP;
stk500_send(pgm, buf, 7);
if (stk500_recv(pgm, buf, 1) < 0) if (stk500_recv(pgm, buf, 1) < 0)
return -1; return -1;

View file

@ -171,11 +171,19 @@ void FirmwareDialog::priv::perform_upload()
std::vector<std::string> args {{ std::vector<std::string> args {{
"-v", "-v",
"-p", "atmega2560", "-p", "atmega2560",
"-c", "wiring", // Using the "Wiring" mode to program Rambo or Einsy, using the STK500v2 protocol (not the STK500).
// The Prusa's avrdude is patched to never send semicolons inside the data packets, as the USB to serial chip
// is flashed with a buggy firmware.
// "-c", "wiring",
// Using the "Arduino" mode to program Einsy's external flash with languages, using the STK500 protocol (not the STK500v2).
// The Prusa's avrdude is patched again to never send semicolons inside the data packets.
"-c", "arduino",
"-P", port, "-P", port,
"-b", "115200", // XXX: is this ok to hardcode? "-b", "115200", // XXX: is this ok to hardcode?
"-D", "-D",
"-u", // disable safe mode
"-U", (boost::format("flash:w:%1%:i") % filename_utf8.data()).str() "-U", (boost::format("flash:w:%1%:i") % filename_utf8.data()).str()
// "-v", "-v", "-v", "-v", "-v", // enable super verbose mode, logging each serial line exchange
}}; }};
BOOST_LOG_TRIVIAL(info) << "Invoking avrdude, arguments: " BOOST_LOG_TRIVIAL(info) << "Invoking avrdude, arguments: "
@ -192,6 +200,8 @@ void FirmwareDialog::priv::perform_upload()
.args(args) .args(args)
.on_run([]() { /* TODO: needed? */ }) .on_run([]() { /* TODO: needed? */ })
.on_message(std::move([q](const char *msg, unsigned /* size */) { .on_message(std::move([q](const char *msg, unsigned /* size */) {
// Debugging output to console, useful when avrdude is executed in a super verbose mode (with -v -v -v).
// printf("%s", msg);
auto evt = new wxCommandEvent(EVT_AVRDUDE, q->GetId()); auto evt = new wxCommandEvent(EVT_AVRDUDE, q->GetId());
auto wxmsg = wxString::FromUTF8(msg); auto wxmsg = wxString::FromUTF8(msg);
evt->SetExtraLong(AE_MESSAGE); evt->SetExtraLong(AE_MESSAGE);