;******************************************************************************* ;** ;** Base code for building data logging device. ;** Memory device designed for 24C64, 8K byte EEPROM ;** ;** Written by Rick Huang, Copyright (C) 2004 ;** Revision YYYY/MM/DD ;** Rev 1. 2004/04/30 - Initial release, code base adapted from USB base code ;** Rev 2. 2004/10/09 - Added ability to capture temperature data off TC-77 sensor ;** Rev 3. 2004/10/10 - Add reliable transfer routine that make bulk transfer reliable ;** Rev 4. 2005/03/19 - Move the system to use dual clock, so data xfer is faster ;** - Also rename the file to V2, as wiring is different ;** ;** ;** Command Functions ;** '1' External EEprom read one byte (Input 16bit address) ;** '2' External EEprom write one byte (Input 16bit address + 8bit data) ;** '3' External EEprom dump data (1024 bytes) ;** '4' Hex test - echo any hex input ;** '5' Thermo Sensor Reading (Output 13 bit data) ;** '6' Clear Timer, Also disable data acq routine ;** '7' Read Timer, Also enable data acq (Output 24bit data in seconds) ;** '8' ;** '9' Clear EEprom, Write 0xff into all the memory ;** ;** ;** This program is free software; you can redistribute it and/or modify ;** it under the terms of the GNU General Public License as published by ;** the Free Software Foundation; either version 2 of the License, or ;** (at your option) any later version. ;** ;** This program is distributed in the hope that it will be useful, ;** but WITHOUT ANY WARRANTY; without even the implied warranty of ;** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;** GNU General Public License for more details. ;** ;** You should have received a copy of the GNU General Public License ;** along with this program; if not, write to the Free Software ;** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ;******************************************************************************* ;**************************************************************************** ;** NOTE: Disable the WDT when debugging the code, however, make sure ;** the WDT is enabled when putting the system into life time test ;** The timeout for WDT is about 2 seconds with 128:1 prescaler ;** (Current version of the code can not use WDT, see note below) ;** ;** The version 2 of the code move the chip communication to PortA, as ;** portB is required for the second clock ;** The CPU will go into sleep in 1min if capture is enabled, when ;** in sleep mode, UART is not running, so no computer control is available ;** Due to the use of sleep mode, the WDT can not be use anymore. ;**************************************************************************** include __config _XT_OSC & _WDT_OFF & _LVP_OFF & _BODEN_OFF & _MCLRE_OFF & _PWRTE_OFF ;***************************************************************** ;** Variables TempW EQU 20h ; ISR use CounterA EQU 21h CounterB EQU 22h SerData EQU 23h Temp EQU 24h Output EQU 25h Counter EQU 26h Zero EQU 27h xEEAddrH EQU 28h ; External EEprom stuff xEEAddrL EQU 29h xEEData EQU 2ah ErrorOut EQU 2bh TempH EQU 2ch TempL EQU 2dh TempCnt EQU 2eh TimeL EQU 30h TimeM EQU 31h TimeH EQU 32h WriteAddrL EQU 33h WriteAddrH EQU 34h RunState EQU 35h RunEnable EQU 36h Rchar EQU 37h Rcount EQU 38h Minute EQU 39h RunState2 EQU 3ah IsSleep EQU 3bh TempSTAT EQU 3ch ; Temp STATUS, ISR use DataBuff EQU 0a0h ; Continuous data buffer for 80 bytes #define _SCL PORTA, 3 ; I2C Clock #define _SCLi INDF, 3 ; I2C Clock on INDF #define _SDA PORTA, 2 ; I2C Data #define _SDAi INDF, 2 ; I2C Data on INDF #define _SCK PORTA, 1 ; SPI Clock #define _SIO PORTA, 0 ; SPI Data #define _SIOi INDF, 0 ; SPI Data direction on INDF #define _TCS PORTB, 3 ; Temperature Sensor select #define _TX PORTB, 2 ; TX for serial Out #define _FASTLO PORTB, 0 ; Fast load ;***************************************************************** ;** Program code here goto Start ORG 4 ;waiting for <0004> ;interrupt vector goto Interrupt ORG 5 ;***************************** DATA StringStart: PWelcomeMsg: addwf PCL, F DT "Welcome!" DT H'0d', H'0a', H'00' PCommandMsg: DT "Command Error?" DT H'0d', H'0a', H'00' PParameterMsg: DT "Parameter Error?" DT H'0d', H'0a', H'00' PNoAckMsg: DT "Missing Ack" DT H'0d', H'0a', H'00' StringEnd: WelcomeMsg EQU 0 CommandMsg EQU PCommandMsg - 1 - 5 ; 5 is the offset where the ParameterMsg EQU PParameterMsg - 1 - 5 ; Strings start NoAckMsg EQU PNoAckMsg - 1 - 5 HexTable: addwf PCL, F DT "0123456789ABCDEF" ;***************************************************************** ;******************** Program Starts here Start: ;********************* Setup WDT clrwdt bsf STATUS, RP0 movlw b'00001111' ;Prescaler to WDT, 1:128 movwf OPTION_REG clrwdt ;********************* Setting up environment movlw b'00000010' ;Serial port in too movwf TRISB ;Set port B I/O movlw b'00000100' movwf TRISA ;Set port A to all output bcf STATUS, RP0 clrf PORTA ;********************** Disable 16f628 specific features movlw b'00000111' ;Disable the analog section movwf CMCON ;********************** Setup UART first bsf STATUS, RP0 movlw d'12' ;set to 6 for 300 -> 32768Hz Clock, High speed ;set to 12 for 19200 -> 4Mhz Clock, High speed movwf SPBRG ;The only rate with low mismatch movlw b'10100111' ;Async 8-bit @ High speed movwf TXSTA bcf STATUS, RP0 movlw b'10110000' ;Enable serial port movwf RCSTA clrf Zero ;*********************** Dumping data fast ;btfss _FASTLO ;goto FastXfer ;*********************** Initialize Timer1 Oscillator bsf T1CON, T1OSCEN ;*********************** Initialize Timer interrupt, 32768Hz clock, 1 sec interrupt using Timer1 movlw 0fch movwf TMR1H movlw 0ffh movwf TMR1L ;Set to overflow in 32768 / 4 = 8192 cycles movlw b'00101111' ;Enable Timer1, prescaler = 1/4, Not Sync movwf T1CON bsf STATUS, RP0 ;2nd bank bsf PIE1, TMR1IE ;Enable Timer 1 interrupt bcf STATUS, RP0 bcf PIR1, TMR1IF ;Clear TMR1 int flag bsf INTCON, PEIE ;Enable peripheral interrupt bsf INTCON, GIE ;Enable all interrupt ;*********************** Initialize Variable clrf IsSleep movlw d'60' movwf Minute ;Not used movlw d'60' movwf RunState ;1min counter movlw d'10' movwf RunState2 ;10min per sample clrf TimeL clrf TimeM clrf TimeH clrf WriteAddrL clrf WriteAddrH bsf _TCS ;Disable Temperature sensor to start movlw 01h movwf RunEnable ;----------------------- call Delay_1ms movlw WelcomeMsg ;Display welcome string on startup call SendString ;----------------------- Wait for command WaitMore: clrwdt movlw TXSTA movwf FSR btfss INDF, TRMT ;Check if there is anything sending goto WaitMore ;Wait till everything is sent btfsc IsSleep, 0 sleep ;Go to sleep only if enabled, and nothing is sending btfsc RCSTA, OERR goto ClearError btfss PIR1, RCIF goto WaitMore movfw RCREG movwf SerData movwf TXREG ;Send it back out call SSpace movlw '1' ;Decode command xorwf SerData, W btfsc STATUS, Z goto ReadxEEone ;External EEprom read one byte movlw '2' xorwf SerData, W btfsc STATUS, Z goto WritexEEone ;External EEprom write one byte movlw '3' xorwf SerData, W btfsc STATUS, Z goto ReadxEEburst ;Dump EEprom data movlw '4' xorwf SerData, W btfsc STATUS, Z goto HexTest ;Testing HEX routine movlw '5' xorwf SerData, W btfsc STATUS, Z goto ReadThermo ;Take a temperature reading movlw '6' xorwf SerData, W btfsc STATUS, Z goto ClearTimer ;Clear Timer movlw '7' xorwf SerData, W btfsc STATUS, Z goto ReadTimer ;Read Timer Value movlw '8' xorwf SerData, W btfsc STATUS, Z goto CmdSendString ;Not in use - TESTING movlw '9' xorwf SerData, W btfsc STATUS, Z goto ClearEE ;Clear EEprom DummyNow: ;For all command not in use movlw CommandMsg call SendString ;Send error command message goto WaitMore ;********************* Clear receving overrun error ClearError: movfw RCREG movfw RCREG bcf RCSTA, CREN bsf RCSTA, CREN goto WaitMore ;---------------------- Commands execution HexTest: call RcvHex ;Command 4: Hex test movfw Output call SendHex call NewLine goto WaitMore ;************************************** External Memory routines ReadxEEone: call RcvHex ;Command 1: xEEprom read movfw Output movwf xEEAddrH call RcvHex movfw Output movwf xEEAddrL ;Read in 2 byte of address call SSpace clrf ErrorOut ;Clear error code call xEEreadsingle movfw xEEData call SendHex call SSpace btfss ErrorOut, 0 goto ReeNoError movlw NoAckMsg ;If Error, send message call SendString ReeNoError: call NewLine goto WaitMore ;***************************** WritexEEone: call RcvHex ;Command 2: xEEprom write movfw Output movwf xEEAddrH call RcvHex movfw Output movwf xEEAddrL ;Read in 2 byte of address call SSpace clrf ErrorOut ;Clear error code call RcvHex movfw Output movwf xEEData call xEEwritesingle call SSpace btfss ErrorOut, 0 goto WeeNoError movlw NoAckMsg ;If Error, send message call SendString WeeNoError: call NewLine goto WaitMore ;***************************** ReadxEEburst: ;Command 3: xEEprom Burst clrf RunEnable ;No data capture during dump call RcvHex movfw Output movwf xEEAddrH call RcvHex movfw Output movwf xEEAddrL ;Read in 2 byte of address call SSpace clrf ErrorOut ;Clear error code call xEEreadsingle call NewLine movfw xEEData call SendHex call xEEreadNext ;Dump 2 byte at a time movfw xEEData call SendHex btfsc ErrorOut, 0 goto BurstReeError ;Check for error btfsc ErrorOut, 0 goto BurstReeError call NewLine ;Loop for 2048-2 times here movlw 10h movwf CounterA movlw 0feh movwf CounterB BurstOuterLoop: BurstInnerLoop: call xEEreadNext ;Dump 2 byte at a time movfw xEEData call SendHex btfsc ErrorOut, 0 goto BurstReeError ;Check for error call xEEreadNext movfw xEEData call SendHex btfsc ErrorOut, 0 goto BurstReeError ;Check for error call NewLine decfsz CounterB, F goto BurstInnerLoop decfsz CounterA, F goto BurstOuterLoop goto WaitMore ;Exit when done BurstReeError: movlw NoAckMsg ;If Error, send message call SendString call NewLine goto WaitMore ;***************************** ClearEE: ;Command 9: Clear EEprom movlw 1fh movwf CounterA movlw 0feh movwf CounterB clrf xEEAddrH clrf xEEAddrL movlw 0ffh movwf xEEData EraseEEloop: call xEEwriteWait call xEEwritesingle incf xEEAddrL, F btfsc STATUS, Z incf xEEAddrH, F decfsz CounterB, F goto EraseEEloop decfsz CounterA, F goto EraseEEloop movlw 'C' call SendChar movlw 'l' call SendChar movlw 'e' call SendChar movlw 'a' call SendChar movlw 'r' call SendChar call NewLine goto WaitMore ;***************************** CmdSendString: ;Command 8: Send ASCII string Again: btfss PIR1, RCIF goto Again movfw RCREG movwf TXREG ; Send it back out movlw 1bh xorwf RCREG, W btfsc STATUS, Z goto ExitSendString ; If ESC is pressed, exit the routine goto Again ExitSendString: call NewLine ; ESC character mess up the first newline call NewLine goto WaitMore LocSendCommand goto WaitMore ;************************************** Command 5, Take a thermo reading ReadThermo: call TakeTempReading call SSpace call SSpace movfw TempH call SendHex movfw TempL call SendHex call NewLine goto WaitMore ;************************************** Command 6, Clear Timer ClearTimer: clrf RunEnable call SSpace call SSpace clrf TimeL clrf TimeM clrf TimeH movlw 'C' call SendChar movlw 'l' call SendChar movlw 'e' call SendChar movlw 'a' call SendChar movlw 'r' call SendChar call NewLine goto WaitMore ;************************************** ReadTimer: bsf RunEnable, 0 call SSpace call SSpace movfw TimeH ;Command 7: Read Timer Value call SendHex call SSpace movfw TimeM call SendHex call SSpace movfw TimeL call SendHex call NewLine goto WaitMore ;**************************************** ;***************************** INTERRUPT Interrupt: movwf TempW swapf STATUS, W ; Save STATUS reg too movwf TempSTAT movlw 20h subwf TMR1H, F ;--------------------------- Runs once per minute btfss RunEnable, 0 ; Check to see if data acq is on goto IntNoRun decfsz RunState, F goto IntNoRun movlw d'60' ; 1min loop movwf RunState bsf IsSleep, 0 ; Start PowerSave mode after 1min decfsz RunState2, F goto IntNoRun movlw d'10' movwf RunState2 ; 10min per sample call TakeTempReading movfw WriteAddrH movwf xEEAddrH movfw WriteAddrL movwf xEEAddrL movfw TempH movwf xEEData call xEEwritesingle movfw TempH ; Between two write, use the time to send debug info call SendHex movfw TempL call SendHex movlw ' ' call SendChar ; Finish of debug info, write the next byte movfw TempL movwf xEEData incf xEEAddrL, F ; Always odd number, will not carry call xEEwriteWait ; Wait for the last write to finish call xEEwritesingle incf WriteAddrL, F ; Always odd number, will not carry incf WriteAddrL, F ; Even number, might carry btfsc STATUS, Z incf WriteAddrH, F movlw 20h xorwf WriteAddrH, W btfss STATUS, Z goto IntEENotFull ; If EEprom is FULL, disable run clrf RunEnable IntEENotFull: IntNoRun: ;-------------------------- incfsz TimeL, F ; Runs once per second goto ExitInt incfsz TimeM, F goto ExitInt incf TimeH, F ExitInt: clrwdt bcf PIR1, TMR1IF swapf TempSTAT, W movwf STATUS ;Restore STATUS swapf TempW, F swapf TempW, W retfie ;***************************** SUBROUTINE ;----------------------------------------------------------------- ; xEEreadsingle: Read a single byte from external EEprom ; Input in xEEAddrH/L (16bit), Output in W xEEreadsingle: call xI2CInit call xI2CStart movlw b'10100000' ; Write, I2C Address 1010-000 call xI2COutByte ; Write is used to set read addr btfsc ErrorOut, 0 ; If error, exit right away goto xEEReadErrorExit movfw xEEAddrH call xI2COutByte btfsc ErrorOut, 0 ; If error, exit right away goto xEEReadErrorExit movfw xEEAddrL call xI2COutByte btfsc ErrorOut, 0 ; If error, exit right away goto xEEReadErrorExit call xI2CStop nop nop nop nop nop nop nop nop call xI2CStart movlw b'10100001' ; Read, I2C Address 1010-000 call xI2COutByte ; btfsc ErrorOut, 0 ; If error, exit right away goto xEEReadErrorExit call xI2CInByte movwf xEEData xEEReadErrorExit: call xI2CStop return ;----------------------------------------------------------------- ; xEEreadNext: Read a single byte from external EEprom ; Output in W, Addr is auto increment xEEreadNext: call xI2CStart movlw b'10100001' ; Read, I2C Address 1010-000 call xI2COutByte ; btfsc ErrorOut, 0 ; If error, exit right away goto xEENextReadError call xI2CInByte movwf xEEData xEENextReadError: call xI2CStop return ;----------------------------------------------------------------- ; xEEwritesingle: Write a single byte to external EEprom ; Input in xEEAddrH/L (16bit), Data in xEEData xEEwritesingle: call xI2CInit call xI2CStart movlw b'10100000' ; Write, I2C Address 1010-000 call xI2COutByte btfsc ErrorOut, 0 ; If error, exit right away goto xEEWriteErrorExit movfw xEEAddrH call xI2COutByte btfsc ErrorOut, 0 ; If error, exit right away goto xEEWriteErrorExit movfw xEEAddrL call xI2COutByte btfsc ErrorOut, 0 ; If error, exit right away goto xEEWriteErrorExit movfw xEEData call xI2COutByte xEEWriteErrorExit: call xI2CStop return ;----------------------------------------------------------------- ; xEEwriteWait: Wait for the EEprom to finish writing ; Input none xEEwriteWait: call xI2CInit xEEwriteWaitLoop: call xI2CStart movlw b'10100000' ; Write, I2C Address 1010-000 call xI2COutByte call xI2CStop btfsc ErrorOut, 0 ; If error, EEprom is writing, wait more goto xEEwriteWaitLoop return ;----------------------------------------------------------------- ; xI2C routines ; xI2CInit - Initialize external I2C bus xI2CInit: movlw TRISA ; Use INDR, make it easier to access movwf FSR bcf _SCLi ; Set SCL to output bcf _SDA ; Always preset SDA to zero bsf _SCL ; Prior to start condition, reset everything nop nop nop nop nop nop nop nop bsf _SDAi ; Stop condition nop nop nop nop nop nop nop nop return ; xI2CStart - Issue a start condition xI2CStart: bcf _SDA ; Always preset SDA to zero bcf _SDAi ; Start condition nop nop nop nop nop nop nop nop nop ; min 600ns for start bcf _SCL return ; xI2CStop - Issue a stop condition xI2CStop: bcf _SDA bcf _SDAi ; SDA must be low to bsf _SCL ; init a stop condition nop nop nop nop nop nop nop nop nop bsf _SDAi ; Stop condition return ;-------------------------------------------------------- ; xI2COutByte - Master send a byte, data in W. ; Register used: Temp, Counter xI2COutByte: movwf Temp movlw d'8' movwf Counter xI2COutLoop: rlf Temp, F bcf _SDA bsf _SDAi btfss STATUS, C bcf _SDAi nop nop nop nop nop bsf _SCL nop nop nop nop nop bcf _SCL decfsz Counter, F goto xI2COutLoop bsf _SDAi ; Read Ack bit nop nop nop nop bsf _SCL nop nop nop nop bcf ErrorOut, 0 btfsc _SDA bsf ErrorOut, 0 ; Ack bit in (ErrorOut, 0) bcf _SCL nop nop nop nop return ; 1200nS for each SCL low ;--------------------------------------------------------- ; xI2COutByte - Master receive a byte, data in W. ; Register used: Temp, Counter xI2CInByte: bsf _SDAi movlw d'8' movwf Counter xI2CInLoop: bsf _SCL nop nop nop nop nop nop bsf STATUS, C btfss _SDA bcf STATUS, C bcf _SCL rlf Temp, F decfsz Counter, F goto xI2CInLoop ; Read Ack bit nop bsf _SCL nop nop nop nop nop nop ; No Ack for single byte read bcf _SCL movfw Temp nop nop nop nop return ;----------------------------------------------------------------- ; TakeTempReading, Read a single sample from TC77 thermo sensor ; Output: TempH, TempL 13bit of data total TakeTempReading: bsf _TCS movlw TRISA ; Use INDR, make it easier to access movwf FSR bsf _SIOi ; Make SIO an input movlw d'13' movwf TempCnt ; Read 13 bits clrf TempL ; Clear out old data bcf _SCK bcf _TCS LoopTempRead: bcf STATUS, C ; Take a reading btfsc _SIO bsf STATUS, C bsf _SCK rlf TempL, F rlf TempH, F ; Load data into Temp Reg bcf _SCK ; Falling clock to load new data decfsz TempCnt, F goto LoopTempRead bsf _TCS return ;----------------------------------------------------------------- ; SendHex, Send a single byte out of serial port, hex format ; Input: data stored in W SendHex: movwf Temp swapf Temp, W andlw 00fh call HexTable HexWaitLoop1: btfss PIR1, TXIF ;Wait till buffer is empty goto HexWaitLoop1 movwf TXREG movf Temp, W andlw 00fh call HexTable HexWaitLoop2: btfss PIR1, TXIF ;Wait till buffer is empty goto HexWaitLoop2 movwf TXREG return ;----------------------------------------------------------------- ; RcvHex, Receive a single byte from serial port in hex format ; Output: data stored in Output RcvHex: btfss PIR1, RCIF goto RcvHex movfw RCREG movwf SerData movwf TXREG ; Send it back out movlw 30h subwf SerData, F movlw 10h subwf SerData, W ; F - W btfsc STATUS, C ; Positive means letter input goto Letter1 RtnLetter1: swapf SerData, W movwf Output RcvHexLoop: btfss PIR1, RCIF goto RcvHexLoop movfw RCREG movwf SerData movwf TXREG ; Send it back out movlw 30h subwf SerData, F movlw 10h subwf SerData, W ; F - W btfsc STATUS, C ; Positive means letter input goto Letter2 RtnLetter2: movfw SerData iorwf Output, F call SSpace return Letter1: movlw 07h subwf SerData, F movlw 10h subwf SerData, W btfsc STATUS, C ; Positive means bad input goto Letter1E goto RtnLetter1 Letter1E: movlw d'32' subwf SerData, F goto RtnLetter1 Letter2: movlw 07h subwf SerData, F movlw 10h subwf SerData, W btfsc STATUS, C ; Positive means bad input goto Letter2E goto RtnLetter2 Letter2E: movlw d'32' subwf SerData, F goto RtnLetter2 ;----------------------------------------------------------------- ; LongDelay, 15 cycles, 3ins/cycle 22.5uS for 8Mhz clock long_delay: movlw d'15' movwf Counter long_delay_L: decfsz Counter, F goto long_delay_L return ;----------------------------------------------------------------- ; Delay_1ms, 5000 cycles, actual is 4590 cycle, 0.918mS for 20Mhz clock Delay_1ms: movlw d'6' movwf CounterA Delay_1ms_loop_O: Delay_1ms_loop_I: decfsz CounterB, F goto Delay_1ms_loop_I decfsz CounterA, F goto Delay_1ms_loop_O return ;----------------------------------------------------------------- ; Delay_100us, 500 cycles, actual is 498 cycle, 99.6uS for 20Mhz clock Delay_100us: movlw d'166' movwf CounterA Delay_100us_loop: decfsz CounterA, F goto Delay_100us_loop return ;----------------------------------------------------------------- ; SendString, send the string out to serial port ; Input: String location in W SendString: movwf Counter SendLoop: btfss PIR1, TXIF ;Wait till buffer is empty goto SendLoop call StringStart iorwf Zero, W btfsc STATUS, Z ;Zero is the end of string return movwf TXREG incf Counter, F movfw Counter goto SendLoop ;----------------------------------------------------------------- ; NewLine, send s newline character into serial port ; Input: None NewLine: btfss PIR1, TXIF ;Wait till buffer is empty goto NewLine movlw 0dh movwf TXREG movlw 0ah NewLineLoop: btfss PIR1, TXIF ;Wait till buffer is empty goto NewLineLoop movwf TXREG return ;----------------------------------------------------------------- ; SSpace, send a space character into serial port ; Input: None SSpace: btfss PIR1, TXIF ;Wait till buffer is empty goto SSpace movlw ' ' movwf TXREG return ;----------------------------------------------------------------- ; SendChar, send a single character into serial port SendChar: btfss PIR1, TXIF ;Wait till buffer is empty goto SendChar movwf TXREG return ;---------------------------------------------------------------- ; Delay 20 cycles Delay20: movlw d'5' movwf Rcount Delay20loop: decfsz Rcount, F goto Delay20loop return ;******************************************** Fast Transfer FastXfer: LockUp: goto LockUp ;***************************************************************** ; Subede wasure de, kokoni iru end