18
Dec 12

Reflow Toaster Oven Vid4 - Temp. Control and Comm. links

Here I discuss the details of me temperature control algorithm as well as some simple interrupt service routines I use to run my communication links (comm. links back to the GUI).

This service routine is one (some-what confusing) state machine that handles the ramp up to my target temperatures as well as holding those temperatures for a (software set) period of time. Ok, so lets get into it:

Here is the entire interrupt service routine so you can see the whole picture before we start:


#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer0_A0_ISR(void)
{

if(temp_count >= 25){	// enter 2x / sec
	switch(temp_state){
		case 0:
			if(temp_last_val == 0){	// checks to see if this is the first time the ISR has been entered
				temp_last_val = avg_half_temp;	// If so, temp last value is set to current average half temp value.
			}

			// change state
			temp_state = 1;
			break;

		case 1:

			if((final_temp >= (TARGET_TEMP))){	// check to see if we are at the target temp
				temp_state = 2;	// if we are at target temp change state
			}else{
				// increase or decrease duty cycle if we haven't reached our target temp
				if(avg_half_temp > (temp_last_val+1)){	// if temp rises more than 1 degree/sec drop down our pwm value
					decreaseDC(PWM_DISPLACEMENT);		// a little so we don't ramp up too fast.
				}else{
					if(avg_half_temp < (temp_last_val+1)){	// if we aren't above a ramp rate of 1degreeC/sec increast pwm value
						increaseDC(PWM_DISPLACEMENT);
					}else{
						// Hold current PWM Value
					}// end else
				}// end else

				temp_state = 1;	// stay in this state
			}
			break;


		case 2:	// we've reached target temp
			if(shutdown_pwm_cnt < 2){	// wait 1 second to help prvent overshoot
				ShutdownPWM();	// shut down PWM
				shutdown_pwm_cnt++;	// counter for 1 second delay
				temp_state = 2;	// stay in this state
			}else{
				temp_state = 3;		// 1 second expired...go to next state
				shutdown_pwm_cnt = 0;	// reset pwm_count
				check_temp_risetime = final_temp; // store current avg_temp value ---> ???????
			}

			break;


		case 3:
			/* This state monitors the current temp to see if it has started
			 * to decrease.  We compare last temp stored in check_temp_risetime
			 * to current temp, every second,  and if the current temp is lower
			 * than the last temp sample we know our temperature is now decreasing.
			 */
			if(shutdown_pwm_cnt == 2){	// if 1 second wait expired
				if(final_temp > check_temp_risetime){	// if the final temp is above last temp sample stay here and keep checking
					temp_state = 3;	// stay in this state
					check_temp_risetime = final_temp;	// store current sample to compare to next text sample
				}else{	// temp has started decreasing
					check_temp_risetime = 0;	// clear for good measure
					temp_state = 4;	// go to next state
				}
				shutdown_pwm_cnt = 0;	// clear counter
			}else{
				shutdown_pwm_cnt++;		// increase counter
			}

			break;


		case 4:
			/* Here we check to see if we are above or below our target temp and
			 * adjust accordingly.  TARGET_TEMP+1 is used because we get more
			 * accurate behavior.  This is because there is some thermal lag
			 * as the toaster cools down.  We basically get a jump on heating up
			 * to prevent the temp from dropping too much below our target.
			 */
				// Check to see if the final temp is still above the target temp
				if(final_temp > (TARGET_TEMP+1)){
					decreaseDC(1000);	// if so decrease duty cycle and cool off
				}else{
					if(final_temp <= (TARGET_TEMP+1)){	// if final temp is less than target temp + 1 increase duty cycle to heat up
						increaseDC(PWM_DISPLACEMENT);
					}else{
						// Hold current PWM Value
					}// end else
				}// end else


				// Change state
				/* Here we figure out if a new temp has been set
				 * and we need to reset this state machine to
				 * climb up to a new target temp.
				 */
				if(final_temp < (TARGET_TEMP - 10)){
					temp_state = 0;
				}else{
					temp_state = 4;
				}
			break;


	}

	temp_last_val = avg_half_temp;
	temp_count = 0;
	P1OUT ^= (BIT0);
}else{
	temp_count++;
}

		TA0CCR0 += 2500;
		//CheckAdjust_flag = 1;

TA0CCTL0 &=~ CCIFG;
}

The first case,


case 0:
			if(temp_last_val == 0){	// checks to see if this is the first time the ISR has been entered
				temp_last_val = avg_half_temp;	// If so, temp last value is set to current average half temp value.
			}

			// change state
			temp_state = 1;
			break;

is pretty simple. Here i check to see if temp_last_val is 0 which tells me whether the ISR is being entered for the first time. If it is i set temp_last_val to the current temp. This is used to keep track of the last temperature so next time I enter the ISR I can figure out if the temperature is increasing or decreasing. After that I go to state 1.

		case 1:

			if((final_temp >= (TARGET_TEMP))){	// check to see if we are at the target temp
				temp_state = 2;	// if we are at target temp change state
			}else{
				// increase or decrease duty cycle if we haven't reached our target temp
				if(avg_half_temp > (temp_last_val+1)){	// if temp rises more than 1 degree/sec drop down our pwm value
					decreaseDC(PWM_DISPLACEMENT);		// a little so we don't ramp up too fast.
				}else{
					if(avg_half_temp < (temp_last_val+1)){	// if we aren't above a ramp rate of 1degreeC/sec increast pwm value
						increaseDC(PWM_DISPLACEMENT);
					}else{
						// Hold current PWM Value
					}// end else
				}// end else

				temp_state = 1;	// stay in this state
			}
			break;

In case 1 I check to see if I'm at my target temp yet. If not I figure out if my ramp rate is greater than 1 degree C per second. If it isn't than I increase my duty cycle...once my ramp rate gets to it's max value or a rate of 1 degree per second I hold the current PWM duty cycle. I stay in this state until I reach the set target temp.

State 2:

case 2:	// we've reached target temp
			if(shutdown_pwm_cnt < 2){	// wait 1 second to help prvent overshoot
				ShutdownPWM();	// shut down PWM
				shutdown_pwm_cnt++;	// counter for 1 second delay
				temp_state = 2;	// stay in this state
			}else{
				temp_state = 3;		// 1 second expired...go to next state
				shutdown_pwm_cnt = 0;	// reset pwm_count
				check_temp_risetime = final_temp; // store current avg_temp value
			}

			break;

Once I get here the target temp has been reached and we immediately power down for 1 second. I did this because it was a simple software fix for minimizing overshoot. Especially at low temps, the overshoot is not even noticeable. It can be seen a little bit on the demo (video 2) at 170 and 215. The count is 2 because this ISR is entered every 500mS. Once the 1 second window expires I head to the next state, reset my pwm counter and store my current temp in check_temp_risetime. The check rise time variable is important because I use that in the next state to see if the oven temp is still increasing or decreasing.

State 3:

case 3:
			/* This state monitors the current temp to see if it has started
			 * to decrease.  We compare last temp stored in check_temp_risetime
			 * to current temp, every second,  and if the current temp is lower
			 * than the last temp sample we know our temperature is now decreasing.
			 */
			if(shutdown_pwm_cnt == 2){	// if 1 second wait expired
				if(final_temp > check_temp_risetime){	// if the final temp is above last temp sample stay here and keep checking
					temp_state = 3;	// stay in this state
					check_temp_risetime = final_temp;	// store current sample to compare to next text sample
				}else{	// temp has started decreasing
					check_temp_risetime = 0;	// clear for good measure
					temp_state = 4;	// go to next state
				}
				shutdown_pwm_cnt = 0;	// clear counter
			}else{
				shutdown_pwm_cnt++;		// increase counter
			}

			break;

This state waits 1 second and then checks to see what the thermal rate in the toaster is positive or negative. I don't want to start heating up again until the temperature has come down a little from the target temperature overshoot (event if it is small) so while the temp is still increasing (even while the PWM system is off) I wait. Once the temp is decreasing I reset counters and what not, then head to the next state. As you can see, the check_temp_risetime is used to store the previous temp value allowing me to figure out when then temp in the toaster changes from increasing to decreasing.

state 4:

case 4:
			/* Here we check to see if we are above or below our target temp and
			 * adjust accordingly.  TARGET_TEMP+1 is used because we get more
			 * accurate behavior.  This is because there is some thermal lag
			 * as the toaster cools down.  We basically get a jump on heating up
			 * to prevent the temp from dropping too much below our target.
			 */
				// Check to see if the final temp is still above the target temp
				if(final_temp > (TARGET_TEMP+1)){
					decreaseDC(1000);	// if so decrease duty cycle and cool off
				}else{
					if(final_temp <= (TARGET_TEMP+1)){	// if final temp is less than target temp + 1 increase duty cycle to heat up
						increaseDC(PWM_DISPLACEMENT);
					}else{
						// Hold current PWM Value
					}// end else
				}// end else


				// Change state
				/* Here we figure out if a new temp has been set
				 * and we need to reset this state machine to
				 * climb up to a new target temp.
				 */
				if(final_temp < (TARGET_TEMP - 10)){
					temp_state = 0;
				}else{
					temp_state = 4;
				}
			break;

This is the last state in this service routine. Since I'm at my target temp now, I want to hold here until another software section changes my target temp. All I do in the first if is monitor the temp and if it falls below my target+1 (I use plus 1 because the system runs smoother since I get a jump on applying power to the thermal elements early....this allows for smoother regulation because of the thermal lag in the system) and if it does I service the system with the appropriate power delivery to get the temp back up. If my temp is above the target I step the power delivered down until I reach the target temp.

The second if statement monitors target temp and if it gets externally set outside of my threshold (set to a value greater than my target temp + 10) I go back to the top and start over ramping up and holding at the new target temp.

Data Package:
Note-I haven't been able to get the python app packed up as an exe yet because of some technical problems, but when I get this figured out I'll update the package. The source code (python GUI and embedded C) is packed up as the aptana project and CCS studion project respectively. It should be simple to import these and check out the code. If importing doesn't work just check out the source files or create your own project and add the source files only to it.

Download Data Package