// NOPPP.C (Revised) - M. Covington 1997, 1998, 1999 // Software for the "No-Parts Pic Programmer" // Modified by Phil Rice 2000 // This is Microsoft C. // Be sure to compile for 8088 (not 286 or 386) for maximum portability. // Command line arguments: None. #define FALSE 0 #define TRUE 1 #define _inp inp #define _outp outp #define _kbhit kbhit #define _getch getch typedef unsigned int word; typedef unsigned char byte, bit; #include #include "noppp.h" //#include #include #include #include #include #include #include #include #include // ********************************************************************* // GLOBAL STATUS VARIABLES // ********************************************************************* int LPT = 0; // which LPT port we're using (1..3) int PORT = 0; // its port address #define PIC16C84 1 #define PIC16F84 2 #define PIC16F83 3 int DEVICE = 0; // which PIC we're programming #define PROGRAM 1 #define VERIFY 0 // desired action in main programming loop char FNAME[255]; // name of file currently loaded char CHOICE = 0; // user's most recent menu choice // ********************************************************************* // I/O UTILITIES // ********************************************************************* #define KBUFSIZE 256 char buf[KBUFSIZE]; // keyboard input buffer char* cleanctrl(char *s) { // truncates string at first ctrl char, int i=0; // cleaning up ^M or ^J left behind by fgets while (s[i] >= ' ') i++; s[i] = 0; return s; } char* getstring() { // read string fgets(buf,KBUFSIZE-1,stdin); cleanctrl(buf); return buf; } int getint() { return atoi(getstring()); }; // read integer char getchoice(char *s) { // read menu choice // prints prompt, then reads letters, uppercasing, // until a letter that is in s is found printf("\n\n Your choice (%s): ",s); do { CHOICE = _getch(); CHOICE = toupper(CHOICE); // toupper evals arg twice, so arg can't be _getch() } while ((CHOICE == ',') || (strchr(s,CHOICE) == NULL)); printf(" %c\n",CHOICE); return CHOICE; } void clearscreen() { _asm { mov ax,0f00h int 010h mov ah,0 int 010h } } void waitkey() { while (_kbhit()) { _getch(); }; puts("\n Press space bar to continue..."); _getch(); while (_kbhit()) { _getch(); }; } void errmsg(char *s) { puts(s); waitkey(); } // ********************************************************************* // TIMING // ********************************************************************* #define IODELAY (_inp(0x61)) // allow time for timer to respond void MyDelay(int k) { // Delay at least k microseconds, possibly a good bit more. // k must be less than 27304. // Minimum delay on a 25-MHz 386 is about 100 microseconds; // on a 133-MHz Pentium, about 18 microseconds. // Uses system timer 2. // When running in a DOS box under OS/2, set HW_TIMER ON in DOS settings. unsigned int w; unsigned char lo,hi; _outp(0x61, (_inp(0x61) & 0xFD) | 1); // spkr off, tmr 2 gate on w = (unsigned int)(k*1.2); _outp(0x43, 0xB0); // tmr 2 mode 0 2-byte load IODELAY; _outp(0x42, (unsigned char)w); // low byte IODELAY; _outp(0x42, (unsigned char)(w>>8)); // high byte IODELAY; do { _outp(0x43,0x80); // latch timer count IODELAY; lo = _inp(0x42); // discard low byte IODELAY; hi = _inp(0x42); // get high byte IODELAY; } while ((hi & 0x80) == 0); // wait for a 1 there, signifying rollover return; } // ********************************************************************* // PARALLEL PORT HARDWARE INTERFACE // *********************************************************************// /* Modified by Phil Rice to use following I/O lines Pin 2, D0 is data to be loaded into the PIC (inverted by programmer) Pin 3, D1 is clock to PIC (inverted by programmer) Pin 4, D2 controls Vcc. 1 = Vcc applied. Pin 5, D3 controls Vpp. 1 = Vpp applied. Pin 10, !Ack returns data from PIC (inverted by programmer, not by PC). Pins 18 to 25 are ground. */ //byte BITS = 0x0F; byte DATA = 0x0F; word portaddr(int n) { // base address of port LPTn return(*(word far *)(0x00000408+2*n-2)); } // Procedures to set and clear the data lines void datawritable() { // Do nothing MyDelay(200); } void datareadable() { // Just make sure data to the PIC is "0" // so the OC inverter in the programmer // will let go of the Data I/O pin DATA &= ~0x01; _outp(PORT, DATA); MyDelay(200); } void datadown() { // Remember, there is an inverter in the programmer DATA |= 0x01; _outp(PORT, DATA); MyDelay(20); } void dataup() { // Remember, there is an inverter in the programmer DATA &= ~0x01; _outp(PORT, DATA); MyDelay(20); } void clockdown() { // Remember, there is an inverter in the programmer DATA |= 0x02; _outp(PORT, DATA); MyDelay(20); } void clockup() { // Remember, there is an inverter in the programmer DATA &= ~0x02; _outp(PORT, DATA); MyDelay(20); } void vppon() { DATA |= 0x08; _outp(PORT, DATA); MyDelay(20000); MyDelay(20000); } void vppoff() { DATA &= ~0x08; _outp(PORT, DATA); MyDelay(20000); MyDelay(20000); } void vccon() { DATA |= 0x04; _outp(PORT, DATA); MyDelay(20000); MyDelay(20000); } void vccoff() { DATA &= ~0x04; _outp(PORT, DATA); MyDelay(20000); MyDelay(20000); } bit datain() { return(((~(byte)_inp(PORT+1)) & 0x40) >> 6); } void allpinslow() { vppoff(); datadown(); clockdown(); vccoff(); } bit detecthardware() { // true if datain follows dataup/down dataup(); if(datain() != 1) { datadown(); return(FALSE); } datadown(); if(datain() != 0) return(FALSE); return(TRUE); } //******************************************************************** // PIC COMMUNICATION ROUTINES //******************************************************************** void sendbit(bit b) { // Sends out 1 bit to PIC if(b) dataup(); else datadown(); MyDelay(10); clockup(); MyDelay(10); // tset1 clockdown(); // data is clocked into PIC on this edge MyDelay(10); // thld1 datadown(); // idle with data line low MyDelay(10); } bit recvbit() { // Receives a bit from PIC bit b; clockup(); MyDelay(10); // tdly3 clockdown(); // data is ready just before this MyDelay(10); b = datain(); MyDelay(10); // thld1 return b; } void sendcmd(byte b) { // Sends 6-bit command from bottom of b int i; datawritable(); MyDelay(20); // thld0 for (i=6; i>0; i--) { sendbit((bit)(b & 1)); b = b >> 1; } MyDelay(20); // tdly2 } void senddata(word w) { // Sends 14-bit word from bottom of w int i; datawritable(); MyDelay(20); // thld0 sendbit(0); // one garbage bit for (i=14; i>0; i--) { sendbit((bit)(w & 1)); // 14 data bits w = w >> 1; } sendbit(0); // one garbage bit MyDelay(20); // tdly2 } word recvdata() { // Receives 14-bit word, lsb first int i; bit b; word w = 0; datareadable(); // SLCTIN up for pull-up MyDelay(20); // thld0 recvbit(); // one garbage bit for (i=0; i<14; i++) { b = recvbit(); w = w | ((word)b << i); // 14 data bits } recvbit(); // another garbage bit; MyDelay(20); // tdly2 return w; } // ********************************************************************* // PIC PROGRAMMING ALGORITHMS // ********************************************************************* // PIC MEMORY MAP // The PIC16F84 has four programmable memory areas // (plus data RAM, which is not programmable). // Config memory is only 1 byte, but is treated like the others. #define PBASE 0 // Base address of each memory #define IBASE 0x2000 #define CBASE 0x2007 #define DBASE 0x2100 #define PSIZEMAX 1024 // Max size of each memory #define ISIZEMAX 4 #define CSIZEMAX 1 #define DSIZEMAX 64 word PSIZE = PSIZEMAX; // Actual size, can be set lower word ISIZE = ISIZEMAX; // for particular CPUs word CSIZE = CSIZEMAX; word DSIZE = DSIZEMAX; word PMEM[PSIZEMAX]; // Arrays representing the memories word IMEM[ISIZEMAX]; word CMEM[CSIZEMAX]; word DMEM[DSIZEMAX]; word PUSED = 0; // Number of valid words in array word CUSED = 0; word IUSED = 0; word DUSED = 0; #define PMASK 0x3fff // Which bits are used in each word word CMASK = 0x001f; // (CMASK depends on processor) #define IMASK 0x3fff #define DMASK 0x00ff word DEFAULTCONFIG = 0x1B; // Initialization for config word void cleararrays () { // Mark the memory arrays as empty PUSED = IUSED = CUSED = DUSED = 0; } bit stuffarray (word address, // Stuff data into a memory array. word array[], // Returns true if successful. word base, word size, word *used, word data[], int count) { int i; if (address-base+count-1 > size) { printf(" Invalid address: %04XH\n",address+count-1); return 0; } for (i=0; i 32) return(0); // Valid byte count cksum = bytecount; i = 3; bytecount = bytecount+3; while (bytecount>0) { bytecount--; sscanf(s+i,"%2x",&b); cksum = cksum+b; // Compute checksum i = i+2; } sscanf(s+i,"%2x",&b); cksum = -cksum; if (cksum == b) return 1; // Test checksum return 0; } void loadhexfile(FILE *f) { // Loads a hex file into memory arrays char s[256]; word i,lo,hi; word linetype = 0; // 0 for data, 1 for end of file word wordcount; // number of 16 bit words on this line word address; // address where they begin word data[8]; // 16 bytes = 8 words max. per line of hex cleararrays(); while((!feof(f)) && (linetype != 1)) { fgets(s,255,f); cleanctrl(s); if (!validhexline(s)) { // Syntax check s[40] = 0; // Truncate invalid line for display if (s[0] != ':') { printf(" Invalid line (skipped): '%s'...\n",s); continue; } else { printf(" Unable to decode line: '%s'...\n",s); goto bailout; } } sscanf(s+1,"%2x",&wordcount); // Parse the line - Intel Hex8M wordcount = wordcount/2; // (double bytes, addresses doubled) sscanf(s+3,"%4x",&address); address = address/2; sscanf(s+7,"%2x",&linetype); if (linetype==1) goto finished; for (i=0; i= DBASE) { if (!stuffarray(address,DMEM,DBASE,DSIZE,&DUSED,data,wordcount)) goto bailout; } else if (address >= CBASE) { if (!stuffarray(address,CMEM,CBASE,CSIZE,&CUSED,data,wordcount)) goto bailout; } else if (address >= IBASE) { if (!stuffarray(address,IMEM,IBASE,ISIZE,&IUSED,data,wordcount)) goto bailout; } else { if (!stuffarray(address,PMEM,PBASE,PSIZE,&PUSED,data,wordcount)) goto bailout; } } // while finished: printf(" Program memory loaded: %5d word(s)\n",PUSED); printf(" Configuration loaded: %5d word(s)\n",CUSED); printf(" ID memory loaded: %5d word(s)\n",IUSED); printf(" Data memory loaded: %5d byte(s)\n",DUSED); return; bailout: cleararrays(); errmsg(" Unable to load file."); FNAME[0] = 0; return; } // ********************************************************************* // USER INTERFACE // ********************************************************************* void banner() { clearscreen(); // clrscr(); puts(" ---------------------------------"); puts(" NOPPP - \"No-Parts\" PIC Programmer"); puts(" Michael A. Covington"); puts(" Version of " __DATE__ " " __TIME__); puts(" ---------------------------------"); if (LPT > 0) printf(" Using LPT%d on %03XH\n",LPT,PORT); puts(" ---------------------------------"); switch (DEVICE) { case PIC16C84: printf(" PIC16C84"); break; case PIC16F84: printf(" PIC16F84"); break; case PIC16F83: printf(" PIC16F83"); break; default: printf(" "); } printf(" %s\n",FNAME); puts(" ---------------------------------\n"); } void selectport() { LPT = 3; PORT = portaddr(LPT); if( !detecthardware() ) { LPT = 2; PORT = portaddr(LPT); if( !detecthardware() ) { LPT = 1; PORT = portaddr(LPT); if( !detecthardware() ) { banner(); puts(" Caution: Programmer hardware not found!\n\n"); puts(" Defaulting to LPT1"); puts(" Press Ctrl-C to cancel or"); errmsg(" "); } } } } void troubleshoot() { allpinslow(); banner(); puts(" TEST A\n"); puts(" Connect negative voltmeter lead to pin 5"); puts(" of PIC socket and check the following voltages:\n"); puts(" Ensure no PIC is in the socket."); puts(" Socket pin 4 < 0.8 V"); puts(" Socket pin 12 < 0.8 V"); puts(" Socket pin 13 < 0.8 V"); puts(" Socket pin 14 < 0.8 V"); puts(" Green LED should be ON <<"); puts(" Red LED should be OFF"); puts(" Orange LED should be OFF"); errmsg(" "); vccon(); banner(); puts(" TEST B\n"); puts(" With negative voltmeter lead still"); puts(" connected to pin 5 in the PIC socket,"); puts(" check the following voltages:\n"); puts(" Socket pin 4 < 0.8 V"); puts(" Socket pin 12 < 0.8 V"); puts(" Socket pin 13 < 0.8 V"); puts(" Socket pin 14 4.75 to 5.25 V <<"); puts(" Green LED should be ON"); puts(" Red LED should be ON <<"); puts(" Orange LED should be OFF"); errmsg(" "); vppon(); banner(); puts(" TEST C\n"); puts(" With negative voltmeter lead still"); puts(" connected to pin 5 in the PIC socket,"); puts(" check the following voltages:\n"); puts(" Socket pin 4 12.0 to 14.0 V <<"); puts(" Socket pin 12 < 0.8 V"); puts(" Socket pin 13 < 0.8 V"); puts(" Socket pin 14 4.75 to 5.25 V"); puts(" Green LED should be ON"); puts(" Red LED should be ON"); puts(" Orange LED should be ON <<"); errmsg(" "); clockup(); banner(); puts(" TEST D\n"); puts(" With negative voltmeter lead still"); puts(" connected to pin 5 in the PIC socket,"); puts(" check the following voltages:\n"); puts(" Socket pin 4 12.0 to 14.0 V"); puts(" Socket pin 12 4.75 to 5.25 V <<"); puts(" Socket pin 13 < 0.8 V"); puts(" Socket pin 14 4.75 to 5.25 V"); puts(" Green LED should be ON"); puts(" Red LED should be ON"); puts(" Orange LED should be ON"); errmsg(" "); dataup(); banner(); puts(" TEST E\n"); puts(" With negative voltmeter lead still"); puts(" connected to pin 5 in the PIC socket,"); puts(" check the following voltages:\n"); puts(" Socket pin 4 12.0 to 14.0 V"); puts(" Socket pin 12 4.75 to 5.25 V"); puts(" Socket pin 13 4.75 to 5.25 V <<"); puts(" Socket pin 14 4.75 to 5.25 V"); puts(" Green LED should be ON"); puts(" Red LED should be ON"); puts(" Orange LED should be ON"); errmsg(" "); datadown(); banner(); puts(" TEST F\n"); puts(" With negative voltmeter lead still"); puts(" connected to pin 5 in the PIC socket,"); puts(" check the following voltages:\n"); puts(" Socket pin 4 12.0 to 14.0 V"); puts(" Socket pin 12 4.75 to 5.25 V"); puts(" Socket pin 13 < 0.8 V <<"); puts(" Socket pin 14 4.75 to 5.25 V"); puts(" Green LED should be ON"); puts(" Red LED should be ON"); puts(" Orange LED should be ON"); errmsg(" "); clockdown(); banner(); puts(" TEST G\n"); puts(" With negative voltmeter lead still"); puts(" connected to pin 5 in the PIC socket,"); puts(" check the following voltages:\n"); puts(" Socket pin 4 12.0 to 14.0 V"); puts(" Socket pin 12 < 0.8 V <<"); puts(" Socket pin 13 < 0.8 V"); puts(" Socket pin 14 4.75 to 5.25 V"); puts(" Green LED should be ON"); puts(" Red LED should be ON"); puts(" Orange LED should be ON"); errmsg(" "); vppoff(); banner(); puts(" TEST H\n"); puts(" With negative voltmeter lead still"); puts(" connected to pin 5 in the PIC socket,"); puts(" check the following voltages:\n"); puts(" Socket pin 4 < 0.8 V <<"); puts(" Socket pin 12 < 0.8 V"); puts(" Socket pin 13 < 0.8 V"); puts(" Socket pin 14 4.75 to 5.25 V"); puts(" Green LED should be ON"); puts(" Red LED should be ON"); puts(" Orange LED should be OFF <<"); errmsg(" "); vccoff(); banner(); puts(" TEST I\n"); puts(" With negative voltmeter lead still"); puts(" connected to pin 5 in the PIC socket,"); puts(" check the following voltages:\n"); puts(" Socket pin 4 < 0.8 V"); puts(" Socket pin 12 < 0.8 V"); puts(" Socket pin 13 < 0.8 V"); puts(" Socket pin 14 < 0.8 V <<"); puts(" Green LED should be ON"); puts(" Red LED should be OFF <<"); puts(" Orange LED should be OFF"); errmsg(" "); banner(); puts(" This completes the voltage test sequence.\n"); errmsg(" "); allpinslow(); exit(0); } void load() { FILE *f; allpinslow(); banner(); printf(" File to load: "); strcpy(FNAME,getstring()); f = fopen(FNAME,"rt"); if (f == NULL) { errmsg(" Unable to open file."); FNAME[0] = 0; return; } loadhexfile(f); fclose(f); errmsg(" Loading complete."); if (CUSED == 0) { banner(); puts(" Caution: HEX file did not contain a configuration word.\n"); puts(" The following settings will be used:\n"); puts(" RC oscillator"); puts(" Watchdog timer disabled"); puts(" Power-up timer enabled"); puts(" Code not read-protected\n"); errmsg(" You can specify other settings in the assembler."); } else if (CMEM[0] != (CMEM[0] & CMASK)) { banner(); puts(" Caution: Configuration word appears to contain invalid bits.\n"); puts(" Your program may have been assembled for a different"); puts(" type of PIC. Check device selection carefully."); errmsg(" "); } } void selectdevice() { allpinslow(); banner(); puts(" Devices supported:\n"); puts(" C PIC16C84"); puts(" F PIC16F84"); puts(" 3 PIC16F83\n\n"); puts(" T Test the programmer circuit\n"); getchoice("C,F,3,T"); switch(CHOICE) { case 'C': DEVICE = PIC16C84; PSIZE = 1024; CMASK = 0x001F; DEFAULTCONFIG = 0x001B; break; case 'F': DEVICE = PIC16F84; PSIZE = 1024; CMASK = 0x3FFF; DEFAULTCONFIG = 0x3FF3; break; case '3': DEVICE = PIC16F83; PSIZE = 512; CMASK = 0x3FFF; DEFAULTCONFIG = 0x3FF3; break; case 'T': troubleshoot(); } } void erase() { int i; vccon(); vppreset(); printf(" Commanding PIC to erase ID, configuration, "); sendcmd(LOADCONFIG); senddata(0x3FFF); for (i=7; i>0; i--) sendcmd(INCREMENTADDRESS); sendcmd(1); sendcmd(7); sendcmd(BEGINPROGRAMMING); MyDelay(20000); sendcmd(1); sendcmd(7); printf(" program, "); progcycle(ERASEPROGRAM,0x3FFF); // is the data word necessary? printf(" data..."); progcycle(ERASEDATA,0x3FFF); // is the data word necessary? allpinslow(); puts(" Done."); allpinslow(); waitkey(); } void program(int mode) { word i; banner(); if (PUSED+IUSED+CUSED+DUSED == 0) { printf(" Load a file first.\n"); goto finish; } vccon(); vppreset(); printf(" Program memory: "); if (!programall(mode,PMASK,LOADPROGRAM,READPROGRAM,PMEM,PBASE,PUSED)) goto finish; sendcmd(LOADCONFIG); // from here on we're in config/ID memory senddata(DEFAULTCONFIG); // loadconfig requires an arg, here it is printf(" ID memory: "); if (!programall(mode,IMASK,LOADPROGRAM,READPROGRAM,IMEM,IBASE,IUSED)) goto finish; for (i=0; i < CBASE-IBASE-IUSED; i++) sendcmd(INCREMENTADDRESS); // get to config memory printf(" Configuration memory: "); if (!programall(mode,CMASK,LOADPROGRAM,READPROGRAM,CMEM,CBASE,CUSED)) goto finish; vppreset(); // Reset address counter in PIC to 0 printf(" Data memory: "); if (!programall(mode,DMASK,LOADDATA,READDATA,DMEM,DBASE,DUSED)) goto finish; puts("\n Programming complete.\n\n"); puts(" For production-grade work, you should now verify"); puts(" the PIC at the maximum and minimum values of Vcc."); finish: allpinslow(); waitkey(); } void queryexit() { allpinslow(); banner(); puts(" Are you sure you want to exit?"); getchoice("Y,N"); if (CHOICE == 'Y') { banner(); errmsg(" You may remove the PIC from the socket now."); exit(0); } } void menu() { allpinslow(); // Clean up after aberrant routines if any banner(); puts(" L Load HEX file"); puts(" S Select type of PIC"); puts(" E Erase PIC"); puts(" P Program PIC"); puts(" V Verify PIC\n"); puts(" X Exit program"); getchoice("L,S,E,P,V,X"); switch(CHOICE) { case 'L': load(); break; case 'S': selectdevice(); break; case 'E': erase(); break; case 'P': program(PROGRAM); break; case 'V': program(VERIFY); break; case 'X': queryexit(); } } main() { allpinslow(); FNAME[0] = 0; // no file is presently loaded selectport(); selectdevice(); // mandatory banner(); puts(" You may insert the PIC in the socket now."); errmsg(" Be very careful not to insert it backward."); while(1) menu(); allpinslow(); return 0; }