summaryrefslogtreecommitdiff
path: root/src/device.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/device.c')
-rw-r--r--src/device.c434
1 files changed, 434 insertions, 0 deletions
diff --git a/src/device.c b/src/device.c
new file mode 100644
index 0000000..53fe054
--- /dev/null
+++ b/src/device.c
@@ -0,0 +1,434 @@
+/* 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "dos_cpt.h"
+#include "sp12.h"
+
+/* Delay by executing not quite empty loops */
+
+void loopDelay(unsigned long loops) {
+
+ unsigned long Ti;
+ float dummy;
+
+ for (Ti = 0; Ti < loops; Ti++) {
+ dummy = rand();
+ }
+}
+
+/* Assume the device connected to the Centronics port is powered, */
+/* but that device sck was not held low during power-up, so a */
+/* reset pulse is needed before the SPI can be enabled. */
+/* Assume also that power has yet to be switched on (for separate */
+/* programmer powered by parallel port, instead of in-circuit */
+/* programming. */
+/* If uC is 1200, emit just one PROGRAM_ENABLE command. */
+/* Else retry until the third byte read back by clockOutCommand() */
+/* is 0x53, or at least 32 retries have been unsuccessful. */
+
+int enableSPI(int uC) {
+
+ unsigned long readBack = 0;
+ int tries = 0;
+ /*
+ * Initialise programming lines.
+ */
+ bit_clear(outbyte, ENABLE | SCK | MOSI | RESET);
+ outportb(portAddress, outbyte);
+ /*
+ * Delay to make sure capacitor on separate programmer is charged.
+ */
+ delay(timeConst.powerOn);
+ /*
+ * If the resetPuls time constant > 0, then Hi pulse device reset
+ */
+ if (timeConst.resetPuls > 0) {
+ bit_set(outbyte, RESET);
+ outportb(portAddress, outbyte);
+ /*
+ * Delay to make sure that a capacitor
+ * from reset to gnd is (dis)charged
+ */
+ loopDelay(timeConst.resetPuls);
+ bit_clear(outbyte, RESET);
+ outportb(portAddress, outbyte);
+ /*
+ * Delay before program (SPI) enable command
+ */
+ delay(timeConst.programEnable);
+ }
+ /*
+ * Command program enable and check synchronization
+ */
+ while (tries < 33) {
+ tries++;
+ readBack = clockOutCommand(PROGRAM_ENABLE);
+ readBack = (readBack & 0x0000FF00L) >> 8;
+ if ((readBack == 0x53) || (uC == 1200)) {
+ break;
+ } else {
+ /*
+ * Pulse SCK lo-hi-lo
+ */
+ bit_clear(outbyte, SCK);
+ outportb(portAddress, outbyte);
+ loopDelay(timeConst.sckLO);
+ bit_set(outbyte, SCK);
+ outportb(portAddress, outbyte);
+ loopDelay(timeConst.sckHI);
+ bit_clear(outbyte, SCK);
+ outportb(portAddress, outbyte);
+ loopDelay(timeConst.sckLO);
+ }
+ }
+ if (tries > 1)
+ printf("Sp12 tried %d times to find a working device.\n", tries);
+ if ((readBack != 0x53) && (uC != 1200))
+ printf("No device connected.\n");
+/* printf("readBack: %#lx, tries: %d\n", readBack, tries); */
+ return(tries);
+}
+
+/* Adapt Sck timing to user's clock speed setting */
+/* (Init sets the timing equal to sck0.5M, and the flag will be */
+/* zero unless altered by the command line.) */
+
+float setSckTiming(float clkSpeed) {
+
+ float signal;
+
+ clkSpeed = (float) 2 / (clkSpeed * 1e6);
+ if (clkSpeed > timeConst.port)
+ timeConst.sckLO = (unsigned long) (timeConst.loop *
+ (clkSpeed - timeConst.port));
+ else
+ timeConst.sckLO = 1;
+ if (strcmp(device.name, "AT90S1200(A)") == 0
+ && (clkSpeed * 2) > timeConst.port)
+ timeConst.sckHI = (unsigned long) (timeConst.loop *
+ (clkSpeed * 2 - timeConst.port));
+ else
+ timeConst.sckHI = timeConst.sckLO;
+// printf("sckLO %ld, sckHI %ld\n", timeConst.sckLO, timeConst.sckHI);
+ signal = 1.8e-6 / clkSpeed;
+ if (strcmp(device.name, "AT90S1200(A)") == 0) {
+ if ((signal / 2) > (1.8e-6 / timeConst.port))
+ signal = 4.0e-6 / timeConst.port;
+ } else {
+ if (signal > (1.8e-6 / timeConst.port))
+ signal = 2.0e-6 / timeConst.port;
+ }
+ return(signal);
+}
+
+/* Before writing to the flash area, the device has to be erased. */
+/* Note: This command also erases the eeprom area (all to 0xFF). */
+/* There is no need for general erase before writing to the */
+/* eeprom area, since the device has an auto-erase built into */
+/* the eeprom write cycle. Thus you can alter one or more eeprom */
+/* addresses without disturbing the rest of the device. */
+
+void eraseDevice(void) {
+
+ clockOutCommand(CHIP_ERASE);
+ /*
+ * Delay before program (SPI) enable sequence
+ */
+ delay(timeConst.chipErase);
+ /*
+ * syncronisation must be ok, so just one PROGRAM_ENABLE
+ * command will be clocked out.
+ */
+ enableSPI(1200);
+}
+
+/* A routine to switch control the optional test bits on the */
+/* parallel port. */
+
+void portControl(int portFlag, int exitFlag) {
+
+ if (exitFlag) {
+ outbyte = (unsigned char) portFlag;
+ } else {
+ outbyte = (unsigned char) portFlag & PORTACTIVE;
+ delay(10);
+ }
+ outportb(portAddress, outbyte);
+}
+
+/* Reads flash and eeprom area's. Return 1 if a flash address does */
+/* not contain 0xFFFF, or 3 if an eeprom address also isn't blank; */
+/* return 2 if the flash is blank, but the eeprom isn't; */
+/* return 0 is both area's are blank. */
+/* 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 */
+
+int blankCheck(long flashLimit, long eepromLimit) {
+
+ long address, writeCmd;
+ int signal = 0;
+
+ if (flashLimit > 0x0000FFFF){
+ writeCmd = LOAD_EXT_ADDR;
+ clockOutCommand(writeCmd);
+ }
+
+ for (address = 0; address < flashLimit; address++) {
+ if (readCodeWord(address) != 0xffff) {
+ signal |= 0x01;
+ break; /* arrea is not blank; exit loop */
+ }
+ }
+ for (address = 0; address < eepromLimit; address++) {
+ if (readDataByte(address) != 0xff) {
+ signal |= 0x02;
+ break; /* arrea is not blank; exit loop */
+ }
+ }
+ return(signal);
+}
+
+
+/* Sends a lock or fuses string to the microcontroller. */
+/* Returns 9 if the writeCommand is zero (not available according */
+/* to _sp12dev). */
+/* The lock or fuses string may represent a binary or hex number. */
+/* If it's binary, the length of the string is compared with */
+/* W_FUSESLEN; a useful sanity check. The function returns 2 if */
+/* the length is wrong. A hex number is not checked, as there is no */
+/* way to know how many leading zeroes are intended. */
+/* Next, the block is ored into the writeCommand and uploaded to */
+/* the device. */
+/* Since (re)enabling brown-out detection usually causes the */
+/* AT90(L)S2333/4433 to hang, the fuses are only written, not */
+/* verified. */
+
+int writeFuses(unsigned long commandHM, unsigned long commandLM, \
+ char *fuses, int WfusesLsb, int WfusesLen) {
+
+ unsigned long fuseWord = 0;
+ int Bi;
+
+ if (!commandHM)
+ return(9);
+ if (fuses[1] == 'x')
+ fuseWord = strtoul(fuses, NULL, 16);
+ else if (strlen(fuses) != WfusesLen)
+ return(2);
+ else
+ fuseWord = strtoul(fuses, NULL, 2);
+ fuseWord = fuseWord << WfusesLsb;
+ fuseWord = fuseWord & commandHM;
+ fuseWord = fuseWord | commandLM;
+// printf("\nfuseword %04lx\n", fuseWord);
+ clockOutCommand(fuseWord);
+ /*
+ * Read back the fuses as a binary string
+ */
+ fuseWord = fuseWord >> WfusesLsb;
+ for (Bi = WfusesLen - 1; Bi >= 0; Bi--) {
+ fuses[Bi] = (fuseWord & 0x01L) + 48;
+ fuseWord = fuseWord >> 1;
+ }
+ fuses[WfusesLen] = '\0';
+ /*
+ * ByteWrite delay not long enough to allow reading back
+ * of true ATmega103/603 fuses directly after this
+ */
+ delay(timeConst.chipErase);
+ return(0);
+}
+
+
+/* Reads the lock or fuse bit block from the microcontroller. */
+/* Returns 9 if the readCommand is zero (not available according */
+/* to _sp12dev). */
+
+int readFuses(unsigned long readCommand, char *fuses, \
+ int RfusesLsb, int RfusesLen) {
+
+ unsigned long fuseWord = 0;
+ int Bi;
+
+ if (!readCommand)
+ return(9);
+
+ fuseWord = clockOutCommand(readCommand);
+ fuseWord = fuseWord >> RfusesLsb;
+ for (Bi = RfusesLen - 1; Bi >= 0; Bi--) {
+ fuses[Bi] = (fuseWord & 0x01L) + 48;
+ fuseWord = fuseWord >> 1;
+ }
+ fuses[RfusesLen] = '\0';
+ return(0);
+}
+
+
+/* Read the signature bits from the device attached to the */
+/* Centronics parallel port. */
+
+void readDeviceCode(void) {
+
+ unsigned long readCmd;
+
+ readCmd = READ_DEVICE | 0x0200L;
+ device.sigByte_2 = (unsigned char) clockOutCommand(readCmd);
+ readCmd = READ_DEVICE | 0x0100L;
+ device.sigByte_1 = (unsigned char) clockOutCommand(readCmd);
+ readCmd = READ_DEVICE | 0x0000L;
+ device.sigByte_0 = (unsigned char) clockOutCommand(readCmd);
+}
+
+
+/* Shifts the command highest bit first into unsigned char output, */
+/* which then is used to clock them out bit by bit through */
+/* Centronics data-7. */
+/* Reads back and returns whatever the uC clocks out on Miso */
+/* while the command is clocked in. */
+/* Power bits prior state assumed on; left on at return. */
+/* SCK prior state assumed lo; left lo at return. */
+
+unsigned long clockOutCommand(unsigned long command) {
+
+ unsigned long inBuf;
+ unsigned long readBack = 0x0L;
+ unsigned char inbyte; /* from Centronics status reg. */
+ int mostSigBit;
+ int i;
+
+ mostSigBit = 31;
+ /*
+ * Bits mostSigBit-0 to be clocked out
+ */
+ for (i = mostSigBit; i >= 0; i--) {
+ /*
+ * Bits mostSigBit-0 to be clocked out
+ */
+ if ((command >> i) & 0x01L)
+ bit_set(outbyte, MOSI);
+ else
+ bit_clear(outbyte, MOSI);
+ bit_clear(outbyte, SCK);
+ outportb(portAddress, outbyte);
+ /*
+ * Minimum delay SCK lo period
+ */
+ loopDelay(timeConst.sckLO);
+ /*
+ * Set bit-0 (SCK) hi, leave all other bits as they are
+ */
+ bit_set(outbyte, SCK);
+ outportb(portAddress, outbyte);
+ /*
+ * Minimum delay SCK hi period
+ */
+ loopDelay(timeConst.sckHI);
+ /*
+ * Fetch parallel port status register, clear all bits
+ * except 7 (MOSI), invert, shift and or into buffer
+ */
+ inbyte = inportb(portStatus);
+ inBuf = ((unsigned long) (inbyte ^ MISO_INV)) >> MISO_BITNR;
+ inBuf = (inBuf & 0x00000001L) << i;
+ readBack = readBack | inBuf;
+ }
+ /*
+ * return SCK to lo
+ */
+ bit_clear(outbyte, SCK);
+ outportb(portAddress, outbyte);
+ return(readBack);
+}
+
+/* Accepts a dataByte and a 16-bit address. Writes the byte 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/or optimizeFlag allows. */
+/* Power bits prior state assumed on; left on at return. */
+/* SCK prior state assumed lo; left lo at return. */
+
+int writeByteVerified(unsigned long writeCommand,
+ unsigned long readCommand, int optimizeFlag) {
+
+ int idx;
+ unsigned char readBack;
+ int failure = 0;
+ unsigned char dataByte;
+ /*
+ * First check if the data isn't already in the uC
+ */
+ dataByte = (unsigned char) writeCommand;
+ readBack = (unsigned char) clockOutCommand(readCommand);
+ if (readBack == dataByte)
+ return(failure);
+ /*
+ * Else write the data into the uC
+ */
+ dataByte = (unsigned char) writeCommand;
+ clockOutCommand(writeCommand);
+ switch (dataByte) {
+ case 0xFF:
+ case 0x7F:
+ case 0x80:
+ case 0x00:
+ loopDelay(timeConst.byteWriteDefault);
+ readBack = (unsigned char) clockOutCommand(readCommand);
+ break;
+ default:
+ loopDelay(timeConst.byteWrite);
+ readBack = (unsigned char) clockOutCommand(readCommand);
+ if ((strcmp(device.name, "AT90S1200(A)") != 0 && optimizeFlag != 10)
+ || optimizeFlag == 11) {
+ if (readBack != dataByte) {
+ timeConst.byteWrite += timeConst.byteWriteDefault / 12;
+ timeConst.byteWriteAdjusted = 1;
+ loopDelay(timeConst.byteWrite);
+ readBack = (unsigned char) clockOutCommand(readCommand);
+ } else {
+ if (timeConst.byteWriteAdjusted == 0)
+ timeConst.byteWrite -= timeConst.byteWriteDefault / 12;
+ }
+ }
+ }
+ idx = 1;
+ if (readBack != dataByte) {
+ timeConst.byteWrite = timeConst.byteWriteDefault;
+ timeConst.byteWriteAdjusted = 0;
+ do {
+ clockOutCommand(writeCommand);
+ loopDelay(timeConst.byteWrite);
+ readBack = (unsigned char) clockOutCommand(readCommand);
+ } while (readBack != dataByte && ++idx < 4);
+ writeRetries += idx - 1;
+ if (readBack != dataByte)
+ failure = 1;
+ }
+ return(failure);
+}
+