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 Zverka
BGA Rework station
Moderators: pedja089, stojke369, [eDo], trax
- Feko
- Stariji član
- Posts: 4394
- Joined: 23-07-2008, 06:18
- Location: Hrvatska, Slavonija, Selo moje malo...
Re: BGA Rework station
Aha, nisam skužio.
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...
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.
}
- Feko
- Stariji član
- Posts: 4394
- Joined: 23-07-2008, 06:18
- Location: Hrvatska, Slavonija, Selo moje malo...
Re: BGA Rework station
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).Feko wrote:Aha, nisam skužio.
... 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.
Re: BGA Rework station
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.
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.
- Feko
- Stariji član
- Posts: 4394
- Joined: 23-07-2008, 06:18
- Location: Hrvatska, Slavonija, Selo moje malo...
Re: BGA Rework station
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 Mogu regulirati i visinu donjeg grijača tako da mi stvarno veći od 400w ne treba.
Evo, stroj dobio i nogare
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 Mogu regulirati i visinu donjeg grijača tako da mi stvarno veći od 400w ne treba.
Evo, stroj dobio i nogare
Re: BGA Rework station
nije lose nije lose moram i ja svoj LCD separator prirpucni postaviti