' temperature_controller.bas ' Therostatic controlled heat controller with variable duty cycle ' in Bascom AVR includes programmable threashold. Turns on resistive ' heater to obtain and maintain target temperature. ' ' Originally designed to maintain temperature inside a humidity ' calibration temperature for calibration of capactive ' humidity sensors. ' ' As it approaches within 1C of target ramps power down ' to avoid overshoot. The closer it gets to target the ' lower the effective duty_cycle. until within 0.1C it's ' effetive duty cycle is 10% of the normal duty cycle. ' ' Duty cycle is set to allow larger heating elements to be used ' than the power supply could normally handle. ' ' Includes inhibit line which external circuit can drive high ' to prevent activation of heater during sensitive calibration ' readings. ' ' Includes command loop to allow duty_cycle, target_temp and ' device ID to be set from serial port. ' ' See: temperature-controller-low-side-swith-thermister-sensor.dch for schematic. ' ' Notes: ' Tested on ATMega168V-10PU but consumed 43% of available ' program space so should fit easily into a 8K chip. Can fit into ' 4K chip if the com port and command parser are removed. ' ' Draws about 10mA when motor is not active. ' The max through the 5V circuit seems to be 20mA with all LED active. ' Switched upto 10 0.95 amps of 12V for resitive heater and the ' FQP1N60 N channel mosfet was barely warm to the touch. This part ' is rated up to 1.2 amps continous. The part# IRLZ24PBF is ' rated to 17 amps in the same form factor but woudl need larger ' wires. ' ' Note: Variable demand from the pulses during partial duty cycle where ' causing field fluctuations in our power supply that where distorting ' my LED 1080P screen. Added a 10,000uf @ 35V to the power input and ' it eliminated all ripples on the screen. I think this is because it ' was not changing the variable demand on the power supply causing it ' to change it's magnetic fields. ' ' NOTE: Found that the duty cycle calculation was causing the load ' to drop under target by more than 0.1C. Temporarily disabled ' until can think up a better algorithm. ' ' TODO: ' Convert the ERT library to return temp C in a int values multiplied ' by 100 rather than eg: 2045 would indicate 20.45C We spend more time ' converting types from single to float that desired plus floating point ' consumes more memory and is slower. $regfile = "M168def.dat" $crystal = 8000000 $hwstack = 64 ' default use 32 for the hardware stack $swstack = 20 ' default use 10 for the SW stack $framesize = 80 ' default use 40 for the frame spac 'enable interupts Open "comd.1:9600,8,n,1,inverted" For Output As #1 Open "comd.0:9600,8,n,1,inverted" For Input As #2 print #1, "Temperature_controller.bas" ' Port Assignments s1_drive alias portd.3 s1_read_channel alias 0 m1_drive alias portd.4 ' I/O port used to activate Resistive heater Redled Alias Portd.2 inhibit alias pinb.6 ' Port Configuration config s1_drive = output config redled = output config m1_drive = output config inhibit = input ' Constants const loop_cycle_length_us = 5000 const mini_loop_cycle_us = 50 ' Persistent storage for the device Dim ER_empty as eram byte Dim ER_dev_id as eram word Dim ER_target_temp As Eram single Dim ER_duty_cycle as ERAM byte ' 0 to 100 where 0 is mostly off ' DRAM variables Dim Tempc As Single Dim Tempf As Single Dim tfloat as single Dim tdelta as single dim tdeltai as word Dim Tbyte As Byte Dim W As Word Dim tword as word Dim target_temp as single Dim duty_cycle as byte dim tduty as byte dim calc_wait_off as byte dim calc_ramp as byte dim dev_id as word dim pcount as word dim ik as byte dim tcnt as Byte ' TODO: Get rid of any not needed here ' Include necessary Libraries $include ..\lib\lib_adc_units_5V.bas ' provides the ADC constants for conversion to true volt. $include ..\lib\lib_adc_util_head.bas $include ..\lib\lib_ertj0eg_temp_sensor_head.bas 'Check ERAM and initialize if needed' dev_id = ER_dev_id if dev_id = 65535 then ER_dev_id = 1 ER_target_temp = 20.00 ER_empty = 0 ER_duty_cycle = 100 end if ' Copy ERAM to DRAM to allow calculations dev_id = ER_dev_id target_temp = ER_target_temp duty_cycle = ER_duty_cycle gosub calc_ramp_from_duty gosub print_settings pcount = 0 s1_drive = 1 ' Turns on power to sensor ' - - - - - - - - - - - - - - - - - Do ' - - MAIN LOOP - - ' - - - - - - - - - - - - - - - - - redled = 1 ' just so we see something active Tempc = get_ertj0eg_temp(s1_read_channel) redled = 0 gosub print_status gosub check_commands ' Inhibit allows an external input on high to ' prevent acivating motor or heat strip. ' This is because the current flowing at that ' scale can affect calibration readings capacitive ' sensors. if inhibit=1 then m1_drive = 0 waitus loop_cycle_length_us goto main_loop_end endif tduty = duty_cycle if target_temp <= Tempc then ' exceeded target turn everything off tduty = 0 m1_drive = 0 waitus loop_cycle_length_us goto main_loop_end end if 'gosub adjust_duty_cycle ' Now actually controll ' the motor. Note in the duty cycle ' = 100 the motor will be left on ' during the next sensor cycle in all ' other duty cycles the motor is off ' during the sensor cycle. if tduty > 99 then ' motor is all the way on no wait states m1_drive = 1 waitus loop_cycle_length_us elseif tduty < 1 then ' motor is all the way off no on states m1_drive = 0 waitus loop_cycle_length_us else ' depending on duty cycle leave motor on ' for part of the time and off for the remainder. ' higher duty cycle means more on cycles. m1_drive = 1 ' activate our motor for tcnt = 1 to tduty ' force our on delay waitus mini_loop_cycle_us next tcnt calc_wait_off = 100 - tduty ' calculated time if calc_wait_off > 0 then m1_drive = 0 ' turn off motor and wait a for tcnt = 1 to calc_wait_off ' force our off delay waitus mini_loop_cycle_us next tcnt end if end if main_loop_end: ' branch here when decsion on motor is already made Loop End ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - adjust_duty_cycle: ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - ' Reduce the duty cycle as we approach our target temp ' so we do not run as high of a risk of overshoot. The closer we ' get to our duty cycle within 1C the more we ramp back power ' to prevent overshoot. 'print #1, "calc_ramp="; calc_ramp;" duty_cycle="; duty_cycle; 'print #1, " tempC="; tempc; " tdelta="; tdelta tdelta = target_temp - tempc tdelta = tdelta * 10 ' increase scale to retain 1/100 scale tdelta = abs(tdelta) ' force to be positive tdeltai = int(tdelta) ' force conversion to int value if tdelta < 1.0 then tword = 10 - tdeltai ' converts 0.2 type delta to 0.8 tword = tword * calc_ramp ' now we multiply our ramp_decrease if duty_cycle > tword then ' if would not result in negative tduty = duty_cycle - tword ' then subtract from normal duty cycle else ' otherwise tduty = duty_cycle / 10 ' simply use 10% of the current duty_cycle ' could result in 0 when duty cycle is less than ' 10%. end if if tduty < 1 then tduty = 1 ' 1% minimum duty cycle end if 'print #1, " tduty="; tduty return ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - calc_ramp_from_duty: ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - calc_ramp = duty_cycle / 20 ' switched from /10 to /20 to slow the ' ramp cycle. if calc_ramp < 2 then calc_ramp = 2 return ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - print_status: ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - pcount = pcount + 1 if pcount > 500 then if inhibit = 1 then print #1, "INHIBIT "; end if print #1, " "; Fusing(Tempc , "-##.#" ); "C" pcount = 0 end if return ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - check_commands: ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - ik = inkey(#2) IF ik > 0 then redled = 1 m1_drive = 0 gosub process_commands redled = 0 end IF return dim cmd as string*2 dim aug as string*6 dim naug as word dim in_str as string*10 dim in_len as Byte dim p_len as byte ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - process_commands: ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - ' arrived here because we think ' we found something in the inbound ' serial array. Can not read using ' inkey because seems to have synchronization ' problems but can read in input lines m1_drive = 0 gosub print_settings print #1,"COMMANDS" print #1, " D=90 - Set duty cycle to 1..100" print #1, " T=28 - Set target temp to 0..125C" print #1, " I=229- Set Dev ID to 1..65,534" print #1, " Q - Exit command mode." do redled = 1 print #1, "input:"; input #2, in_str redled = 0 in_str = trim(in_str) in_str = ucase(in_str) 'print #1, "S="; in_str if in_str = "Q" then print #1, "exit commands" exit DO end if in_len = len(in_str) if in_len > 2 then p_len = in_len - 2 cmd = left(in_str,2) aug = mid(in_str,3,p_len) if len(aug) > 0 then naug = val(aug) gosub process_command else naug = 0 end IF 'print #1, " cmd="; cmd; " aug="; aug else print #1, "" end if loop return ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - process_command: ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - 'arrived here because it looks like 'we have a valid command to process if cmd = "T=" then if naug >= 0 and naug <= 250 then tfloat = val(aug) ' reconvert to get single if tfloat <> target_temp then 'print #1, "tsingle="; tfloat target_temp = tfloat tfloat = ER_target_temp if tfloat <> target_temp then ER_target_temp = target_temp end IF gosub print_settings end IF end IF elseif cmd = "D=" then if naug > 0 and naug <100 and naug <> duty_cycle then duty_cycle = naug tbyte = ER_duty_cycle if duty_cycle <> tbyte then ER_duty_cycle = tbyte end if gosub calc_ramp_from_duty gosub print_settings end if elseif cmd = "I=" then if naug > 0 and naug < 65535 and naug <> dev_id then dev_id = naug ER_dev_id = naug end if gosub print_settings else print #1, "cmd not recognized" endif return ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - print_settings: ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - print #1, "" print #1, "dev_id="; dev_id print #1, "target_temp="; Fusing(target_temp , "-##.##" ); "C" print #1, "duty_cycle="; duty_cycle return $include ..\lib\lib_adc_util.bas $include ..\lib\lib_ertj0eg_temp_sensor.bas