avrdude/avrprog.c

985 lines
20 KiB
C

/*
* Copyright 2000 Brian S. Dean <bsd@bsdhome.com>
* 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.
*
* Make the following connections:
*
* Parallel Port Atmel AVR
* ------------- ----------------------------
* Pin 2 -> Vcc
* Pin 3 -> PB7(SCK) CLOCK IN
* Pin 4 -> PB5(MOSI) Instruction input
* Pin 5 -> /RESET
* Pin 10 <- PB6(MISO) Data out
*
*/
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <stdarg.h>
#include <sys/stat.h>
#include </sys/dev/ppbus/ppi.h>
#include </sys/dev/ppbus/ppbconf.h>
#define PARALLEL "/dev/ppi0"
char * version = "$Id$";
char * progname;
/*
* bit definitions for AVR device connections
*/
#define AVR_POWER 0x01 /* bit 0 of data register */
#define AVR_CLOCK 0x02 /* bit 1 of data register */
#define AVR_INSTR 0x04 /* bit 2 of data register */
#define AVR_RESET 0x08 /* bit 3 of data register */
#define AVR_DATA 0x40 /* bit 6 of status register */
/*
* PPI registers
*/
enum {
PPIDATA,
PPICTRL,
PPISTATUS
};
/*
* AVR memory designations
*/
typedef enum {
AVR_EEPROM,
AVR_FLASH,
AVR_FLASH_LO,
AVR_FLASH_HI
} AVRMEM;
/*
* variable declarations required for getopt()
*/
char *optarg;
int optind;
int optopt;
int opterr;
int optreset;
/*
* set 'get' and 'set' appropriately for subsequent passage to ioctl()
* to get/set the specified PPI registers.
*/
int ppi_getops ( int reg, unsigned long * get, unsigned long * set )
{
switch (reg) {
case PPIDATA:
*set = PPISDATA;
*get = PPIGDATA;
break;
case PPICTRL:
*set = PPISCTRL;
*get = PPIGCTRL;
break;
case PPISTATUS:
*set = PPISSTATUS;
*get = PPIGSTATUS;
break;
default:
fprintf ( stderr, "%s: avr_set(): invalid register=%d\n",
progname, reg );
return -1;
break;
}
return 0;
}
/*
* set the indicated bit of the specified register.
*/
int ppi_set ( int fd, int reg, int bit )
{
unsigned char v;
unsigned long get, set;
int rc;
rc = ppi_getops ( reg, &get, &set );
if (rc)
return -1;
ioctl(fd, get, &v);
v |= bit;
ioctl(fd, set, &v);
return 0;
}
/*
* clear the indicated bit of the specified register.
*/
int ppi_clr ( int fd, int reg, int bit )
{
unsigned char v;
unsigned long get, set;
int rc;
rc = ppi_getops ( reg, &get, &set );
if (rc)
return -1;
ioctl(fd, get, &v);
v &= ~bit;
ioctl(fd, set, &v);
return 0;
}
/*
* get the indicated bit of the specified register.
*/
int ppi_get ( int fd, int reg, int bit )
{
unsigned char v;
unsigned long get, set;
int rc;
rc = ppi_getops ( reg, &get, &set );
if (rc)
return -1;
ioctl(fd, get, &v);
v &= bit;
return (v == bit);
}
/*
* toggle the indicated bit of the specified register.
*/
int ppi_toggle ( int fd, int reg, int bit )
{
unsigned char v;
unsigned long get, set;
int rc;
rc = ppi_getops ( reg, &get, &set );
if (rc)
return -1;
ioctl(fd, get, &v);
v ^= bit;
ioctl(fd, set, &v);
return 0;
}
/*
* pulse the indicated bit of the specified register.
*/
int ppi_pulse ( int fd, int reg, int bit )
{
ppi_toggle(fd, reg, bit);
ppi_toggle(fd, reg, bit);
return 0;
}
/*
* transmit and receive a bit of data to/from the AVR device
*/
int avr_txrx_bit ( int fd, int bit )
{
unsigned char d;
int r;
ioctl(fd, PPIGDATA, &d);
r = ppi_get(fd, PPISTATUS, AVR_DATA);
if (bit)
ppi_set(fd, PPIDATA, AVR_INSTR);
else
ppi_clr(fd, PPIDATA, AVR_INSTR);
ppi_pulse(fd, PPIDATA, AVR_CLOCK);
return r;
}
/*
* transmit and receive a byte of data to/from the AVR device
*/
unsigned char avr_txrx ( int fd, unsigned char byte )
{
int i;
unsigned char r, b, rbyte;
rbyte = 0;
for (i=0; i<8; i++) {
b = (byte >> (7-i)) & 0x01;
r = avr_txrx_bit ( fd, b );
rbyte = rbyte | (r << (7-i));
}
return rbyte;
}
/*
* transmit an AVR device command and return the results; 'cmd' and
* 'res' must point to at least a 4 byte data buffer
*/
int avr_cmd ( int fd, unsigned char cmd[4], unsigned char res[4] )
{
int i;
for (i=0; i<4; i++) {
res[i] = avr_txrx(fd, cmd[i]);
}
return 0;
}
/*
* read a byte of data from the indicated memory region
*/
unsigned char avr_read_byte ( int fd, AVRMEM memtype, unsigned short addr )
{
unsigned char cmd[4];
unsigned char res[4];
switch (memtype) {
case AVR_FLASH_LO:
cmd[0] = 0x20;
break;
case AVR_FLASH_HI:
cmd[0] = 0x28;
break;
case AVR_EEPROM:
cmd[0] = 0xa0;
addr &= 0x7f;
break;
default:
fprintf(stderr, "%s: avr_read_byte(); internal error: invalid memtype=%d\n",
progname, memtype);
exit(1);
break;
}
cmd[1] = addr >> 8; /* high order bits of address */
cmd[2] = addr & 0x0ff; /* low order bits of address */
cmd[3] = 0; /* don't care */
avr_cmd(fd, cmd, res);
return res[3];
}
/*
* read 'n' words of data from the indicated memory region. If the
* flash memory is being read, n*2 bytes will be read into 'buf'; if
* the eeprom is being read, 'n' bytes will be read into 'buf'.
*/
int avr_read ( int fd, AVRMEM memtype, unsigned start, unsigned n,
unsigned char * buf, int bufsize )
{
unsigned char rbyte, memt;
unsigned short end, i, bi;
switch (memtype) {
case AVR_FLASH :
memt = AVR_FLASH_LO;
break;
case AVR_EEPROM :
memt = memtype;
break;
default:
fprintf(stderr, "%s: avr_read(); internal error: invalid memtype=%d\n",
progname, memtype);
exit(1);
break;
}
end = start+n;
bi = 0;
for (i=start; i<end; i++) {
/* eeprom or low byte of flash */
rbyte = avr_read_byte(fd, memt, i);
fprintf ( stderr, " \r%4u 0x%02x", i, rbyte );
if (bi < bufsize) {
buf[bi++] = rbyte;
}
if (memtype == AVR_FLASH) {
/* flash high byte */
rbyte = avr_read_byte(fd, AVR_FLASH_HI, i);
fprintf ( stderr, " 0x%02x", rbyte );
if (bi < bufsize) {
buf[bi++] = rbyte;
}
}
}
fprintf ( stderr, "\n" );
return 0;
}
/*
* write a byte of data to the indicated memory region
*/
int avr_write_byte ( int fd, AVRMEM memtype, unsigned short addr, unsigned char data )
{
unsigned char cmd[4], res[4];
unsigned char r;
int ready;
int tries;
switch (memtype) {
case AVR_FLASH_LO:
cmd[0] = 0x40;
break;
case AVR_FLASH_HI:
cmd[0] = 0x48;
break;
case AVR_EEPROM:
cmd[0] = 0xc0;
addr &= 0x7f;
break;
default:
fprintf(stderr, "%s: avr_write_byte(); internal error: invalid memtype=%d\n",
progname, memtype);
exit(1);
break;
}
cmd[1] = addr >> 8; /* high order bits of address */
cmd[2] = addr & 0x0ff; /* low order bits of address */
cmd[3] = data; /* data */
avr_cmd(fd, cmd, res);
tries = 0;
ready = 0;
while (!ready) {
usleep(5000); /* flash write delay */
r = avr_read_byte(fd, memtype, addr);
if (data == 0x7f) {
usleep(20000); /* long delay for 0x7f since polling doesn't work */
ready = 1;
}
else if (r == data) {
ready = 1;
}
tries++;
if (!ready && tries > 10) {
/*
* we couldn't write the data, indicate our displeasure by
* returning an error code
*/
return -1;
}
}
return 0;
}
/*
* write 'bufsize' bytes of data to the indicated memory region.
*/
int avr_write ( int fd, AVRMEM memtype, unsigned start,
unsigned char * buf, int bufsize )
{
unsigned char data, memt;
unsigned short end, i, bi;
int nl;
int rc;
switch (memtype) {
case AVR_FLASH :
end = start+bufsize/2;
memt = AVR_FLASH_LO;
break;
case AVR_EEPROM :
end = start+bufsize;
memt = memtype;
break;
default:
fprintf(stderr, "%s: avr_write(); internal error: invalid memtype=%d\n",
progname, memtype);
exit(1);
break;
}
bi = 0;
for (i=start; i<end; i++) {
/* eeprom or low byte of flash */
data = buf[bi++];
nl = 0;
rc = avr_write_byte(fd, memt, i, data );
fprintf(stderr, " \r%4u 0x%02x", i, data);
if (rc) {
fprintf(stderr, " ***failed; ");
nl = 1;
}
if (memtype == AVR_FLASH) {
/* high byte of flash */
data = buf[bi++];
rc = avr_write_byte(fd, AVR_FLASH_HI, i, data );
fprintf(stderr, " 0x%02x", data);
if (rc) {
fprintf(stderr, " ***failed; " );
nl = 1;
}
}
if (nl)
fprintf(stderr, "\n");
}
fprintf ( stderr, "\n" );
return 0;
}
/*
* issue the 'program enable' command to the AVR device
*/
int avr_program_enable ( int fd )
{
unsigned char cmd[4] = {0xac, 0x53, 0x00, 0x00};
unsigned char res[4];
avr_cmd(fd, cmd, res);
if (res[2] != cmd[1])
return -1;
return 0;
}
/*
* issue the 'chip erase' command to the AVR device
*/
int avr_chip_erase ( int fd )
{
unsigned char data[4] = {0xac, 0x80, 0x00, 0x00};
unsigned char res[4];
avr_cmd(fd, data, res);
usleep(20000);
return 0;
}
/*
* read the AVR device's signature bytes
*/
int avr_signature ( int fd, unsigned char sig[4] )
{
unsigned char cmd[4] = {0x30, 0x00, 0x00, 0x00};
unsigned char res[4];
int i;
for (i=0; i<4; i++) {
cmd[2] = i;
avr_cmd(fd, cmd, res);
sig[i] = res[3];
}
return 0;
}
/*
* apply power to the AVR processor
*/
void avr_powerup ( int fd )
{
ppi_set(fd, PPIDATA, AVR_POWER); /* power up */
usleep(100000);
}
/*
* remove power from the AVR processor
*/
void avr_powerdown ( int fd )
{
ppi_clr(fd, PPIDATA, AVR_POWER); /* power down */
}
/*
* initialize the AVR device and prepare it to accept commands
*/
int avr_initialize ( int fd )
{
int rc;
int tries;
avr_powerup(fd);
ppi_clr(fd, PPIDATA, AVR_CLOCK);
ppi_clr(fd, PPIDATA, AVR_RESET);
ppi_pulse(fd, PPIDATA, AVR_RESET);
usleep(20000); /* 20 ms */
/*
* enable programming mode, try up to 32 times in order to possibly
* get back into sync with the chip if we are out of sync.
*/
tries = 0;
do {
rc = avr_program_enable ( fd );
if (rc == 0)
break;
ppi_pulse(fd, PPIDATA, AVR_CLOCK);
tries++;
} while (tries < 32);
/*
* can't sync with the device, maybe it's not attached?
*/
if (tries == 32) {
fprintf ( stderr, "%s: AVR device not responding\n", progname );
return -1;
}
return 0;
}
/*
* infinite loop, sensing on the pin that we use to read data out of
* the device; this is a debugging aid, you can insert a call to this
* function in 'main()' and can use it to determine whether your sense
* pin is actually sensing.
*/
int ppi_sense_test ( int fd )
{
unsigned char v, pv;
pv = 1;
do {
usleep(100000); /* check every 100 ms */
v = ppi_get(fd, PPISTATUS, AVR_DATA);
if (v != pv) {
fprintf ( stderr, "sense bit = %d\n", v );
}
pv = v;
} while(1);
return 0;
}
/*
* usage message
*/
void usage ( void )
{
fprintf ( stderr,
"\nUsage: %s [-r] [-e|-f] [-u InputFile|-o Outputfile]\n"
"\n"
" Available Options:\n"
" -r : erase the flash and eeprom (required before programming)\n"
" -e : select eeprom for reading or writing\n"
" -f : select flash for reading or writing\n"
" -u InputFile : write data from this file\n"
" -o OutputFile : write data to this file\n"
"\n",
progname );
}
/*
* main routine
*/
int main ( int argc, char * argv [] )
{
int fd;
int rc, exitrc;
int i;
unsigned char buf[2048];
unsigned char sig[4];
int ch;
int iofd;
int flash, eeprom, doread, erase, dosig;
int size;
char * outputf;
char * inputf;
char * p1, * p2;
iofd = -1;
outputf = NULL;
inputf = NULL;
doread = 1;
eeprom = 0;
flash = 0;
erase = 0;
dosig = 0;
progname = rindex(argv[0],'/');
if (progname)
progname++;
else
progname = argv[0];
/*
* Print out an identifying string so folks can tell what version
* they are running
*/
p1 = strchr(version,',');
if (p1 == NULL)
p1 = version;
else
p1 += 3;
p2 = strrchr(p1,':');
if (p2 == NULL)
p2 = &p1[strlen(p1)];
else
p2 += 3;
fprintf(stderr, "\n");
fprintf(stderr, "AVRProg: Copyright 2000 Brian Dean, bsd@bsdhome.com\n");
fprintf(stderr, " Revision " );
for (i=0; i<p2-p1; i++)
fprintf(stderr, "%c", p1[i]);
fprintf(stderr, "\n");
/*
* check for no arguments
*/
if (argc == 1) {
usage();
return 0;
}
/*
* process command line arguments
*/
while ((ch = getopt(argc,argv,"?efo:rsu:")) != -1) {
switch (ch) {
case 'e': /* select eeprom memory */
if (flash) {
fprintf(stderr,"%s: -e and -f are incompatible\n", progname);
return 1;
}
eeprom = 1;
break;
case 'r': /* perform a chip erase */
erase = 1;
break;
case 's': /* read out the signature bytes */
dosig = 1;
break;
case 'f': /* select flash memory */
if (eeprom) {
fprintf(stderr,"%s: -e and -f are incompatible\n", progname);
return 1;
}
flash = 1;
break;
case 'o': /* specify output file */
if (inputf) {
fprintf(stderr,"%s: -o and -u are incompatible\n", progname);
return 1;
}
doread = 1;
outputf = optarg;
if (strcmp(outputf,"-")==0) {
iofd = fileno(stdout);
}
else {
iofd = open ( outputf, O_WRONLY|O_CREAT|O_TRUNC,
S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
if (iofd < 0) {
fprintf(stderr, "%s: can't open output file \"%s\": %s\n",
progname, outputf, strerror(errno));
return 1;
}
}
break;
case 'u': /* specify input (upload) file */
if (outputf) {
fprintf(stderr,"%s: -o and -u are incompatible\n", progname);
return 1;
}
doread = 0;
inputf = optarg;
iofd = open ( inputf, O_RDONLY, 0);
if (iofd < 0) {
fprintf(stderr, "%s: can't open input file \"%s\": %s\n",
progname, inputf, strerror(errno));
return 1;
}
break;
case '?': /* help */
usage();
return 1;
break;
default:
fprintf(stderr, "%s: invalid option -%c\n", progname, ch);
usage();
return 1;
break;
}
}
/*
* open the parallel port
*/
fd = open ( PARALLEL, O_RDWR );
if (fd < 0) {
fprintf ( stderr, "%s: can't open device \"%s\": %s\n",
progname, PARALLEL, strerror(errno) );
return 1;
}
exitrc = 0;
/*
* initialize the chip in preperation for accepting commands
*/
rc = avr_initialize(fd);
if (rc < 0) {
fprintf ( stderr, "%s: initialization failed, rc=%d\n", progname, rc );
exitrc = 1;
goto main_exit;
}
fprintf ( stderr, "%s: AVR device initialized and ready to accept instructions\n",
progname );
if (erase) {
/*
* erase the chip's flash and eeprom memories, this is required
* before the chip can accept new programming
*/
fprintf(stderr, "%s: erasing chip\n", progname );
avr_chip_erase(fd);
avr_initialize(fd);
fprintf(stderr, "%s: done.\n", progname );
}
if (dosig) {
/*
* read out the on-chip signature bytes
*/
fprintf(stderr, "%s: reading signature bytes: ", progname );
avr_signature(fd, sig);
for (i=0; i<4; i++)
fprintf(stderr, "0x%02x ", sig[i]);
fprintf(stderr, "\n");
}
if (iofd < 0) {
/*
* Check here to see if any other operations were selected and
* generate an error message because if they were, we need either
* an input or and output file, but one was not selected.
* Otherwise, we just shut down.
*/
if (eeprom||flash) {
fprintf(stderr, "%s: you must specify an input or an output file\n",
progname);
}
exitrc = 1;
goto main_exit;
}
if (!(eeprom||flash)) {
/*
* an input file or an output file was specified, but the memory
* type (eeprom or flash) was not specified.
*/
fprintf(stderr,
"%s: please specify either the eeprom (-e) or the flash (-f) memory\n",
progname);
exitrc = 1;
goto main_exit;
}
if (doread) {
/*
* read out the specified device memory and write it to a file
*/
if (flash) {
size = 2048;
fprintf ( stderr, "%s: reading flash memory:\n", progname );
rc = avr_read ( fd, AVR_FLASH, 0, size/2, buf, size );
if (rc) {
fprintf ( stderr, "%s: failed to read all of flash memory, rc=%d\n",
progname, rc );
exitrc = 1;
goto main_exit;
}
}
else if (eeprom) {
size = 128;
fprintf ( stderr, "%s: reading eeprom memory:\n", progname );
rc = avr_read ( fd, AVR_EEPROM, 0, size, buf, size );
if (rc) {
fprintf ( stderr, "%s: failed to read all of eeprom memory, rc=%d\n",
progname, rc );
exitrc = 1;
goto main_exit;
}
}
/*
* write it out to the specified file
*/
rc = write ( iofd, buf, size );
if (rc < 0) {
fprintf(stderr, "%s: write error: %s\n", progname, strerror(errno));
exitrc = 1;
goto main_exit;
}
else if (rc != size) {
fprintf(stderr, "%s: wrote only %d bytes of the expected %d\n",
progname, rc, size);
exitrc = 1;
goto main_exit;
}
}
else {
/*
* write the selected device memory using data from a file
*/
if (flash) {
size = 2048;
}
else if (eeprom) {
size = 128;
}
/*
* read in the data file that will be used to write into the chip
*/
rc = read(iofd, buf, size);
if (rc < 0) {
fprintf(stderr, "%s: read error from \"%s\": %s\n",
progname, inputf, strerror(errno));
exitrc = 1;
goto main_exit;
}
size = rc;
/*
* write the buffer contents to the selected memory type
*/
if (flash) {
fprintf(stderr, "%s: writing %d bytes into flash memory:\n",
progname, size);
rc = avr_write ( fd, AVR_FLASH, 0, buf, size );
if (rc) {
fprintf ( stderr, "%s: failed to write flash memory, rc=%d\n",
progname, rc );
exitrc = 1;
goto main_exit;
}
}
else if (eeprom) {
fprintf(stderr, "%s: writing %d bytes into eeprom memory:\n",
progname, size);
rc = avr_write ( fd, AVR_EEPROM, 0, buf, size );
if (rc) {
fprintf ( stderr, "%s: failed to write eeprom memory, rc=%d\n",
progname, rc );
exitrc = 1;
goto main_exit;
}
}
}
main_exit:
/*
* program complete
*/
avr_powerdown(fd);
ppi_clr(fd, PPIDATA, 0xff);
ppi_set(fd, PPIDATA, AVR_RESET);
close(fd);
close(iofd);
fprintf(stderr, "\n" );
return exitrc;
}