/* * Copyright 2001 Brian S. Dean * All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY BRIAN S. DEAN ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BRIAN S. DEAN BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * */ /* $Id$ */ /* * Code to program an Atmel AVR AT90S device using the parallel port. * * Pin definitions can be changed via a config file. Below is the * default pin configuration. * * Make the following (minimal) connections: * * Parallel Port Programmer Function * ------------- ----------------------------- * Pins 2-5 -> Vcc (see note below) * Pin 7 -> AVR /RESET * Pin 8 -> AVR SCK (clock input) * Pin 9 -> AVR MOSI (instruction in) * Pin 10 <- AVR MISO (data out) * Pin 18 Signal Ground * * Additionally, the following conntections can be made to enable * additional features: * * Parallel Port Programmer Function * ------------- ----------------------------- * Pin 1 STATUS LED, active low (program or verify error) * Pin 6 -> /ENABLE ('367 bus driver) * Pin 14 STATUS LED, active low (ready) * Pin 16 STATUS LED, active low (programming) * Pin 17 STATUS LED, active low (verifying) * * Pin 6 can be tied to the enable line of a 74HC367 in order to * isolate and buffer the data to and from the PC parallel port. * This is useful for connecting to a device in-circuit, and keeps * the state of the parallel port pins from interfering with the * normal operation of the target system. When programming is * complete, this pin is driven high, causing to pins of the '367 to * float. * * NOTE on Vcc connection: make sure your parallel port can supply an * adequate amount of current to power your device. 6-10 mA per pin * is common for parallel port signal lines, but is not guaranteed, * especially for notebook computers. For additional power, use * multiple pins tied together with Schottky diodes. If in doubt, * don't risk damaging your parallel port, use an external power * supply. * */ #include #include #include #include #include #include #include #include #include #include "avr.h" #include "fileio.h" #include "pindefs.h" #include "ppi.h" #include "term.h" #define DEFAULT_PARALLEL "/dev/ppi0" extern char * avr_version; extern char * fileio_version; extern char * main_version; extern char * ppi_version; extern char * term_version; #define N_MODULES 5 char ** modules[5] = { &avr_version, &fileio_version, &main_version, &ppi_version, &term_version }; char * version = "1.3.0"; char * main_version = "$Id$"; char * progname; char progbuf[PATH_MAX]; /* temporary buffer of spaces the same length as progname; used for lining up multiline messages */ unsigned int pinno[N_PINS]; /* * usage message */ void usage ( void ) { fprintf(stderr, "Usage: %s -p partno [-e] [-E exitspec[,exitspec]] [-f format] " "[-F] [-V]\n" " %s[-i filename] [-m memtype] [-o filename] [-P parallel] " "[-t]\n\n", progname, progbuf); } /* * parse the -E string */ int getexitspecs ( char *s, int *set, int *clr ) { char *cp; while ((cp = strtok(s, ","))) { if (strcmp(cp, "reset") == 0) { *clr |= ppi_getpinmask(pinno[PIN_AVR_RESET]); } else if (strcmp(cp, "noreset") == 0) { *set |= ppi_getpinmask(pinno[PIN_AVR_RESET]); } else if (strcmp(cp, "vcc") == 0) { if (pinno[PPI_AVR_VCC]) *set |= pinno[PPI_AVR_VCC]; } else if (strcmp(cp, "novcc") == 0) { if (pinno[PPI_AVR_VCC]) *clr |= pinno[PPI_AVR_VCC]; } else { return -1; } s = 0; /* strtok() should be called with the actual string only once */ } return 0; } int parse_cvsid ( char * cvsid, char * name, char * rev, char * datetime ) { int i, j; if (strncmp(cvsid,"$Id: ",5) != 0) return -1; name[0] = 0; rev[0] = 0; datetime[0] = 0; i = 0; j = 5; while (cvsid[j] && cvsid[j] != ',') name[i++] = cvsid[j++]; name[i] = 0; while (cvsid[j] && cvsid[j] != ' ') j++; if (cvsid[j]) j++; i = 0; while (cvsid[j] && cvsid[j] != ' ') rev[i++] = cvsid[j++]; rev[i] = 0; if (cvsid[j]) j++; i = 0; while (cvsid[j] && cvsid[j] != ' ') datetime[i++] = cvsid[j++]; if (cvsid[j] == ' ') { datetime[i++] = cvsid[j++]; while (cvsid[j] && cvsid[j] != ' ') datetime[i++] = cvsid[j++]; } datetime[i] = 0; return 0; } int print_module_versions ( FILE * outf, char * timestamp ) { char name[64], rev[16], datetime[64]; int y, m, d, h, min, s; int i; int rc; int maxtime; struct tm t; time_t now; maxtime = 0; for (i=0; i maxtime) { maxtime = now; strcpy(timestamp, datetime); strcat(timestamp, " GMT"); } } if (outf) fprintf(outf, " %-10s %-5s %s\n", name, rev, datetime); } if (outf) fprintf(outf, "\n"); #if 0 gmtime_r(&maxtime, &t); fprintf(stderr, "Latest revision date is %04d/%02d/%02d %02d:%02d:%02d\n", t.tm_year+1900, t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec); #endif if (outf) fprintf(outf, "\n"); return 0; } #define MAX_LINE_LEN 1024 #define MAX_PIN_NAME 64 int parse_config(int lineno, char * infile, char * config, char * s, unsigned int * pinno, char * desc, int desclen) { char pin_name[MAX_PIN_NAME]; char * p; int i; int pins; unsigned int value; unsigned int v; char * e; pins = 0; p = s; while (1) { while (*p && isspace(*p)) p++; if (*p == 0) { if (pins == 0) { fprintf(stderr, "%s: warning: no pins configured using config entry \"%s\" " "at line %d of %s\n", progname, config, lineno, infile); } return 0; } /* * parse the pin name */ pin_name[0] = 0; i = 0; while (*p && (i 9)) { fprintf(stderr, "%s: error at line %d of %s: VCC must be one or more " "pins from the range 2-9\n", progname, lineno, infile); return -1; } value |= (1 << (v-2)); } else { if ((v <= 0) || (v >= 18)) { fprintf(stderr, "%s: error at line %d of %s: pin must be in the " "range 1-17\n", progname, lineno, infile); return -1; } value = v; } p = e; while (*p && (isspace(*p)||(*p == ','))) p++; } if (strcasecmp(pin_name, "VCC")==0) pinno[PPI_AVR_VCC] = value; else if (strcasecmp(pin_name, "BUFF")==0) pinno[PIN_AVR_BUFF] = value; else if (strcasecmp(pin_name, "RESET")==0) pinno[PIN_AVR_RESET] = value; else if (strcasecmp(pin_name, "SCK")==0) pinno[PIN_AVR_SCK] = value; else if (strcasecmp(pin_name, "MOSI")==0) pinno[PIN_AVR_MOSI] = value; else if (strcasecmp(pin_name, "MISO")==0) pinno[PIN_AVR_MISO] = value; else if (strcasecmp(pin_name, "ERRLED")==0) pinno[PIN_LED_ERR] = value; else if (strcasecmp(pin_name, "RDYLED")==0) pinno[PIN_LED_RDY] = value; else if (strcasecmp(pin_name, "PGMLED")==0) pinno[PIN_LED_PGM] = value; else if (strcasecmp(pin_name, "VFYLED")==0) pinno[PIN_LED_VFY] = value; else { fprintf(stderr, "%s: error at line %d of %s: invalid pin name \"%s\"\n", progname, lineno, infile, pin_name); return -1; } pins++; } } while (*p && (*p == ':')) p++; } return 0; } int read_config(char * infile, char * config, unsigned int * pinno, char * desc, int desclen) { FILE * f; char line[MAX_LINE_LEN]; char buf[MAX_LINE_LEN]; char configname[MAX_PIN_NAME]; int len, lineno, rc, cont; char * p, * q; int i; for (i=0; i= MAX_LINE_LEN) { fprintf(stderr, "%s: buffer length of %d exceed at line %d of \"%s\"\n", progname, MAX_LINE_LEN, lineno, infile); return -2; } if (cont) continue; /* continuation line, keep going */ /* * read the configuration name from the beginning of the line */ p = buf; i = 0; while (*p && (i < MAX_PIN_NAME) && (!(isspace(*p)||(*p == ':')))) { configname[i++] = *p; p++; } if (i == MAX_PIN_NAME) { fprintf(stderr, "%s: configuration name too long at line %d of \"%s\"\n", progname, lineno, infile); return -3; } configname[i] = 0; /* * position 'p' to the beginning of the pin information */ while (*p && (isspace(*p) || (*p == ':'))) p++; if (strcasecmp(configname, config) == 0) { strlcpy(desc, "no description", desclen); rc = parse_config(lineno, infile, config, p, pinno, desc, desclen); if (rc) { fprintf(stderr, "%s: error parsing config file \"%s\" at line %d\n", progname, infile, lineno); return -3; } return 0; } buf[0] = 0; } /* * config entry not found */ fprintf(stderr, "%s: config entry \"%s\" not found in file \"%s\"\n", progname, config, infile); return -5; } static char vccpins_buf[64]; char * vccpins_str(unsigned int pmask) { unsigned int mask; int pin; char b2[8]; char * b; b = vccpins_buf; b[0] = 0; for (pin = 2, mask = 1; mask < 0x80; mask = mask << 1, pin++) { if (pmask & mask) { sprintf(b2, "%d", pin); if (b[0] != 0) strcat(b, ","); strcat(b, b2); } } return b; } void pinconfig_display(char * p, char * config, char * desc) { char vccpins[64]; if (pinno[PPI_AVR_VCC]) { snprintf(vccpins, sizeof(vccpins), " = pins %s", vccpins_str(pinno[PPI_AVR_VCC])); } else { vccpins[0] = 0; } fprintf(stderr, "%sProgrammer Pin Configuration: %s (%s)\n", p, config ? config : "DEFAULT", desc); fprintf(stderr, "%s VCC = 0x%02x %s\n" "%s BUFF = %d\n" "%s RESET = %d\n" "%s SCK = %d\n" "%s MOSI = %d\n" "%s MISO = %d\n" "%s ERR LED = %d\n" "%s RDY LED = %d\n" "%s PGM LED = %d\n" "%s VFY LED = %d\n", p, pinno[PPI_AVR_VCC], vccpins, p, pinno[PIN_AVR_BUFF], p, pinno[PIN_AVR_RESET], p, pinno[PIN_AVR_SCK], p, pinno[PIN_AVR_MOSI], p, pinno[PIN_AVR_MISO], p, pinno[PIN_LED_ERR], p, pinno[PIN_LED_RDY], p, pinno[PIN_LED_PGM], p, pinno[PIN_LED_VFY]); } void verify_pin_assigned(int pin, char * desc) { if (pinno[pin] == 0) { fprintf(stderr, "%s: error: no pin has been assigned for %s\n", progname, desc); exit(1); } } #define MAX_DESC_LEN 80 /* * main routine */ int main ( int argc, char * argv [] ) { int fd; /* file descriptor for parallel port */ int rc; /* general return code checking */ int exitrc; /* exit code for main() */ int i; /* general loop counter */ int ch; /* options flag */ int size; /* size of memory region */ int len; /* length for various strings */ unsigned char sig[4]; /* AVR signature bytes */ unsigned char nulldev[4]; /* 0xff signature bytes for comparison */ struct avrpart * p, ap1; /* which avr part we are programming */ struct avrpart * v, ap2; /* used for verify */ int readorwrite; /* true if a chip read/write op was selected */ int ppidata; /* cached value of the ppi data register */ int vsize=-1; /* number of bytes to verify */ char timestamp[64]; char configfile[PATH_MAX]; /* pin configuration file */ char * pinconfig; char desc[MAX_DESC_LEN]; /* options / operating mode variables */ int memtype; /* AVR_FLASH or AVR_EEPROM */ int doread; /* 1=reading AVR, 0=writing AVR */ int erase; /* 1=erase chip, 0=don't */ char * outputf; /* output file name */ char * inputf; /* input file name */ int ovsigck; /* 1=override sig check, 0=don't */ char * parallel; /* parallel port device */ int terminal; /* 1=enter terminal mode, 0=don't */ FILEFMT filefmt; /* FMT_AUTO, FMT_IHEX, FMT_SREC, FMT_RBIN */ int nowrite; /* don't actually write anything to the chip */ int verify; /* perform a verify operation */ int ppisetbits; /* bits to set in ppi data register at exit */ int ppiclrbits; /* bits to clear in ppi data register at exit */ char * exitspecs; /* exit specs string from command line */ readorwrite = 0; parallel = DEFAULT_PARALLEL; outputf = NULL; inputf = NULL; doread = 1; memtype = AVR_FLASH; erase = 0; p = NULL; ovsigck = 0; terminal = 0; filefmt = FMT_AUTO; nowrite = 0; verify = 1; /* on by default; XXX can't turn it off */ ppisetbits = 0; ppiclrbits = 0; exitspecs = NULL; pinconfig = NULL; strcpy(configfile, CONFIG_DIR); i = strlen(configfile); if (i && (configfile[i-1] != '/')) strcat(configfile, "/"); strcat(configfile, "avrprog.conf"); for (i=0; imem[AVR_FLASH|AVR_EEPROM]) * is the same as what is on the chip */ LED_ON(fd, pinno[PIN_LED_VFY]); fprintf(stderr, "%s: verifying %s memory against %s:\n", progname, avr_memtstr(memtype), inputf); fprintf(stderr, "%s: reading on-chip %s data:\n", progname, avr_memtstr(memtype)); rc = avr_read ( fd, v, memtype ); if (rc < 0) { fprintf(stderr, "%s: failed to read all of %s memory, rc=%d\n", progname, avr_memtstr(memtype), rc); LED_ON(fd, pinno[PIN_LED_ERR]); exitrc = 1; goto main_exit; } fprintf(stderr, "%s: verifying ...\n", progname); rc = avr_verify(p, v, memtype, vsize); if (rc < 0) { fprintf(stderr, "%s: verification error; content mismatch\n", progname); LED_ON(fd, pinno[PIN_LED_ERR]); exitrc = 1; goto main_exit; } fprintf(stderr, "%s: %d bytes of %s verified\n", progname, rc, avr_memtstr(memtype)); LED_OFF(fd, pinno[PIN_LED_VFY]); } main_exit: /* * program complete */ avr_powerdown(fd); ppi_setall(fd, PPIDATA, ppidata); /* * disable the 74367 buffer, if connected; this signal is active low */ ppi_setpin(fd, pinno[PIN_AVR_BUFF], 1); LED_OFF(fd, pinno[PIN_LED_RDY]); close(fd); fprintf(stderr, "\n%s done. Thank you.\n\n", progname); return exitrc; }