' MeterStampBox.bs2
' Box with Meter and speaker
' John Saunders 11/24/2009
' {$STAMP BS2}
' {$PBASIC 2.5}
' Separated the recording of digital inputs
'Stamp Ports
DS1302CE PIN 0 'Output: DS1302 Chip Enable
DS1302IO PIN 1 'Bi-directional:DS1302 Data
DIO1 PIN 2 'Input or Output:Digital I/O #1, D-25 pin 19
DIO2 PIN 3 'Input or Output:Digital I/O #2, D-25 pin 20
UPSW PIN 4 'Input: UP Switch
DNSW PIN 5 'Input: Down Switch
SDOut PIN 6 'Output to SD Interface
SDIn PIN 7 'Input from SD Interface
SelPort VAR OUTC 'Selector for 138 and 259 chips, pins 8,9,10
En138 PIN 11 'Output:High pulse to enable the 138
CLK PIN 12 'Output:Clocks all
InPort PIN 13 'Input:166 and ADC Data
En259 PIN 14 'Output:259 gate
OutPort PIN 15 'Output:Display RS,74HC164 & 166 serial in,259 D
'Initialization
Dispinitlen CON 3
DispInitAddr CON 0
DispInitData DATA $38,$0C,$01,$06 'Need $38(2-line) and use cmd $C0 for second 8 char pos
'String Declarations
'These are used with DispBCDString to generate most of the display fields
DateString DATA $89,"/",$87,0
YearString DATA "/",$8D,0
YearAdjString DATA "20",$8D," ADJ",0
MinAdjString DATA "Min ",$83,0
HourAdjString DATA "Hour ",$85,0
TimeString DATA $85,":",$83,":",$81,0
RecordString DATA $89,"/",$87,"/",$8D,",",$85,":",$83,":",$81,",",0
VoltString DATA " V",0
MAString DATA " MA",0
'Display parameters
Char VAR Byte 'General use
DECdata VAR Word 'General use
UpperByte VAR DECData.HIGHBYTE
LowerByte VAR DECData.LOWBYTE
SignBit VAR DECdata.BIT15
div VAR Byte 'dividend used in writing numbers, and general use
rem VAR Word 'remainder used in writing multiple-digit numbers, and general use
upperrem VAR rem.HIGHBYTE
lowerrem VAR rem.LOWBYTE
Iter VAR Nib 'for loop variable
DispMode VAR Nib 'Controls the way a decimal value is to be displayed
'Control variables and External Digital Inputs
IOdata VAR Byte 'Data to be either input or output, also display R/S
Mode VAR Nib 'Bits 0-2 are the mode switch encoded
DigInputs VAR Byte 'Two Input/Outputs, the Serial Input and the five digital inputs,
SERIO VAR DigInputs.LOWNIB.BIT0
DigIOA VAR DigInputs.LOWNIB.BIT1
DigIOB VAR DigInputs.LOWNIB.BIT2
DigInA VAR DigInputs.LOWNIB.BIT3
DigInB VAR DigInputs.HIGHNIB.BIT0
DigInC VAR DigInputs.HIGHNIB.BIT1
DigInD VAR DigInputs.HIGHNIB.BIT2
DigInE VAR DigInputs.HIGHNIB.BIT3
OldMode VAR Nib
SerialIn CON 0 'Selects Serial In for InPort
BallFlag VAR Bit 'One selects Black
'138 select constants
DispEn CON 0
SHLD CON 9 'Also sets gate high since 166 has synchronous load
ADCICE CON 2
ADCVCE CON 3
Chime CON 4
CLR259 CON 5
Beep CON 6
'259 select constants
Black CON 0
White CON 1
PSEn CON 3
DigAOut CON 7
DigBOut CON 5
DigCOut CON 2
DIGDOut CON 4
'Dallas DS1302 RTC addresses and variables
WRProtData CON 0
WRProtAddr CON $8E
OSCEnAddr CON $80
DSRangeAddr CON $D0
DSIntervalAddr CON $D2
TCHData CON %10100101
TCHAddr CON $90
ReadMonthAddr CON $89
ReadDateAddr CON $87
WriteHourAddr CON $84
WriteMinuteAddr CON $82
DSAddr VAR Byte 'DS1302 register address
DSData VAR Byte 'DS1302 data is in BCD format
Tens VAR DSData.HIGHNIB
Units VAR DSData.LOWNIB
OldHour VAR Byte 'To sound the hour
'Rogue Secure Digital Interface variables
DoneFlag VAR Bit 'Recording has been completed
RecDig VAR Bit 'If 1 digital inputs are recorded, else displayed
Interval VAR Byte 'The number of iterations between recordings
RecordCount VAR Byte 'The countdown to record time
NoPrompt VAR Bit 'SD interface did not give a prompt
EnRecord VAR Bit '1 = recording enabled - white ball
MinInterval CON 10 'Minimum recording interval
MonTens VAR LowerByte.HIGHNIB 'used to grnerate filenames
MonUnits VAR LowerByte.LOWNIB
DayTens VAR lowerrem.HIGHNIB
DayUnits VAR lowerrem.LOWNIB
'Analog parameters
Current VAR Word
Voltage VAR Word
voltoffset CON 4 'Correction for voltage amplifier offset, in counts
ampoffset CON 4 'Correction for current amplifier offset, in counts
Range VAR Nib 'Index into AmpParams for the selected shunt
MaxRange CON 10 'Display value,Value Display mode, multiplier,divider,Decimal Display mode
AmpParams DATA 125,$A,4,2,$F, 25,1,4,1,$F, 38,1,14,2,$F, 50,1,4,0,$F, 63,1,24,2,$F,
75,1,14,1,$F, 100,2,9,0,$F, 125,2,4,2,$B, 150,2,3,1,$B, 250,2,4,1,$B, 1,3,9,0,$B
Start:
DIRS = %1101111101000001
HIGH En259 'Establishes the idle level for the 259 command register
LOW En138 'Establishes the idle level for the 138 command decoder
Selport = CLR259 'Clear the 259
PULSOUT En138,10
LOW DS1302CE 'Establishes the idle level for the DS1302 RTC
DECData = 0
GOSUB InitDisp
SelPort = PSen 'Turn on the isolation DC-DC converter for the current input
Outport = 1
PULSOUT En259, 10
Outport = 0
'Commented out because of lack of memory
'DsAddr = DSRangeAddr + 1 'Restore previous session range
'GOSUB DS1302OUT
'Range = Units
'IF Range > MaxRange THEN
range = MaxRange
'ENDIF
DsAddr = DsIntervalAddr + 1 'Restore previous session interval
GOSUB DS1302OUT
Interval = DSData
DoneFlag = 1 'Makes a wait before recording
RecordCount = Interval 'Make a delay on startup to allow the Rogue to boot
EnRecord = 0 'Must restart recording manually
BallFlag = 1 'Black
GOSUB MoveBall
Main: 'Main loop
GOSUB GetInputData 'Mode and digital inputs
GOSUB MeasureVolt
GOSUB MeasureAmp
RecordCount = RecordCount - 1
'Check IF time to record
IF (RecordCount = $05) THEN
DoneFlag = 0 'Permits a new recording
ELSEIF (RecordCount < $01) AND (DoneFlag = 0) AND (EnRecord = 1) THEN
GOSUB Record 'Record date, time, current, voltage, external inputs
RecordCount = Interval
ENDIF
' Erase display if mode changes or switch closed
IF (Mode <> OldMode) OR (UPSW = 0) OR (DNSW = 0) THEN
GOSUB SoundBeep
Char = 1
GOSUB DispCmd
ENDIF
OldMode = Mode
Char = $80 'Displays and actions for the left 8 character positions
GOSUB DispCmd
SELECT Mode
CASE 0 '"A" Start or Stop recording
GOSUB DispAmp
IF (UPSW = 0) THEN
EnRecord = 1
BallFlag = 0 'White
GOSUB MoveBall
RecordCount = Interval
ENDIF
IF (DNSW = 0)THEN
EnRecord = 0
BallFlag = 1 'Black
GOSUB MoveBall
ENDIF
CASE 1 '"T"
DECData = TimeString
GOSUB DispBCDString
CASE 2 '"R"
GOSUB DispInputs
CASE 3 '"I"
GOSUB DispAmp
CASE ELSE '"M" and "H"
DECData = YearAdjString
GOSUB DispBCDString
ENDSELECT
Char = $C1 'The actions and display for the right-hand field
GOSUB DispCmd
SELECT Mode
CASE 0 '"A"
Decdata = Voltage
DispMode = $F
GOSUB DispDec
Decdata = VoltString
GOSUB DispBCDString
CASE 1 '"T"
DECData = DateString
GOSUB DispBCDString
CASE 2 '"R"
GOSUB DispInterval
CASE 3 '"I"
GOSUB DispRange
CASE 4 '"M"
GOSUB AdjMin
CASE 5 '"H"
GOSUB AdjHour
ENDSELECT
IF (EnRecord = 0) THEN main
PAUSE 850
GOTO main
STOP
'subroutines:
'subroutines for Samsung KS0066 LCD display controllers:
InitDisp: 'Set up no. of lines, display on, cursor off, write direction, erase
FOR Iter = 0 TO DispInitLen
READ DispInitAddr + Iter,Char
GOSUB DispCmd
PAUSE 200
NEXT
RETURN
DispCmd: 'puts an instruction (Char) to control the display
SHIFTOUT Outport,CLK,1,[Char\8] 'clocks the instruction into the 164 shift register, MSB first
LOW outport 'instruction function is low, outport does double duty
SelPort = DispEn
PULSOUT En138,10 'clocks the instruction and the function into the display
LOW En138
PAUSE 1
RETURN
DispChar: 'puts one character (Char)onto the display
SHIFTOUT Outport,CLK,1,[Char\8] 'clocks the character into the shift register
HIGH outport 'data function is high
SelPort = DispEn
PULSOUT En138,10 'clocks the character into the display
LOW En138
RETURN
DispDec: 'Displays DECData at current cursor position in Digital
IF DispMode = 0 THEN OneDigit
IF DispMode = 1 THEN TwoDigit
IF (DispMode = 2) OR (DispMode = $C) OR (DispMode = $A) THEN ThreeDigit
'4-digit
div = DECData/1000
rem = DECData//1000
Char = div + "0"
GOSUB DispChar
DECData = rem
ThreeDigit:
div = DECData/100
rem = DECData//100
Char = div + "0"
GOSUB DispChar
IF (Dispmode = $F) OR (DispMode = $C) THEN
Char = "."
GOSUB DispChar
ENDIF
DecData = rem
TwoDigit:
div = DECData/10
rem = DECData//10
Char = div + "0"
GOSUB DispChar
IF (Dispmode = $B) OR (DispMode = $A) THEN
Char = "."
GOSUB DispChar
ENDIF
DECData = rem
OneDigit:
Char = DECdata + "0"
GOSUB DispChar
RETURN
'DECdata points to a DATA array which contains ASCII characters and DS1302 register addresses
DispBCDString: 'Display dates, times, and character strings, null-terminated
Iter = 0
DO 'Decode the instructions in the control string until a null character
READ (DECData + Iter),DSAddr
IF DSAddr = 0 THEN RETURN
IF DSAddr > $80 THEN 'This must be an address in the DS1302, no ASCII character used here is this high
GOSUB DS1302OUT
Char = Tens + "0" 'Converts BCD to ascii, this is the upper nibble of DSData
GOSUB DispChar
Char = Units + "0" 'Converts BCD to ascii, this is the lower nibble of DSData
GOSUB DispChar
ELSE 'Already a literal ASCII character (less than $80)
Char = DSAddr
GOSUB DispChar
ENDIF
Iter = Iter+1 'Get next DATA array value
LOOP
RETURN
DispAmp:
Decdata = Current
READ AmpParams + 4 + (5*range),DispMode 'The location of the decimal point varies by range
GOSUB DispDec
Decdata = MAString
GOSUB DispBCDString
RETURN
'subroutines for the Dallas DS1302 real-time clock with battery backup
DS1302IN: 'Put the byte in DSData into the DsAddr location
HIGH DS1302CE 'Enable the DS1302
SHIFTOUT DS1302IO,CLK,0,[DSAddr\8] 'Put the register address (read) into the chip
SHIFTOUT DS1302IO,CLK,0,[DSData\8] 'The chip responds on the same line
LOW DS1302CE
RETURN
DS1302OUT: 'Read the contents of DSAddr into DsData
HIGH DS1302CE 'Enable the DS1302
SHIFTOUT DS1302IO,CLK,0,[DSAddr\8] 'Put the register address (write) into the chip
SHIFTIN DS1302IO,CLK,1,[DSdata\8] 'Put the new value into the chip
LOW DS1302CE
RETURN
'Due to lack of program space, a separate program is loaded (SetTimeDate.bs2) to load the RTC
'from ASCII files on a SD chip
'This should not be required again, since the RTC has its own long-term battery
AdjMin: 'To correct for drift
Decdata = MinAdjString
GOSUB DispBCDString
DsAddr = WriteMinuteAddr + 1
GOSUB DS1302OUT
IF (UPSW = 0) AND (DsData < 58) THEN
DsData = DsData + 1
DsAddr = WriteMinuteAddr
GOSUB DS1302IN
PAUSE 500
ENDIF
IF (DNSW = 0)AND (DsData > 0) THEN
DsData = DsData - 1
DsAddr = WriteMinuteAddr
GOSUB DS1302IN
PAUSE 500
ENDIF
RETURN
ADjHour: 'For Daylight Savings
Decdata = HourAdjString
GOSUB DispBCDString
DsAddr = WriteHourAddr + 1
GOSUB DS1302OUT
IF OldHour <> Dsdata THEN 'The chime only sounds in "H" mode
GOSUB SoundChime
ENDIF
OldHour = Dsdata
IF (UPSW = 0) AND (DsData < 23) THEN
DsData = DsData + 1
DsAddr = WriteHourAddr
GOSUB DS1302IN
PAUSE 500
ENDIF
IF (DNSW = 0)AND (DsData > 0) THEN
DsData = DsData - 1
DsAddr = WriteHourAddr
GOSUB DS1302IN
PAUSE 500
ENDIF
RETURN
'subroutines for the front panel switch, ball, loudspeaker and and external digital inputs
GetInputData: 'Rotary Switch and external inputs
SelPort = SHLD 'Selects the 74LS166 enable output of the 74HCT138
HIGH En138 'Enable Load Mode
PULSOUT CLK,10 'The 74LS166 uses a synchronous load
LOW En138 'Shift mode
SHIFTIN InPort,CLK,0,[IOdata\8] 'Shift inputs from the 166 shift register, MSB first
Mode = IOdata & $07
DigInputs = (IOData & $F8)
'These three are not connected to the 166: they replace the mode bits in DigInputs
'They could be used for an external serial device
IF DIO1 = 1 THEN 'A direct Stamp port with pullup
DigInputs = DigInputs | $02
ENDIF
IF DIO2 = 1 THEN 'A direct Stamp port with pullup
DigInputs = DigInputs | $04
ENDIF
'Commented out because of lack of memory
SelPort = SerialIn 'A floating unused input of the 74HC153 data selector
IF InPort = 1 THEN
DigInputs = DigInputs | $01
ENDIF
RETURN
DispInputs: 'Displays or records: DIOA,DIOB,DIGIn A ... DigIn E
IOData = $01
FOR Iter = 0 TO 7
IF (DigInputs & IOData) > 0 THEN
Char = "1"
ELSE
Char = "0"
ENDIF
IF RecDig = 0 THEN
GOSUB DispChar
ELSE
SEROUT SDOut,84,[",",Char]
ENDIF
IOData = IOData << 1
NEXT
RETURN
SoundChime: 'This is a kit for several fancy door chimes on a tiny board
Selport = Chime 'They are played in fixed sequence
PULSOUT En138, 30 'It only needs a short low pulse to trigger
RETURN
MoveBall: 'This is a mechanical military fault indicator with 2 coils
SelPort = BallFlag 'Controls which 259 output is used
LOW Outport 'Needs a positive pulse on the selected port, left negative
LOW En259 'This lets the Outport signal pass through the register
PULSOUT Outport, 10000 'Length arrived at by experiment
HIGH En259 'Disable the 259 while negative, will hold this value
RETURN
SoundBeep: 'This driver is wired-OR with the Chime to the loudspeaker.
SelPort = Beep 'Use sparingly as it has a very high current draw.
FREQOUT En138,15,1000 'Pulsing the enable will make the sound
LOW En138
RETURN
'Subroutines for the Rogue Electronics MMC - Secure Digital serial interface
DispInterval: 'The interval between recordings, in cycles of main
DecData = Interval
DispMode = 2
GOSUB DispDec
Char = " "
GOSUB DispChar
IF EnRecord = 0 THEN NoCountDisplay
DecData = RecordCount
DispMode = 2
GOSUB DispDec
NoCountDisplay:
DSAddr = DSIntervalAddr
IF (UPSW = 0) THEN
Interval = Interval + 1
DsData = Interval 'Save this value in the DS1302
GOSUB DS1302IN
Range = Units
ENDIF
IF (DNSW = 0)AND (Interval > MinInterval) THEN
Interval = Interval - 1
DsData = Interval 'Save this value in the DS1302
GOSUB DS1302IN
ENDIF
RETURN
'Generates one record of a file readable by EXCEL or NUMBERS
Record: 'Writes the time and date and the analog values into the SD chip
SEROUT SDOut,84,2,["S 1 8",CR] '80 ms timeout for missing bytes
SERIN SDIn,84,1000,closerecord,[Char] 'Wait for the ">" prompt
HIGH DS1302CE 'Making a daily filename
SHIFTOUT DS1302IO,CLK,0,[ReadMonthAddr\8]
SHIFTIN DS1302IO,CLK,1,[DECdata\8]
LOW DS1302CE
HIGH DS1302CE
SHIFTOUT DS1302IO,CLK,0,[ReadDateAddr\8]
SHIFTIN DS1302IO,CLK,1,[rem\8]
LOW DS1302CE
'Filenames for this device must start with a "/",names require 8.3 format, alpha-numeric only
'Files are created if non-existant, but directories must exist, separate with a "/"
SEROUT SDOut,84,["O 1 A /M",(MonTens + "0"),(MonUnits + "0"),(DayTens + "0"),(DayUnits + "0"),".CSV",CR]
SERIN SDIn,84,1000,closerecord,[Char] 'Wait for the ">" prompt, can be a long time for an open
IF Char <> ">" THEN closerecord 'An error message is returned, probably no SD chip inserted
DecData = RecordString 'Defines the date and time fields
Iter = 0
DO
READ (DECData + Iter),DSAddr
IF DSAddr = 0 THEN EndDateTime
IF DSAddr > $80 THEN 'This is an address in the DS1302, get the value from the RTC
HIGH DS1302CE
SHIFTOUT DS1302IO,CLK,0,[DSAddr\8]
SHIFTIN DS1302IO,CLK,1,[DSdata\8]
LOW DS1302CE
upperrem = Tens + "0"
lowerrem = Units + "0"
SEROUT SDOut,84,["W 1 2",CR,upperrem,lowerrem]
SERIN SDIn,84,1000,closerecord,[Char] 'Wait for the ">" prompt, no need to check it
ELSE
SEROUT SDOut,84,["W 1 1",CR,DSAddr] 'An ASCII character ("/",":" or ",")
SERIN SDIn,84,1000,closerecord,[Char] 'Wait for the ">" prompt
ENDIF
Iter = Iter+1
LOOP
EndDateTime:
'It's better to get the count right, if not, error on the high side and timeout
SEROUT SDOut,84,["W 1 29",CR,DEC4 Current,",",DEC4 Voltage]
RecDig = 1
GOSUB DispInputs
RecDig = 0
SEROUT SDOut,84,[CR,LF]
SERIN SDIn,84,1000,closerecord,[Char] 'Wait for the ">" prompt
NoPrompt = 0
GOSUB SoundBeep
GOTO RecOK
closerecord:
NoPrompt = 1
RecOK:
SEROUT SDOut,84,["C 1",CR] 'Close the file
DoneFlag = 1
RETURN
'Subroutines for the analog inputs
'Measures the current input and converts for display or recording according to the range
MeasureAmp:
READ AmpParams + 2 + (5*Range),Div 'The multiplier for this range, less 1
SelPort = ADCICE
DecData = 0
FOR Char = 0 TO Div 'Multiplication is performed by adding readings
HIGH En138 'Enable the TLC1549 Serial 10-bit ADC
rem = 0
SHIFTIN INPort,CLK,MSBPRE,[rem\10]
LOW En138
Decdata = rem + Decdata - ampoffset 'Accumulate readings and compensate for zero error
NEXT
IF SignBit = 1 THEN 'Avoids confusing display for zero input volts
DecData = 0
ENDIF
READ AmpParams + 3 +(5*range),Div 'The power of 2 to divide the accumulated value
Decdata = Decdata >> Div 'Divide by 2 to the power div
Current = Decdata 'Save for recording and display
RETURN
'Display the full-scale current value and set the range according to the external shunt
DispRange:
'First display the current full-scale value
READ AmpParams + 1 + (5*Range),DispMode 'How to display the full-scale value
READ AmpParams + (5*range),DECData 'The full-scale value
IF Decdata < 10 THEN
Decdata = 1000 * DECdata
ENDIF
GOSUB DispDec
Decdata = MAString
GOSUB DispBCDString
'Adjust the value as required by the external shunt resistor
DsAddr = DSRangeAddr
IF (UPSW = 0) AND (Range < MaxRange) THEN
Range = Range + 1
PAUSE 500
DsData = Range 'Save this value in the DS1302
GOSUB DS1302IN
ENDIF
IF (DNSW = 0) AND (Range > 0) THEN
Range = Range - 1
PAUSE 500
DsData = Range 'Save this value in the DS1302
GOSUB DS1302IN
ENDIF
RETURN
MeasureVolt: 'Measures and stores the voltage input
SelPort = ADCVCE
DecData = 0
FOR Iter = 0 TO 1 'Multiply by 2 the 10-bit value by summing 2 readings
HIGH En138 'Enable the TLC1549 Serial 10-bit ADC
rem = 0
SHIFTIN INPort,CLK,MSBPRE,[rem\10]
LOW En138
Decdata = rem + Decdata - voltoffset
NEXT
IF SignBit = 1 THEN 'Avoids confusing display for zero input volts
DecData = 0
ENDIF
Voltage = Decdata 'Save for recording and display
RETURN