Pulse Width Modulation Example PWM Heater

techElder

Well-known member
Here's something that I've been working on. I'm going to have to put it down for a little while, so I won't have time to finish wiring up the first prototype. The protoboard version works perfectly. I've included photos that might give others some comfort with their design practices. Nothing is ever pretty at the beginning of a design cycle.

My project evolved with the need to maintain a set temperature within a small vessel. There weren't any real tight tolerances, so I was barely worried about overshoot or undershoot. I provided a way to adjust the set point in increments of 10 degrees, and my observation is that it does that within 5 degrees.

I learned much about the "pulsout" command on the Picaxe 08M (especially how to SPELL it!) Additionally, I made use of the MIN and MAX operators just because Hippy said he had a rough time, at first, figuring them out. (I just HAD to try to get to his level! :) )

Anyway, I created this control routine as a generic subroutine that with a little effort could be placed in any program. I can see it being used to great advantage in the new 18M2 as a separate task and with touch controls.

If you see something that could use a tweak, I'm more than receptive. (I'm putting this up in thanks for the help I've received from other forum members.)

Code:
'
' PWM Example (C) 2010 Paul N. Lawrence
' Version: 2010-08-13-0
' Filename: PWM_example_program.bas

' Program by: Paul Lawrence August 2010 (Texasclodhopper on Picaxe Forum)

' The program will maintain a temperature warmer than the ambient temperature.

' Version: 1.0

' Target Processor: PICAXE-08M

'------------------------------------------------------------------------------
'-[ DESCRIPTION ]-------
'------------------------------------------------------------------------------

' Proportional control via PWM of a Nichrome heater wire that wraps around a vessel
' 		for the purpose of heating the vessel.

' The LM34DZ is installed with the heating element to measure the temperature.

' The user interface (UI) consists of an LED that has a brightness proportional to
'		the heating element duty cycle, a button and a blinking LED to indicate which
'		of 9 settings for temperature setpoint is to be used. Each push on the button
'		moves to the next setting and stays there. The LED blinks the number of the
'		index into the following table of settings:
'			0 - ignored	- 82
'			1 - 40 F.	- 82	(raw ADC number from a 5 volt ADC reference)
'			2 - 50 F.	- 102
'			3 - 60 F.	- 123
'			4 - 70 F.	- 143
'			5 - 80 F.	- 164
'			6 - 90 F.	- 184
'			7 - 100 F.	- 205
'			8 - 110 F.	- 225
'			9 - 120 F.	- 246
'
' The following lines contain an accurate formula for scaling and presenting the 
'		temperature from the ADC readings.
'			symbol REFx10 = 50	' for display purpose
'			gwDummyVar = rtSetTemp * REFx10 + 50 / 100
'			SERTXD ("Set Temp = ",#gwDummyVar,CR,LF)
'			gwDummyVar = rtAvgTemp * REFx10 + 50 / 100
'			SERTXD ("Temperature = ",#gwDummyVar," Duty = ",#rtDuty,CR,LF)


#picaxe 08M
#no_end

symbol rtMAXDUTY = 704			' 704 = 88%; 760 = 95%; maximum duty cycle allowed for PWM
symbol rtMINDUTY = 80			' 80 = 10%; minimum duty cycle allowed for PWM
symbol rtINCDUTY = 50			' maximum adjustment of the duty cycle
symbol rtPERIOD = 199			' 199 = 5KHz; pulse width modulation (PWM) period

symbol rtTEMPPIN = 4				' pin where the temperature ADC is to be taken
symbol rtPWMPIN = 2				' pin where PWM works
symbol UIOPIN = 1					' pin where user interface (UI) output is
symbol UIIPIN = 3					' pin where user interface (UI) input is

										' w0
symbol adjButton = b2			' w1
symbol gbCounter = b3			' w1
symbol setPointIndex = b4		' w2
symbol rtDuty = w3
symbol rtAvgTemp = w4
symbol rtSetTemp = w5
symbol gwDummyVar = w6

'------------------------------------------------------------------------------
'-[ INITIALIZE ]-------
'------------------------------------------------------------------------------

init:

let dirs = %00000111				' output pin0, pin1; pin2
let rtDuty = rtMINDUTY			' start at lowest duty cycle; 160 is 20% duty cycle
let rtSetTemp = 143				' 143 is 70 degrees F.; 205 is 100 degrees F.
let setPointIndex = 3			' at program start 3 gets incremented to 4 is 70 degrees F.

low UIOPIN							' UI output

pause 500

goto SetPoint						' initiate set point and activate a startup blink

'------------------------------------------------------------------------------
'-[ MAIN PROGRAM ]-------
'------------------------------------------------------------------------------

main:

gosub RegulateTemp				' check and adjust PWM for current temperature

adjButton = 0						' init for button command

for gwDummyVar = 1 to 80

	BUTTON UIIPIN,0,255,0,adjButton,1,SetPoint ' branch if button goes to ground; no repeat
										' BUTTON pin,downstate,delay,rate,bytevariable,targetstate,address
	
	pause 125						' 80 times 125 = 10 second delay between readings while scanning button
	
next


goto main


SetPoint:							' UI for setting set point temperature
										' each push increases set point 10 degrees; wraps around to 1 from 9
	inc setPointIndex
	
	if setPointIndex > 9 then	' only use 1 through 9
		setPointIndex = 1
	endif

	LOOKUP setPointIndex,(82,82,102,123,143,164,184,205,225,246),rtSetTemp ' raw numbers from ADC; zero index ignored
	'							 40,40, 50, 60, 70, 80, 90,100,110,120	degrees F.

	for gbCounter = 1 to setPointIndex ' blinks = index of setpoint from lookup

		high UIOPIN : pause 100 : low UIOPIN : pause 400

	next

goto main

'------------------------------------------------------------------------------
'-[ SUBROUTINES ]-------
'------------------------------------------------------------------------------

RegulateTemp:	' "rt..." preamble for variables used in this routine

' Program by: Paul Lawrence August 2010 (Texasclodhopper on Picaxe Forum)

' Version: 1.0

' This routine should be placed in a loop that calls it periodically (10 seconds.) The rtSetTemp 
'		variable should be supplied a value that represents the ADC number to compare with the ADC 
'		reading taken within the routine.

' Routine produces a pulse width modulation (PWM) signal on rtPWMPIN with a period of rtPERIOD and
'		a duty cycle of rtDuty (pwmout rtPWMPIN, rtPERIOD, rtDuty). The temperature sensor is closely 
'		coupled to the heater element and returns a value. This value is normally 10mV per degree 
'		Farenheit for the LM34DZ. The voltage value from the LM34DZ temperature sensor is then 
'		converted by the ADC on rtTEMPPIN relative to the reference (5VDC). Those conversions are 
'		averaged 64 times into rtAvgTemp. This average reading is compared to the temperature number 
'		in rtSetTemp (set elsewhere). 

' For a reading that is LESS THAN the set temperature, the duty cycle number, rtDuty, is reduced by 
'		a value that is twice the difference between the reading and the setting. A maximum change 
'		is limited by rtINCDUTY, and a maximum duty cycle is limited by rtMAXDUTY.

' For a reading that is MORE THAN the set temperature, the duty cycle number, rtDuty, is increased by 
'		a value that is twice the difference between the reading and the setting. A maximum change 
'		is limited by rtINCDUTY, and a minimum duty cycle is limited by rtMINDUTY.

' For a reading that is EQUAL TO the set temperature, no change is made to the PWM.

		#rem
			symbol rtMAXDUTY = 704	' 704 = 88%; 760 = 95%
			symbol rtMINDUTY = 80	' 80 = 10%
			symbol rtINCDUTY = 50	' maximum adjustment of the duty cycle
			symbol rtPERIOD = 199	' 199 = 5KHz; pulse width modulation (PWM) period
			symbol rtTEMPPIN = 4		' pin where the temperature ADC is to be taken
			symbol rtPWMPIN = 2		' pin where PWM works
			symbol gbCounter = b3	' w1
			symbol rtDuty = w3		' w3
			symbol rtAvgTemp = w4	' w4
			symbol rtSetTemp = w5	' w5
			symbol gwDummyVar = w6	' w6
		#endrem
	
	rtAvgTemp = 0						' start average from zero

	for gbCounter = 1 to 64		' take 64 readings for the average

		readadc10 rtTEMPPIN, gwDummyVar	' get a reading from temperature sensor

		rtAvgTemp = rtAvgTemp + gwDummyVar	' add it to previous readings

	next

	rtAvgTemp = rtAvgTemp / 64		' get the average

	if rtAvgTemp < rtSetTemp then	' the reading is lower than the set temperature

		gwDummyVar = rtSetTemp - rtAvgTemp * 2 MAX rtINCDUTY	' get an adjustment number up to a maximum

		rtDuty = rtDuty + gwDummyVar MAX rtMAXDUTY	' add the adjustment up to a maximum

	elseif rtAvgTemp > rtSetTemp then	' the reading is higher than the set temperature

		gwDummyVar = rtAvgTemp - rtSetTemp * 2 MAX rtINCDUTY	' get an adjustment number up to a maximum

		rtDuty = rtDuty - gwDummyVar MIN rtMINDUTY	' subtract the adjustment down to a minimum

	'else
		' rtAvgTemp is equal to rtSetTemp, do nothing

	endif
	
	pwmout rtPWMPIN, rtPERIOD, rtDuty	' adjust the PWM as needed

	' SERTXD ("rtPERIOD = ",#rtPERIOD," Duty = ",#rtDuty,CR,LF)

return
(I use a photo server called TinyPic.com . )
THE SCHEMATIC
PROTO BOARD
1ST PROTOTYPE PARTIAL
 

Attachments

Last edited:
Top