/* SP12: A serial programmer for working with Atmel AVR uCs */ /* Copyright (C) 1997-2008 Ken Huntington, Kevin Towers, */ /* Artur Pundsack, Pitronics. */ /* This program is free software; you can redistribute it and/or */ /* modify it under the terms of the GNU General Public License */ /* as published by the Free Software Foundation; either version 2 */ /* of the License, or (at your option) any later version. */ /* This program is distributed in the hope that it will be useful, */ /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ /* GNU General Public License for more details. */ /* You should have received a copy of the GNU General Public License */ /* along with this program; if not, write to the Free Software */ /* Foundation, Inc., 59 Temple Place - Suite 330, Boston, */ /* MA 02111-1307, USA. */ /* Pitronics can be reached by email: sbolt@xs4all.nl */ /* Kevin Towers can be reached by email: ktowers@omnexcontrols.com */ /* Ken Huntington can be reached by email: kenh@compmore.net */ /* Artur Pundsack can be reached by email: ap@pa-tec.de */ #include #include #include #include "dos_cpt.h" #include "sp12.h" /* Accepts a 16-bit address. Reads the codeWord in two steps */ /* from the device connected to the Centronics port. */ /* Power bits prior state assumed on; left on at return. */ /* SCK prior state assumed lo; left lo at return. */ /* AP, 15.01.2008: - The adresswidth has been increased from */ /* 16 to 32 bits (int -> long), */ /* - Extended addressing support has been added */ unsigned int readCodeWord(unsigned long address) { unsigned char readByte; unsigned int codeWord; unsigned long readCmd; unsigned long writeCmd; /* For large ATMegas like ATMega2561 an extended address has to */ /* be set when crossing the 64k boundary. */ if (address > 0x0000FFFF) { writeCmd = LOAD_EXT_ADDR | ((address & 0x00FF0000) >> 8); clockOutCommand(writeCmd); } codeWord = 0; readCmd = READ_PROG_HI | ((address & 0x0000FFFF) << 8); readByte = (unsigned char) clockOutCommand(readCmd); codeWord = codeWord | readByte; readCmd = READ_PROG_LO | ((address & 0x0000FFFF) << 8); readByte = (unsigned char) clockOutCommand(readCmd); codeWord = (codeWord << 8) | readByte; return(codeWord); } /* Accepts a 16-bit codeWord and a 32-bit address. Writes the code */ /* in two steps with no delays into the ATMega device connected to */ /* the Centronics port. */ /* Power bits prior state assumed on; left on at return. */ /* SCK prior state assumed lo; left lo at return. */ /* AP, 15.01.2008: - The adresswidth has been increased from */ /* 16 to 32 bits (int -> long), */ /* - Extended addressing support has been added */ void loadCodePage(unsigned long address, unsigned int codeWord) { unsigned char loByte; unsigned char hiByte; unsigned long writeCmd; loByte = (unsigned char) codeWord & 0x00FF; hiByte = (unsigned char) ((codeWord & 0xFF00) >> 8); /* For large ATMegas like ATMega2561 an extended address has to */ /* be set once for the first page and when crossing the 64k */ /* boundary. */ if ((ExtAddr != 1) && (address > 0x0000FFFF)) { writeCmd = LOAD_EXT_ADDR | ((address & 0x00FF0000) >> 8); clockOutCommand(writeCmd); ExtAddr = 1; /* The 64k boundary has been detected */ } writeCmd = LOAD_PAGE_LO | ((address & 0x000000FF) << 8); writeCmd = writeCmd | loByte; clockOutCommand(writeCmd); writeCmd = LOAD_PAGE_HI | ((address & 0x000000FF) << 8); writeCmd = writeCmd | hiByte; clockOutCommand(writeCmd); } /* Accepts a 16-bit page number and programs it into the ATMega */ /* device connected to the Centronics parallel port. */ /* The page is read back and compared with the buffer; if all */ /* is well, 0 is returned, else 1. */ /* Power bits prior state assumed on; left on at return. */ /* SCK prior state assumed lo; left lo at return. */ int writeCodePage(unsigned int flashBuf[], unsigned long address) { unsigned long writeCmd; unsigned int idx; unsigned char readByte; unsigned char testByte; writeCmd = WRITE_PAGE | ((address & 0x0000FFFF) << 8); clockOutCommand(writeCmd); if (POLL_RDY_BSY) { /* time-out 64ms */ testByte = (unsigned char) ~POLL_RDY_BSY; for (idx = 0; idx < 64; idx++) { delay(1); readByte = (unsigned char) clockOutCommand(POLL_RDY_BSY); if ((readByte & testByte) == 0) break; } } else { loopDelay(timeConst.pageWrite); } for (idx = address + 1 - device.pageSize; idx <= address; idx++) { if (flashBuf[idx] != 0xffff) { if (readCodeWord(idx) != flashBuf[idx]) return(1); } } return(0); } /* Accepts a 16-bit codeWord and a 16-bit address. Writes the code */ /* in two steps into the device connected to the Centronics port and */ /* verifies arrival. Returns 1 if verify fails three times, else 0. */ /* The time constant byteWrite is adjusted dynamically, */ /* if the device is not a 1200(A) and optimizeFlag allows. */ /* Power bits prior state assumed on; left on at return. */ /* SCK prior state assumed lo; left lo at return. */ /* AP, 15.01.2008: This function is only available for non page mode!*/ int writeFlashVerified(unsigned int address, unsigned int codeWord, int optimizeFlag) { unsigned char loByte; unsigned char hiByte; unsigned long readCmd, writeCmd; int failure; loByte = (unsigned char) codeWord & 0x00FF; hiByte = (unsigned char) ((codeWord & 0xFF00) >> 8); writeCmd = WRITE_PROG_HI | ((long) address << 8); writeCmd = writeCmd | hiByte; readCmd = READ_PROG_HI | ((long) address << 8); failure = writeByteVerified(writeCmd, readCmd, optimizeFlag); if (failure == 0) { writeCmd = WRITE_PROG_LO | ((long) address << 8); writeCmd = writeCmd | loByte; readCmd = READ_PROG_LO | ((long) address << 8); failure = writeByteVerified(writeCmd, readCmd, optimizeFlag); } return(failure); } /* Expects flashBuf[] to be filled with data. */ /* If a (page) write fails, 1 is returned, else 0. */ /* Power bits prior state assumed on; left on at return. */ /* SCK prior state assumed lo; left lo at return. */ /* AP, 15.01.2008: - Added reset ext. address marker */ /* - Set the extended adress byte if necessary */ int writeFlashArea(unsigned int flashBuf[], long bufLimit, int optimizeFlag) { long address, previous, writeCmd; int writeFlg = 0; int idx = 1; previous = 0; printf("...............................................................\r"); if (optimizeFlag != 11) timeConst.pageWriteAdjusted = 1; /* AP, 15.01.2008: Added, Reset the extended address marker and */ /* set the extended adress byte if neccessary. */ ExtAddr = 0; if (device.flashLimit > 0x0000FFFF){ writeCmd = LOAD_EXT_ADDR; clockOutCommand(writeCmd); } for (address = 0; address < bufLimit; address++) { if (device.pageMode) { /* * To speed things up: Skip locations containing 0xffff * (The device has been erased before writing started) */ if (flashBuf[address] != 0xffff) { loadCodePage(address, flashBuf[address]); writeFlg = 1; } if (writeFlg && (address > 0) && ((address & (device.pageSize - 1)) == device.pageSize - 1)) { writeFlg = 0; if (writeCodePage(flashBuf, address) == 1) { if (++idx < 6) { /* * recalculate first address of the failed page * once only. Note: The calculation below puts * address at the last address value of the previous * page. But address is incremented _after_ this * calculation, _before_ loadCodePage is called * again. */ if (idx == 2) address = address - device.pageSize; if (timeConst.pageWriteAdjusted == 1) writeRetries++; } else { printf("!!\n"); return (1); } if (optimizeFlag == 11) { timeConst.pageWrite += timeConst.pageWrite / 2; if (timeConst.pageWrite > 1.5 * timeConst.pageWriteDefault) { printf("!!\n"); return (1); } timeConst.pageWriteAdjusted = 1; } } else { if (optimizeFlag == 11 && timeConst.pageWriteAdjusted == 0) { timeConst.pageWrite -= timeConst.pageWrite / 2; } idx = 1; } } } else { if (flashBuf[address] != 0xffff) { if (writeFlashVerified((unsigned int) address, flashBuf[address], optimizeFlag) == 1) { printf("!!\n"); return (1); } } } if (address >= (previous + bufLimit / 64)) { putchar('o'); fflush(stdout); previous = address; } } /* * In page mode, a sequence like pageWrite reducing from 64ms * to 32, 16, 8 and 4ms, then adjusting back upwards to 6ms * and finally 9ms is valid; this would cause writeRetries * to be incremented once. Therefore: */ if (device.pageMode && writeRetries == 1) writeRetries = 0; printf("\n"); return (0); } /* (expects flashBuf[] to be filled with 0xFFFF) */ /* Loops readCodeWord into flashBuf[] */ /* Power bits prior state assumed on; left on at return. */ /* SCK prior state assumed lo; left lo at return. */ /* AP, 15.01.2008: - Set the extended adress byte if necessary */ void readFlashArea(unsigned int flashBuf[], long bufLimit) { long address, previous, writeCmd; previous = 0; printf("...............................................................\r"); if (device.flashLimit > 0x0000FFFF){ writeCmd = LOAD_EXT_ADDR; clockOutCommand(writeCmd); } for (address = 0; address < bufLimit; address++) { flashBuf[address] = readCodeWord(address); if (address >= (previous + bufLimit / 64)) { putchar('o'); fflush(stdout); previous = address; } } printf("\n"); }