Created
April 7, 2013 05:23
-
-
Save kcuzner/5329144 to your computer and use it in GitHub Desktop.
A temperature logger written for the PIC16F628A
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; 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