' A PID controller using the Hitachi HD44780 LCD controller chip in 4-bit mode Const HD44780_DB3 As Byte = 9 'Most significant bit Const HD44780_DB2 As Byte = 10 Const HD44780_DB1 As Byte = 11 Const HD44780_DB0 As Byte = 12 'Least significant bit Const HD44780_ENABLE As Byte = 13 'to pin 6 'Const HD44780_READWRITE As Byte = 14 '1=Read 0=Write, unused so tied to ground Const HD44780_REGSELECT As Byte = 15 '1=Data 0=Instruction, to pin 4 Const LCDREAD As Byte = 1 Const LCDWRITE As Byte = 0 Const LCDDATA As Byte = 1 Const LCDINSTRUCTION As Byte = 0 Const INPUT_MODE As Byte = 5 'to Pin 5 Const INPUT_UP As Byte = 6 'to Pin 6 Const INPUT_DOWN As Byte = 7 'to Pin 7 Const INPUT_COMMIT As Byte = 8 'to Pin 8 Const TEMPERATURE_IN As Byte = 20 'to Pin 20 Dim sTemperatureActual As Single Dim sTemperatureActualLast As Single Dim sTemperatureSetpoint As Single Dim sHeatProportion As Single Dim sHeatIntegral As Single Dim sHeatDerivative As Single Dim sCoolProportion As Single Dim sCoolIntegral As Single Dim sCoolDerivative As Single Dim bWorkingDisplay As Boolean Dim sTmpString As String 'used for LCDPrintString Dim TaskStack(0 To 40) As Byte Sub Main() Dim iCount As Integer Dim sTemp As Single Delay (0.5) 'in case of rescue Call PutPin(26,0) ' green led on Call LCDInit() Call PIDInit() 'CallTask "MyTask", TaskStack Do Call PIDModeDisplayWork () Call PIDModeGetSettingsHeat () Call PIDModeGetSettingsCool () ' Call PIDModeGetSettingsPeriodic1 () ' Call PIDModeGetSettingsPeriodic2 () ' Call PIDModeGetSettingsMenu () Loop While (sTemperatureSetpoint < 101.0) Call PutPin(26,1) ' green led off End Sub 'Main Sub MyTask () Dim nonDimVolts As Single Dim sTemp As Single Dim bCount As Byte Do While (True) sTemp = sTemperatureActual / 500.0 For bCount = 1 to 10 Call GetADC(TEMPERATURE_IN, nonDimVolts) sTemp = sTemp + ((nonDimVolts - sTemp)/100.0) 'smooths value jumps Call Sleep (0.01) Next sTemperatureActualLast = sTemperatureActual sTemperatureActual = sTemp * 500.0 sTemperatureActual = CSng(CLng(sTemperatureActual * 10.0))/10.0 'trunc 0.1 ' Just to see that something is happening. This will be removed when it all works Call PutPin(25,0) ' red led on Call Sleep (0.25) Call PutPin(25,1) ' red led off Call Sleep (0.25) Loop End Sub Sub GetCurrentTemperature () Dim nonDimVolts As Single Dim sTemp As Single Dim bCount As Byte sTemp = sTemperatureActual / 500.0 For bCount = 1 to 10 Call GetADC(TEMPERATURE_IN, nonDimVolts) sTemp = sTemp + ((nonDimVolts - sTemp)/100.0) 'smooths value jumps Call Sleep (0.01) Next sTemperatureActualLast = sTemperatureActual sTemperatureActual = sTemp * 500.0 sTemperatureActual = CSng(CLng(sTemperatureActual * 10.0))/10.0 'trunc 0.1 End Sub Function PIDGetButton () As Byte If GetPin(INPUT_UP) = 0 Then Call Sleep (0.1) 'debounce PIDGetButton = INPUT_UP Exit Function ElseIf GetPin(INPUT_DOWN) = 0 Then Call Sleep (0.1) 'debounce PIDGetButton = INPUT_DOWN Exit Function ElseIf GetPin(INPUT_COMMIT) = 0 Then Call Sleep (0.1) 'debounce PIDGetButton = INPUT_COMMIT Exit Function ElseIf GetPin(INPUT_MODE) = 0 Then Call Sleep (0.1) 'debounce PIDGetButton = INPUT_MODE Exit Function Else PIDGetButton = 0 Exit Function End If End Function Sub PIDInit () Call PutPin(INPUT_MODE, bxInputPullup) ' Pull-up Call PutPin(INPUT_UP, bxInputPullup) ' Pull-up Call PutPin(INPUT_DOWN, bxInputPullup) ' Pull-up Call PutPin(INPUT_COMMIT, bxInputPullup) ' Pull-up bWorkingDisplay = False Call GetADC(TEMPERATURE_IN, sTemperatureActual) sTemperatureActual = sTemperatureActual * 500.0 sTemperatureActualLast = sTemperatureActual sTemperatureSetpoint = 1.0 sHeatProportion = 2.0 sHeatIntegral = 200.0 sHeatDerivative = 100.0 sCoolProportion = 300.0 sCoolIntegral = 100.0 sCoolDerivative = 10.0 End Sub Sub PIDModeDisplayWork () Call GetCurrentTemperature () Call LCDClearDisplay () Call LCDGotoRowCol(0,0) sTmpString = "Actual Temp:" Call LCDPrintString () Call LCDGotoRowCol(0,12) Call LCDPrintValueField(sTemperatureActual, True, 6) Call LCDSendCharacter (bx11011111) 'degrees sign Call LCDGotoRowCol(1, 3) sTmpString = "Setpoint:" Call LCDPrintString () Call LCDGotoRowCol(1,12) Call LCDPrintValueField(sTemperatureSetpoint, True, 6) Call LCDSendCharacter (bx11011111) 'degrees sign bWorkingDisplay = True Do Loop Until PIDFieldValueGet (sTemperatureSetpoint, 1, 12, 6, True) = INPUT_MODE bWorkingDisplay = False End Sub 'PIDModeDisplayWork Sub PIDModeGetSettingsHeat () Call LCDClearDisplay () Call LCDGotoRowCol(0,1) sTmpString = "[Heat PID]" Call LCDPrintString () Call LCDGotoRowCol(0,14) sTmpString = "Prop:" Call LCDPrintString () Call LCDGotoRowCol(0,19) Call LCDPrintValueField (sHeatProportion, False, 4) Call LCDGotoRowCol(1,1) sTmpString = "Int:" Call LCDPrintString () Call LCDGotoRowCol(1,5) Call LCDPrintValueField (sHeatIntegral, False, 4) Call LCDGotoRowCol(1,13) sTmpString = "Deriv:" Call LCDPrintString () Call LCDGotoRowCol(1,19) Call LCDPrintValueField (sHeatDerivative, False, 4) Top: If PIDFieldValueGet (sHeatProportion, 0, 19, 4, False) = INPUT_MODE Then Exit Sub End If If PIDFieldValueGet (sHeatIntegral, 1, 5, 4, False) = INPUT_MODE Then Exit Sub End If If PIDFieldValueGet (sHeatDerivative, 1, 19, 4, False) = INPUT_MODE Then Exit Sub End If Goto Top End Sub 'PIDModeGetSettingsHeat Sub PIDModeGetSettingsCool () Call LCDClearDisplay () Call LCDGotoRowCol(0,1) sTmpString = "[Cool PID]" Call LCDPrintString () Call LCDGotoRowCol(0,14) sTmpString = "Prop:" Call LCDPrintString () Call LCDGotoRowCol(0,19) Call LCDPrintValueField (sCoolProportion, False, 4) Call LCDGotoRowCol(1,1) sTmpString = "Int:" Call LCDPrintString () Call LCDGotoRowCol(1,5) Call LCDPrintValueField (sCoolIntegral, False, 4) Call LCDGotoRowCol(1,13) sTmpString = "Deriv:" Call LCDPrintString () Call LCDGotoRowCol(1,19) Call LCDPrintValueField (sCoolDerivative, False, 4) Top: If PIDFieldValueGet (sCoolProportion, 0, 19, 4, False) = INPUT_MODE Then Exit Sub End If If PIDFieldValueGet (sCoolIntegral, 1, 5, 4, False) = INPUT_MODE Then Exit Sub End If If PIDFieldValueGet (sCoolDerivative, 1, 19, 4, False) = INPUT_MODE Then Exit Sub End If GoTo Top End Sub Function PIDFieldValueGet ( ByRef sValue As Single, ByVal bRow As Byte, _ ByVal bCol As Byte, ByVal bWidth As Byte, ByVal bDecimal As Boolean) As Byte Dim bInput As Byte Dim sTmp As Single Dim sIncrement As Single If bDecimal = True Then sIncrement = 0.1 Else sIncrement = 1.0 End If Call LCDGotoRowCol(bRow, bCol + bWidth) sTmp = sValue Do If (bWorkingDisplay = True) Then Call GetCurrentTemperature() If (sTemperatureActualLast <> sTemperatureActual) Then Call LCDGotoRowCol(0,12) Call LCDPrintValueField(sTemperatureActual, True, 6) Call LCDGotoRowCol(bRow, bCol + bWidth) End If End If 'bWorkingDisplay bInput = PIDGetButton() Select Case bInput Case INPUT_UP sTmp = sTmp + sIncrement Case INPUT_DOWN sTmp = sTmp - sIncrement Case INPUT_COMMIT sValue = sTmp Call Sleep (0.2) 'debounce Case INPUT_MODE ' do nothing - function will bailout anyway End Select 'bInput If (bInput = INPUT_UP) Or (bInput = INPUT_DOWN) Then Call LCDGotoRowCol(bRow, bCol) Call LCDPrintValueField (sTmp, bDecimal, bWidth) End If Loop While (bInput <> INPUT_COMMIT) And (bInput <> INPUT_MODE) PIDFieldValueGet = bInput End Function Sub LCDInit() Call PutPin(HD44780_ENABLE, bxOutputLow) Call PutPin(HD44780_REGSELECT, bxOutputLow) Call Sleep(0.050) 'for LCD chip startup Call LCDPut4Bits ( bx00110000 ) 'function set (assumes 8-bit at startup) Call PulseOut(HD44780_ENABLE, 0.00005, bxOutputHigh) Call Sleep(0.005) Call LCDPut4Bits ( bx00110000 ) 'function set (assumes 8-bit at startup) Call PulseOut(HD44780_ENABLE, 0.00005, bxOutputHigh) Call Sleep(0.00015) Call LCDPut4Bits ( bx00110000 ) 'function set (assumes 8-bit at startup) Call PulseOut(HD44780_ENABLE, 0.00005, bxOutputHigh) Call LCDPut4Bits ( bx00100000 ) 'Now actually sets to 4-bit mode Call PulseOut(HD44780_ENABLE, 0.00005, bxOutputHigh) Call LCDFunctionSet () 'sets the complete function set Call LCDDisplayOFF () Call LCDClearDisplay () Call LCDEntryModeSet () Call LCDDisplayON () End Sub Sub LCDFunctionSet8 () '8-bit, 2 lines, no-scroll, blinking cursor Call LCDSendCommand ( bx00111000 ) End Sub Sub LCDFunctionSet () '4-bit, 2 lines, no-scroll, blinking cursor Call LCDSendCommand ( bx00101000 ) End Sub Sub LCDDisplayON () Call LCDSendCommand ( bx00001111 ) End Sub Sub LCDDisplayOFF () Call LCDSendCommand ( bx00001000 ) End Sub Sub LCDEntryModeSet () Call LCDSendCommand ( bx00000110 ) End Sub Sub LCDClearDisplay () Dim bMask As Byte Dim bCount As Byte Dim bByte As Byte Call PutPin(HD44780_REGSELECT, LCDINSTRUCTION) Call PutPin(HD44780_ENABLE, bxOutputLow) bByte = bx00000001 bMask = bx10000000 For bCount = 0 to 3 'Put most significant bits first Call PutPin(HD44780_DB3 + bCount, ((bByte And bMask)\bMask)) bMask = bMask \ 2 Next Call PulseOut(HD44780_ENABLE,0.00005, bxOutputHigh) For bCount = 0 to 3 'Send last 4 bits Call PutPin(HD44780_DB3 + bCount, ((bByte And bMask)\bMask)) bMask = bMask \ 2 Next Call PulseOut(HD44780_ENABLE,0.00005, bxOutputHigh) Call Sleep(0.0016) 'Clear Display needs more time End Sub Sub LCDReturnHome () Call LCDSendCommand ( bx00000010 ) Call Sleep(0.0016) End Sub ' Left-pads bWidth string with spaces. Sub LCDPrintValueField(ByVal sSingle As Single, ByVal bDecimal As Boolean, ByVal bWidth As Byte) Dim bLength As Byte Dim sTmp As String If bDecimal = True Then sTmp = CStr(CLng(sSingle * 10.0)) Else sTmp = CStr(CLng(sSingle)) End If bLength = CByte(Len(sTmp)) If bDecimal = True Then bLength = bLength + 1 ' for decimal point End If Do While (bWidth - bLength) > 0 ' print right padding Call LCDSendCharacter( CByte(Asc(" ")) ) bWidth = bWidth -1 Loop If bDecimal = True Then sTmpString = Mid(sTmp, 1, CInt(bLength - 2)) Call LCDPrintString () Call LCDSendCharacter( CByte(Asc(".")) ) sTmpString = Mid(sTmp, CInt(bLength-1), 1) Call LCDPrintString () Else sTmpString = sTmp Call LCDPrintString () End If End Sub ' LCDPrintString prints global sTmpString instead of using stack Sub LCDPrintString () Dim bCount As Byte Dim bCharPos As Byte Dim bMask As Byte Dim bByte As Byte Dim sChar As String * 1 Call PutPin(HD44780_REGSELECT, LCDDATA) Call PutPin(HD44780_ENABLE, bxOutputLow) For bCharPos = 1 To CByte(Len(sTmpString)) sChar = Mid(sTmpString, CInt(bCharPos), 1) bByte = Asc(sChar) bMask = bx10000000 For bCount = 0 to 3 'Put most significant bits first Call PutPin(HD44780_DB3 + bCount, ((bByte And bMask)\bMask)) bMask = bMask \ 2 Next Call PulseOut(HD44780_ENABLE,0.00005, bxOutputHigh) For bCount = 0 to 3 'Put next 4 bits Call PutPin(HD44780_DB3 + bCount, ((bByte And bMask)\bMask)) bMask = bMask \ 2 Next Call PulseOut(HD44780_ENABLE,0.00005, bxOutputHigh) Call Sleep(0.00005) Next End Sub Sub LCDGotoRowCol (ByVal bRow As Byte, ByVal bCol As Byte) Dim bMask As Byte Dim bCount As Byte Dim bByte As Byte Call PutPin(HD44780_REGSELECT, LCDINSTRUCTION) Call PutPin(HD44780_ENABLE, bxOutputLow) bByte = bx10000000 Or (bRow * 64 + bCol) bMask = bx10000000 For bCount = 0 to 3 'Put most significant bits first Call PutPin(HD44780_DB3 + bCount, ((bByte And bMask)\bMask)) bMask = bMask \ 2 Next Call PulseOut(HD44780_ENABLE,0.00005, bxOutputHigh) For bCount = 0 to 3 'Put next 4 bits Call PutPin(HD44780_DB3 + bCount, ((bByte And bMask)\bMask)) bMask = bMask \ 2 Next Call PulseOut(HD44780_ENABLE,0.00005, bxOutputHigh) Call Sleep(0.00005) End Sub 'Sub LCDSetDDRAMAddr (ByVal bAddr As Byte) ' Call LCDSendCommand ( bx10000000 Or bAddr) 'bAddr is 7-bits 'End Sub Sub LCDSendCommand (ByVal bByte As Byte) Dim bMask As Byte Dim bCount As Byte Call PutPin(HD44780_REGSELECT, LCDINSTRUCTION) Call PutPin(HD44780_ENABLE, bxOutputLow) bMask = bx10000000 For bCount = 0 to 3 'Put most significant bits first Call PutPin(HD44780_DB3 + bCount, ((bByte And bMask)\bMask)) bMask = bMask \ 2 Next Call PulseOut(HD44780_ENABLE,0.00005, bxOutputHigh) For bCount = 0 to 3 'Put most significant bits first Call PutPin(HD44780_DB3 + bCount, ((bByte And bMask)\bMask)) bMask = bMask \ 2 Next Call PulseOut(HD44780_ENABLE,0.00005, bxOutputHigh) Call Sleep(0.00005) End Sub Sub LCDSendCharacter (ByVal bByte As Byte) Dim bMask As Byte Dim bCount As Byte Call PutPin(HD44780_REGSELECT, LCDDATA) Call PutPin(HD44780_ENABLE, bxOutputLow) bMask = bx10000000 For bCount = 0 to 3 'Put most significant bits first Call PutPin(HD44780_DB3 + bCount, ((bByte And bMask)\bMask)) bMask = bMask \ 2 Next Call PulseOut(HD44780_ENABLE,0.00005, bxOutputHigh) For bCount = 0 to 3 'Put next 4 bits Call PutPin(HD44780_DB3 + bCount, ((bByte And bMask)\bMask)) bMask = bMask \ 2 Next Call PulseOut(HD44780_ENABLE,0.00005, bxOutputHigh) Call Sleep(0.00005) End Sub ' 4-bit interface - ENABLE pulse expects WRITE mode Sub LCDPutByte (ByVal bByte As Byte) Dim bMask As Byte Dim bCount As Byte bMask = bx10000000 For bCount = 0 to 3 'Put most significant bits first Call PutPin(HD44780_DB3 + bCount, ((bByte And bMask)\bMask)) bMask = bMask \ 2 Next Call PulseOut(HD44780_ENABLE,0.00005, bxOutputHigh) For bCount = 0 to 3 'Put most significant bits first Call PutPin(HD44780_DB3 + bCount, ((bByte And bMask)\bMask)) bMask = bMask \ 2 Next End Sub Sub LCDPut4Bits (ByVal bByte As Byte) 'msb's only Dim bMask As Byte Dim bCount As Byte bMask = bx10000000 For bCount = 0 to 3 'Put most significant bits first Call PutPin(HD44780_DB3 + bCount, ((bByte And bMask)\bMask)) bMask = bMask \ 2 Next End Sub