;=============================================================================== ; Title: Soldering Station heater library using PWM. ; ; Author: Rob Jansen, Copyright (c) 2021..2021, all rights reserved. ; ; Revisions ; --------- ; 2021-05-06 : Initial version. ; ; Description: Controls the heating of the soldering station. Heatings is done ; autonomous and interrupt based using timer 2 and Pulse Width ; Modulation using the CCP2 output assigned to RA5. ; A LED will blink when the target temperature is reached. ; ; Sources: - ; ; --------------------------------- Pins --------------------------------------- ; Some constants we need control the pins. const bit LED_ON = HIGH const bit LED_OFF = LOW ; Heating LED. alias led is pin_A1 led = LED_OFF pin_A1_direction = OUTPUT ; The heater is directly controlled by the hardware PWM output. This is pin ; CCP2 which will be reassigned to RA5. pin_CCP2_RA5_direction = OUTPUT ; Duty cycle alias. alias pwm_duty_cycle is CCPR2L -- ================== Constant and variable declarations ======================= ; The heater needs some minimal bias for reaching the set temperature. const HEATER_BIAS = 1 ; Degrees Celsius ; LED indicator counter for getting a stable LED blink cycle. const byte LED_INDICATOR_MAX = 250 ; LED blink frequency when heating temperature is reached. const byte HEATER_LED_BLINK = 100 ; 100 * 10 ms is 1 second on and 1 second off. ; Global variables. var word heater_target_temperature var word heater_current_temperature var byte led_indicator_timer var byte led_blink_timer var bit heater_enabled var bit heater_is_ready -- ========================= Functions and Procedures ========================== ; Initializes the heater driver. This heater works interrupt based and uses a ; PWM frequencey of 500 Hz. The heater is not active procedure heater_init() Is led_indicator_timer = 0 led_blink_timer = HEATER_LED_BLINK heater_enabled = FALSE heater_is_ready = FALSE led = LED_OFF ; Set heater temperatures so that it prevents the heater from turning on. heater_target_temperature = 0 heater_current_temperature = 0 ; We will use Timer2 as heater driver. T2CON_TMR2ON = FALSE ; Timer 2 off PIR1_TMR2IF = FALSE PIE1_TMR2IE = FALSE ; For PWM PR2 holds the Timer Period using the following formula: ; Period = (PR2 + 1) * 4 * Tosc * Timer2 prescale value ; where Tosc = 1/Fosc and Fosc = 32.000.000 Hz ; Note that for PWM the postscaling has no effect on the PWM only on ; the timer interrupt! ; Setting the prescaler at 64 and a PR4 of 249 gives for PWM: ; (249 + 1) * 4 * 1/32.000.000 * 64 = 2 ms Period Cycle (about 500 Hz) ; For the timer interrupt we use a postscale of 5 so the interrupt ; occurs every 10 ms. T2CON_T2CKPS = 0b11 ; Prescaler divide by 64 T2CON_T2OUTPS = 0b0100 ; Postscaler is 1:5 used for timer interrupt. PR2 = 249 TMR2 = 0 ; Set CCP2CON to single output PWM Mode CCP2CON_P2M = 0b00 ; Select PWM Mode, signal active high. CCP2CON_CCP2M = 0b1100 ; The Pulse Width uses the value in CCPR2L and two LSB's in DC1B to get 10 bit ; resolution. We ignore these two LSB's since their significance is small ; (10 bits are needed since it runs 4 times as fast, see formula below) ; The Pulse Width becomes: CCPR2L+DC1B * Tosc * Timer2 prescale value ; Set the initial value of the Duty cycle in CCPR2L to 0 (heater inactive). pwm_duty_cycle = 0 ; Select CCP2 to use Timer 2. CCPTMRS_C2TSEL = 0b00 ; Re-assign CCP2 to RA5. APFCON1_CCP2SEL = TRUE PIE1_TMR2IE = FALSE ; Disble Timer 2 interrupt. PIR1_TMR2IF = FALSE ; Clear Timer 2 interrupt flag. T2CON_TMR2ON = TRUE ; Start Timer 2 and so the PWM. end procedure ; Heater control is called from the timer 2 interrupt every 10 ms. procedure heater_control() is pragma interrupt if PIR1_TMR2IF & PIE1_TMR2IE then PIR1_TMR2IF = FALSE ; The PWM operation is done in hardware. Check if we need to adjust ; the duty cycle of the PWM. if heater_current_temperature > heater_target_temperature then ; Too hot, switch off. pwm_duty_cycle = 0 heater_is_ready = FALSE ; The led blinking - if active - is switched off after some time. if led_indicator_timer != 0 then led_indicator_timer = led_indicator_timer - 1 else led = LED_OFF end if elsif (heater_target_temperature - heater_current_temperature) > HEATER_BIAS then ; We put the heater at maximum as long as the temperature difference is more ; than the heater bias. pwm_duty_cycle = 255 heater_is_ready = FALSE ; The led blinking - if active - is switched off after some time. if led_indicator_timer != 0 then led_indicator_timer = led_indicator_timer - 1 else led = LED_ON end if else ; Target temperature reached, keep it warm. pwm_duty_cycle = HEATER_BIAS heater_is_ready = TRUE led_indicator_timer = LED_INDICATOR_MAX end if ; If we are heating up we put the led on. If we are close to the target temperature ; the LED will blink. The LED will be off when the current temperature is higher ; than the target temperature. To get a stable blink we use an indicator timer. if led_indicator_timer != 0 then if led_blink_timer != 0 then led_blink_timer = led_blink_timer - 1 else led_blink_timer = HEATER_LED_BLINK led = !led end if end if end if end procedure ; Enable the heater functionality. procedure heater_enable() is PIE1_TMR2IE = TRUE ; Enable Timer 2 interrupt. end procedure ; Disable the heater functionality and disable heater and LED. procedure heater_disable() is PIE1_TMR2IE = FALSE ; Disable Timer 2 interrupt. heater_is_ready = FALSE pwm_duty_cycle = 0 ; Stops the heater. led_indicator_timer = 0 led = LED_OFF end procedure ; Set the target temperature of the heater. procedure set_heater_target_temperature(word in temperature) is var bit heater_enable ; Disable the heater interrupt temporary to prevent errors. heater_enable = PIE1_TMR2IE PIE1_TMR2IE = FALSE heater_target_temperature = temperature PIE1_TMR2IE = heater_enable end procedure ; Set the current temperature of the heater. procedure set_heater_current_temperature(word in temperature) is var bit heater_enable ; Disable the heater interrupt temporary to prevent errors. heater_enable = PIE1_TMR2IE PIE1_TMR2IE = FALSE heater_current_temperature = temperature PIE1_TMR2IE = heater_enable end procedure ; Returns TRUE if the heater is warming or cooling down. function heater_is_busy() return bit is var bit heater_enable var bit return_value ; Disable the heater interrupt temporary to prevent errors. heater_enable = PIE1_TMR2IE PIE1_TMR2IE = FALSE return_value = !heater_is_ready PIE1_TMR2IE = heater_enable return return_value end function ; Returns TRUE if the heater reached his target temperature. function heater_on_target() return bit is var bit heater_enable var bit return_value ; Disable the heater interrupt temporary to prevent errors. heater_enable = PIE1_TMR2IE PIE1_TMR2IE = FALSE return_value = led_indicator_timer != 0 ; return_value = led_indicator_timer < LED_START_INDICATOR_COUNT PIE1_TMR2IE = heater_enable return return_value end function