Advertisements

Resistance Soldering: Firmware

Here’s the firmware driving the Atmel 89C2051 in my resistance soldering gizmo. You could substitute any 8051-style microcontroller without much difficulty at all. With a bit more difficulty, you could even use an Arduino Pro Mini.

As you already know from recent posts, I’d jettison all the fancy gate control circuity and use a single bit driving a triac optoisolator. Redefine one of the GATE_CLAMP_x or GATE_DRIVE_x bits accordingly, then toss all but the last six Pat* timing structures overboard. That gets you duty cycle control in 1/6 increments: the triac will be turned on for one to six cycles of every six.

You’ll probably have a serial LCD with a standard bit rate, so change the OSCILLATOR_FREQ to match your 11.0592 MHz crystal. Those of you in 50 Hz land can get 1/5 duty cycle control after you set LINE_FREQ accordingly; change the Pat* entries, too.

Operation is straightforward: one pair of those fancy keyboard switches selects the triac trigger pulse sequence, the other selects the total burn time in 0.1 second units. You should use only patterns 11 through 16, which are the 1/6 through 6/6 duty cycles.

Note: patterns 1 through 10 perform weird sub-cycle triggering to illustrate various topics I covered in the columns and shouldn’t be used for anything else. You don’t want to hammer your transformer with a series of half-cycle pulses, for example, because you don’t want to magnetize the core with a DC bias.

Use the SDCC compiler to get the corresponding HEX file. I have not, in actual point of fact, recompiled this since burning the original microcontroller, so I’d expect the new HEX (actually, *.ihx) file to be completely different due to nothing more than improved optimizations and suchlike. Let me know how it works out, but, by and large, you’re on your own.

I have a couple of tubes of 2051s, so if you want to build one of these gizmos and don’t want to use another 8051-family micro (or, better, port the code to an Arduino), send me a small box stuffed with as many dollar bills as you think appropriate and I’ll send you a programmed chip. No warranty, express or implied: you’re really on your own after that. I highly recommend that you do not take me up on this offer, OK?

Herewith, The Source…

// Resistance soldering unit control program
// Circuit Cellar - June 2008
// Ed Nisley KE4ZNU

#include <8051.h>
#include <stdio.h>

//-------------------------------------------------------------------------------------------
// I/O bits

// The bit patterns in the TriacEvent_t struct match the output bit locations
// These are low-active at the output pins, but we think of them as 1=ON here

#define GATE_CLAMP_LOW		P1_4		// out - clamp low-going gate pulses to zero
#define GATE_CLAMP_LOW_MASK	0x10
#define GATE_CLAMP_LOW_BIT	4

#define GATE_CLAMP_HIGH		P1_5		// out - clamp high-going gate pulses to zero
#define GATE_CLAMP_HIGH_MASK	0x20
#define GATE_CLAMP_HIGH_BIT	5

#define GATE_DRIVE_LOW		P1_6		// out - drive gate low
#define GATE_DRIVE_LOW_MASK	0x40
#define GATE_DRIVE_LOW_BIT	6

#define GATE_DRIVE_HIGH		P1_7		// out - drive gate high
#define GATE_DRIVE_HIGH_MASK	0x80
#define GATE_DRIVE_HIGH_BIT	7

#define GATE_BIT_MASK		0xf0		// overall bitmask for these output bits
#define GATE_BIT_PORT		P1		// which port they're on

#define BUTTON_CONTACT		P1_0		// in - high when tip contact active
#define BUTTON_FIRE		P1_1		// in - high when foot switch pressed
#define TRACE2			P1_2		// out - toggled in loops and so forth
#define TRACE3			P1_3		// out - toggled in IRQ handlers

#define TRACE0			P3_0		// out -- toggled during burn sequence

#define LINE_SYNC			P3_2		// in - INT0 from line monitor optoisolator
#define LINE_SYNC_EDGE		IE0		//      interrupt edge detect bit
#define LINE_SYNC_EDGE_ENABLE	IT0		//      enable edge detection for this IRQ

#define BUTTON_TIME_INC		P3_3		// in - high to increment time
#define BUTTON_TIME_DEC		P3_4		// in - high to decrement time
#define BUTTON_PATTERN_INC	P3_5		// in - high to increment pattern index
#define BUTTON_PATTERN_DEC	P3_7		// in - high to decrement pattern index

#define TRACE_SERIAL		0		// nonzero to trace serial operations
#define TRACE_PATTERN		1		// nonzero to trace pattern start

//-------------------------------------------------------------------------------------------
// Triac control timings
// The ratio LINE_FREQ / TRIAC_PATTERN_CYCLES should be 10 to make decimal seconds work out nicely
//  ... so those of you in 50-Hz land will have only five cycles per pattern...

#define OSCILLATOR_FREQ		12.0000E6				// crystal frequency
#define TIMER_TICK_FREQ		(OSCILLATOR_FREQ / 12)		// CPU instruction cycle frequency, Hz

#define LINE_FREQ			60		// power line frequency, Hz

#define TRIAC_PATTERN_FREQ	10		// Patterns per second

#define TRIAC_PATTERN_CYCLES	(LINE_FREQ / TRIAC_PATTERN_FREQ)	// Power cycles per pattern

// Because the VFL display requires about three stop bits at 9600 b/s,
//  serial output must be paced at no more than 13 chars per power-line cycle
//  so 8 chars per cycle (one per phase) works out perfectly well
// If you want more phases or faster data, you must adjust accordingly
// As it turns out, my VFL doesn't use standard serial rates anyway, but the thought was nice...
//  and the pacing still gives a full update in about 50 chars / (8 chars / cycle) = 6 cycles = 1/10 sec

#define PHASES_PER_CYCLE	8					// phases per line cycle

#define PHASE_TICKS	(TIMER_TICK_FREQ / (PHASES_PER_CYCLE * LINE_FREQ))	// ticks per phase

#define PHASES_PER_PATTERN	(PHASES_PER_CYCLE * TRIAC_PATTERN_CYCLES)

#define TIMER_OVERHEAD		20					// IRQ handler overhead ticks

#define LINE_SYNC_DELAY		(410E-6 * TIMER_TICK_FREQ)	// zero-crossing detection delay in ticks

// Event records contain
//  match EventPhase to the Phase timer: when it matches, then output bits happen
//   0 = start of first cycle, max value = PHASES_PER_PATTERN-1
//  output bits correctly aligned, but 1=active so they must be flipped before output

typedef struct {
	unsigned char EventPhase;
	unsigned char TriacBits;
} TriacEvent_t;

#define PB(t,b) {t,b}

TriacEvent_t __code Pat0[] = {		// 0 - all drivers off, always
	PB(0,0)
	};

TriacEvent_t __code Pat1[] = {		// 1 - single high trigger
	PB(0,GATE_DRIVE_HIGH_MASK),		//     similar to Figure 1 in April 2008 column
	PB(1,0)
	};

TriacEvent_t __code Pat2[] = {		// 2 - single high, clamped second half-cycle
	PB(0,GATE_DRIVE_HIGH_MASK),
	PB(1,0),
	PB(4,GATE_CLAMP_LOW_MASK | GATE_CLAMP_HIGH_MASK),
	PB(8,0)
	};

TriacEvent_t __code Pat3[] = {		// 3 - peak +V half-cycle, then one full cycle, clamped
	PB(2,GATE_DRIVE_HIGH_MASK),		//     similar to Figure 3 in April 2008 column
	PB(3,0),
	PB(4,GATE_DRIVE_LOW_MASK),
	PB(8,GATE_DRIVE_HIGH_MASK),
	PB(12,GATE_CLAMP_LOW_MASK | GATE_CLAMP_HIGH_MASK),
	PB(14,0)
	};

TriacEvent_t __code Pat4[] = {		// 4 - peak +V half-cycle, then half cycle, clamped
	PB(2,GATE_DRIVE_HIGH_MASK),		//     this gives one complete cycle
	PB(3,0),
	PB(4,GATE_DRIVE_LOW_MASK),
	PB(6,GATE_CLAMP_LOW_MASK | GATE_CLAMP_HIGH_MASK),
	PB(16,0)
	};

TriacEvent_t __code Pat5[] = {		// 5 - similar to 3 with additional half cycle
	PB(2,GATE_DRIVE_HIGH_MASK),		//     this gives two complete cycles
	PB(3,0),
	PB(4,GATE_DRIVE_LOW_MASK),
	PB(8,GATE_DRIVE_HIGH_MASK),
	PB(12,GATE_DRIVE_LOW_MASK),
	PB(14,GATE_CLAMP_LOW_MASK | GATE_CLAMP_HIGH_MASK),
	PB(20,0)
	};

TriacEvent_t __code Pat6[] = {		// 6 -
	PB(0,0)
	};

TriacEvent_t __code Pat7[] = {		// 7 -
	PB(0,0)
	};

TriacEvent_t __code Pat8[] = {		// 8 -
	PB(0,0)
	};

TriacEvent_t __code Pat9[] = {		// 9 -
	PB(0,0)
	};

TriacEvent_t __code Pat10[] = {		// 10 -
	PB(0,0)
	};

TriacEvent_t __code Pat11[] = {		// 11 - 1/6: 1 0 0 0 0 0
	PB(2,GATE_DRIVE_HIGH_MASK),
	PB(3,GATE_DRIVE_LOW_MASK),
	PB(7,GATE_CLAMP_LOW_MASK | GATE_CLAMP_HIGH_MASK)
	};

TriacEvent_t __code Pat12[] = {		// 12 - 2/6: 1 0 0 1 0 0
	PB(2,GATE_DRIVE_HIGH_MASK),
	PB(3,GATE_DRIVE_LOW_MASK),
	PB(7,GATE_CLAMP_LOW_MASK | GATE_CLAMP_HIGH_MASK),
	PB(26,GATE_DRIVE_HIGH_MASK),
	PB(27,GATE_DRIVE_LOW_MASK),
	PB(31,GATE_CLAMP_LOW_MASK | GATE_CLAMP_HIGH_MASK)
	};

TriacEvent_t __code Pat13[] = {		// 13 - 3/6: 1 0 1 0 1 0
	PB(2,GATE_DRIVE_HIGH_MASK),
	PB(3,GATE_DRIVE_LOW_MASK),
	PB(7,GATE_CLAMP_LOW_MASK | GATE_CLAMP_HIGH_MASK),
	PB(18,GATE_DRIVE_HIGH_MASK),
	PB(19,GATE_DRIVE_LOW_MASK),
	PB(23,GATE_CLAMP_LOW_MASK | GATE_CLAMP_HIGH_MASK),
	PB(34,GATE_DRIVE_HIGH_MASK),
	PB(35,GATE_DRIVE_LOW_MASK),
	PB(39,GATE_CLAMP_LOW_MASK | GATE_CLAMP_HIGH_MASK)
	};

TriacEvent_t __code Pat14[] = {		// 14 - 4/6: 1 1 0 1 1 0
	PB(2,GATE_DRIVE_HIGH_MASK),
	PB(3,GATE_DRIVE_LOW_MASK),
	PB(7,GATE_DRIVE_HIGH_MASK),
	PB(11,GATE_DRIVE_LOW_MASK),
	PB(15,GATE_CLAMP_LOW_MASK | GATE_CLAMP_HIGH_MASK),
	PB(26,GATE_DRIVE_HIGH_MASK),
	PB(27,GATE_DRIVE_LOW_MASK),
	PB(31,GATE_DRIVE_HIGH_MASK),
	PB(35,GATE_DRIVE_LOW_MASK),
	PB(39,GATE_CLAMP_LOW_MASK | GATE_CLAMP_HIGH_MASK)
	};

TriacEvent_t __code Pat15[] = {		// 15 - 5/6: 1 1 1 1 1 0
	PB(2,GATE_DRIVE_HIGH_MASK),
	PB(3,GATE_DRIVE_LOW_MASK),
	PB(7,GATE_DRIVE_HIGH_MASK),
	PB(11,GATE_DRIVE_LOW_MASK),
	PB(15,GATE_DRIVE_HIGH_MASK),
	PB(19,GATE_DRIVE_LOW_MASK),
	PB(23,GATE_DRIVE_HIGH_MASK),
	PB(27,GATE_DRIVE_LOW_MASK),
	PB(31,GATE_DRIVE_HIGH_MASK),
	PB(35,GATE_DRIVE_LOW_MASK),
	PB(39,GATE_CLAMP_LOW_MASK | GATE_CLAMP_HIGH_MASK)
	};

TriacEvent_t __code Pat16[] = {		// 16 - 6/6: 1 1 1 1 1 1
	PB(2,GATE_DRIVE_HIGH_MASK),
	PB(3,GATE_DRIVE_LOW_MASK),
	PB(7,GATE_DRIVE_HIGH_MASK),
	PB(11,GATE_DRIVE_LOW_MASK),
	PB(15,GATE_DRIVE_HIGH_MASK),
	PB(19,GATE_DRIVE_LOW_MASK),
	PB(23,GATE_DRIVE_HIGH_MASK),
	PB(27,GATE_DRIVE_LOW_MASK),
	PB(31,GATE_DRIVE_HIGH_MASK),
	PB(35,GATE_DRIVE_LOW_MASK),
	PB(39,GATE_DRIVE_HIGH_MASK),
	PB(43,GATE_DRIVE_LOW_MASK),
	PB(47,GATE_DRIVE_HIGH_MASK)
	};

__code TriacEvent_t * __code pTriacPatterns[] = {	// pointers to patterns
	Pat0,Pat1,Pat2,Pat3,Pat4,Pat5,Pat6,Pat7,Pat8,Pat9,
	Pat10,Pat11,Pat12,Pat13,Pat14,Pat15,Pat16
};

// Number of duty-cycle patterns (0 = all off)
#define TRIAC_NUM_PATTERNS	(sizeof(pTriacPatterns) / sizeof( __code * ))

unsigned char PatternSelection = 11;			// default selected entry in pTriacPatterns

//-------------------------------------------------------------------------------------------
// These are mostly the (dreaded) global variables modified by the IRQ handlers
// The initial values set up for the first line sync IRQ

// Phase and output control

volatile unsigned char Phase = 0xff;			// phase within each pattern, init to -1
volatile unsigned char EventIndex;				// step through TriacEvents records

typedef enum {BURN_IDLE, BURN_CONTACT, BURN_ACTIVE, BURN_DONE} BurnState_t;

BurnState_t BurnState = BURN_IDLE;				// what we're doing right now

volatile __bit BurnEnable;					// true when output is active

typedef struct {
	unsigned char Seconds;
	unsigned char Tenths;
} Time_t;

volatile Time_t BurnTime = {1,0};			// displayable burn time
Time_t FullTime = {1,0};				// ditto, selected time

volatile unsigned char Cycles;			// power-line cycle counter, wraps each second

// Serial output

#define SERIAL_RATE	62500				// which depends on the crystal, of course!

volatile char SerialCharOut;				// char to send
volatile bit SerialOutReady;				// 1 for new char, 0 when sent

unsigned char Heartbeat;				// simple activity indicator
__code unsigned char ActivityChar[8] = {"-\\|/-\\|/"};	// remember \\ is just one character

// Button debouncing accumulators
// Incremented when pressed, zeroed when released... ticks = power-line cycles
// I used Hall-effect switches that don't bounce, so the initial delay is very short
//  For real debouncing set ON much longer

#define BUTTON_ON		3				// if continuously pressed this long, it's on!

volatile unsigned char Button_Time_Inc;
volatile unsigned char Button_Time_Dec;
volatile unsigned char Button_Pattern_Inc;
volatile unsigned char Button_Pattern_Dec;

volatile unsigned char Button_Contact;
volatile unsigned char Button_Fire;

//-------------------------------------------------------------------------------------------
// Utilities

#define SEC_TENTHS(s,t) (60*s + 6*t)			// convert seconds+tenths to cycles

void Delay(unsigned char CycleCount) {			// rough-and-ready delay by power-line cycles
unsigned char PrevCycle;

	PrevCycle = Cycles;

	while (CycleCount) {
		if (PrevCycle != Cycles) {
			CycleCount--;
			PrevCycle = Cycles;
		}
	}

}

void IncrementTime(Time_t *pTime) {

	if (pTime->Tenths >= 9) {
		pTime->Tenths = 0;
		pTime->Seconds++;			// wraps at 255 and we don't care at all
	}
	else {
		pTime->Tenths++;
	}
}

void DecrementTime(Time_t *pTime) {

	if (pTime->Tenths) {
		pTime->Tenths--;
	}
	else {
		if (pTime->Seconds) {		// saturate at 0.0
			pTime->Tenths = 9;
			pTime->Seconds--;
		}
	}

	if ((pTime->Tenths == 0) && (pTime->Seconds == 0)) {
		pTime->Tenths = 1;
	}

}

unsigned char TestButton(unsigned char Button) {

	return (Button >= BUTTON_ON);

}

//-------------------------------------------------------------------------------------------
// Display controls
// Rows and columns start at zero, of course

#define DISP_ROWS	2
#define DISP_COLS	20

#define DISP_POS_CMD	'\x10'

void InitDisplay(void) {

	puts("Ed\x1f\x11\x14");				// filler, reset, no scroll, no cursor

}

void SetCursor(unsigned char row, unsigned char col) {

	putchar(DISP_POS_CMD);
	putchar(row * DISP_COLS + col);

}

void RefreshDisplay(void) {

	SetCursor(1,19);
	putchar(ActivityChar[Heartbeat++ & 0x07]);

//	SetCursor(0,0);
	if (BurnEnable) {
//                       012345 67 89
		printf_tiny("BURN %d.%d  ",BurnTime.Seconds,BurnTime.Tenths);
	}
	else {
		printf_tiny("Time %d.%d  ",FullTime.Seconds,FullTime.Tenths);
	}

	SetCursor(0,10);
//                 abcde f0123
	printf_tiny("Patt %d ",PatternSelection);

	SetCursor(1,0);
	if (TestButton(Button_Contact))
//                0123456789abcdef0123
		puts("<<Ready!>>");
	else
		puts("No contact");

}

//-------------------------------------------------------------------------------------------
// Handler for line-sync input
// Synchronize triac bits to power line positive half-cycle
// Forces Timer 0 interrupt after exit to ensure synchronization
//  Timer 0 will not be running when this IRQ occurs!

void LineSyncIRQ(void) __interrupt (0) __using(1) {

	TRACE3 = 0;

	if (Phase >= (PHASES_PER_PATTERN - 1)) {	// if this is line sync after last pattern phase IRQ
		TRACE0 = 0;
		Phase = 0xff;				// preload to get 0 on PhaseIRQ fallthru
		EventIndex = 0;				// restart pattern on that phase

		if ((!BurnEnable) && BurnState == BURN_ACTIVE) {	// if should be active
			BurnEnable = 1;			//  then allow startup on this cycle
#if TRACE_PATTERN
			TRACE2 = 1;				// flag pattern startup
#endif
		}

		if (BurnEnable) {				// outputs active?
			if (BurnTime.Tenths) {			// tick time backwards
				BurnTime.Tenths--;		// continue running on 0.1 -> 0.0 tick
			}
			else {
				if (BurnTime.Seconds) {
					BurnTime.Tenths = 9;
					BurnTime.Seconds--;
			     }
			     else BurnEnable = 0;		// off after 1 tick for 0.0
			}
		}
	}

	if ((Phase % PHASES_PER_CYCLE) == (PHASES_PER_CYCLE - 1)) {	// if line sync after last phase in cycle
		TRACE0 = 0;					// make a blip
		TRACE0 = 1;
		TRACE0 = 0;					// then leave low
	}

	TF0 = 1;				// always force Timer 0 interrupt immediately after us

// Tick cycle counter

	if (Cycles < (LINE_FREQ - 1)) {
		Cycles++;
	}
	else {
		Cycles = 0;
	}

// Sample switches & twiddle debounce accumulators
// Buttons are rarely pressed, so that case goes pretty quickly
// To get even faster, skip it all if burn is in progress
// Remember that I'm using +active Hall-effect switches

	if (BUTTON_TIME_INC) {						// switch pressed?
		Button_Time_Inc += (Button_Time_Inc < 255);	// yes, increment and saturate
	}
	else {
		Button_Time_Inc = 0;					// no, flush
	}

	if (BUTTON_TIME_DEC) {
		Button_Time_Dec += (Button_Time_Dec < 255);
	}
	else {
		Button_Time_Dec= 0;
	}

	if (BUTTON_PATTERN_INC) {
		Button_Pattern_Inc += (Button_Pattern_Inc < 255);
	}
	else {
		Button_Pattern_Inc= 0;
	}

	if (BUTTON_PATTERN_DEC) {
		Button_Pattern_Dec += (Button_Pattern_Dec < 255);
	}
	else {
		Button_Pattern_Dec = 0;
	}

	if (BUTTON_CONTACT) {					// active high
		Button_Contact += (Button_Contact < 255);
	}
	else {
		Button_Contact = 0;
	}

	if (BUTTON_FIRE) {					// active high
		Button_Fire += (Button_Fire < 255);
	}
	else {
		Button_Fire = 0;
	}

#if TRACE_PATTERN
	TRACE2 = 0;
#endif

	TRACE3 = 1;

}

//-------------------------------------------------------------------------------------------
// Handler for Timer 0
// This meters out the triac control bits
// Turns off Timer 0 during last phase in each cycle, so line-sync IRQ will re-sync us

void PhaseIRQ(void) __interrupt (1) __using(1) {

TriacEvent_t *pEvent;

	TRACE3 = 0;

	TR0 = 0;						// Reload phase timer
	if (++Phase) {					// step to next phase. Is it nonzero?
		TH0 = ((int)(-(PHASE_TICKS - TIMER_OVERHEAD)) >> 8) & 0xff;		// nonzero = normal tick
		TL0 =  (int)(-(PHASE_TICKS - TIMER_OVERHEAD)) & 0xff;
	}
	else {
		TH0 = ((int)(-(PHASE_TICKS - LINE_SYNC_DELAY)) >> 8) & 0xff;	// zero = after line sync
		TL0 =  (int)(-(PHASE_TICKS - LINE_SYNC_DELAY)) & 0xff;
	}
	TR0 = 1;						// and start it up

	if (! BurnEnable) {				// if outputs should not be active
		GATE_BIT_PORT |= GATE_DRIVE_HIGH_MASK | GATE_DRIVE_LOW_MASK;	// force drive off
		GATE_BIT_PORT &= ~(GATE_CLAMP_HIGH_MASK | GATE_CLAMP_LOW_MASK);	// force clamp on
	}

	pEvent = pTriacPatterns[PatternSelection] + EventIndex;

	if (Phase == pEvent->EventPhase) {		// event time match?
		if (BurnEnable) {				// change outputs only if in active burn time
			GATE_BIT_PORT ^= GATE_BIT_MASK & (GATE_BIT_PORT ^ ~(pEvent->TriacBits));
		}
		EventIndex++;				// step to next event in pattern list
	}

	if ((Phase % PHASES_PER_CYCLE) == (PHASES_PER_CYCLE - 1)) {	// if now in last phase of cycle
		TR0 = 0;					//  ... next line sync will restart timer
		TRACE0 = 1;					// short blip to mark this point
		TRACE0 = 0;
		if (Phase == (PHASES_PER_PATTERN - 1)) {
			TRACE0 = 1;				// flag final transition of pattern
		}
	}

	if (SerialOutReady) {				// if char ready to send
		SBUF = SerialCharOut;			//   do it (TI will always be clear!)
		SerialOutReady = 0;			//   and mark it as gone
	}

	TRACE3 = 1;

}

//-------------------------------------------------------------------------------------------
// Serial character I/O
// This is utterly crude...

/************
char getchar(void) {

	if (RI) {
		RI = 0;
		return SBUF;
	}
	else {
		return (char) 0;
	}

}
*****************/

// Output must be synced to the phase IRQs to properly pace the chars to the VFL display...
// So we hand this off to the Timer0 IRQ

void putchar(char c) {

	while (SerialOutReady) {
#if TRACE_SERIAL
		TRACE2 = ! TRACE2;
#else
		continue;
#endif
	}

#if TRACE_SERIAL
	TRACE2 = 1;
#endif

	SerialCharOut = c;
	SerialOutReady = 1;

	return;

}

//-------------------------------------------------------------------------------------------

void main(void) {

__bit SomethingChanged;

// Set up hardware

	TCON = 0;				// Timers off, software control
	PCON |= SMOD;			// double the serial bit rate

	TMOD = 0x21;			// Timer 1 = 8 bit auto-reload, Timer 0 = 16-bit

	TL1 = TH1 = 256 - ((2 * OSCILLATOR_FREQ) / (32 * 12 * SERIAL_RATE));

	SCON = 0x50;			// serial mode 1
	TR1 = 1;				// start Timer 1

// Sync to incoming power-line signal
// Timer 0 is off so line-sync will start normally

	LINE_SYNC_EDGE_ENABLE = 1;	// make INT0 edge-triggered
	LINE_SYNC_EDGE = 0;

	while (!LINE_SYNC_EDGE) {	// hang until first edge
		TRACE3 = !TRACE3;
	}
	TRACE3 = 1;

	IE = 0x83;				// Ints enabled, Timer0 IRQ enabled, INT0 enabled

	InitDisplay();			// set up the display
	SetCursor(0,0);			// don't know why this is needed the first time, but it is...

//          0123456789abcdef0123
	puts("CC June 08\r\n"
           "Ed Nisley 20 Feb 08");

	Delay(SEC_TENTHS(3,0));

	InitDisplay();			// clear the decks!

// Get sane input to start... just keep rewriting the message, it's shorter

	while (TestButton(Button_Fire)) {
		SetCursor(0,0);
//			0123456789abcdef0123
		puts("Release tip switch!");
	}

	InitDisplay();

// Repeat forever...

	while (1) {

// If nothing else happens, update the display about twice a second

		SomethingChanged = !(Cycles % (LINE_FREQ / 2));

// Handle timing and pattern-selection buttons

		if (TestButton(Button_Time_Inc)) {
			IncrementTime(&FullTime);
			SomethingChanged = 1;
		}

		if (TestButton(Button_Time_Dec)) {
			DecrementTime(&FullTime);
			SomethingChanged = 1;
		}

		if (TestButton(Button_Pattern_Inc) && (PatternSelection < (TRIAC_NUM_PATTERNS - 1))) {
			PatternSelection++;
			SomethingChanged = 1;
		}

		if (TestButton(Button_Pattern_Dec) && PatternSelection) {
			PatternSelection--;
			SomethingChanged = 1;
		}

// Convert contact & footswitch buttons into output control
// Ignore nearly all the ugly race conditions...

		switch (BurnState) {
		case BURN_IDLE :
			if (TestButton(Button_Contact)) {		// first we need contact
				BurnState = BURN_CONTACT;
				SomethingChanged = 1;
			}
			break;
		case BURN_CONTACT :
			if (!TestButton(Button_Contact)) {		// no contact = restart
				BurnState = BURN_IDLE;
				SomethingChanged = 1;
			}
			else if (TestButton(Button_Fire)) {		// foot switch active?
				BurnTime.Tenths = FullTime.Tenths;	// set up burn duration
				BurnTime.Seconds = FullTime.Seconds;
				BurnState = BURN_ACTIVE;
				while (!BurnEnable) {			// wait for IRQ to activate burning
					continue;
				}
				SomethingChanged = 1;
			}
			break;
		case BURN_ACTIVE :
			if (!TestButton(Button_Contact)) {		// no contact = restart
				BurnState = BURN_IDLE;
				BurnEnable = 0;
			}
			else if (!BurnEnable) {				// burn completed?
				BurnState = BURN_DONE;
			}
			SomethingChanged = 1;				// always update display
			break;
		case BURN_DONE :
			if (!TestButton(Button_Fire)) {		// foot switch released?
				BurnState = BURN_IDLE;
				SomethingChanged = 1;
			}
			break;
		default :
			BurnEnable = 0;
			BurnState = BURN_IDLE;
			SomethingChanged = 1;
		}

// Update display if anything interesting happened

		if (SomethingChanged) {
			RefreshDisplay();
		}

	}

}

Advertisements
  1. #1 by John Rehwinkel on 2010-09-10 - 09:42

    If I were to port this to an AVR/Arduino, I’d probably also use a 4-bit parallel LCD interface, taking advantage of the pins freed up by going to 1-bit triac control. That way, I could use a generic $2 LCD instead of a $15 serial one. But my current project isn’t for resistance soldering, it’s a capacitive discharge spot welder.

    • #2 by Ed on 2010-09-10 - 11:45

      Absolutely!

      I used the VFL just because it was sitting in the box, waiting for an assignment… plus it was bright & cheerful and engaged the otherwise unused serial output.

    • #3 by smellsofbikes on 2010-09-10 - 14:45

      I’d be interested in seeing your cap discharge spotwelder. I have the cap and electrodes but haven’t gotten around to building it, so I’d love to see what you’ve come up with.

  1. Resistance Soldering: Electrodes « The Smell of Molten Projects in the Morning