Skip to content

Instantly share code, notes, and snippets.

@kcuzner
Created April 7, 2013 05:23
Show Gist options
  • Save kcuzner/5329144 to your computer and use it in GitHub Desktop.
Save kcuzner/5329144 to your computer and use it in GitHub Desktop.
A temperature logger written for the PIC16F628A
; A data logger utilizing a DS1631 Temperature Sensor, 24AA256 EEPROM, and a 32.767Khz crystal
;
; Kevin Cuzner
;Serial EEPROM format:
;0: Data length low byte
;1: Data length high byte
;2: RTC start hour
;3: RTC start minute
;4: RTC start second
;5: RTC start date
;6: RTC start month
;7: RTC start year
;...data...
;Serial EEPROM data format:
;n: flags: 0: normal temperature data (set) or power failure data (reset)
;If normal temperature data:
;n+1: Temperature byte
;n+2: Change in time from last reading high byte
;n+3: Change in time from last reading low byte
;If power failure data:
;n+1: Temperature byte
;n+2: new RTC hour
;n+3: new RTC minute
;n+4: new RTC second
;n+5: new RTC date
;n+6: new RTC month
;n+7: new RTC year
;When this starts up it compares the EEPROM start date to the date that the RTC says.
;If the RTC specifies a date before the one recorded in the EEPROM, the red light
;will blink in an on-on-off pattern continuously. This means
;the device has had a total power failure and the RTC is no longer accurate and it will not
;record any more data. Otherwise, unless bytes 0 and 1 are 0x0008 it will append a
;power failure record to the end of the data. If bytes 0 and 1 are 0x0008 it will begin
;recording temperature data. After doing any of those actions, the device will sleep for one
;second, wake up, write new data if the temperature has changed or if the dT exceedes 0xFFFF,
;go back to sleep for one second, and repeat the process. Every time it wakes up it will either
;flash the red or green light for .25 seconds. red = bad, green = good. If the red light is not
;flashing in the on-on-off pattern then there is no more room in memory. If it flashes green,
;another byte has been written or something is going on with the buttons.
;Any hardware error (such as a missing I2C component will result in a red-green flashing light
;The function of the two buttons are as follows:
;Hold down 1 until green flashes, continue holding one, and hold down two until green flashes: Reset memory to beginning. This will be followed by 3 quick flashes in green-red-green
;Hold down 2 until green flashes, continue holding two, and hold down one until green flashes: Start recording data. Will be followed by 3 quick flashes in green-red-red
;Hold down 2 until green flashes, release two, and hold down one until green flashes: Stop recording data. Will be followed by 3 quick flashes in green-green-red
#include "P16F628A.INC"
__config _INTOSC_OSC_CLKOUT & _WDT_OFF & _LVP_OFF & _CP_OFF
cblock 0x20
isrStat ;isr var
i2cStat ;i2c vars
i2cI
i2cJ
i2cK
i ;counters
j
k
lastHour ;stores the last stored time and also acts as rtc temps
lastMinute
lastSecond
lastDate
lastMonth
lastYear
rtcHour
rtcMinute
rtcSecond
rtcDate
rtcMonth
rtcYear
bcdTemp ;temps for the bcd conversion
bcdTemp2
eepromTemp2 ;can be used by all eeprom routines
eepromTemp3
eepromTemp4
eepromAddrL ;set these to wherever the external EEPROM routines should start writing
eepromAddrH
endc
cblock 0x70
isrW
i2cTemp
eepromTemp
endc
#define SDA PORTB, 1 ;i2c data
#define SCK PORTB, 0 ;i2c clock
#define RED PORTB, 2 ;red LED
#define GREEN PORTB, 3 ;green LED
#define BTN1 PORTB, 4 ;button 1
#define BTN2 PORTB, 5 ;button 2
#define TRIGGER PORTB, 7 ;oscilloscope trigger
#define DS1631R 0x91 ;read ds1631 address
#define DS1631W 0x90 ;write ds1631 address
#define DS1631_START 0x51 ;start command for ds1631
#define DS1631_STOP 0x22 ;stop command for ds1631
#define DS1631_READT 0xAA ;read temperature command for ds1631
#define DS1631_TH 0xA1 ;access tempH command for ds1631
#define DS1631_TL 0xA2 ;access tempL command for ds1631
#define DS1631_CONFIG 0xAC ;access comfig command for ds1631
#define DS1631_POR 0x54 ;software POR command for ds1631
#define EEPROMR 0xA1 ;read eeprom address
#define EEPROMW 0xA0 ;write eeprom address
#define DS1337R 0xD1 ;read rtc address
#define DS1337W 0xD0 ;write rtc address
org 0
;call EraseExternalEEPROM
;goto FatalError
;goto Start
goto TestRoutine
org 4
movwf isrW ;store W
swapf STATUS, W ;grab status
bcf STATUS, RP0
movwf isrStat ;store Status
goto ISR
TestRoutine
call I2CInit
bsf STATUS, RP0
bcf TRIGGER
bcf GREEN
bcf STATUS, RP0
clrw i
clrf eepromAddrL
clrf eepromAddrH
t movf i, W
bsf TRIGGER
bcf TRIGGER
;call WriteExternalEEPROM
call ReadExternalEEPROM
;movwf j
;movf i, W
;xorwf j, W
;bcf GREEN
;btfsc STATUS, Z
;bsf GREEN
incf i
goto t
Start ;startup routine
;first, read from the internal eeprom. if the first 4 bytes do not equal 0x0315202A then we need to reset the entire chip including the external memory
;temperature sensor setup
call I2CStart
movlw DS1631W ;we will write the temp sensor
call I2CWrite
movwf i
btfss i, 0 ;was it there?
goto FatalError ;no.
movlw DS1631_CONFIG ;we will write to the config
call I2CWrite
movlw 0x02 ;8-bit resolution, continuous conversion, polarity high
call I2CWrite
call I2CStop
call I2CStart
movlw DS1631W ;writing again
call I2CWrite
movlw DS1631_START ;start conversion
call I2CWrite
movwf i
btfss i, 0 ;did it acknowlege?
goto FatalError ;no.
call I2CStop
;read the first 8 bytes off the eeprom
call I2CStart
movlw EEPROMW ;set eeprom address
call I2CWrite
movwf i
btfss i, 0 ;was it there?
goto FatalError ;no.
clrw
call I2CWrite ;set the read to be at 0
clrw
call I2CWrite
call I2CRestart ;restart the bus
movlw EEPROMR ;read eeprom
call I2CWrite
movlw 1
call I2CRead ;read the first 8 bytes
movwf i
movlw 1
call I2CRead
movwf j
movlw 1
call I2CRead
movwf lastHour
movlw 1
call I2CRead
movwf lastMinute
movlw 1
call I2CRead
movwf lastSecond
movlw 1
call I2CRead
movwf lastDate
movlw 1
call I2CRead
movwf lastMonth
movlw 0 ;this is the last byte
call I2CRead
movwf lastYear
call I2CStop
;first, is the current address set to a valid value? (0x0008-0x7FFF)
movf j, W ;grab the high byte
sublw 0x7F
btfss STATUS, C ;did we borrow?
goto FirstTimeRun ;yes. this value is too big
movf j, W ;grab the high byte again
xorlw 0 ;is it 0?
btfsc STATUS, Z
goto $ + 2 ;yes. check to see if it is greater than 0x0008
goto $ + 5 ;no. we are done checking
movf i, W ;grab the low byte
sublw 0x08
btfsc STATUS, C ;did we borrow?
goto FirstTimeRun ;no.this value is too big
;now read the current time off of the RTC
call I2CStart
movlw DS1337R
call I2CWrite
movwf i
btfss i, 0 ;was it there?
goto FatalError ;no.
call I2CStop
call ReadRTCTime ;actually read the time
;now compare the two times. the fastest way to do this is to go from the year down
movf lastYear, W ;grab lastYear
subwf rtcYear, W ;lastYear - rtcYear
btfsc STATUS, C ;did borrowing occur?
goto PowerLossError ;no, the rtcYear is less than lastYear. error.
movf lastMonth, W ;grab lastMonth
bsf STATUS, RP0
bcf GREEN
bcf STATUS, RP0
bsf GREEN
call MediumDelay
bcf GREEN
call MediumDelay
goto $ - 4
ISR
swapf isrStat, W
movwf STATUS
swapf isrW
swapf isrW, W
retfie
Main ;the main program loop...not much in the loop here
goto $
FirstTimeRun ;this will set up the device for its first run
bsf STATUS, RP0
bcf GREEN
bcf STATUS, RP0
bcf RED
bsf GREEN
call MediumDelay
bcf GREEN
call MediumDelay
goto $ - 4
PowerLossError ;un-recoverable power loss error, clock needs reprogramming
call I2CStop
bsf STATUS, RP0 ;bank 1
bcf RED ;red & green outputs
bcf GREEN
bcf STATUS, RP0 ;bank 0
PowerLossFlash
bsf RED
call MediumDelay
bcf RED
call MediumDelay
bsf RED
call MediumDelay
bcf RED
call MediumDelay
call MediumDelay
call MediumDelay
goto PowerLossFlash
FatalError ;un-recoverable error (hardware failure)
call I2CStop
bsf STATUS, RP0 ;bank 1
bcf RED ;red & green outputs
bcf GREEN
bcf TRIGGER
bcf STATUS, RP0 ;bank 0
clrf eepromAddrL
clrf eepromAddrH
movlw 0xAA
call WriteExternalEEPROM
FatalFlash
bsf RED
bcf GREEN
clrf eepromAddrL
clrf eepromAddrH
bsf TRIGGER
bcf TRIGGER
call ReadExternalEEPROM
call MediumDelay
bcf RED
bsf GREEN
call MediumDelay
goto FatalFlash ;continue flashing
I2CInit ;initializes the I2C bus
swapf STATUS, W
bcf STATUS, RP0 ;bank 0
movwf i2cStat
bsf STATUS, RP0 ;bank 1
bsf SCK ;clock high
bsf SDA ;data high
bcf STATUS, RP0 ;bank 0
swapf i2cStat, W
movwf STATUS
return
I2CStart ;starts the I2C bus and sets up pins accordingly
swapf STATUS, W
bcf STATUS, RP0 ;bank 0
movwf i2cStat
bsf STATUS, RP0 ;bank 1
bsf SDA ;release data
bsf SCK ;grab clock
bcf SDA ;grab data
bcf SCK ;grab clock
bcf STATUS, RP0 ;bank 0
bcf SDA ;data low
bcf SCK ;clock low
swapf i2cStat,W
movwf STATUS
return
I2CStop ;stops the I2C bus and sets up pins accordingly. enters bank 0
swapf STATUS, W
bcf STATUS, RP0 ;bank 0
movwf i2cStat
bsf STATUS, RP0 ;bank 1
bsf SCK ;release clock
bsf SDA ;release data
bcf STATUS, RP0 ;bank 0
swapf i2cStat, W
movwf STATUS
return
I2CRestart ;restarts the I2C bus
swapf STATUS, W
bcf STATUS, RP0 ;bank 0
movwf i2cStat
bsf STATUS, RP0 ;bank 1
bsf SDA ;release data
bsf SCK ;release clock (this creates neither a start nor stop)
bcf SDA ;grab data
bcf SCK ;grab clock
bcf STATUS, RP0 ;bank 0
bcf SDA ;make data low
bcf SCK ;make clock low (start condition)
swapf i2cStat, W
movwf STATUS
return
I2CWrite ;writes W to the bus and returns if an ACK was recieved
movwf i2cTemp
swapf STATUS, W
bcf STATUS, RP0 ;bank 0
movwf i2cStat
movlw 8
movwf i2cI
I2CWrite_bitbang
call I2CClockLow ;clock goes low
btfsc i2cTemp, 7
goto $ + 2
goto $ + 3
bsf SDA
goto $ + 2
bcf SDA
call I2CClockHigh ;clock goes high
rlf i2cTemp ;rotate left
decfsz i2cI ;repeat 8 times
goto I2CWrite_bitbang
call I2CClockLow ;clock goes low
bsf STATUS, RP0 ;bank 1
bsf SDA ;data input
bcf STATUS, RP0 ;bank 0
clrf i2cTemp ;prepare for a nack
call I2CClockHigh ;clock goes high
btfss SDA ;is data low?
incf i2cTemp ;yes (it is an ack)
call I2CClockLow ;clock goes low
btfss SDA ;is data still low?
goto $ - 1 ;it needs to be high before we continue
bsf STATUS, RP0 ;bank 1
bcf SDA ;data output
bcf STATUS, RP0 ;bank 0
bcf SDA ;data ends low
swapf i2cStat, W
movwf STATUS ;restore status
movf i2cTemp, W ;return the ack state
return
I2CRead ;reads into W. If W:0 is set when this function starts, an ACK is sent. Otherwise, NACK is sent.
movwf i2cTemp
swapf STATUS, W
bcf STATUS, RP0 ;bank 0
movwf i2cStat
movlw 8
movwf i2cI
clrf i2cJ ;we will stick the thing read into j
bsf STATUS, RP0 ;bank 1
bsf SDA ;data input
bcf STATUS, RP0 ;bank 0
call I2CClockLow ;clock goes low
call I2CClockHigh ;clock goes high
rlf i2cJ ;rotate j for this bit
btfsc SDA ;is data 1 or 0?
bsf i2cJ, 0
call I2CClockLow ;clock goes low
decfsz i2cI ;repeat 8 times
goto $ - 8
bsf STATUS, RP0 ;bank 1
bcf SDA ;data output
bcf STATUS, RP0 ;bank 0
bsf SDA
btfsc i2cTemp, 0 ;should we send an ack?
bcf SDA ;yes
call I2CClockHigh ;cycle the clock
nop
call I2CClockLow
bcf SDA ;end with SDA low
movf i2cJ, W ;move what we read into temp so it can be accessed anywhere
movwf i2cTemp
swapf i2cStat, W
movwf STATUS
movf i2cTemp, W ;return what we read
return
I2CClockHigh ;set the clock to high (must be in bank 0)
bsf STATUS, RP0 ;bank 1
bsf SCK ;clock input (lets it rise to 1)
bcf STATUS, RP0 ;bank 0
btfss SCK ;is the clock high yet?
goto $ - 1 ;no.
return
I2CClockLow ;set the clock to low (must be in bank 0)
bsf STATUS, RP0 ;bank 1
bcf SCK ;clock output (lets it fall)
bcf STATUS, RP0 ;bank 0
bcf SCK ;down it goes
nop ;match high
return
ConvertBCD ;converts nibble-oriented bcd in w into a number and returns it in w
movwf bcdTemp
movwf bcdTemp2
rrf bcdTemp ;rotate to the top nibble
rrf bcdTemp
rrf bcdTemp
rrf bcdTemp
movf bcdTemp ;get the status of bcdTemp
movlw 10
btfss STATUS, Z ;is bcdTmp zero?
goto $ + 4 ;yes. we are done here
addwf bcdTemp2 ;no. add 10 to bcdTemp2
decfsz bcdTemp
goto $ - 2 ;keep adding 10 until it is zero
movf bcdTemp, W ;grab the final answer into W
return
ReadRTCTime ;reads and updates the time off the rtc
call I2CStart
movlw DS1337W ;set read pointer
call I2CWrite
movwf i
btfss i, 0 ;was it there?
goto FatalError ;no.
clrw
call I2CWrite ;clear the read pointer
call I2CRestart ;restart the bus
movlw DS1337R ;read the rtc
call I2CWrite
movlw 1
call I2CRead ;read seconds
call ConvertBCD
movwf rtcSecond
movlw 1
call I2CRead ;read minutes
call ConvertBCD
movwf rtcMinute
movlw 1
call I2CRead ;read hours
andlw 0x3F ;chop off the 12/24 bit
call ConvertBCD
movwf rtcHour
movlw 1
call I2CRead ;read day of week (not stored)
movlw 1
call I2CRead ;read date
call ConvertBCD
movwf rtcDate
movlw 1
call I2CRead ;read month
andlw 0x7F ;chop off the century bit (we don't care about that)
call ConvertBCD
movwf rtcMonth
movlw 0
call I2CRead ;read year
call ConvertBCD
movwf rtcYear
call I2CStop ;all done!
MediumDelay ;1/2 second delay
movlw 0xA7
movwf i
movlw 0x62
movwf j
decfsz i, f
goto $+2
decfsz j, f
goto $ - 3
return
ReadInternalEEPROM ;reads the current internal eeprom data at the selected address and returns it in w
bsf STATUS, RP0 ;bank 1
bsf EECON1, RD
movf EEDATA, W
bcf STATUS, RP0 ;bank 0
return
WriteInteralEEPROM ;writes the current internal eeprom data at the selected address with the value in w
bsf STATUS, RP0 ;bank 1
bsf EECON1, WREN
movwf EEDATA
btfsc INTCON, GIE ;are interrupts on?
goto $ + 10 ;yes
movlw 0x55
movwf EECON2 ;write 55h
movlw 0xAA
movwf EECON2 ;write AAh
bsf EECON1, WR ;now write it
btfsc EECON1, WR ;are we still writing?
goto $ - 1 ;yes
bcf EECON1, WREN
bcf STATUS, RP0
return ;we are done for the section that doesn't have interrupts
bcf INTCON, GIE ;part if interrupts are enabled:
movlw 0x55
movwf EECON2 ;write 55h
movlw 0xAA
movwf EECON2 ;write AAh
bsf EECON1, WR ;now write it
btfsc EECON1, WR ;are we still writing?
goto $ - 1 ;yes
bcf EECON1, WREN
bsf INTCON, GIE
bcf STATUS, RP0
return ;we are done for the section that has interrupts
EraseExternalEEPROM ;sets the entire external eeprom to 0xFF for each byte
movlw 4 ;number of pages to write (4 for a 256kbit eeprom)
movwf eepromTemp
clrw ;starting address
movwf eepromTemp2 ;high
movwf eepromTemp3 ;low
call I2CStart ;set the current address
movlw EEPROMW
call I2CWrite
movf eepromTemp2, W
call I2CWrite
movf eepromTemp3, W
call I2CWrite
movlw 64 ;start writing 64 0xFF's to the page
movwf eepromTemp4
movlw 0xFF
call I2CWrite
decfsz eepromTemp4 ;repeat 64 times
goto $ - 3
decfsz eepromTemp ;repeat the page write 4 times
goto $ + 2 ;increment the address
goto $ + 7 ;we are done
movlw 64 ;add 64 to the address
addwf eepromTemp3
btfsc STATUS, C ;did we carry?
incf eepromTemp2 ;yes.
call I2CStop ;stop the bus
goto $ - 21 ;repeat around
call I2CStop ;stop the bus for the last time
return
WriteExternalEEPROM ;writes one byte in W to the external eeprom in eepromAddrL and eepromAddrH. returns if successful
movwf eepromTemp ;store what we are writing
swapf STATUS, W ;save the status
bcf STATUS, RP0 ;bank 0
movwf eepromTemp2
movlw 0xFF ;we will try this many times to get it to acknowlege
movwf eepromTemp3
call I2CStart
movlw EEPROMW
call I2CWrite
movwf eepromTemp4
btfss eepromTemp4, 0 ;did it acknowledge?
goto $ + 2
goto $ + 5
call I2CStop ;it didn't acknowledge, so ask it again
decfsz eepromTemp3 ;make sure we don't get in an endless loop
goto $ - 9
goto WriteExternalEEPROM_Error ;well, it never acknowledged. error.
movf eepromAddrL, W
call I2CWrite ;write the address
movf eepromAddrH, W
call I2CWrite
movf eepromTemp, W
call I2CWrite ;write the value to the eeprom
call I2CStop
swapf eepromTemp2, W ;restore everything
movwf STATUS
movlw 1 ;we were successful
return
WriteExternalEEPROM_Error ;there was an error
swapf eepromTemp2, W
movwf STATUS
clrw
return
ReadExternalEEPROM ;reads one byte into w at the address in eepromAddrL and eepromAddrH
swapf STATUS, W ;store status
bcf STATUS, RP0
movwf eepromTemp2
movlw 0xFF ;we will try this many times to get it to acknowlege
movwf eepromTemp3
call I2CStart
movlw EEPROMW
call I2CWrite
movwf eepromTemp4
btfss eepromTemp4, 0
goto $ + 2
goto $ + 5
call I2CStop
decfsz eepromTemp3 ;make sure we don't get stuck in an endless loop
goto $ - 9
goto ReadExternalEEPROM_Error
movf eepromAddrL, W
call I2CWrite
movf eepromAddrH, W
call I2CWrite
call I2CStart ;this actually restarts the bus
;movlw EEPROMR
;call I2CWrite
;clrw
;call I2CRead
;movwf eepromTemp
call I2CStop
swapf eepromTemp2, W
movwf STATUS
;movf eepromTemp, W ;and return it
movlw 0xA5
return
ReadExternalEEPROM_Error
swapf eepromTemp2, W
movwf STATUS
clrw
return
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment