PDA

View Full Version : TEC controller



NickMu
- 18th August 2016, 15:58
Hi All,

I’ve been away from this forum as an active poster for some time now due to some health and work problems.
I’m slowly going back in my old routines and I got a small challenging project involving the resurrection of a nice, two compartment wine cooler (about 21 bottles).
My preliminary troubleshooting shows that both TEC controller boards are no longer doing their job and there are a lot of missing segments in the LED display. The TECs are still performing well (using a PC PSU I had them running for less than 30 minutes and the temperature inside went down to less than 50⁰F with a 75⁰F ambient). I’m planning on using the same PSU for my rebuild. I’m using the 12V outputs from it and each TEC draws about 5A.
I decided, just to impress the wife, to replace all the electronics with my own. To keep it challenging I’m planning on doing it with as little part orders and use / recycle some of the junk I almost forgot I had.
There will be a 4x20 LCD display with or without serial backpack (“in stock”). I have different samples of different calibers of 16F series PICs on prototyping boards ready to re-wire and program.
I will be using two temperature sensors / thermostats (DS1820 / DS1821 type (“in stock”)). One sensor will be on the TEC cold side heatsink monitoring the possible TEC freeze and the other will help maintain the set temperature in each chamber.
I can easily write PBP code to drive the display, set / display temperature and so on but the only thing I’m missing is a simple, reliable, safe and efficient TEC controller.
My goal is to create a standalone module, linear or PWM (preferably), that will deal with the TEC under a master controller. I only need the cooling so this should simplify the device a little.
Some Google search got me few results but most of the good ones are only working in up to 5V.
The bottom line here is: can someone recommend the best approach to this and eventually send me some information on an actually proven working controller?

In mean time I will start exploring the PWM avenue using my learnings in this thread:
http://www.picbasic.co.uk/forum/showthread.php?t=18317 (http://www.picbasic.co.uk/forum/showthread.php?t=18317)

As soon as I get my setup and some results I will return with a report.

Thank you for your attention.

Nick

NickMu
- 24th August 2016, 14:37
Hi All,

After gathering all the stock ingredients I ventured to prototype something to get me started (see attached schematic). Most of the stuff was already made from older projects so I just needed some re-wiring.
To my surprise, at the first try, I was able to drive a TEC with an 80KHz output from an older PIC 12F683 board. I will have to look back to find the code that runs it but it basically outputs 80KHz of about 40% duty cycle PWM for about 3 seconds and about 75% for 3 seconds.
The output voltage to the TEC goes from 5.5V to 8.5V with a current draw of 1A and respectively 3A.
Checking with an old scope the TEC voltage is pretty clean.
I separately built a refrigeration box out of ¾” Styrofoam and I was able to maintain a bottle of wine at about 45⁰F for two days running the TEC under 5.5 V from a separate PSU. I will do some more testing like this to figure out the minimum voltage that is needed to maintain a decent temperature in the cooler without having to turn the TEC OFF. I will do so after replacing the first wine bottle which became “a casualty of the project” and I opened it to celebrate the first successful test. This is becoming a fun project. LOL
Next step will be to fine tune the switching frequency for efficiency and the needed PWM range to get my lowest and highest output.

I will keep you posted and I might come back with some clarifications and maybe questions. I will also post the code that I’m running after I get it optimized.

Nick

NickMu
- 24th August 2016, 17:06
I trying to control PWM output on a PIC12F683 and I wrote the following code for a quick test:


@ device pic12F683, intrc_osc_noclkout, wdt_on, mclr_off, protect_off

OSCCON=%01110111
DEFINE OSC 8

ADCON0=%00001101
ANSEL = %00111000
TRISIO=%00011000
CMCON0=%00000111

i var byte
pwm_out var GPIO.2

CCP1CON = %00001100
PR2 = 15
CCPR1L = 0
CCP1CON.5 = 0
CCP1CON.4 =0


Pause 200
T2CON.2 = 1
Goto main
Main:
for i=8 to 14
CCPR1L = i
CCP1CON.5=0
CCP1CON.4=0
pause 250
CCPR1L = i
CCP1CON.5=0
CCP1CON.4=1
pause 250
CCPR1L = i
CCP1CON.5=1
CCP1CON.4=0
pause 250
CCPR1L = i
CCP1CON.5=1
CCP1CON.4=1
pause 250
next
for i=14 to 8 step -1
CCPR1L = i
CCP1CON.5=1
CCP1CON.4=1
pause 250
CCPR1L = i
CCP1CON.5=1
CCP1CON.4=0
pause 250
CCPR1L = i
CCP1CON.5=0
CCP1CON.4=1
pause 250
CCPR1L = i
CCP1CON.5=0
CCP1CON.4=0
pause 250
next
CCPR1L=0
Pause 5000
Goto Main


What I’m trying to do is use the finest possible steps in controlling the PWM.
Please ignore the low case spelling on some lines of the code. I did not realized I have the auto capitalize feature enabled and I became lazy. The code works since the Micro Code Studio fixes them for me. I know is a bad and risky habit and I will try to fix it.
The code rumps up and down from 5.5V output to 9.5V in 28 steps and after 5 seconds stop repeats the cycle which should take 14 seconds without the pause. It is just to easily explore all possibilities.

The question is: Can I somehow combine the CCPR1L and CCP1CON<5:4> in one single variable so it is easier to handle in the FOR / NEXT loop and make the code more elegant?

Any input will be greatly appreciated.

Nick

Dave
- 25th August 2016, 11:57
This is how I do it for a word sized variable that gives you 10 bits of resolution:

CCPF = CCPF MIN MAXPWM
CCPR1L = CCPF >> 2 'UPDATE PWM1 UPPER 8 BITS
CCP1CON = $0C | ((CCPF & $3) << 4) 'UPDATE PWM1 LOWER 2 BITS

pedja089
- 25th August 2016, 12:09
For CCP1CON bits this is much faster
CCP1CON.5=CCPF.1
CCP1CON.4=CCPF.0

NickMu
- 25th August 2016, 14:16
Thank you Dave and Pedja089 for your input.
I will try implementing your suggestions and report back the results. Unfortunately I just started work so it has to wait for a few good hours.
I was thinking to try something like this subroutine but it was too late last night:



Set_pwm_pars:
CCP1CON.4 = Pwm_pars.0
CCP1CON.5 = Pwm_pars.1
CCPR1L = Pwm_pars >> 2
RETURN
Main:
FOR i = min_pwm to max_pwm
Pwm_pars = i
GOSUB Set_pwm_pars
PAUSE 250
NEXT
PAUSE 5000
GOTO Main


My goal for now is to get enough steps with about 0.25V between them so the TEC will not complain.
BTW does anyone know if slow, occasional (every few minutes) 0.25V steps combined with heavy capacitor filtering will affect the TEC?

Thanks again for your input.

Nick

NickMu
- 25th August 2016, 22:48
Just a quick update: I’ve tried all three suggested methods and they all work as expected.
I will do some more hardware changes, now that I have a stable test code. I will experiment especially with frequency changes and inductors values. I’m trying to get as much efficiency as possible.
What is encouraging though is that running the prototype for long time does not generate heat at all. The main power components are cold and the current draw (from the 12V source) swings between 1A and 2.5A.
I will post the latest working code as soon as I get it cleaned up.

Nick

NickMu
- 26th August 2016, 14:44
This is the working code. It steps up through 21 steps of PWM (from 30 to 50) and then steps down (from 50 to 20). The result is an output voltage between 5.5V and 8.5V (the numbers are rounded up). This is design for testing the flexibility of the system only. Final code will implement a temperature controlling algorithm. BTW I switched to PIC16F819 since this is the PIC I'm going to use for this task.

Code based on Dave's input (183):


@__config_device_pic16f819
@__config_wdt_off
@__config_hs_osc
@__config_pwrt_on
@__config_mclr_off
@__config_lvp_off
@__config_protect_on
@__config_CCP1_RB2

OSCCON=%01110111
DEFINE OSC 8

i var word
pwm_out var PORTB.2
pwm_pars var word
min_pwm con 30
max_pwm con 50
CCP1CON = %00001100
PR2 = 15
CCPR1L = 0
CCP1CON.5 = 0
CCP1CON.4 =0

High pwm_out
Pause 500
Low pwm_out
T2CON.2 = 1
goto Main
set_pwm_pars:
CCPR1L = pwm_pars >> 2
CCP1CON=$0C|((pwm_pars&$3)<<4)
RETURN
Main:
for i=min_pwm to max_pwm
pwm_pars = i
gosub set_pwm_pars
pause 250
next
for i=max_pwm to min_pwm step -1
pwm_pars = i
gosub set_pwm_pars
pause 250
next
Pause 5000
Goto Main


With the same main loop I changed the subroutine set_pwm_pars based on Pedja089 input (163):



set_pwm_pars:
CCP1CON.5 = pwm_pars.1
CCP1CON.4 = pwm_pars.0
CCPR1L = pwm_pars >> 2
RETURN


Finally my simplified version for the subroutine (187):



set_pwm_pars:
CCP1CON.5 = pwm_pars.1
CCP1CON.4 = pwm_pars.0
CCPR1L = pwm_pars / 4
RETURN


The output from all is the same (as expected). Cannot comment on execution speed and for my purposes it doesn’t really matter. I will only set a step, based on the temperature control algorithm, and sit on it for a while. I’m sure that the wine will not mind a few microseconds shifted response.
The one difference between the three versions is the code space required. The numbers in parentheses are showing that.

So far I’m happy with my results.
I will work on the hardware for the next few days and start working on the temperature control algorithm after that.
Before I start this new task I’m asking all the people in this forum that might have some experience in this field: Am I on the right track? Is there a better way to do it?
I love its simplicity and the fact that I fully understand how it works but in the same time I hate to find it out that is wrong after I already spent a lot of time on it.

As always looking forward for input.

Nick

Dave
- 26th August 2016, 15:08
The only problem I can see is that you are assuming the other bits in the CCP1CON are always set correctly? For the sacrifice of a few extra instructions I would prefer to have ALL register bits set as in: CCP1CON=$0C|((pwm_pars&$3)<<4)

NickMu
- 26th August 2016, 15:51
Hi Dave,

You are making a good point. It comes with lock of experience on my side.
As I said before I have two good qualities: learning extremely slow and, in contrast, forgetting extremely fast.
At this point I’m not looking to save program space so I guess playing safe will be a good idea.
Any input on the hardware side?

Nick

NickMu
- 29th August 2016, 21:45
I got all the hardware working and wrote a simple temperature control program. Nothing fancy.
The code drives the TEC with different power output levels depending on how far the real temperature is from the set temperature.
I don’t have yet a mechanism of setting the target temperature but this is just to prove that the system works.
Since the major hardware is in place and the cooler is basically functional I will settle for now for a fixed, preprogrammed target temperature.
The ICSP connector for the 16F819 is accessible and I will play with the code to get more features.
I’m forced to take a break from the project but I will get back to it when time permits.
For now 62⁰F should be fine on most of the wines I drink so I’ll make it default.
Anyway it has been a fun weekend project and what makes it special is the fact that I did not have to order any parts. Recycling old junk makes me feel really good.
For all interested below is the simple control code, not fully tested since I just finished it last night. There are few things in the code that are not fully implemented (serial output will be used for a LCD display, hysteresis, id (to diferenciate between the two chambers). These variables will be used for future development when time comes.



@__config_device_pic16f819
@__config_wdt_off
@__config_hs_osc
@__config_pwrt_on
@__config_mclr_off
@__config_lvp_off
@__config_protect_on

DEFINE OSC 8

DEFINE DEBUG_REG PORTB
DEFINE DEBUGIN_REG PORTB
DEFINE DEBUG_BIT 6
DEFINE DEBUGIN_BIT 7
DEFINE DEBUG_MODE 1
DEFINE DEBUG_BAUD 38400

DEFINE ADC_BITS 8
DEFINE ADC_CLOCK 3
DEFINE ADC_SAMPLEUS 50
ADCON1=%01001110

pwm_out var PORTB.2

adval var word
val_1 var byte
val_2 var byte
val_3 var byte
new var word
act_temp var byte
set_temp var byte
new_temp var byte
temp_index var byte
i var word
pwm_pars var word
id var byte

start_ch con 126
end_sym con 251
samples con 39
divider con 20
hysteresis con 2
min_pwm con 30
max_pwm con 50

CCP1CON = %00001100
PR2 = 15
CCPR1L = 0
CCP1CON.5 = 0
CCP1CON.4 =0

High pwm_out
Pause 500
Low pwm_out
T2CON.2 = 1
set_temp=62
goto main

send_out:
debug start_ch,id,val_1,val_2,val_3,end_sym
return

set_pwm_pars:
CCPR1L = pwm_pars >> 2
CCP1CON=$0C|((pwm_pars&$3)<<4)
return

read_act_temp:
new = 0
For temp_index = 1 TO samples
ADCIN 0,adval
new = new + adval
Next
act_temp=new/divider
If act_temp <= set_temp then
pwm_pars = 0
else
pwm_pars = min_pwm+(act_temp - set_temp)
if pwm_pars>max_pwm then pwm_pars=max_pwm
endif
gosub set_pwm_pars
val_1=act_temp
val_2=pwm_pars
val_3=set_temp
Return

Main:
gosub read_act_temp
gosub send_out
pause 1500
goto Main
end


Regards,

Nick

Dave
- 30th August 2016, 11:41
Nick, The statement:

if pwm_pars>max_pwm then pwm_pars=max_pwm
endif

can be made as such: pwm_pars = pwm_pars min max_pwm

That way no if then is needed.

NickMu
- 30th August 2016, 13:38
Hi David,

Thank you for the hint. It will be first on my “To do” list.
I noticed it in your first post and mark it as a good candidate for the time when I will do code optimization. I just slapped the code together to see how the system works in a dynamic scenario and prove the concept. The way I thought it is that when the actual temperature is way higher than set temperature the system will push maximum power. The closer the two temperatures are the system reduces its output. Only when the actual temperature is lower than set temperature the TEC will be turned OFF. This reduces the number of ON / OFF cycles which I understand the TECs are not happy with.
I am open to any other suggestions that will improve the project.

Nick

NickMu
- 7th September 2016, 19:26
I’m in between two trips and had a little time to add a new feature to the code: hysteresis.
Adding few more variables and a call to te_control subroutine (code below) in the Main loop I can smoothly control the temperature with a ±2⁰F (good enough for me now).
One added bonus is that when restarting the TE after it was OFF I’m starting it with a safe voltage level output (start_pwm = 40) that will make sure the fans will not hesitate starting. After one cycle the regular rule, drive the TE as hard as the difference (act_temp – set_temp) is, kicks back in place. Short bench testing shows that it works like it should. When returning from next trip I will do more testing and also play with the values of min_pwm, start_pwm and max_pwm in order to get the desired temperature range with maximum efficiency and minimum ON / OFF TEC cycles.



te_control:
If act_temp < (set_temp-hysteresis) then
pwm_pars =0
pwm_flag=1
endif

If act_temp > (set_temp + hysteresis) and pwm_flag =1 then
pwm_pars = start_pwm
pwm_flag=0
goto jump
endif

If act_temp > set_temp and pwm_flag =0 then
pwm_pars = min_pwm + (act_temp - set_temp)
endif
jump:
gosub set_pwm_pars
Return


My questions are:
Is there a more elegant way of doing it?
Am I covering all logical possibilities or some more code is needed?

Any input will be appreciated.

Nick

richard
- 8th September 2016, 02:57
according to this a pid style of control may extend the life of your peltier device, thermal cycling is detrimental

https://thermal.ferrotec.com/technology/thermoelectric-reference-guide/thermalref10/

NickMu
- 8th September 2016, 15:14
Hi Richard,

Thank you for the link. I’ve been reading a lot about TEC controllers lately but did not find this one.
I know my goal might be too ambitious but so far the results are encouraging.
All I did so far is trying to create a poor man’s PID system at its simplest form.
Based on the temperature difference (act_temp – set_temp) I change the output level of my PWM.
It might be a little abrupt in the beginning but after the temperature stabilizes there will be only small steps in the voltage output (about 0.15V per step). That puts me lower than maximum admitted ripple for the TEC (5 – 10%) and it is only an occasional change not continuous like a non-filtered AC to DC voltage.
If I can find the right range (more testing is needed and unfortunately I must travel a lot for the next few weeks) I might not have to turn the TEC OFF at all and only lower the output voltage to maintain the temperature, which in turn keeps the TEC happy.
Since I’m having the fans paralleled with the TEC I must keep this min_pwm adove a certain value. Some brushless fan manufacturers recommend 5V as minimum running voltage and 7V as minimum starting voltage. That is the reason why I’m starting the system with a kick.
Not sure if this is the best approach reason for which I’m posting here.

Thank you for the input. I hope others will be interested in this project and bring new ideas to the table.

Nick

pedja089
- 8th September 2016, 15:28
Why not add another sensor on hot side of TEC to control fans independently from TEC?

NickMu
- 8th September 2016, 16:25
Hi Pedja089,

This might be one of the improvements that I thought about too, in case this first minimal approach will not work.
I’m also planning on adding a temperature sensor / thermostat on the cold side (if my testing finds it necessary) to prevent freezing of the TEC. I have some trusty DS1821 that can be programmed for different trigger temperatures.
I wanted to start simple which is the reason why I’m using the same output for fans and TEC for now.
The nice part is that the cooler is back together, I have access to all wiring and I can do all the work /changes externally.

Thank you for interest and support.

Nick

NickMu
- 16th September 2016, 21:45
This is the latest addition to the TEC controller: One pot on ADC1 sets the temperature.
I’ve done it such way so with a linear pot the target temperature can be set between 40 and 65 degrees F.



'Last release 9_16_16 Implements pot for setting set_temp
@__config_device_pic16f819
@__config_wdt_off
@__config_hs_osc
@__config_pwrt_on
@__config_mclr_off
@__config_lvp_off
@__config_protect_on

DEFINE OSC 8

DEFINE DEBUG_REG PORTB
DEFINE DEBUGIN_REG PORTB
DEFINE DEBUG_BIT 6
DEFINE DEBUGIN_BIT 7
DEFINE DEBUG_MODE 1
DEFINE DEBUG_BAUD 38400

DEFINE ADC_BITS 8
DEFINE ADC_CLOCK 3
DEFINE ADC_SAMPLEUS 50
ADCON1=%01001110

pwm_out var PORTB.2

adval var word
val_1 var byte
val_2 var byte
val_3 var byte
new var word
act_temp var byte
set_temp var byte
new_temp var byte
temp_index var byte
i var word
pwm_pars var word
id var byte
pwm_flag var bit

start_ch con 126
end_sym con 251
samples con 39
divider con 20
hysteresis con 1
min_pwm con 30
max_pwm con 50
full_pwm con 40

CCP1CON = %00001100
PR2 = 15
CCPR1L = 0
CCP1CON.5 = 0
CCP1CON.4 =0

High pwm_out
Pause 500
Low pwm_out
T2CON.2 = 1
' set_temp=69
id="1"
pwm_flag=0
goto main

send_out:
debug start_ch,id,val_1,val_2,val_3,end_sym
return

set_pwm_pars:
CCPR1L = pwm_pars >> 2
CCP1CON=$0C|((pwm_pars&$3)<<4)
return

read_act_temp:
new = 0
For temp_index = 1 TO samples
ADCIN 0,adval
new = new + adval
Next
act_temp=new/divider
return
read_set_temp:
new = 0
For temp_index = 1 TO 9
ADCIN 1,adval
new = new + adval
Next
set_temp= 40 + new/100 'keeps set_temp between 40 and 65 deg. F
return

decide:
If act_temp < (set_temp-hysteresis) then
pwm_pars =0
pwm_flag=1
endif
If act_temp > set_temp and pwm_flag =0 then
pwm_pars = min_pwm+(act_temp - set_temp)
goto jump
endif
If act_temp > (set_temp + hysteresis) and pwm_flag =1 then
pwm_pars = start_pwm
pwm_flag=0
endif
jump:
gosub set_pwm_pars
val_1=act_temp
val_2=pwm_pars
val_3=set_temp
Return
Main:
gosub read_act_temp
gosub read_set_temp
gosub decide
gosub send_out
pause 1500
goto Main
end


I think that I will settle for that now. I will just test its performance for the next week or so and wrap the project up for this particular cooler.
A friend of mine is offering me a huge 125 bottles cooler with refrigeration problems (old Freon compressor type). If this first attempt will prove successful I will spend more time saving the big cooler which is a lot nicer and a lot more expencie than this one.
I manage to get 6 brand new TECs, more powerful than the other ones (7-8 Amps) and I will start experimenting with them and different setups.
It might take a little time but I will keep you posted.

Thank you everyone for the input.

Nick