Skip to content

Commit

Permalink
Merge pull request #1505 from stefanrueger/add-wiring-reset-delay
Browse files Browse the repository at this point in the history
Add -xdelay=n for -c wiring to modify sleep after dtr/rts reset
  • Loading branch information
stefanrueger authored Sep 28, 2023
2 parents 2f9cd84 + aa2f451 commit b76750d
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 90 deletions.
8 changes: 5 additions & 3 deletions src/arduino.c
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,12 @@ static int arduino_open(PROGRAMMER *pgm, const char *port) {
usleep(250 * 1000);
// Pull the RTS/DTR line low to reset AVR
serial_set_dtr_rts(&pgm->fd, 1);
usleep(50 * 1000);
// Set the RTS/DTR line back to high
// Max 100 us: charging a cap longer creates a high reset spike above Vcc
usleep(100);
// Set the RTS/DTR line back to high, so direct connection to reset works
serial_set_dtr_rts(&pgm->fd, 0);
usleep(50 * 1000);

usleep(100 * 1000);

/*
* drain any extraneous input
Expand Down
9 changes: 7 additions & 2 deletions src/avrdude.1
Original file line number Diff line number Diff line change
Expand Up @@ -1782,15 +1782,20 @@ Show help menu and exit.
.El
.It Ar Wiring
When using the Wiring programmer type, the
following optional extended parameter is accepted:
following optional extended parameters are accepted:
.Bl -tag -offset indent -width indent
.It Ar snooze=<0..32767>
.It Ar snooze=<n>
After performing the port open phase, AVRDUDE will wait/snooze for
.Ar snooze
milliseconds before continuing to the protocol sync phase.
No toggling of DTR/RTS is performed if
.Ar snooze
is greater than 0.
.It Ar delay=<n>
Add a <n> milliseconds delay after resetting the part through toggling the
DTR/RTS lines. This can be useful if a board takes a particularly long
time to exit from external reset. <n> can be negative, in which case the
default 100 ms delay after issuing reset will be shortened accordingly.
.It Ar help
Show help menu and exit.
.El
Expand Down
9 changes: 7 additions & 2 deletions src/doc/avrdude.texi
Original file line number Diff line number Diff line change
Expand Up @@ -1438,12 +1438,17 @@ Show help menu and exit.
@cindex @code{-x} Wiring
@item Wiring

The Wiring programmer type accepts the following extended parameter:
The Wiring programmer type accepts the following extended parameters:
@table @code
@item @samp{snooze=@var{0..32767}}
@item @samp{snooze=<n>}
After performing the port open phase, AVRDUDE will wait/snooze for
@var{snooze} milliseconds before continuing to the protocol sync phase.
No toggling of DTR/RTS is performed if @var{snooze} > 0.
@item @samp{delay=<n>}
Add a <n> milliseconds delay after reset. This can be useful if a board
takes a particularly long time to exit from external reset. <n> can be
negative, in which case the default 100 ms delay after issuing reset will
be shortened accordingly.
@item @samp{help}
Show help menu and exit.
@end table
Expand Down
6 changes: 4 additions & 2 deletions src/stk500.c
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,11 @@ int stk500_getsync(const PROGRAMMER *pgm) {
// This code assumes a negative-logic USB to TTL serial adapter
// Pull the RTS/DTR line low to reset AVR: it is still high from open()/last attempt
serial_set_dtr_rts(&pgm->fd, 1);
usleep(20*1000);
// Set the RTS/DTR line back to high
// Max 100 us: charging a cap longer creates a high reset spike above Vcc
usleep(100);
// Set the RTS/DTR line back to high, so direct connection to reset works
serial_set_dtr_rts(&pgm->fd, 0);
usleep(20*1000);
stk500_drain(pgm, 0);
}

Expand Down
9 changes: 5 additions & 4 deletions src/urclock.c
Original file line number Diff line number Diff line change
Expand Up @@ -2220,12 +2220,13 @@ static int urclock_open(PROGRAMMER *pgm, const char *port) {
usleep(20*1000);
// Pull the RTS/DTR line low to reset AVR
serial_set_dtr_rts(&pgm->fd, 1);
usleep(20*1000);
// Set the RTS/DTR line back to high
// Max 100 us: charging a cap longer creates a high reset spike above Vcc
usleep(100);
// Set the RTS/DTR line back to high, so direct connection to reset works
serial_set_dtr_rts(&pgm->fd, 0);

if((100+ur.delay) > 0)
usleep((100+ur.delay)*1000); // Wait until board comes out of reset
if((120+ur.delay) > 0)
usleep((120+ur.delay)*1000); // Wait until board comes out of reset

pmsg_debug("%4ld ms: enter urclock_getsync()\n", avr_mstimestamp());
if(urclock_getsync(pgm) < 0)
Expand Down
127 changes: 50 additions & 77 deletions src/wiring.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,13 @@
* e.g. chip erase).
* DTR and RTS signals are diddled to set the board into programming mode.
*
* Also includes an extended parameter to introduce a delay after opening
* to accommodate multi-layered programmers/bootloaders. If the extended
* parameter 'snooze' > 0, then no DTR/RTS toggle takes place, and
* AVRDUDE will wait that amount of time in milliseconds before syncing.
* Also includes an extended parameter to introduce a delay after opening to
* accommodate multi-layered programmers/bootloaders. If the extended
* parameter 'snooze' > 0, then no DTR/RTS toggle takes place, and AVRDUDE
* will wait that amount of time in milliseconds before syncing. If the
* extended parameter 'delay' is set then this number of milliseconds is
* added to the usual delay of 80 ms after toggling DTR/RTS.
*
* Unfortunately, there is no way to easily chain private programmer data
* when we "inherit" programmer types as we have (stk500v2). Sooooo, a
* *cringe* global variable is used to store the snooze time.
*/

#include "ac_cfg.h"
Expand All @@ -55,84 +54,60 @@
/*
* Private data for this programmer.
*/
struct wiringpdata
{
/*
* We just have the single snooze integer to carry around for now.
*/
int snoozetime;
struct wiringpdata {
int snoozetime, delay;
};


/* wiringpdata is our private data */
/* pdata is stk500v2's private data (inherited) */

#define WIRINGPDATA(x) ((struct wiringpdata *)(x))

#define STK500V2PDATA(pgm) ((struct pdata *)(pgm->cookie))

#define WIRINGPDATA(pgm) ((struct wiringpdata *)(((struct pdata *)(pgm->cookie)) -> chained_pdata))

static void wiring_setup(PROGRAMMER * pgm)
{
void *mycookie;

/*
* First, have STK500v2 backend allocate its own private data.
*/
static void wiring_setup(PROGRAMMER *pgm) {
// First, have STK500v2 backend allocate its own private data
stk500v2_setup(pgm);

/*
* Now prepare our data
*/
if ((mycookie = malloc(sizeof(struct wiringpdata))) == 0) {
pmsg_error("out of memory allocating private data\n");
exit(1);
}
memset(mycookie, 0, sizeof(struct wiringpdata));
WIRINGPDATA(mycookie)->snoozetime = 0;

/*
* Store our own cookie in a safe place for the time being.
*/
STK500V2PDATA(pgm)->chained_pdata = mycookie;
// Then prepare our data and store in a safe place for the time being
((struct pdata *)(pgm->cookie))->chained_pdata = cfg_malloc(__func__, sizeof(struct wiringpdata));
}

static void wiring_teardown(PROGRAMMER * pgm)
{
void *mycookie;

mycookie = STK500V2PDATA(pgm)->chained_pdata;

free(mycookie);

static void wiring_teardown(PROGRAMMER *pgm) {
free(((struct pdata *)(pgm->cookie))->chained_pdata);
stk500v2_teardown(pgm);
}

static int wiring_parseextparms(const PROGRAMMER *pgm, const LISTID extparms) {
LNODEID ln;
const char *extended_param;
const char *extended_param, *errstr;
int rv = 0;
void *mycookie = STK500V2PDATA(pgm)->chained_pdata;

for (ln = lfirst(extparms); ln; ln = lnext(ln)) {
extended_param = ldata(ln);

if (str_starts(extended_param, "snooze=")) {
int newsnooze;
if (sscanf(extended_param, "snooze=%i", &newsnooze) != 1 ||
newsnooze < 0) {
pmsg_error("invalid snooze time '%s'\n", extended_param);
int val = str_int(extended_param+7, STR_INT32, &errstr);
if(errstr || val < 0) {
pmsg_error("-x%s: %s\n", extended_param, errstr? errstr: "snooze time cannot be negative");
rv = -1;
continue;
}
pmsg_notice2("wiring_parseextparms(): snooze time set to %d ms\n", newsnooze);
WIRINGPDATA(mycookie)->snoozetime = newsnooze;

pmsg_notice2("%s(): snooze time set to %d ms\n", __func__, val);
WIRINGPDATA(pgm)->snoozetime = val;
continue;
}
if (str_eq(extended_param, "help")) {
} else if (str_starts(extended_param, "delay=")) {
int val = str_int(extended_param+6, STR_INT32, &errstr);
if(errstr) {
pmsg_error("-x%s: %s\n", extended_param, errstr);
return -1;
}
pmsg_notice2("%s(): delay set to %d ms\n", __func__, val);
WIRINGPDATA(pgm)->delay = val;
continue;
} else if (str_eq(extended_param, "help")) {
msg_error("%s -c %s extended options:\n", progname, pgmid);
msg_error(" -xsnooze=<arg> Wait <arg> [ms] before protocol sync after port open\n");
msg_error(" -xsnooze=<arg> Wait snooze [ms] before protocol sync after port open\n");
msg_error(" -xdelay=<arg> Add delay [ms] after reset, can be negative\n");
msg_error(" -xhelp Show this help menu and exit\n");
exit(0);
}
Expand All @@ -146,51 +121,49 @@ static int wiring_parseextparms(const PROGRAMMER *pgm, const LISTID extparms) {

static int wiring_open(PROGRAMMER *pgm, const char *port) {
int timetosnooze;
void *mycookie = STK500V2PDATA(pgm)->chained_pdata;
union pinfo pinfo;

strcpy(pgm->port, port);
pinfo.serialinfo.baud = pgm->baudrate ? pgm->baudrate: 115200;
pinfo.serialinfo.cflags = SERIAL_8N1;
serial_open(port, pinfo, &pgm->fd);

/* If we have a snoozetime, then we wait and do NOT toggle DTR/RTS */

if (WIRINGPDATA(mycookie)->snoozetime > 0) {
timetosnooze = WIRINGPDATA(mycookie)->snoozetime;
// If we have a snoozetime, then we wait and do NOT toggle DTR/RTS
if (WIRINGPDATA(pgm)->snoozetime > 0) {
timetosnooze = WIRINGPDATA(pgm)->snoozetime;

pmsg_notice2("wiring_open(): snoozing for %d ms\n", timetosnooze);
while (timetosnooze--)
usleep(1000);
pmsg_notice2("wiring_open(): done snoozing\n");
} else {
/* Perform Wiring programming mode RESET. */
/* This effectively *releases* both DTR and RTS. */
/* i.e. both DTR and RTS rise to a HIGH logic level */
/* since they are active LOW signals. */

// This code assumes a negative-logic USB to TTL serial adapter
// Set RTS/DTR high to discharge the series-capacitor, if present
pmsg_notice2("wiring_open(): releasing DTR/RTS\n");

serial_set_dtr_rts(&pgm->fd, 0);
usleep(50*1000);

/* After releasing for 50 milliseconds, DTR and RTS */
/* are asserted (i.e. logic LOW) again. */

// Pull the RTS/DTR line low to reset AVR
pmsg_notice2("wiring_open(): asserting DTR/RTS\n");

serial_set_dtr_rts(&pgm->fd, 1);
usleep(50*1000);

/* Set high, so a direct connection to reset works. */
// Max 100 us: charging a cap longer creates a high reset spike above Vcc
usleep(100);
// Set the RTS/DTR line back to high, so direct connection to reset works
serial_set_dtr_rts(&pgm->fd, 0);

int delay = WIRINGPDATA(pgm)->delay;
if((100+delay) > 0)
usleep((100+delay)*1000); // Wait until board comes out of reset
}

/* drain any extraneous input */
// Drain any extraneous input
stk500v2_drain(pgm, 0);

if (stk500v2_getsync(pgm) < 0)
if (stk500v2_getsync(pgm) < 0) {
pmsg_error("stk500v2_getsync() failed; try -xdelay=n with some n in [-80, 100]\n");
return -1;
}

return 0;
}
Expand Down

0 comments on commit b76750d

Please sign in to comment.