BGA Rework station

Projekti naših članova foruma koji su još uvijek u fazi izrade.

Moderators: pedja089, stojke369, [eDo], trax

vladav87
Posts: 4
Joined: 25-04-2010, 18:30
Location: Leskovac, Srbija

Re: BGA Rework station

Post by vladav87 »

Hehe, razumemo se skroz.
Procitao sam sve od a do š i sve razumem.
Ali ti mene nisi razumeo :)
To termostat sam naveo iz razloga sto ne vidim da je inplementiran kakav algoritam vec tehnologija pali gasi grejac i trudi se da odrzis tu temp za to vreme :)
A za razliku od tog pogledaj ovo http://hobbybotics.com/projects/hobbybo ... ler-v8-03/
Znam, ovaj dizajn je dosta skuplji ali po svemu ovde vidjenom trebao bi da radi vrhunski posao sa pid-om i pwm-om :D Zverka
User avatar
Feko
Stariji član
Stariji član
Posts: 4394
Joined: 23-07-2008, 06:18
Location: Hrvatska, Slavonija, Selo moje malo...

Re: BGA Rework station

Post by Feko »

Aha, nisam skužio. :roll:
A vjerojatno nije PID kontrola ali kako sam naveo u prijašnjim postovima kako diže temperaturu možda za stupanj/dva nekad zna odstupati i to samo u početnom dijelu do 100 stupnjeva jer je grijač dosta "trom" i još isijava toplinu iako je isključen.

kod u prilogu pa neka netko pogleda...možda se i varam...

Code: Select all

/*
	original code
	4-26-05
	Copyright Spark Fun Electronics© 2005
	Nathan Seidle 
	spark@sparkfun.com

	this version of code:
	Copyright Kit Ryan ©2008
	Kit Ryan
	jc.ryan@verizon.net
*/

//This program has been essentially completely rewritten and greatly expanded from the original Spark Fun model software.

//#pragma CLOCK_FREQ 8000000 - this statement replaced with osccon statement at beginning of Main()  Put back if using boot loader.

//#define Baud_9600

#include <boostc.h>				// misc functions, such as MAKESHORT, etc.  Included via <system.h>
#include <PIC16F88.h> // SourceBoost device hardware map

//####need following statement if boot loader is not used. Otherwise, boot loader sets config bits********
#pragma DATA _CONFIG1, _CP_OFF & _INTRC_IO & _CCP1_RB0 & _DEBUG_OFF & _WRT_PROTECT_OFF & _CPD_OFF & _WDT_OFF & _PWRTE_ON & _LVP_OFF & _BODEN_OFF
#pragma DATA _EEPROM, 	0x00,0x3c,0x00,0x32,0x00,0x5a,0x00,0x96,0x00,0x5a,0x00,0xb4,0x00,0x28,0x00,0xd7,0x00,0x3c,0x00,0x96,
0x00,0x1e,0x00,0x32,0x00,0x0a,0x00,0x28,0x00,0x0a,0x00,0x1e,0x00,0x0a,0x00,0x1e,0x00,0x0a,0x00,0x1e,
0x00,0x3c,0x00,0x32,0x00,0x5a,0x00,0x96,0x00,0x5a,0x00,0xb4,0x00,0x28,0x00,0xd7,0x00,0x3c,0x00,0x96,
0x00,0x1e,0x00,0x32,0x00,0x0a,0x00,0x28,0x00,0x0a,0x00,0x1e,0x00,0x0a,0x00,0x1e,0x00,0x0a,0x00,0x1e,
0x14,0x01,0x01,0x04,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00;
// These locations are for Programmed time/temp values.  0-9 (10 total) steps x 2 integers each (2 bytes/int) for time
// and temp = 40 bytes per program x 2 = 80 bytes (locations 0 - 79). They are initialized as  
// 60, 50, 90, 150, 90, 180, 40, 215, 60, 150, 30, 50, 10, 40, 10, 30, 10, 30,10, 30
// Following that, there are stored 'settings' variables which can be changed using Setup or Calibrate functions:
//	80 = (1 byte) tc (time constant of the oven heating cycle in seconds.  Default = 20s)
//  81 = (1 byte) temp_units on display (F or C - default: C)
//  82 = (1 byte) relay_LED  (on or off with relay activation. Default = ON)
//	83 = (1 byte) t_inc (adjusts the step rate for speed_button from 1 to 10 second/degree steps. Default = 5)
//	84 = (1 byte) clock_cal  (adjust the master INTRC clock around 8MHz +/- 2% (the room temp accuracy range) Default = 5)

#define STATUS_LED      portb.3
#define BUTTON_UP       portb.0
#define BUTTON_DOWN     portb.1
#define BUTTON_SELECT   portb.7

#define RELAY           portb.4
#define go				adcon0.2	//set A/D conversion start bit.
#define OFF 0
#define ON  1
#define TRUE 1
#define FALSE 0

#define D7              porta.3		//Data pin 7 on LCD (write)/ also LCD busy indicator (read).
#define D6              porta.2		//Data pin 6 on LCD
#define D5              porta.1		//Data pin 5 on LCD
#define D4              porta.0		//Data pin 4 on LCD
#define LCD_E               porta.7   //operation enable (read/write) signal (strobe)
#define LCD_R_W             porta.6   //read / write select
#define LCD_RS              portb.6   //register select

#define     CLR_DISP        0b00000001     //Clear display
#define     CUR_HOME        0b00000010    //Move cursor home and clear screen memory
#define     DISP_ON         0b00001100    //Turn visible LCD on.  Also will turn off cursor and blinking.
#define     SET_CURSOR      0b10000000    //SET_CURSOR + X : Sets cursor position to X
#define		CUR_BLNK_ON		0b00001111		//turn on cursor and blinking
#define BR_CONST  51


//Global Variables
//============================

//unsigned char a;	//misc variables
unsigned char m_seconds, eeaddress, err_num=0; //
unsigned int total_seconds, display_seconds, step_seconds, a_temp1=0, a_temp2=0; //total = elapsed time; step = for each step.
signed int tot_err=0, proj_actual=0, proj_temp=0;	//these could be over +/- 128.
signed int  proj_err; //projected temp error at tc/2 seconds in the future (which is based on slope)
unsigned int step_time, step_temp, prev_step_temp, set_temp=0;  
unsigned char W_TEMP;  //need to keep these global so they don't get lost during interrupts
unsigned long actual_temp;	//actual temperature in either C or F units
unsigned char STATUS_TEMP, step_number; 
unsigned char PCLATH_TEMP, temp;  //'temp' is a generic variable used locally but declared just once
unsigned char timer_on = FALSE, temp_on = FALSE, relay_on = FALSE; //Timer and temp updates are off to start
unsigned char eeaddoffset;		//Address offset is set to either 0 (for program 1) or 40 (decimal) for program 2.  This value 
//is the offset for the stored program data in EEPROM and makes addressing faster.
unsigned char cur_pos;		//cursor position across top row of display.  Selected position to change
unsigned char digit_100, digit_10, digit_1;			//first, second and third digits in either time or temp display
unsigned char choice, e_data, e_address, button_push, button_release, long_push;
unsigned int  two_second_timer=0;
unsigned char update_flag=FALSE, menu_index=0, state, prev_state, step, mode, prev_mode, calmode, set_mode, timer_int = FALSE;
unsigned char temp_units, time_units, relay_LED, t_inc, clock_cal, tc;  //these are Setup-related variables stored in EEPROM. 
//tc is the time constant for the oven (in secs).  How long it takes the coils to get hot.

//============================
//End Global Variables

//Interrupt Vectors
//============================
//Interrupt register save handler


//============================

//Function definitions
//============================

void delay_ms(unsigned int x);
void init_lcd(void);
void LCD_wait(void);
void boot_up(void);
void send_cmd(unsigned char d, unsigned char cmd);	//sends a byte of either command or data to the LCD
void printf_lcd(const char *nate);						//print a string to the display
unsigned char onboard_eeread(unsigned char e_address);				//read byte from eeprom
void onboard_eewrite(unsigned char e_data, unsigned char e_address); //write byte to eeprom
unsigned char mod_digit (unsigned char value, unsigned char direction);
unsigned int get_temp (void);								//read thermocouple
void put_edata (unsigned char e_address, unsigned int value);		//write integer to eeprom
unsigned int get_edata (unsigned char e_address);					//read integer from eeprom
void update_display (unsigned int value, unsigned char location);	//converts and displays a 3-digit number for temp or time display on top row of LCD
unsigned int speed_button (unsigned int value, unsigned char direction, unsigned char disp_pos); //increments/decrements a 3-digit number quickly
unsigned int get_temp(void);
unsigned char bin2hex(char x);
//void send_char (unsigned char n);		//sends two nybbles of data to the lcd.
void init_state (unsigned char timer, unsigned char temp);  //initializes each state with common code
void serial_putchar(unsigned char s_out);		//puts character to the serial port
void convert_int (unsigned int value);		//convert integer into 3 one-digit values for display or transmission
void serial_putnumber (unsigned int value);	//sends a 3 digit ASCII number to the serial port
unsigned int proj_value (signed int x2, signed int x3, signed int y1, signed int y2); //projects a straight line value
void send_tunits (void);  //send the C or F character to the screen for temperature units
//______________________________________________
//Interrupt register save handler
void int_save_registers(void)
{

	asm			//had to use assembly code here because could not get access to the W register in C
	{
		MOVWF	_W_TEMP;	//Copy W to TEMP register (note - '_' means declared in C code section)
		MOVF	_status,0;	//Swap status to be saved into W
		CLRF	_status;	//bank 0, regardless of current bank, clears IPR, RP1, RP0
		MOVWF	_STATUS_TEMP;	//Save STATUS to bank zero STATUS_TEMP register
		MOVF	_pclath,0;	//Copy PCLATH to W
		MOVWF	_PCLATH_TEMP;	//Save PCLATH to PCLATH_TEMP
		CLRF	_pclath;	//Page zero, regardless of current page
	}
	

}

//______________________________________________
//Interrupt register restore handler
void int_restore_registers(void)
{
	asm
	{
		MOVF	_PCLATH_TEMP,0;	//bring old PCLATH into W
		MOVWF	_pclath;		//Restore PCLATH register
		MOVF	_STATUS_TEMP,0;	//Bring old STATUS_TEMP into W (sets bank to original state)
		MOVWF	_status;		//Restore STATUS register
		MOVF	_W_TEMP,0;		//Bring old W_TEMP into W = restore W
	}
}
//______________________________________________
void interrupt(void)
{
	
	int_save_registers();

	//  char sv_FSR = fsr;  // save FSR if required

	if(pir1.0) //Timer1 Overflow Interrupt
	{
		//Setup Timer1 to fire every 100ms
		//1 TMR1 click is 4us (with prescaler of 8). 100ms / .004ms = 25000 clicks
		//but you lose something during the reset process, so reduce clicks by 0.4%(100 clicks)
		//65535 - 25000 = 40535 = 0x9E57
		//65535 - 24900 = 40635 = 0x9EBB
		//####modify to take the clock_cal adjustment into account
		t1con.0 = OFF;  //stop timer1 for a moment
		tmr1h = 0x9E; //set high byte
		tmr1l = 0x57 + (clock_cal * 20); //set low byte.  Clock_cal is limited to a range of 1-10 in Setup.
		m_seconds++;
		if(m_seconds == 10)		//10 x 100ms = 1 second!
		{
			m_seconds = 0;
			total_seconds++;
			timer_int = TRUE;	//set 1 second interrupt flag
		}
		if (total_seconds >= 999) 	//restart timer
		{
			total_seconds = 0;
		}
		pir1.0 = 0; //Clear INT Flag
		t1con.0 = ON;  //restart timer1 operation 	
	};
	//    fsr = sv_FSR;               // restore FSR if saved
	int_restore_registers(); 
}

//______________________________________________
//==============================================
void main ()
{

	//Mode options: 
	//***the following lines are for info only.  There are too many characters to store these strings in data memory so they are implemented 
	//as literal string constants in program memory in the body of the code below****
	/*		//these are display screens for the lower lcd line
		Illegal state			//state (20)  - used for resetting the prev_state value to force 1st time through restarts
		"MM x   x  Nx/Sel", 	//state (0)
		"MA R/R R-On  /Ex",		//state (1)		
		"MA S/R R-Off /Ex",		//state (1)
		"SA U/U D/D   /Ex",		//state (2)
		"P1 R/R Jmp Ed/Ex",		//state (3)
		"P1 S/R Jmp Ed/Ex",		//state (3)
		"E1 U/U D/D Nx/Ex",		//state (13)
		"P2 R/R Jmp Ed/Ex",		//state (4)
		"P2 S/R Jmp Ed/Ex",		//state (4)
		"E2 U/U D/D Nx/Ex",		//state (14)
		"SU Up  Dn  Nx/Ex" 	};	//state (5) 
*/
	//****Need the following osccon statement if boot loader is not used****
	//set INTRC oscillator for 8 MHz
	osccon = 0b01110000; //Setup internal oscillator for 8MHz
	boot_up();
	//Setup Timer1 to fire every 100ms for interrupts
	//1 TMR1 click is 4us (with prescaler of 8). 100ms / .004ms = 25000 clicks
	//65535 - 25000 = 40535 = 0x9E57.  First time only.  Make shorter after that.
	t1con.4 = 1;	//set prescaler to divide by 8
	t1con.5 = 1; 
	intcon.7=1;		//enable global interrupts
	intcon.6=1;		//enable peripheral interrupts
	pie1 = 0b00000001;		 //enable timer1 interrupt only
	t1con.1 = 0;	//use internal oscillator/4 clock
	tmr1h = 0x9E; //set high byte
	tmr1l = 0x57; //set low byte but save code space - will be tweaked during the interrupt routine with clock_cal.
	
	send_cmd(CLR_DISP, TRUE);
	trisb = 0b10000011;			//set portb pins for inputting button pushes
	t1con.0 = ON;  //start timer1 operation.  Uses interrupt handler after this.
	state = 0; 
	prev_state = 20;	//default starting condition is the main menu state. Set prev_state to a non-existent number to force initialization
	//	cur_pos = 0;		//
	prev_mode = 1;			//initialize menu location to 1st selection - "Manual"
	button_release = FALSE;
	choice = 'n';	//select a non-existent button choice to start off.

	while(1)	//MAIN LOOP: loop while waiting for timer interrupts or button pushes
	{
		//Scan for button pushes.  Short pushes are less than 2 seconds.  Long pushes are over 2 seconds and can have a different effect.
		//==================================================== 		
		button_push = FALSE;  //clear this indicator or you may get extra, false button push actions
		long_push = FALSE;
		update_flag = FALSE;	//start each loop with this setting
		two_second_timer = total_seconds;
		if (BUTTON_SELECT == 1 && BUTTON_UP == 1 && BUTTON_DOWN ==1)
		{			//This test (all three buttons are released simultaneously) will reset all the button indicators.
			button_release = TRUE;  //don't record a new button push until the previous one has been released.
			//do not automatically reset this variable each loop or you may miss button pushes due to the short loop time.
			//that is a button could only be pushed between this statement and one of the following if tests!
			choice = 'n';     //reset choice to none to avoid some weird problems
			delay_ms(50); 		//delay slightly to avoid button bounce
		};			

		if (BUTTON_UP == 0 && button_release == TRUE)  //if Up button pushed and no other button is already pushed.  
		//This test avoids the problem of conflicts if multiple buttons are pushed at once.  Only the first one
		//pushed will be acted upon and the other pushes ignored until all the buttons have been released.
		{
			choice = 'u';
			button_release = FALSE;
			while (BUTTON_UP ==0 && (two_second_timer + 2 > total_seconds));   //wait for button release or 2s
			button_push = TRUE;			//in either case, set button push indicator
		};
		
		if(BUTTON_DOWN == 0 && button_release == TRUE)  //if Down button pushed and no other button is already pushed.  
		//This test avoids the problem of conflicts if multiple buttons are pushed at once.  Only the first one
		//pushed will be acted upon and the other pushes ignored until all the buttons have been released.
		{
			choice = 'd';
			button_release = FALSE;
			while (BUTTON_DOWN ==0 && (two_second_timer + 2 > total_seconds));   //wait for button release or 2s
			button_push = TRUE;			//in either case, set button push indicator
		};
		
		if(BUTTON_SELECT == 0 && button_release == TRUE)  //if Select button pushed and no other button is already pushed.  
		//This test avoids the problem of conflicts if multiple buttons are pushed at once.  Only the first one
		//pushed will be acted upon and the other pushes ignored until all the buttons have been released.  
		{  
			choice = 's';
			button_release = FALSE;
			while (BUTTON_SELECT ==0 && (two_second_timer + 2 > total_seconds));   //wait for button release or 2s
			button_push = TRUE;		//in either case, set button push indicator
		};
		
		//for any case where button was pushed, check 2s timer    
		if(two_second_timer + 2 <= total_seconds)	//was it a long push?
		long_push = TRUE; 			//set indicator for long button pushes

		//execute the buttons and lcd updates associated with the particular state the program is in
		//if no button push, then just do lcd updates per state and/or 1 second timer
		//if button push, take action and/or change state immediately - don't wait for 1 second timer
		if (timer_int == TRUE)		//update lcd only once per second - makes for smoother temp readings.
		{
			timer_int = FALSE;		//reset 1 second timer
			//send time and temp to serial port every second
			serial_putnumber(total_seconds);  //
			serial_putnumber(set_temp);     //send desired temp
			serial_putnumber(get_temp());	//send actual temp
			//				a_temp3 = a_temp2;				
			a_temp2 = a_temp1;				//save last temp difference reading. Need for temp projection algorithm.
			a_temp1 = get_temp();			//note that get_temp read at other places in the program may not be exactly 1 second later.
			if (relay_on)
			serial_putchar ('1');		//if relay is on, send a 1
			else
			serial_putchar ('0');		//else relay is off, send a 0

			serial_putchar (13);		//CR, LF after each time/temp pair
			serial_putchar (10);
			if (temp_on == TRUE) 
			{
				update_display (get_temp(), 12);	//Display current temp reading
			}
			if (timer_on == TRUE)
			{	update_display (total_seconds,2);	//Update timer display if on
				step_seconds++;		//also increment the time within each step in program mode
			}
			else if (timer_on == FALSE)								//timer is OFF to get to here
			total_seconds = display_seconds;	//keep timer in hold mode
			if (relay_on == TRUE)		//turn on relay.  Do this here so relay only changes 1/sec max.
			{
				portb.4 = 1;
				if (relay_LED == TRUE)
				portb.3 = 1;			//turn on LED (if selected)
			}
			else			//relay_on must be FALSE, so turn relay off.
			{
				portb.4 = 0;
				portb.3 = 0;
			}	
		};

		switch (state)
		{

			//---------------Main Menu-----------------------
		case 0:

			if (prev_state != 0)		//first time through, set up display
			{
				send_cmd(CLR_DISP, TRUE);	//Clear screen and put cursor to 1st position on top line
				printf_lcd ("Mode:");		//Display top LCD line of display
				send_cmd(SET_CURSOR + 64, TRUE);			//setup lower line of display
				printf_lcd ("MM  x  x  Nx/Sel");	//button functions (x means no function)
				prev_state = 0;
				mode = prev_mode-1;		//this will put the correct top line screen on even coming back from other mode state.
				button_push = TRUE;		//keep track of previous mode number in Main Menu
				choice = 's';
				long_push = FALSE;
				init_state (FALSE, FALSE);

			};
			if (button_push == TRUE && choice == 's')	
			{
				//if Select button has been pushed.
				//ignore 'u' or 'd' button pushes in this mode
				if (long_push == TRUE)		//for a long push, this is a selection of that mode as the new state
				state = mode;
				else 	//else, must be a short push, just go to the next menu selection possibility
				{
					++mode;
					prev_mode = mode;
					send_cmd(SET_CURSOR + 7, TRUE);		//save variable space by sending only the unique parts of the string to the LCD
					if (mode == 6)
					mode = 1;					//cycle back to the beginning if there are no more modes
					switch (mode)	//mode is used only in the main menu. State becomes the mode value after selection
					{
					case 1:							//can't use a variable here - too big for the memory
						printf_lcd ("   Manual");	
						break;
					case 2:
						printf_lcd ("Semi-Auto");
						break;
					case 3:
						printf_lcd ("Program 1");
						break;
					case 4:
						printf_lcd ("Program 2");
						break;
					case 5:
						printf_lcd ("    Setup");
						break;
					};
				};
				
				//no action to be taken on Main Menu screen for update_flag = true.
			};
			break;	//end Case 0

			//------------------Manual Mode---------------------------------
		case 1:		//Manual Mode - initially in timer-stop/relay-off condition
			//Manual, separate control of the timer and the relay.

			if (prev_state != 1)		//first time through, set up display
			{
				send_cmd(SET_CURSOR, TRUE);
				printf_lcd ("    0s      000");
				send_tunits();
				send_cmd(SET_CURSOR + 64, TRUE);
				printf_lcd ("MA R/R R-on  /Ex");
				prev_state = 1;
				init_state (FALSE, TRUE);	//initialize common code
				set_temp = 0;  //needed to clear the variable value if coming into Manual mode from another mode.

			};

			if (button_push == TRUE)  
			{
				switch (choice)
				{
				case 'u':		//if Up button short or long push								

					if (long_push == TRUE)				//Up button, long push - reset timer to zero
					{
						prev_state = 20;		//force restart of this state to reset timer.  Turns off relay, too.
					}
					else 							//Up button, short push - start (or restart) or stop timer (toggle each time)
					{
						if (timer_on == FALSE) 
						{
							total_seconds = display_seconds;	//start or restart timer at the current display value
							send_cmd (SET_CURSOR + 67, TRUE);			//change run to stop option
							printf_lcd ("S");
							timer_on = TRUE;
						}
						else						//timer_on must = TRUE to get here
						{
							display_seconds = total_seconds;	//save current timer value for future restart
							send_cmd (SET_CURSOR + 67, TRUE);			//change stop to run option
							printf_lcd ("R");
							timer_on = FALSE;
						};
					};
					break;

				case 'd':		//if Down button short or long push - toggle relay 						

					if (relay_on == FALSE) 	//if relay was OFF, turn it ON
					{
						relay_on = TRUE;
						send_cmd (SET_CURSOR + 73, TRUE);		
						printf_lcd ("off");		//set 'd' button option to turn relay OFF if pushed
						send_cmd (SET_CURSOR + 7, TRUE);	//send warning to screen that relay is on
						printf_lcd ("RELAY");
					}
					else					//relay must have been ON to get to here
					{
						relay_on = FALSE;	//turn relay and LED OFF here
						send_cmd (SET_CURSOR + 73, TRUE);		
						printf_lcd ("on ");
						send_cmd (SET_CURSOR + 7, TRUE);	//turn off relay warning note
						printf_lcd ("     ");
					};	
					break;

				case 's':		//if Select button has been pushed.							
					if (long_push == TRUE)				//Select button, long push - Exit back to main menu
					{
						state = 0;				//return to main menu
					};
					break;
					
				};	//end Switch
			};		//end If
			break;	//end Case 1

			//-------------Semi-Automatic Mode---------------


		case 2:		//semi-auto mode - stop condition **add timer function if code length permits**
			//works same as Manual mode except it will hold the set temperature.
			if (prev_state != 2)		//first time through, set up display
			{
				send_cmd(SET_CURSOR, TRUE);
				printf_lcd ("       000");
				send_tunits();

				printf_lcd (" 000");
				send_tunits();

				send_cmd(SET_CURSOR + 64, TRUE);
				printf_lcd ("SA U/U D/D   /Ex");
				update_display (get_temp(), 12);	//update actual temp only, not time, in the stop condition
				prev_state = 2;
				init_state (FALSE, TRUE);	//initialize common code
				set_temp = 0;			//initial temperature setting for semi-automatic mode. (in Celsius)


			}
			
			
			if (button_push == TRUE)  
			{
				switch (choice)		//delete everything except returning to main menu - too much code for memory!!!!
				{
				case 'u': case 'd':	//if Up or Down button has been pushed, increment desired temp setting.
					
					set_temp = speed_button (set_temp, choice,7);
					break;

				case 's':		//if Select button has been pushed.							
					if (long_push == TRUE)				//Select button, long push - Exit back to main menu
					state = 0;				//return to main menu
					break;

				}	//end Switch
			}		//end If
			//in any event, update the relay setting based on difference between set and actual temps.
			
			update_display (set_temp, 7);
			//		        actual_temp = get_temp();

			if (get_temp() < set_temp)
			relay_on = TRUE;			//if oven temp is too low, turn on relay
			else
			relay_on = FALSE;			//if oven temp is too high, turn off relay (and LED)

			break;	//end Case 2




			//--------------Programmed Mode------------------

		case 3: case 4:		//Program 1 or 2 - stop condition

			if (prev_state != 3 && prev_state != 4)		//first time through, set up displays
			{
				send_cmd(SET_CURSOR, TRUE);
				printf_lcd ("0 000s 000");		//step --- elapsed time --- desired temp@end of this step --- actual temp
				send_tunits();

				printf_lcd (" 000");
				send_tunits();

				send_cmd(SET_CURSOR + 64, TRUE);
				printf_lcd ("P1 R/R Jmp Ed/Ex");		//run/stop/reset  --- Jump to step --- Edit mode/Exit back to Main Menu
				if (state == 3)
				{
					printf_lcd ("P1 R/R Jmp Ed/Ex");		//run/stop/reset  --- Jump to step --- Edit mode/Exit back to Main Menu
					prev_state = 3;
					eeaddoffset = 0;
				};
				if (state == 4)
				{
					send_cmd(SET_CURSOR + 65, TRUE);
					printf_lcd ("2");		//identify this as running Program2
					prev_state = 4;
					eeaddoffset = 40;
				};

				init_state (FALSE, TRUE);	//initialize common code

				step_number = 0;		//initialize the current data with step 0 data to prepare to run the program.
				eeaddress = (0 + eeaddoffset);  //Get step 0 data address.  Program1 data starts @ 0, Program2 data starts @ 40.
				step_time = get_edata (eeaddress);  //get first step_time from memory
				step_seconds = 0;
				step_temp = get_edata (eeaddress+2);  //first step_temp data is 2 bytes past time data
				update_display (step_time, 2);		//show step 0 values for time and temp on the display
				update_display (step_temp, 7);
				//				//initialize time and temp values for calculation of temp ramp setting below.
				prev_step_temp = 38;		//starting point for warm up in Celsius
				if (temp_units == FALSE)
				prev_step_temp = 100;	//in F


			};  //end initialization if group


			if (timer_on == TRUE) 
			{
				if (step_seconds > step_time)	//if we hit the end of one of the current step periods

				{				//load next step time and temp from memory
					//if time step = 000, that is taken as the end of the program - reset and stop
					step_number++  ;	//increment step number
					step_seconds = 0;	//reset timer for the new step (each step is timed starting from 0)
					eeaddress = (4*step_number)+ eeaddoffset;
					step_time = get_edata (eeaddress);  //get next step_time from memory
					prev_step_temp = step_temp;		//save for temp change calcs
					step_temp = get_edata (eeaddress+2);  //next step_temp data is 2 bytes past time data
					send_cmd (SET_CURSOR, TRUE);		//update step number on display
					send_cmd (bin2hex (step_number), FALSE);
					if (step_time == 0)		//indicator that program has completed.  Stop timer and relay, return to program start condition.
					{
						timer_on = FALSE;
						relay_on = FALSE;
						prev_state = 20;	//pretend we just came in from the main menu again to reset the program.
					}
				}

				//Algorithm is to put linear temp slope between the previous temp and the new desired temp. Compare 
				//desired temp with actual and turn relay on/off accordingly.  Check once/second max. Will eventually
				//add a ramp up/down function to reduce overshoot problems on the specific oven.

				//calculate what the current set temperature should be.  This is the displayed value.
				//Algorithm puts a linear temp slope between the previous step temp and the current step temp.	
				set_temp = proj_value (step_time, step_seconds, prev_step_temp, step_temp);

				//units are always C in program memory
				//must update the program temp display, too.

				update_display (set_temp, 7);

				if ((step_seconds + tc/2) <= step_time)	//if tc/2 seconds ahead of current time in still in this step, use current slope
				proj_temp = proj_value (step_time, step_seconds+tc/2, prev_step_temp, step_temp);
				
				else		//tc/2 seconds ahead of current time is in the next step so get that data.
				proj_temp = proj_value (get_edata (eeaddress + 4), tc/2-(step_time - step_seconds), step_temp, get_edata (eeaddress + 6));
				
				//now project actual temp ahead tc seconds and compare.  Slope is done over 2 points.  
				//Since the slope is a derivative function, it is subject to a lot of noise. Need to include absolute difference, too.

				proj_actual = get_temp() + ((tc/6)*(a_temp1-a_temp2));	//

				proj_err = (get_temp()- set_temp) + (proj_actual - proj_temp); //P and I factors for PID control.If error is negative, relay should be turned on.

				if (proj_err <= 0)
				relay_on = TRUE;			//if oven temp is too low, turn on relay (and LED if selected)
				else
				relay_on = FALSE;			//if oven temp is too high, turn off relay (and LED)

			}
			else 
			total_seconds = display_seconds;	//otherwise, just keep resetting the timer to previous reading

			
			if (button_push == TRUE)
			{				
				switch (choice)
				{
				case 'u':		//if Up button short or long push								

					if (long_push == TRUE)				//Up button, long push - reset timer to zero
					{
						prev_state = 20;		//force restart of this state to reset timer.  Turns off relay, too.
					}
					else 							//Up button, short push - start (or restart) or stop timer (toggle each time)
					{
						if (timer_on == FALSE) 
						{
							total_seconds = display_seconds;	//start or restart timer at the current display value
							send_cmd (SET_CURSOR + 67, TRUE);			//change run to stop option
							printf_lcd ("S");
							timer_on = TRUE;
						}
						else						//timer_on must = TRUE to get here
						{
							display_seconds = total_seconds;	//save current timer value for future restart
							send_cmd (SET_CURSOR + 67, TRUE);			//change stop to run option
							printf_lcd ("R");
							timer_on = FALSE;
							relay_on = FALSE;			//always turn off oven if timer is stopped

						};
					};
					break;

				case 'd':		// Down button, short push - jump to next step						
					//Always put the relay and timer into off condition when using jump.
					//Also, reset the timer back to zero
					
					//for long or short Jump button pushes, take the Jump action
					display_seconds = 0;	//
					total_seconds = 0;
					send_cmd (SET_CURSOR + 67, TRUE);			//change stop to run option
					printf_lcd ("R");
					relay_on = FALSE;				//turn off relay and LED (even if LED wasn't on -avoid another if test).
					timer_on = FALSE;
					//load next step time and temp from memory
					//if time step = 000, that is taken as the end of the program - cycle back to step 0.
					step_number++  ;	//increment step number

					eeaddress = (4*step_number)+ eeaddoffset;
					step_time = get_edata (eeaddress);  //get next step_time from memory
					if (step_time == 0)		//indicator for end of program.  Cycle through to step 0 and keep going.
					{
						prev_state = 20;	//start this mode again.  Least code method.

					};
					prev_step_temp = step_temp;		//save for temp change calcs
					step_temp = get_edata (eeaddress+2);  //next step_temp data is 2 bytes past time data

					update_display (step_time, 2);		//send step time to the display
					update_display (step_temp, 7);		//send step temp to the display
					send_cmd (SET_CURSOR, TRUE);			//update display for step, time and temp changes
					send_cmd (bin2hex(step_number), FALSE);
					
					break;

				case 's':		//if Select button has been pushed.
					
					prev_state = state;							
					if (long_push == TRUE)				//Select button, long push - Exit back to Main Menu
					state = 0;		
					else		//Select button, short push - go to program editing mode
					state += 10;		//shift current state to program mode.  13 for Program 1, 14 for Program 2.
					break;
					
				};	//end Switch
			};		//end If
			break;	//end of Cases 3 and 4


			//------------------Edit Program Mode -------------------------
		case 13: case 14:		//Program - edit mode. State 13 for Program1, state 14 for Program2.
			//Remember that each program step time is for that step only, not the whole elapsed time.  Had to do that to save program space.


			if (prev_state != 13 && prev_state !=14)		//first time through, set up displays
			{
				//always get here from Program mode with display so don't need to update the top line here again
				send_cmd(SET_CURSOR + 64, TRUE);
				printf_lcd ("E1 U/U D/D Nx/Ex");
				if (state == 13)
				{
					//						printf_lcd ("E1 U/U D/D Nx/Ex");		//increase value --- decrease value --- Next cursor position/Exit back to Program Mode
					prev_state = 13;
					eeaddoffset = 0;			//use first 40 EEPROM memory locations for Program1 data
				};
				if (state == 14)
				{	
					send_cmd(SET_CURSOR + 65, TRUE);
					printf_lcd ("2 ");		//identify this as program 2
					prev_state = 14;
					eeaddoffset = 40;			//use locations 40-80 for Program2 data
				};

				init_state (FALSE, FALSE);	//initialize common code

				step_number = 0;		//initialize the current data with step 0 data to prepare for editing.
				eeaddress = (0 + eeaddoffset);  //Get step 0 data address.  It's 40 locations past the beginning for Program 2. 
				step_time = get_edata (eeaddress);  //get first step_time from memory
				step_temp = get_edata (eeaddress+2);  //first step_temp data is 2 bytes past time data
				send_cmd(SET_CURSOR, TRUE);	//reposition cursor to beginning of top line to print step number
				send_cmd(bin2hex(step_number), FALSE);
				update_display (step_time, 2);	//top line numbers get updated here
				update_display (step_temp, 7);	//units are the same as for Program mode.
				send_cmd (SET_CURSOR, TRUE);	//start with cursor at the "step" position = 0 of 16 total positions in the top LCD row.
				send_cmd (CUR_BLNK_ON, TRUE);		//turn on cursor and blinking
				cur_pos = 0;
			};
			
			if (button_push == TRUE)

			{        
				switch (cur_pos)		//it's more efficient to sort through cursor position first for this mode.
				//Note: ignore cursor positions (cases) which are not either step, time or temp
				
				{
				case 0:		//cursor is at 0 = "step number" position
					
					step_number = mod_digit (step_number, choice);  //choice is 'u' or 'd'. 's' button push is ignored by mod_digit()
					eeaddress = (step_number*4 + eeaddoffset);  //Get step data address.  Program2 is 40 bytes past the step data for Prgm 1 
					step_time = get_edata (eeaddress);  //get step_time from memory
					if (step_time == 0)
					{
						step_number = 0;	//step_num = 0 is the signal for end of program, so cycle back to beginning of the program.
					}								

					step_temp = get_edata (eeaddress+2);  //step_temp data is 2 bytes past time data
					update_display (step_time, 2);		//update the whole display to show the data for the current step number
					update_display (step_temp, 7);
					send_cmd(SET_CURSOR, TRUE);		//put cursor back to the step_number position (0)		
					send_cmd(bin2hex(step_number), FALSE);		//note:cursor automatically shifts right one character after writing!!
					send_cmd (SET_CURSOR, TRUE);		//shift it back!
					send_cmd (CUR_BLNK_ON, TRUE);		//turn on cursor and blinking						
					break;	//end Case 0


				case 5:	// Cursor is in the time position						
					if (choice == 'u' || choice == 'd')		//if Up or Down button has been pushed, increment desired time setting.
					step_time = speed_button (step_time, choice,2);
					eeaddress = (step_number*4 + eeaddoffset);  //Calc step_time data address.  Program 2 is 40 bytes past the same data for Prgm 1 
					put_edata (eeaddress, step_time);  //Save new step_time value to programmed memory
					break;	//end Case 5

				case 10:		// Cursor is in the temp position
					if (choice == 'u' || choice == 'd')		//if Up or Down button has been pushed, increment desired temp setting.
					step_temp = speed_button (step_temp, choice,7);
					eeaddress = (step_number*4 + eeaddoffset + 2);  //Calc step_temp data address. Temp is 2 bytes past time data location.
					put_edata (eeaddress, step_temp);  //Save new step_temp value to programmed memory
					break;	//end Case 10


				};	//end Switch



				
				if (choice == 's')		//if Select button has been pushed.  **don't forget to turn off cursor/blinking upon exit
				{
					
					if (long_push == TRUE)				//Select button, long push - Exit back to Program Run Mode
					{					
						prev_state = state;
						state -= 10;			//either 3 or 4 for Program1 or Program2.
						send_cmd (DISP_ON, TRUE);		//turns off cursor blinking		
					}	
					else		//Select button, short push - mover cursor to time or temp position
					{
						if (cur_pos == 5)
						cur_pos = 10;	//move to "temp" position
						else if (cur_pos == 0)	//if cursor at "step" position,
						cur_pos = 5;	//move to "time" position
						else
						cur_pos = 0;	//else reset to "step" position
						send_cmd (SET_CURSOR + cur_pos, TRUE);	//tell LCD where cursor is
						send_cmd (CUR_BLNK_ON, TRUE);		//turn on cursor and blinking
					};
				};


			};		//end if
			break;	//end of Cases 13 and 14	



			//--------------Setup Mode--------------------

		case 5:		//Setup Mode - for temp_units (C/F), time_units (SSSs/M:SS), relay_LED (O/O), data logging (O/O), start temp (0,30C)
			//will save data to EEPROM starting at address 80.  Data is stored as either 1 (TRUE) or 0 (FALSE) since all options have only
			//2 choices.
			unsigned char value;
			
			if (prev_state != 5)		//first time through, set up display
			{

				send_cmd(SET_CURSOR + 64, TRUE);	//write lower line of lcd					
				printf_lcd ("SU Up  Dn  Nx/Ex");
				prev_state = 5;
				init_state (FALSE, FALSE);	//initialize common code
				set_mode = 1;
				button_push = TRUE;		//force one time through the lcd top line screen display section below
				choice = 'n';			//no real button was pushed for this first time through so take no specific action
			};

			if (button_push == TRUE)  
			{
				switch (choice)
				{
				case 's':		//if Select button has been pushed.							
					if (long_push == TRUE)				//Select button, long push - Exit back to main menu
					state = 0;				//return to main menu
					else		//select button, short push - goto next variable
					{
						set_mode++;
						if (set_mode == 6)
						set_mode = 1;					//cycle back to the beginning	
					}
					break;

				case 'd': case 'u':			//modify variable.  
					eeaddress = (79 + set_mode);  //calc EEPROM address of data.  Use simple method to save space
					value = onboard_eeread(eeaddress);  //get current temp_unit value from EEPROM memory
					if (set_mode == 1)
					{
						if (choice == 'u')
						value += 1;


						if (choice == 'd')
						value -= 1;

						update_display (value, 12);
					}
					if (set_mode == 2 || set_mode ==3) 
					{
						if (value == TRUE)
						value = FALSE;
						else if (value == FALSE)
						value = TRUE;
					}
					if (set_mode == 4 || set_mode == 5)
					value = mod_digit (value, choice);	//keep the speed_button increment range between 1 and 10
					onboard_eewrite (value,eeaddress); //save current value in EEPROM
					break;

				};   //end switch

				//update the Setup display for any changes from button pushes
				eeaddress = (79 + set_mode);  //calc EEPROM address of data.  Use simple method to save space
				value = onboard_eeread(eeaddress);  //get current variable value from EEPROM memory

				send_cmd(SET_CURSOR, TRUE);	//go to beginning of top line of display	
				switch (set_mode)	//update printout for the correct setup variable
				{
				case 1:		//tc = time constant (warm up delay lag)
					tc = value;	//update current time units					
					printf_lcd ("Time Const =   s"); //use string constants - not enough memory for variable storage
					update_display (value, 12);
					break;

				case 2:
					temp_units = value;	//update current temp units
					printf_lcd ("Temp Units  =  ");
					send_cmd (SET_CURSOR + 15, TRUE);
					send_tunits();
					break;
				case 3:
					relay_LED = value;
					printf_lcd ("Relay LED  =");
					send_cmd (SET_CURSOR + 12, TRUE);
					if (value == TRUE)
					printf_lcd (" ON ");
					else printf_lcd (" OFF");
					break;
				case 4:
					t_inc = value;		//stored values range from 0 - 9.  Displayed and used values are 1 - 10.
					printf_lcd ("Tm/tmp Incr =   ");
					send_cmd (SET_CURSOR + 14, TRUE);
					if (value == 9)		
					{
						send_cmd ('1', FALSE);
						send_cmd ('0', FALSE);   //display a hex 10 value since we're only using 1 space
					}							
					else 
					send_cmd (bin2hex (value+1), FALSE); //t_inc range from 1 to 10
					break;
				case 5:
					clock_cal = value;
					printf_lcd ("Clock cal  =    ");
					send_cmd (SET_CURSOR + 14, TRUE);
					if (value == 9)
					{
						send_cmd ('1', FALSE);
						send_cmd ('0', FALSE);   //display a hex 10 value since we're only using 1 space
					}
					else 
					send_cmd (bin2hex (value+1), FALSE);
					break;
				};	//end Switch
			}; //end if
			break;	//end of case 5
			
		default:		//if we get an erroneous state, do nothing.
			break;

		};
		//##if there's been a state change from a button push - do we  need to cycle through again???
		//		}; end if - may not need
	};
}			//end main

//====================================================
//Project Value takes in point and slope information and projects or interpolates an intermediate value
//assumes that x2 is always greater than x1 and both are positive.  y2 may or may not be greater than y1 but is always a positive number.
//It also assumes that x1 is always zero since each step_time restarts the clock.  Also, x3 is between zero and x2 along the x axis.
unsigned int proj_value (signed int x2, signed int x3, signed int y1, signed int y2)
{
	signed int value;
	value = y1 + (x3*(y2 - y1))/x2;
	return value;
}
//______________________________________________________________________________

//converts an integer into 3 ASCII digits and sends them out the serial port followed by a tab.
//The tab seems to be the preferred separation character for reading a file into Excel.
void serial_putnumber (unsigned int value)
{

	convert_int (value);					//put ASCII values into the digit_ series of variables
	serial_putchar(bin2hex(digit_100));		//send 3 digits to the serial port
	serial_putchar(bin2hex (digit_10));
	serial_putchar(bin2hex (digit_1));
	serial_putchar (59);					//separate values with semicolon (ascii 59, or comma 44)
}

//_____________________________________________________
//Sends the temperature units character to the screen. Done to reduce code size. temp_units is TRUE for Celsius.
void send_tunits (void)
{
	if (temp_units == TRUE)
	send_cmd ('C',FALSE);
	else
	send_cmd ('F', FALSE);
}
//______________________________________________
//Sends a byte to the LCD in two nybbles
void send_cmd(unsigned char c, unsigned char cmd)  //if cmd is TRUE, this is a command, else it is an ASCII data char to be displayed
{
	LCD_wait();
	LCD_R_W = 0; //set LCD to write
	LCD_E = 0;	//start with strobe at low setting
	if (cmd == TRUE)		//for a command, set RS=0,  for data, set RS=1.
	LCD_RS = 0; //set LCD to command input mode
	else LCD_RS = 1;   //else set to data mode
	//    send_char (c);  //high nybble first
	D7 = c.7;
	D6 = c.6;
	D5 = c.5;
	D4 = c.4;
	LCD_E = 1; delay_ms(1);  LCD_E = 0; //Toggle the Enable Pin

	D7 = c.3;		//now low nybble
	D6 = c.2;
	D5 = c.1;
	D4 = c.0;
	LCD_E = 1; 
	delay_ms(1); 
	LCD_E = 0;
}	
//____________________________________________________
// (unsigned char value, unsigned char direction) - Increments or decrements a single digit by 1, up or down as selected
//direction is either 'u' or 'd' for up or down.  Will wrap around 9 to 0 and vice-versa.
unsigned char mod_digit (unsigned char value, unsigned char direction)
{
	if (direction == 'u')
	{
		value++;
		if (value >= 10)
		value = 0;
	};
	if (direction == 'd')
	{
		if (value <= 0)
		value = 9;
		else
		value--;
	};
	return value;
}

//____________________________________________________
//Speed_button - increments or decrements a value either singly or repeatedly.  Returns the final value.
unsigned int speed_button (unsigned int value, unsigned char direction, unsigned char disp_pos)
{
	if (direction == 'u')		//if Up button has been pushed, increment desired time/temp setting.
	{ 
		
		//for short push, just go 1 increment. For long push, keep incrementing until button release. 								
		value += (t_inc+1);				//increment time or temp by 1 to 10  seconds/degrees at a time
		if (value > 999)	//put a 3-digit limit on temp setting to fit display width
		value = t_inc+1;		//wrap around zero to save time in setting high values
		update_display (value, disp_pos);
		while (BUTTON_UP == 0)
		{
			value += (t_inc+1);				//increment repeatedly as long as button stays pushed
			if (value > 999)	//put a 3-digit limit on time/temp setting to fit display width
			value = t_inc+1;	//
			update_display (value, disp_pos);
			delay_ms(100); 			//10 numbers per second increment
			//							if (BUTTON_UP == 1)
			//								long_push = FALSE;	//need to detect when the button is stopped being pushed inside this loop!
		};

	}

	if (direction == 'd')		//if Down button has been pushed, decrement desired time/temp setting.
	{ 
		//for short push, just decrement once. For long push, keep decrementing until button release. 
		if (value <= 0)
		value = 999;		//do wrap around zero to save time in getting to high values								
		else value -= (t_inc+1);				//decrement 1 to 10 seconds/degrees at a time
		update_display (value, disp_pos);
		while (BUTTON_DOWN == 0)
		{
			if (value <= 0)
			value = 999;		//wrap around zero								
			else value -= (t_inc+1);				//decrement repeatedly while button stays pushed
			update_display (value, disp_pos);
			delay_ms(100); 			//10 numbers per second increment
		};
	}
	return value;
}

//___________________________________________________ 
//Convert a binary integer value into 3 one-digit characters for either serial port transmit or LCD display
//Answers are returned via the global variables digit_100, etc
void convert_int (unsigned int value)
{
	digit_100 = value / 100;
	value %= 100; //Mod operator to cut off the 100s 
	digit_10 = value / 10;
	value %= 10;
	digit_1 = value;	 
}

//_____________________________________________________	

//Update display - updates 3-digit time or temp given an integer value for the number and an LCD position in the top row to place the first number. 
//the units measure must be added by the calling routine afterward. 
//assumes 3 digit value is in the range 0 - 999, otherwise you get strange characters on the screen 
void update_display (unsigned int value, unsigned char location)
{

	convert_int (value);		//convert integer into 3 separate digits
	send_cmd(SET_CURSOR + location, TRUE);		//note: cursor is automatically shifted to the right after each char write
	if (digit_100 == 0) send_cmd (' ', FALSE);  //suppress leading zeros
	else send_cmd (bin2hex(digit_100), FALSE);
	if (digit_100 == 0 && digit_10 == 0) send_cmd (' ', FALSE);
	else send_cmd (bin2hex(digit_10), FALSE);
	send_cmd (bin2hex(digit_1), FALSE);
}	


//______________________________________________

//Returns the current temperature in Celsius or Farenheit as selected in Setup
#define AVG_AMT 16		//sets # readings to average
unsigned int get_temp(void)
{
	unsigned char x;
	unsigned long temperature = 0;
	unsigned int total = 0;

	//Read Channel 4
	adcon1 = 0b11000000; //extra divide by 2 for clock (bit 6). Right justified (bit 7)    
	adcon0 = 0b01100001; //Select Fosc/16 for 8mhz, and channel RA4/AN4, A/D on


	for(x = 0 ; x < AVG_AMT ; x++)
	{
		delay_ms(1);
		go = 1; //Convert RA4 to digital
		while(go == 1);	//wait for A/D conversion to complete.  Resets "go" to 0 when done.   
		MAKESHORT(temperature, adresl, adresh);		//joins the two bytes into one integer result
		total += temperature; //Add on to the total
	}
	temperature = total / AVG_AMT;	//these convert the reading from millivolts into degrees Celsius
	temperature = temperature * 5000; 	//example:if ADC output is 75, then * 5000 (supply mV) = 375000
	temperature /= 1024; 			//10 bits = 1024 steps.  375000 / 1024 = 366mV
	if (temp_units == FALSE)	//FALSE is for Farenheit units
	{
		temperature *=9;               //this preserves the accuracy for 1 degree F changes
		temperature /= 50;						//convert to Farenheit
		temperature +=32;
	}
	else temperature /= 10;		//TRUE is for Celsius.

	return temperature;
}

//________________________________________
//Init vars and ports
void boot_up(void)
{

	ansel = 0b00010000; //Turn RA4/AN4 to Analog
	
	porta = 0b00000000;  //clear PORT A latch
	trisa = 0b00010000;  //set PORT A pin direction: 0 = Output, 1 = Input (Temp on RA4)
	//initialize globabl variables
	m_seconds = 0;
	total_seconds =0;
	step_seconds =0;


	trisb = 0b10000011; //RB3(LED)is output. RB6(RS)is output. RB4 (relay) is output. Pins 0, 1, & 7 are input from buttons.
	option_reg.7 = 0; //RBPU = 0 - Enable PORTB Internal weak Pull-ups (to drive LED)- needed for sensing button pushes!!!!
	portb = 0b00000000;	// output 0 Volts on RB3 for start of flashing the LED  
	
	init_lcd();		//set up LCD for nibble interface, 2 x 16 lines display.

	send_cmd(SET_CURSOR, TRUE);	//set to beginning of 1st line on LCD	
	printf_lcd("SparkFun & Ryan ");
	
	send_cmd(SET_CURSOR + 64, TRUE);	//64 = 40H is the beginning of the 2nd line on LCD
	printf_lcd(" OvenFlow v1.0  ");
	

	portb = 0b00001000;	//turn on LED on pin RB3
	delay_ms(2000); //wait to show initial display
	portb = 0b00000000;  //turn off LED on pin RB3
	//### serial port init code here
	//Initialize Serial Port
	txsta = 0x24;		//Enable Tx, BRGH=1
	rcsta = 0x90;		//Enable SPort, Enable Rx
	spbrg = BR_CONST;	//9600 Baud with 8MHz INTRC clock and BR_CONST = 51
	//load memory resident constants
	tc = onboard_eeread(80); 
	temp_units = onboard_eeread(81);
	relay_LED = onboard_eeread(82);
	t_inc = onboard_eeread(83);
	clock_cal = onboard_eeread(84);



}
//______________________________________________
//Intializes a few things first time through each change in state.  Saves a few instructions overall.
void init_state (unsigned char timer, unsigned char temp)
{
	timer_on = timer;		//Back to Main Menu, make sure relay and stop watch timer are off
	temp_on = temp;			//keep temp reading up to date
	display_seconds = 0;	//save or reset initial time value of 000s for stop watch function.
	relay_on = FALSE;			//make sure relay and LED are off from any previous mode operation
	total_seconds = 0;		//reset timer to 0.
	step_seconds = 0;		
	
}
//_____________________________________
//Initializes the 4-bit parallel interface to the HD44780
void init_lcd(void)
{
	//Wait for LCD busy bit to clear
	//LCD_wait();
	
	LCD_RS = 0;   //(0 = instruction input; 1 = data input)            
	LCD_R_W = 0;  //(0 = Write to LCD;  1 = read from LCD)
	LCD_E = 0;	  //start with strobe at low setting

	//Tell the LCD we are using 4bit data communication
	//===============================================================
	// wait at least 0.1 secs after power up to insure LCD is ready for input
	//the following sequence of 4 instructions will put the LCD into 4 bit mode.
	//note that only the lower 4 bits in the port are connected to the LCD.
	//The first 3 instructions with delays are actually a manual reset of the entire LCD which
	//duplicates the normal power-up reset function - just in case.  The 4th instruction
	//0x02 actually puts the LCD into 4 bit mode.
	
	
	delay_ms(100);    //wait at least 40 ms per data sheet
	porta = 0b00000011;		//0x3 at bits D4 and D5 on LCD panel
	LCD_E = 1;   LCD_E = 0;
	
	delay_ms(10);	//wait at least 4 ms per data sheet
	porta = 0b00000011;		//0x3
	LCD_E = 1;  LCD_E = 0;

	delay_ms(1);	//wait at least 100 us per data sheet
	send_cmd (0b00110010, TRUE); //send 0x3 a third time (like a reset function) then 0x2 to put lcd in 4 bit mode
	send_cmd(0b00101000, TRUE);	//2-line display, 5x7 matrix
	send_cmd(DISP_ON, TRUE);  	//turn on display but not cursor or blinking
	send_cmd(CLR_DISP, TRUE);	//start with a blank display in case of boot up anomalies on screen

} 

//______________________________________________
//Checks the busy flag and waits until LCD is ready for next command
void LCD_wait(void)
{
	bit i = 1;
	
	trisa = 0b00011111;

	LCD_R_W = 1; //Tell LCD to output status
	LCD_RS = 0;               

	while(i == 1)
	{
		LCD_E = 1; delay_ms(1);
		i = D7; //Read data bit 7 - Busy Flag
		LCD_E = 0;
		
		LCD_E = 1; delay_ms(1); LCD_E = 0; //Toggle E to get the second four bits of the status byte - but we don't care
	}
	
	trisa = 0b00010000; //reset pins to output except RA4; 0 = Output, 1 = Input (TEMP on RA4)
}


//______________________________________________
//Returns an integer(2-byte time or temp) from the onboard eeprom starting at e_address (hi - lo byte order)
unsigned int get_edata (unsigned char e_address)
{
	unsigned char x,y;
	unsigned int value;
	x = onboard_eeread(e_address+1);		//get lo byte first
	y = onboard_eeread(e_address);			//get hi byte
	MAKESHORT (value,x,y);					//converts lo/hi bytes into a 2-byte integer
	return value;
}
//_________________________________________________
//Writes an integer(2-byte time or temp) to the onboard eeprom starting at e_address (hi - lo byte order)
void put_edata (unsigned char e_address, unsigned int value)
{
	unsigned char e_data=0;
	LOBYTE (e_data, value); 
	onboard_eewrite(e_data, e_address+1);
	HIBYTE (e_data, value); 
	onboard_eewrite(e_data, e_address);
}
//________________________________________________
//Reads one byte from the onboard eeprom at e_address up to 256
unsigned char onboard_eeread(unsigned char e_address)

{
	eecon1.7 = 0; //Point to EEPROM Memory

	//Do a read
	eeadr = e_address; //Set the address to read
	eecon1.0 = 1; //Read it
	
	return eedata; //Read that EEPROM value
}    

//______________________________________________
//Write e_data byte to the onboard eeprom at e_address up to 256
//Can write EEPROM in single bytes. No pre-erasure required.
void onboard_eewrite(unsigned char e_data, unsigned char e_address)
{
	bit temp_intcon = intcon.7;
	
	eecon1.7 = 0; //Point to EEPROM data block
	eecon1.4 = 0; //Preform write only  

	pir2.4 = 0; //Clear the write completion intr flag
	eeadr = e_address; //Set the address
	eedata = e_data; //Give it the data
	eecon1.2 = 1; //Enable EE Writes
	intcon.7 = 0; //Disable Intrs
	
	//Specific EEPROM write steps
	
	eecon2 = 0x55;
	eecon2 = 0xAA;
	eecon1.1 = 1;
	//Specific EEPROM write steps

	while(pir2.4 == 0); //Wait for write to complete
	pir2.4 = 0; //Clear the write completion intr flag

	eecon1.2 = 0; //Disable EEPROM Writes

	intcon.7 = temp_intcon; //Set GIE to its original state
}


//______________________________________________
//Returns ASCII Conversion of Decimal/Hex Single Char values (0 - F)

unsigned char bin2hex(char x)
{	
	//return ASCII character equal to x in binary/decimal for printing)	
	if (0 <= x <= 9) return (x + '0');
	if (10 <= x <= 15) return (x-10 + 'A');
	return '?';

}


//______________________________________________
//Prints an ASCII string to the LCD including variables
void printf_lcd(const char *nate) //*nate is input string (already in ASCII) to be printed
{

	unsigned char i, k;
	
	for(i = 0 ; ; i++)
	{

		//       k = nate[i];
		if (nate[i] == '\0') 		//all strings end with a hidden '\0' character
		
		return;

		send_cmd(nate[i], FALSE);
	}    
}




//______________________________________________
//General short delay   *********may be able to delete the library delay function (which has a lot of nops in it).
void delay_ms(unsigned int x)
{
	//Clocks out at 1006us per 1ms
	unsigned char y, z;
	for ( ; x > 0 ; x--)
	for ( y = 0 ; y < 4 ; y++)
	for ( z = 0 ; z < 69 ; z++);
}
//________________________________________________


//Writes Character to Serial Port. 
void serial_putchar(unsigned char s_out)
{
wait:					//pir1.4 is the TXIF flag to indicate that the TXREG transmit buffer register is empty.
	if ( pir1.4 == 0 ) //While transmit buffer register (TXREG) in not empty wait to load next char.
	goto wait;
	//##must break this up into hi and low bytes for sending ##//
	txreg = s_out;		//transmit buffer register is now empty and can be loaded with char "s_out" for transmission.

	//note that transmission occurs immediately from loading the TXREG register.
}
User avatar
Feko
Stariji član
Stariji član
Posts: 4394
Joined: 23-07-2008, 06:18
Location: Hrvatska, Slavonija, Selo moje malo...

Re: BGA Rework station

Post by Feko »

Feko wrote:Aha, nisam skužio. :roll:
... stupanj/dva nekad zna odstupati i to samo u početnom dijelu do 100 stupnjeva jer je grijač dosta "trom" i još isijava toplinu iako je isključen.
Ovo sam krivo naveo...početno grijanje, tražena temperatura je recimo 50, i tu nabije i 60 stupnjeva jer grijač isijava (tu bi vjerojatno došla do izražaja PID kontrola) onda dalje do 100 stupnjeva je puno bolja kontrola možda 3-4 stupnja pokaže više ali svakako se diže tražena temperatura pa je to skroz tu negdje oko tražene temp...a iznad 100 stupnjeva se maltene ponaša kao PID (ledica koja signalizira rad grijača 2-3 sekunde radi pa sekund-dvije ne).
vladav87
Posts: 4
Joined: 25-04-2010, 18:30
Location: Leskovac, Srbija

Re: BGA Rework station

Post by vladav87 »

Bas tako, tu pid nastupa i nema tih temperaturnih @pikova@.
Koliko mogu da vidim, mislim da u tvom kodu nema inplementiran pid.
Ali ako vec dobro radi posao, zasto da ne.. jeftinije je i sigurnije resenje od pc410 i rex c100 sa ali-ja i sl.
Jedino je moja zamerka sto je eto malo prepusteno grejacu na volju da upravlja temp zbog svoje inertnosti, a sto veci grejac to i veci problem.
User avatar
Feko
Stariji član
Stariji član
Posts: 4394
Joined: 23-07-2008, 06:18
Location: Hrvatska, Slavonija, Selo moje malo...

Re: BGA Rework station

Post by Feko »

Da.
Ja sam se kod izrade vodio idejom da iskoristim dijelove na lageru, nešto od PICa ili Atmela koje imam i displej 2x16...u prvotnoj ideji je samo gornji grijač trebao biti upravljan elektronikom a donji samo preko dimmera. No na kraju je odluka pala da pravim duplu elektroniku. Jedino što sam morao kupiti su grijači i relativno skupi AD595.
Ma ja sam prezadovoljan :D Mogu regulirati i visinu donjeg grijača tako da mi stvarno veći od 400w ne treba.
Evo, stroj dobio i nogare :wink:
Image
Image
User avatar
grunf
Pravo uznapredovao :)
Pravo uznapredovao :)
Posts: 286
Joined: 19-04-2008, 14:22
Location: Cvjećara

Re: BGA Rework station

Post by grunf »

nije lose nije lose :D moram i ja svoj LCD separator prirpucni postaviti :D
Post Reply