Datalogger
;--------------------------------------------------------------------------------------------------------------------------------------
;
; MOTORCYCLE DATA LOGGER - ASSEMBLY SOURCE CODE FOR THE MICROCHIP PIC 16F877
;
;
; Author : Andries C. Tip
; andries.tip@planet.nl
;
; Start of programming : May 2002
; Last Revision Date : December 29, 2006
;
; Program code written with MPLAB IDE v7.00 from Microchip
;
;--------------------------------------------------------------------------------------------------------------------------------------
;
; Define firmware version, this number can be read by computer
;
#define fwv0 a'1' ; version number
#define fwv1 a'7' ; build number, most significant
#define fwv2 a'9' ; build number, least significant
;
;--------------------------------------------------------------------------------------------------------------------------------------
;
; History:
;
; logger179.asm Bugfix: in the interrupt routine the movwf status_temp came after clrf status !!
; logger178.asm Bugfix: bank23 and bank32 macro's were faulty (rp1 instead of rp0) but were not used in the program
; logger177.asm First Public Release in datalogger
;
;--------------------------------------------------------------------------------------------------------------------------------------
;
; Font: Bitstream Vera Sans Mono (size 8 for screen display, size 6 for printing)
;
; Tabsize: 8 characters
;
;--------------------------------------------------------------------------------------------------------------------------------------
;
; Functional description of this firmware:
;
; This firmware is used in a datalogger that will record relevant data from sensors of a motorcycle and
; consists of a Microchip PIC 16F877, IIC EEPROM chips, IIC clock chip etc. After recording, the data can be
; read by using a computer with RS232 communication. The computer is also used for configuring the datalogger
; with settings such as logging rate, wheel diameter, input channels and so forth.
;
; The logging rate (measurements/second) can be given seperately for each channel.
;
; The rotary switch is used to start and stop the data logging. Multiple records can be taken before data readout
; by the computer is needed. Each record will have its own time and date recorded. The mark button can be
; pressed at any time during the logging and can be used to indicate periods in the computer graphs.
;
; Note: For reasons of clarity the external eeprom pages (64 bytes each) are called blocks instead of pages to avoid confusion
; with the memory pages which are used within the Microchip PIC chip.
;
;--------------------------------------------------------------------------------------------------------------------------------------
;
; Pinout:
;
; Port A0 = Analog input 0 Lambda sensor [0..1 V]
; Port A1 = Analog input 1 Voltage [0..5 V]
; Port A2 = Analog input 2 K-Type Thermocouple [0..1024? Celcius ?]
; Port A3 = Analog input 3 Air Temperature (NTC)
; Port A4 = Digital input Clock input 32768 Hz from Clock/Timer chip, used as interrupt
; Port A5 = Analog input 4 Water Temperature (NTC)
;
; Port B0 = Digital input Run
; Port B1 = Digital input _Mark_
; Port B2 = Digital input Brake
; Port B3 = Digital input Laptime
; Port B4 = Digital input _TC disconnected_
; Port B5 = Digital input BoardsupplyOn
; Port B6 = Digital output Status DuoLed Red
; Port B7 = Digital output Status DuoLed Green
;
; Port C0 = Digital output Computer CTS
; Port C1 = Digital input Speed [0..255 km/hr]
; Port C2 = Digital input RPM [0..16383 RPM]
; Port C3 = Digital in/output IIC SDA, clock chip, serial eeproms
; Port C4 = Digital output IIC SCL, clock chip, serial eeproms
; Port C5 = Digital input Computer RTS
; Port C6 = Digital output Computer RX (configure as input so the usart can use this pin !)
; Port C7 = Digital input Computer TX
;
; Port D0 = Digital output Led0, leds are rpm indicator/shift light
; Port D1 = Digital output Led1
; Port D2 = Digital output Led2
; Port D3 = Digital output Led3
; Port D4 = Digital output Led4
; Port D5 = Digital output Led5
; Port D6 = Digital output Led6
; Port D7 = Digital output Led7
;
; Port E0 = Analog input 5 Throttle [0..100 %]
; Port E1 = Analog input 6 Longitudinal acceleration from G-sensor chip
; Port E2 = Analog input 7 Lateral acceleration from G-sensor chip
;
;--------------------------------------------------------------------------------------------------------------------------------------
; Format of data file as composed by computer from datalogger settings and record data
;--------------------------------------------------------------------------------------------------------------------------------------
;Datalogger data file
;
;[Information]
;Serial number=2743
;Start of logging=12/12/04 15:33:57
;Original filename=D:\Datalogger\Data\Track_0018.dat
;Record size=2093104 << record size in bits
;
;[Errors]
;Log event overflow=1
;Thermocouple disconnected=0
;Memory full=1
;
;[Settings]
;Wheel circumference=1967
;Wheel radius=313.1
;RPM maximum=5000
;
;[Channels]
;RPM=32
;Speed=32
;Throttle=32
;Thermocouple=32
;Lambda=32
;Voltage=32
;Airtemp=32
;Watertemp=32
;LongAccel=32
;LatAccel=32
;Mark=32
;Brake=32
;
;[Data]
;0 PC 0 PC 0 L..... << raw data
;
; End of file. << just to inform user
;
;--------------------------------------------------------------------------------------------------------------------------------------
; PIC 14-bit core instruction set
;--------------------------------------------------------------------------------------------------------------------------------------
;
; Mnemonic Description Function Status affected Instr.Cycles
;
; ADDLW k Add literal to W k + W > W C, DC, Z 1
; ADDWF f,d Add W and f W + f >d C, DC, Z 1
; SUBLW k Subtract W from literal k - W > W C, DC, Z 1
; SUBWF f,d Subtract W from f f - W > d C, DC, Z 1
; BCF f, b Bit clear f 0 > f ( b ) 1
; BSF f, b Bit set f 1 > f ( b ) 1
; BTFSC f, b Bit test, skip if clear skip if f ( b ) = 0 1(2)*
; BTFSS f, b Bit test, skip if set skip if f ( b ) = 1 1(2)*
; CLRF f Clear f 0 >f Z 1
; CLRW Clear W 0 >W Z 1
; INCF f,d Increment f f + 1 > d Z 1
; INCFSZ f,d Increment f, skip if zero f + 1 > d, skip if 0 1(2)*
; DECF f,d Decrement f f - 1 > d Z 1
; DECFSZ f,d Decrement f, skip if zero f - 1 > d, skip if 0 1(2)*
; GOTO k Goto address (k is nine bits) k > PC (9 bits) 1
; CALL k Call subroutine PC + 1 > TOS, k >PC 2
; RETURN Return from subroutine TOS > PC 2
; RETLW k Return with literal in W k > W, TOS > PC 2
; RETFIE Return from interrupt TOS > PC, 1 > GIE 2
; MOVLW k Move literal to W k > W 1
; MOVF f,d Move f f > d Z 1
; MOVWF f Move W to f W > f 1
; SWAPF f,d Swap halves f f ( 0:3 ) > f ( 4:7) > d 1
; RLF f,d Rotate left through carry C < 76543210 < C 1
; RRF f,d Rotate right through carry C > 76543210 > C 1
; COMF f,d Complement f .NOT. F > d Z 1
; ANDLW k AND literal and W k .AND. W > W Z 1
; ANDWF f,d AND W and f W .AND. F >d Z 1
; IORLW k Inclusive OR literal and W k .OR. W > W Z 1
; IORWF f,d Inclusive OR W and f W .OR. F >d Z 1
; XORLW k Exclusive OR literal and W k .XOR. W > W Z 1
; XORWF f,d Exclusive OR W and f W .XOR. F >d Z 1
; NOP No operation 1
; OPTION Load OPTION register W > OPTION Register 1
; CLRWDT T Clear watchdog timer 0 > WDT (and prescaler) _TO , _PD 1
; SLEEP Go into standby Mode 0 > WDT, stop oscillator _TO , _PD 1
; TRIS f Tristate port f (only A,B,C!) W > I/O control register f 1
;
;
; * = If the program counter (PC) is modified, or a conditional test is true, the instruction requires two cycles. The second
; cycle is executed as a NOP
;
;
; Field description:
;
; f register file address (0x00 to 0x7F)
; w working register (accumulator)
; b bit address within an 8-bit file register
; k literal field, constant data or label
; d destination select, d = 0 means store result in w, d = 1 means store result in file register f (default is d = 1)
; PC program counter
; TO time-out bit
; PD power-down bit
;
;
; Subtraction: Carry = 1 result is positive or zero
; Carry = 0 result is negative (borrow)
; Addition: Carry = 1 result is > 255 (overflow)
; Carry = 0 result is <=255
;
;--------------------------------------------------------------------------------------------------------------------------------------
; Configuration
;--------------------------------------------------------------------------------------------------------------------------------------
list p=16f877 ; directive to define processor
radix dec ; all numbers are decimal unless stated otherwise
errorlevel -219 ; suppress 'Invalid RAM location specified' warnings (editor warns unneededly about these
; addresses because of unused registers at the same locations in bank1
errorlevel -302 ; suppress 'not in register bank' warnings
errorlevel -305 ; suppress 'default destination' warnings
errorlevel -306 ; suppress 'crossing page boundary' warnings
;--------------------------------------------------------------------------------------------------------------------------------------
; Version
;--------------------------------------------------------------------------------------------------------------------------------------
__IDLOCS h'0000' ; store the firmware version number in the pic identification memory locations
;--------------------------------------------------------------------------------------------------------------------------------------
; Name registers
;--------------------------------------------------------------------------------------------------------------------------------------
w EQU H'0000' ; used in instructions like decsfz
f EQU H'0001' ; to indicate destination
;----- Register Files--------------------------------------------------
; BANK 0 :
indf EQU H'0000' ; indirect file register
tmr0 EQU H'0001' ; timer 0 module register
pcl EQU H'0002' ; program counter (low byte)
status EQU H'0003' ; status register
fsr EQU H'0004' ; indirect data memory address pointer
porta EQU H'0005' ; port A
portb EQU H'0006' ; port B
portc EQU H'0007' ; port C
portd EQU H'0008' ; port D
porte EQU H'0009' ; port E
pclath EQU H'000A' ; write buffer for program counter (upper 5 bits)
intcon EQU H'000B' ; interrupt control register
pir1 EQU H'000C' ; peripheral interrupt flags
pir2 EQU H'000D' ; CCP 2/SSP bus collision/eeprom write operation interrupt flags
tmr1l EQU H'000E' ; timer 1 register low byte
tmr1h EQU H'000F' ; timer 1 register high byte
t1con EQU H'0010' ; timer 1 control register
tmr2 EQU H'0011' ; timer 2
t2con EQU H'0012' ; timer 2 control register
sspbuf EQU H'0013' ; SSP receive/xmit register
sspcon EQU H'0014' ; SSP control register 1
ccpr1l EQU H'0015' ; CCP 1 low byte
ccpr1h EQU H'0016' ; CCP 1 high byte
ccp1con EQU H'0017' ; CCP 1 control register
rcsta EQU H'0018' ; UART receive status and control register
txreg EQU H'0019' ; UART xmit data register
rcreg EQU H'001A' ; UART receive register
ccpr2l EQU H'001B' ; CCP 2 low byte
ccpr2h EQU H'001C' ; CCP 2 high byte
ccp2con EQU H'001D' ; CCP 2 control register
adresh EQU H'001E' ; A/D result register high byte
adcon0 EQU H'001F' ; A/D operation control register
; BANK 1 :
optionreg EQU H'0081' ; option register
trisa EQU H'0085' ; Port A data direction control register
trisb EQU H'0086' ; Port B data direction control register
trisc EQU H'0087' ; Port C data direction control register
trisd EQU H'0088' ; Port D data direction control register
trise EQU H'0089' ; Port E data direction control register
pie1 EQU H'008C' ; peripheral interrupt enable
pie2 EQU H'008D' ; CCP 2/SSP bus collision/eeprom write operation interrupt enable
pcon EQU H'008E' ; power control register
sspcon2 EQU H'0091' ; SSP control register 2
pr2 EQU H'0092' ; timer 2 period register
sspadd EQU H'0093' ; SSP (I2C mode) address register
sspstat EQU H'0094' ; SSP status register
txsta EQU H'0098' ; UART xmit status and control register
spbrg EQU H'0099' ; UART baud rate generator speed control value
adresl EQU H'009E' ; A/D result register low byte
adcon1 EQU H'009F' ; A/D pin control register
; BANK 2 :
eedata EQU H'010C' ; eeprom data register low byte
eeadr EQU H'010D' ; eeprom address register low byte
eedath EQU H'010E' ; eeprom data register high byte
eeadrh EQU H'010F' ; eeprom address register high byte
; BANK 3 :
eecon1 EQU H'018C' ; eeprom control register 1
eecon2 EQU H'018D' ; eeprom control register 2
;----- STATUS Bits ----------------------------------------------------
irp EQU 7
rp1 EQU 6
rp0 EQU 5
not_to EQU 4
not_pd EQU 3
z EQU 2
dc EQU 1
c EQU 0
;----- INTCON Bits ----------------------------------------------------
gie EQU 7
peie EQU 6
t0ie EQU 5
inte EQU 4
rbie EQU 3
t0if EQU 2
intf EQU 1
rbif EQU 0
;----- PIR1 Bits ------------------------------------------------------
pspif EQU 7
adif EQU 6
rcif EQU 5
txif EQU 4
sspif EQU 3
ccp1if EQU 2
tmr2if EQU 1
tmr1if EQU 0
;----- PIR2 Bits ------------------------------------------------------
eeif EQU 4
bclif EQU 3
ccp2if EQU 0
;----- T1CON Bits -----------------------------------------------------
t1ckps1 EQU 5
t1ckps0 EQU 4
t1oscen EQU 3
t1sync EQU 2
tmr1cs EQU 1
tmr1on EQU 0
;----- T2CON Bits -----------------------------------------------------
toutps3 EQU 6
toutps2 EQU 5
toutps1 EQU 4
toutps0 EQU 3
tmr2on EQU 2
t2ckps1 EQU 1
t2ckps0 EQU 0
;----- SSPCON Bits -----------------------------------------------------
wcol EQU 7
sspov EQU 6
sspen EQU 5
ckp EQU 4
sspm3 EQU 3
sspm2 EQU 2
sspm1 EQU 1
sspm0 EQU 0
;----- CCP1CON Bits ----------------------------------------------------
ccp1x EQU 5
ccp1y EQU 4
ccp1m3 EQU 3
ccp1m2 EQU 2
ccp1m1 EQU 1
ccp1m0 EQU 0
;----- RCSTA Bits ----------------------------------------------------
spen EQU 7
rx9 EQU 6
sren EQU 5
cren EQU 4
adden EQU 3
ferr EQU 2
oerr EQU 1
rx9d EQU 0
;----- CCP2CON Bits ----------------------------------------------------
ccp2x EQU 5
ccp2y EQU 4
ccp2m3 EQU 3
ccp2m2 EQU 2
ccp2m1 EQU 1
ccp2m0 EQU 0
;----- ADCON0 Bits ----------------------------------------------------
adcs1 EQU 7
adcs0 EQU 6
chs2 EQU 5
chs1 EQU 4
chs0 EQU 3
go_notdone EQU 2
adon EQU 0
;----- OPTION Register Bits ------------------------------------------
not_rbpu EQU 7
intedg EQU 6
t0cs EQU 5
t0se EQU 4
psa EQU 3
ps2 EQU 2
ps1 EQU 1
ps0 EQU 0
;----- TRISE Bits ----------------------------------------------------
ibf EQU 7
obf EQU 6
ibov EQU 5
pspmode EQU 4
dirpe2 EQU 2
dirpe1 EQU 1
dirpe0 EQU 0
;----- PIE1 Bits ------------------------------------------------------
pspie EQU 7
adie EQU 6
rcie EQU 5
txie EQU 4
sspie EQU 3
ccp1ie EQU 2
tmr2ie EQU 1
tmr1ie EQU 0
;----- PIE2 Bits ------------------------------------------------------
eeie EQU 4
bclie EQU 3
ccp2ie EQU 0
;----- PCON Bits ------------------------------------------------------
not_por EQU 1
not_bor EQU 0
;----- SSPCON2 Bits ----------------------------------------------------
gcen EQU 7
ackstat EQU 6
ackdt EQU 5
acken EQU 4
rcen EQU 3
pen EQU 2
rsen EQU 1
sen EQU 0
;----- SSPSTAT Bits ---------------------------------------------------
smp EQU 7
cke EQU 6
d_nota EQU 5
p EQU 4
s EQU 3
r_notw EQU 2
ua EQU 1
bf EQU 0
;----- TXSTA Bits ----------------------------------------------------
csrc EQU 7
tx9 EQU 6
txen EQU 5
sync EQU 4
brgh EQU 2
trmt EQU 1
tx9d EQU 0
;----- ADCON1 Bits ----------------------------------------------------
adfm EQU 7
pcfg3 EQU 3
pcfg2 EQU 2
pcfg1 EQU 1
pcfg0 EQU 0
;----- EECON1 Bits ----------------------------------------------------
eepgd EQU 7
wrerr EQU 3
wren EQU 2
wr EQU 1
rd EQU 0
;--------------------------------------------------------------------------------------------------------------------------------------
; RAM definition
;--------------------------------------------------------------------------------------------------------------------------------------
__MAXRAM H'1FF'
__BADRAM H'8F'-H'90', H'95'-H'97', H'9A'-H'9D', H'105', H'107'-H'109', H'185', H'187'-H'189', H'18E'-H'18F'
;--------------------------------------------------------------------------------------------------------------------------------------
; Configuration directive
;--------------------------------------------------------------------------------------------------------------------------------------
; '__CONFIG' directive is used to embed configuration data within .asm file.
_CP_ALL EQU H'0FCF'
_CP_HALF EQU H'1FDF'
_CP_UPPER_256 EQU H'2FEF'
_CP_OFF EQU H'3FFF'
_DEBUG_ON EQU H'37FF'
_DEBUG_OFF EQU H'3FFF'
_WRT_ENABLE_ON EQU H'3FFF'
_WRT_ENABLE_OFF EQU H'3DFF'
_CPD_ON EQU H'3EFF'
_CPD_OFF EQU H'3FFF'
_LVP_ON EQU H'3FFF'
_LVP_OFF EQU H'3F7F'
_BODEN_ON EQU H'3FFF'
_BODEN_OFF EQU H'3FBF'
_PWRTE_OFF EQU H'3FFF'
_PWRTE_ON EQU H'3FF7'
_WDT_ON EQU H'3FFF'
_WDT_OFF EQU H'3FFB'
_LP_OSC EQU H'3FFC'
_XT_OSC EQU H'3FFD'
_HS_OSC EQU H'3FFE'
_RC_OSC EQU H'3FFF'
__CONFIG _CP_OFF & _WDT_OFF & _BODEN_ON & _PWRTE_ON & _XT_OSC &_WRT_ENABLE_ON & _LVP_OFF & _DEBUG_OFF & _CPD_OFF
;--------------------------------------------------------------------------------------------------------------------------------------
; Register bank macro's
;--------------------------------------------------------------------------------------------------------------------------------------
bank0 MACRO ; select register bank 0
bcf status,rp0
bcf status,rp1
ENDM
bank1 MACRO ; select register bank 1
bsf status,rp0
bcf status,rp1
ENDM
bank2 MACRO ; select register bank 2
bcf status,rp0
bsf status,rp1
ENDM
bank3 MACRO ; select register bank 3
bsf status,rp0
bsf status,rp1
ENDM
bank02 MACRO ; change from bank 0 to bank 2
bsf status,rp1
ENDM
bank20 MACRO ; change from bank 2 to bank 0
bcf status,rp1
ENDM
bank01 MACRO ; change from bank 0 to bank 1
bsf status,rp0
ENDM
bank10 MACRO ; change from bank 1 to bank 0
bcf status,rp0
ENDM
bank23 MACRO ; change from bank 2 to bank 3
bsf status,rp0
ENDM
bank32 MACRO ; change from bank 3 to bank 2
bcf status,rp0
ENDM
;--------------------------------------------------------------------------------------------------------------------------------------
; Program memory page macro's
;--------------------------------------------------------------------------------------------------------------------------------------
page0 MACRO ; select program memory page 0 (hex 0000 up to hex 07FF)
bcf pclath,3
bcf pclath,4
ENDM
page1 MACRO ; select program memory page 0 (hex 0800 up to hex 0FFF)
bsf pclath,3
bcf pclath,4
ENDM
page2 MACRO ; select program memory page 0 (hex 1000 up to hex 17FF)
bcf pclath,3
bsf pclath,4
ENDM
page3 MACRO ; select program memory page 0 (hex 17FF up to hex 1FFF)
bsf pclath,3
bsf pclath,4
ENDM
;--------------------------------------------------------------------------------------------------------------------------------------
; Constants definitions
;--------------------------------------------------------------------------------------------------------------------------------------
switch EQU 0 ; port B, rotary switch on datalogger (low=on/high=log)
not_mark EQU 1 ; port B, marking switch at vehicle handlebar/steer (low=pressed)
brake EQU 2 ; port B, switch connected to brake handle/pedal (high=pressed)
laptime EQU 3 ; port B, extra laptime circuit to receive infrared beam from light along race track **** to do
not_tcd EQU 4 ; port B, thermocouple disconnected (low=disconnected)
boardsuppl EQU 5 ; port B, external power supply to datalogger (low=battery power only,high=exteral power on)
led_red EQU 6 ; port B, red status led (see below)
led_green EQU 7 ; port B, green status led (see below)
; status led:
; red = datalogger switched on
; green = logging
; yellow = logging, but memory almost full
; flashing red = one of the following errors:
; - logging stopped because memory is full
; - no channels enabled for logging
; - the table of contents (toc) is full, there is a maximum of 20 records
rts_in EQU 5 ; Port C Pin 5 is input for uart flow control
cts_out EQU 0 ; Port C Pin 0 is output for uart flow control
@tocstart EQU d'56' ; start of table of contents in internal eeprom, runs up to and includes address 255
;--------------------------------------------------------------------------------------------------------------------------------------
; Variable definitions
;--------------------------------------------------------------------------------------------------------------------------------------
; variables used at top of bank 0,1,2,3, available from any bank: (Hex 70-7F)
w_temp EQU 0x70 ; variable used for context saving during interrupts
status_temp EQU 0x71 ; variable used for context saving during interrupts
pclath_temp EQU 0x72 ; variable used for context saving during interrupts
fsr_temp EQU 0x73 ; variable used for context saving during interrupts
numlow EQU 0x74 ; least significant or lower byte of 16 bit number (least significant byte of 24 bit number)
nummiddle EQU 0x75 ; most significant or upper byte of 16 bit number (middle byte of 24 bit number)
numhigh EQU 0x76 ; extended byte (most significant byte of 24 bit number)
adtemp EQU 0x77 ; used in a/d conversion for channel selection and as delay counter
adchannel EQU 0x78 ; channel number used in a/d conversion
templow EQU 0x7A ; used in 8, 16 and 24 bit number read from buffer and transmission
tempmiddle EQU 0x7B ; used in 16 and 24 bit number read from buffer and transmission
temphigh EQU 0x7C ; used in 24 bit number transmission
errors EQU 0x7D ; errors flags, bits will be when errors occur during logging (see below)
flags1 EQU 0x7E ; status flag bits (see below)
flags2 EQU 0x7F ; status flag bits (see below)
; bits of 'errors' register:
logoflow EQU 0 ; previous log event was not finished when next event was started, logdata will be corrupted
tcdiscon EQU 1 ; the thermocouple wire has been disconnected during logging, tc-logdata will be corrupted
err_mf EQU 2 ; the external eeprom memory got full during logging
; bits of 'flags1' register:
stx EQU 0 ; stx (start of text) byte received
etx EQU 1 ; etx (end of text) byte received
withdata EQU 2 ; command has data bytes
command EQU 3 ; all bytes of command have been received, now ready for execution of command
changese EQU 7 ; backdoor to change device serial number
; bits of 'flags2' register:
lognow EQU 0 ; timer0 interrupt just occured which means data should be logged instantly
tocfull EQU 1 ; table of contents in pic internal eeprom is full
memfull EQU 2 ; external eeproms are all full
rpmbusy EQU 3 ; do not disturb rpm calculation
spdbusy EQU 4 ; do not disturb speed calculation
shlight EQU 5 ; this flag is set when shift light should be on, used in timer interrupt to flash light
norpm EQU 6 ; indicate we did not get a rpm pulse so calculate a dropping rpm rate
nospeed EQU 7 ; indicate we did not get a speed pulse so calculate a dropping speed value
; variables used in bank 0 (Hex 20-6F)
rx_byte EQU 0x20 ; byte received from UART
rx_parity EQU 0x21 ; byte used in parity checking
rx_checksum EQU 0x22 ; byte used in receive checksum calculation
rx_pointer EQU 0x23 ; pointer for command input buffer
rx_maxpos EQU 0x24 ; maximum location of received command bytes
tx_byte EQU 0x25 ; byte ready to be sent
tx_checksum EQU 0x26 ; used in transmission checksum calculation
tx_parity EQU 0x27 ; used in parity calculation
tx_numberl EQU 0x28 ; used in 8, 16 and 24 bit number transmission
tx_numberm EQU 0x29 ; used in 16 and 24 bit number transmission
tx_numberh EQU 0x2A ; used in 24 bit number transmission
tx_counter EQU 0x2B ; used in 8 and 16 bit number transmission
rx_buffer00 EQU 0x2D ; start of serial communications command receive buffer:
rx_buffer01 EQU 0x2E ; register addresses 2D and 2E are used for two command bytes,
rx_buffer02 EQU 0x2F ; addresses from 2F - 6F are used for a maximum of 64 data bytes
rx_buffer03 EQU 0x30 ;
rx_buffer04 EQU 0x31 ;
rx_buffer05 EQU 0x32 ;
rx_buffer06 EQU 0x33 ;
rx_buffer07 EQU 0x34 ;
rx_buffer08 EQU 0x35 ;
rx_buffer09 EQU 0x36 ;
rx_buffer10 EQU 0x37 ;
rx_buffer11 EQU 0x38 ;
rx_buffer12 EQU 0x39 ;
rx_buffer13 EQU 0x3A ;
rx_buffer14 EQU 0x3B ;
rx_buffer15 EQU 0x3C ;
rx_buffer16 EQU 0x3D ;
rx_buffer17 EQU 0x3E ;
rx_buffer18 EQU 0x3F ;
rx_buffer65 EQU 0x6E ;
rx_buffer66 EQU 0x6F ; plus one checksum byte (note: same locations as blockbuffer in bank1 !)
; variables used in bank 1 (Hex A0-EF)
iicdata EQU 0xA0 ; data byte
iicalow EQU 0xA1 ; address low byte (8 bits)
iicahigh EQU 0xA2 ; address high byte (7 bits)
iicchip EQU 0xA3 ; number of external eeprom chip (0..7)
mem_alow EQU 0xA4 ; address select, low byte
mem_ahigh EQU 0xA5 ; address select, high byte
mem_chip EQU 0xA6 ; chip select for (block) write operations to external eeprom
regpointer EQU 0xA7 ; register pointer in block buffer
bitpointer EQU 0xA8 ; bit pointer in block buffer
bitcounter EQU 0xA9 ; temporary counter used in shift operations for block buffer storage
bitmask EQU 0xAA ; mask for one bit add operation in block buffer
databyte0 EQU 0xAB ; used in copy and shift operations for storing data in the block buffer
databyte1 EQU 0xAC ; used in copy and shift operations for storing data in the block buffer
databyte2 EQU 0xAD ; used in copy and shift operations for storing data in the block buffer
toc_pointer EQU 0xAE ; pointer for table of contents in internal eepromm
blockbuff00 EQU 0xAF ; location of first byte in 64 byte block buffer for writes to external eeprom
blockbuff63 EQU 0xEE ; location of last byte in block buffer (same locations as rx_buffer in bank0 !)
toc_test EQU 0xEF ; testing for free location in table of contents in internal eeprom
; variables used in bank 2 (Hex 10-6F)
iee_address EQU 0x10 ; pointer for write operations to pic internal eeprom
timer1y EQU 0x11 ; extend range of timer1 with one byte to three bytes
timer1z EQU 0x12 ; extend range of timer1 with one byte to four bytes
new1cap0 EQU 0x13 ; captures byte 0 of timer 1
new1cap1 EQU 0x14 ; captures byte 1 of timer 1
new1cap2 EQU 0x15 ; captures byte 2 of timer 1
new1cap3 EQU 0x16 ; captures byte 3 of timer 1
old1cap0 EQU 0x17 ; holds previous value of captured byte 0 of timer 1
old1cap1 EQU 0x18 ; holds previous value of captured byte 1 of timer 1
old1cap2 EQU 0x19 ; holds previous value of captured byte 2 of timer 1
old1cap3 EQU 0x1A ; holds previous value of captured byte 3 of timer 1
new2cap0 EQU 0x1B ; captures byte 0 of timer 1
new2cap1 EQU 0x1C ; captures byte 1 of timer 1
new2cap2 EQU 0x1D ; captures byte 2 of timer 1
new2cap3 EQU 0x1E ; captures byte 3 of timer 1
old2cap0 EQU 0x1F ; holds previous value of captured byte 0 of timer 1
old2cap1 EQU 0x20 ; holds previous value of captured byte 1 of timer 1
old2cap2 EQU 0x21 ; holds previous value of captured byte 2 of timer 1
old2cap3 EQU 0x22 ; holds previous value of captured byte 3 of timer 1
divcounter EQU 0x23 ; used in division routine
nrator0 EQU 0x24 ; used in division routine
nrator1 EQU 0x25 ; used in division routine
nrator2 EQU 0x26 ; used in division routine
nrator3 EQU 0x27 ; used in division routine
denom_r0 EQU 0x28 ; used in division routine of ccp1 (RPM)
denom_r1 EQU 0x29 ; used in division routine
denom_r2 EQU 0x2A ; used in division routine
denom_r3 EQU 0x2B ; used in division routine
denom_s0 EQU 0x2C ; used in division routine of ccp2 (Speed)
denom_s1 EQU 0x2D ; used in division routine
denom_s2 EQU 0x2E ; used in division routine
denom_s3 EQU 0x2F ; used in division routine
remain0 EQU 0x30 ; used in division routine
remain1 EQU 0x31 ; used in division routine
remain2 EQU 0x32 ; used in division routine
remain3 EQU 0x33 ; used in division routine
rpm_low EQU 0x34 ; engine rpm, result low byte of division
rpm_high EQU 0x35 ; engine rpm, result high byte of division
speed EQU 0x36 ; vehicle speed, result byte of division
mult_a0 EQU 0x37 ; used in 16 bit multiplication routine
mult_a1 EQU 0x38 ; used in 16 bit multiplication routine
mult_b0 EQU 0x39 ; used in 16 bit multiplication routine
mult_b1 EQU 0x3A ; used in 16 bit multiplication routine
speed_const0 EQU 0x3B ; used in 16 bit multiplication routine
speed_const1 EQU 0x3C ; used in 16 bit multiplication routine, 32 bit result
speed_const2 EQU 0x3D ; used in 16 bit multiplication routine, 32 bit result
speed_const3 EQU 0x3E ; used in 16 bit multiplication routine, 32 bit result
multcounter EQU 0x3F ; used in 16 bit multiplication routine, 32 bit result
rpmmax_low EQU 0x40 ; maximum rpm rate, low byte, copied from eeprom
rpmmax_high EQU 0x41 ; maximum rpm rate, high byte, copied from eeprom
rpmmax_0 EQU 0x42 ; maximum rpm rate time interval, least significant byte, used in comparison for shift light
rpmmax_1 EQU 0x43 ; maximum rpm rate time interval, more significant byte 1, used in comparison for shift light
rpmmax_2 EQU 0x44 ; maximum rpm rate time interval, more significant byte 2, used in comparison for shift light
rpmmax_3 EQU 0x45 ; maximum rpm rate time interval, most significant byte, used in comparison for shift light
ccp1interval0 EQU 0x46 ; temporary rpm interval storage
ccp1interval1 EQU 0x47 ; temporary rpm interval storage
ccp1interval2 EQU 0x48 ; temporary rpm interval storage
ccp1interval3 EQU 0x49 ; temporary rpm interval storage
ccp2interval0 EQU 0x4A ; temporary speed interval storage
ccp2interval1 EQU 0x4B ; temporary speed interval storage
ccp2interval2 EQU 0x4C ; temporary speed interval storage
ccp2interval3 EQU 0x4D ; temporary speed interval storage
intervalr0 EQU 0x4E ; present timer1 value
intervalr1 EQU 0x4F ; present timer1 value
intervalr2 EQU 0x50 ; present timer1 value
intervalr3 EQU 0x51 ; present timer1 value
intervals0 EQU 0x52 ; present timer1 value
intervals1 EQU 0x53 ; present timer1 value
intervals2 EQU 0x54 ; present timer1 value
intervals3 EQU 0x55 ; present timer1 value
lastintr0 EQU 0x56 ; interval between last two rpm pulses
lastintr1 EQU 0x57 ; interval between last two rpm pulses
lastintr2 EQU 0x58 ; interval between last two rpm pulses
lastintr3 EQU 0x59 ; interval between last two rpm pulses
lastints0 EQU 0x5A ; interval between last two speed pulses
lastints1 EQU 0x5B ; interval between last two speed pulses
lastints2 EQU 0x5C ; interval between last two speed pulses
lastints3 EQU 0x5D ; interval between last two speed pulses
pulses EQU 0x5E ; number of pulses per crankshaft revolution
; not used
; not used
; not used
clockaddr EQU 0x66 ; holds address for static ram and control byte operations in IIC clock chip
clockdata EQU 0x67 ; temporary storage for data for static ram operations
seconds EQU 0x68 ; buffer for IIC clock chip settings
minutes EQU 0x69 ;
hours EQU 0x6A ;
day EQU 0x6B ;
date EQU 0x6C ;
month EQU 0x6D ;
year EQU 0x6E ;
clockctrl EQU 0x6F ; settings byte in clock chip
; variables used in bank 3 (Hex 90-EF)
tablepointer EQU 0x90 ; used in table lookups
logcounter EQU 0x91 ; counter used during logger, increased every 1/32 of a second
freq_rpm EQU 0x92 ; setting for log frequency of rpm channel
freq_speed EQU 0x93 ;
freq_lambda EQU 0x94 ;
freq_voltage EQU 0x95 ;
freq_tc EQU 0x96 ;
freq_air EQU 0x97 ;
freq_water EQU 0x98 ;
freq_throttle EQU 0x99 ;
freq_long EQU 0x9A ;
freq_lat EQU 0x9B ;
freq_mark EQU 0x9C ;
freq_brake EQU 0x9D ;
num_records EQU 0x9E ; counter for number of records
current_rec EQU 0x9F ; select record for downloading
rec_loopcntr EQU 0xA0 ; byte used in addition loop
pointer_low EQU 0xA1 ; pointer to record data in external memory
pointer_high EQU 0xA2 ;
pointer_chip EQU 0xA3 ;
endpoint_low EQU 0xA4 ; pointer to end of record data in external memory
endpoint_high EQU 0xA5 ;
endpoint_chip EQU 0xA6 ;
recsizea0 EQU 0xA7 ; registers used in record size calculation
recsizea1 EQU 0xA8 ;
recsizea2 EQU 0xA9 ;
recsizeb0 EQU 0xAA ;
recsizeb1 EQU 0xAB ;
recsizeb2 EQU 0xAC ;
;---------------------------------------------------------------------------------------------------------------------------
;---------------------------------------------------------------------------------------------------------------------------
; Data EEPROM Contents (PIC16F877: 256 bytes)
;--------------------------------------------------------------------------------------------------------------------------------------
;--------------------------------------------------------------------------------------------------------------------------------------
org h'2100' ; start of eeprom data memory
de d'175' ; 000 vehicle outer wheel circumference in millimeters (default 1967 mm), low byte
de d'7' ; 001 vehicle outer wheel circumference in millimeters, high byte
de d'255' ; 002 maximum rpm rate (default 10000 rpm) [0..16383], low byte
de d'63' ; 003 maximum rpm rate high byte
de d'2' ; 004 pulses per revolution
de d'0' ; 005
de d'0' ; 006
de d'0' ; 007
de d'0' ; 008
de d'0' ; 009
de d'0' ; 010
de d'0' ; 011
de d'0' ; 012
de d'0' ; 013
de d'0' ; 014
de d'0' ; 015 error flags bits, any errors during logging are stored here
de b'11111111' ; 016 rpm, channel logging frequency values
de b'11111111' ; 017 speed logrates (see also 'logdata' routine):
de b'11111111' ; 018 lambda
de b'11111111' ; 019 voltage 00000000 32 Hz
de b'11111111' ; 020 thermocouple 00000001 16 Hz
de b'11111111' ; 021 air temperature 00000011 8 Hz
de b'11111111' ; 022 water temperature 00000111 4 Hz
de b'11111111' ; 023 throttle 00001111 2 Hz
de b'11111111' ; 024 long acceleration 00011111 1 Hz or every second
de b'11111111' ; 025 lat acceleration 00111111 1/2 Hz or every 2 seconds
de b'11111111' ; 026 mark switch 01111111 1/4 Hz or every 4 seconds
de b'11111111' ; 027 brake switch 11111111 never
de d'0' ; 028
de d'0' ; 029
de d'0' ; 030
de d'0' ; 031
de d'0' ; 032
de d'0' ; 033
de d'0' ; 034
de d'0' ; 035
de d'0' ; 036
de d'0' ; 037
de d'0' ; 038
de d'0' ; 039
de d'0' ; 040
de d'0' ; 041
de d'0' ; 042
de d'0' ; 043
de d'0' ; 044
de d'0' ; 045
de d'0' ; 046
de d'0' ; 047
de d'0' ; 048
de d'0' ; 049
de d'0' ; 050
de d'0' ; 051
de d'0' ; 052
de d'0' ; 053
de b'1' ; 054 record incremental number
de b'0' ; 055 record incremental number
de d'0' ; 1, 056 start location of table of contents for data records
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ; all bytes clear because nothing been recorded yet
de d'0' ; 2
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ; 3
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ; 4
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ; 5
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ; 6
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ; 7
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ; 8
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ; 9
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ; 10
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ; 11
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ; 12
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ; 13
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ; 14
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ; 15
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ; 16
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ; 17
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ; 18
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ; 19
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ; 20
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
de d'0' ;
;######################################################################################################################################
; Start of program code
;######################################################################################################################################
;--------------------------------------------------------------------------------------------------------------------------------------
;--------------------------------------------------------------------------------------------------------------------------------------
;--------------------------------------------------------------------------------------------------------------------------------------
org h'0000' ; tell the assembler to place following program code at the processor reset vector,
; which is the start of program memory page 0
; this code executes when a reset occurs
; Note: The contents of the PCLATH register are unchanged after a RETURN or RETFIE instruction
; is executed ! The user must rewrite the contents of the PCLATH register for any subsequent
; subroutine calls or goto instructions (meaning: make sure page bits are set properly !)
RESET bcf pclath,3 ; select program memory page 0 (hex 0000 up to hex 07FF)
bcf pclath,4 ; select program memory page 0 (hex 0000 up to hex 07FF)
clrf status ; ensure bank bits are clear
goto INITIALIZE ; skip interrupt routine and subroutines and go to beginning of program
;--------------------------------------------------------------------------------------------------------------------------------------
org h'0004' ; tell the assembler to place following program code at interrupt vector location
; this code executes when an interrupt occurs
INTERRUPT movwf w_temp ; save off current w register contents (will be stored in any bank !)
swapf status,w ; swap status to be saved into w
movwf status_temp ; save status register to status_temp register in bank 0
clrf status ; bank 0, regardless of current bank, clears irp,rp1,rp0 (for other than 16F877)
movf fsr,w ; save fsr register
movwf fsr_temp ;
movf pclath,w ; only required if using pages 1,2 and/or 3
movwf pclath_temp ; save pclath
clrf pclath ; select page zero, regardless of current page
; see what caused the interrupt and act upon it:
; page selection should not change in interrupt routines
bsf flags2,norpm ; assume we will not get rpm or speed pulses
bsf flags2,nospeed ; flag will be reset in ccp1 and ccp2 interrupt code
bank0 ; test if the CCP1 module did a capture, if so, go calculate engine rpm pulse interval, but
btfsc pir1,ccp1if ; only if we can use the present timer 1 values as we may get ccp and timer 1 irq's
call INT_CCP1 ; at the same time ! else we wait until the timer 1 value has been increased, bank0 return
bank0 ; test if the CCP2 module did a capture, if so, go calculate speed pulse interval, but
btfsc pir2,ccp2if ; only if we can use the present timer 1 values as we may get ccp and timer 1 irq's
call INT_CCP2 ; at the same time ! else we wait until the timer 1 value has been increased
bank0
btfsc pir1,tmr1if ; test if timer 1 (which has 16 bits) has overflowed from 65535 to 0
call INT_TIMER1 ; if so, go increase the two extra counter bytes we use to extend the range, bank0 return
bank0 ;
btfsc pir1,rcif ; test if UART has received a byte from a connected computer through the RS232
call INT_RX ; if so, store valid bytes in buffer until command is complete, then set command flag
call INT_SHIFT ; when calculated rpm rate is more than the given maximum turn on shift light, bank0 return
; done handling interrupts
INT_RESTORE bank0 ; make sure we are in the right bank (for other than 16F877)
movf pclath_temp,w ; restore the register contents to their values from before the interrupt
movwf pclath ; restore pclath
movf fsr_temp,w ;
movwf fsr ; restore fsr
swapf status_temp,w ; swap status_temp register into w (sets bank to original state)
movwf status ; restore pre-isr status register contents
swapf w_temp,f ;
swapf w_temp,w ; restore pre-isr w register contents
retfie ; return from interrupt, re-enable interrupts
;--------------------------------------------------------------------------------------------------------------------------------------
; Handle Timer1 overflow interrupt
;--------------------------------------------------------------------------------------------------------------------------------------
INT_TIMER1 bank2 ;
incf timer1y,f ; increase the lower of two bytes which are used to extend the range of timer 1 to a
skpnz ; total of four bytes, did this byte roll over from 255 to 0 ?
incf timer1z,f ; yes, also increase the upper of the two bytes
btfsc flags2,norpm ; did we get a rpm pulse ?
call INT_DROP_RPM ; no, calculate a dropping rpm rate, bank2 return
btfsc flags2,nospeed ; did we get a speed pulse ?
call INT_DROP_SPEED ; no, calculate a dropping speed value, bank2 return
INT_TIMER1_SL movlw b'11111111' ; now see about the shift light, this is value for all leds on
btfsc timer1y,0 ; we use this timer bit to flash the shift light as a stroboscope, is this bit high ?
btfss flags2,shlight ; yes, so should we turn on the shift light ?
clrw ; no, value for all leds off
bank20 ;
movwf portd ; yes, turn shift light on
bcf pir1,tmr1if ; clear timer 1 receive interrupt flag bit
return ; done, return in bank0
;--------------------------------------------------------------------------------------------------------------------------------------
; Handle CCP1 capture interrupt
;--------------------------------------------------------------------------------------------------------------------------------------
INT_CCP1 ; since we can have both timer 1 overflow and ccp interrupt request at the same
; time it is important to update the timer 1 extension bytes at the right time,
; now see if we can use the present timer 1 values to calculate the engine RPM rate
; from CCP module 1 using the time (t) between two captures, otherwise we exit this
; routine and come back later when timer 1 has been incremented
; here we calculate the interval given by the rpm rate and use it to turn on or off the
; the so called 'shift light' to let the driver know it is time to shift gears,
; we do not want to calculate the actual RPM value on every interrupt since the division
; routine takes up too much processor time, the actual rpm calculation in done during logging
; or command execution
; engine RPM rate calculation:
; F [Hz] = 1 / t [s]
; RPM = 60 / t [s]
; RPM = 60 * timer1clockrate / interval [timer cycles]
; RPM = (60 * 3686400/4) Mhz / interval [instruction cycles]
; RPM = 55,296,000 / interval [instruction cycles]
; RPM = 55,296,000 / (capturevalue - previouscapturevalue)
; RPM = 55,296,000 / (new1cap - old1cap)
; quotient = numerator / denominator
; calculate interval (32 bits) and store result in denom:
; current = timer1z timer1y ccpr1h ccpr1l
; new = new1cap3 new1cap2 new1cap1 new1cap0
; old = old1cap3 old1cap2 old1cap1 old1cap0
; interval = denom_r3 denom_r2 denom_r1 denom_r0
; see also CALC_RPM and COPY_RPM routines
bcf flags2,norpm ; we did get a rpm pulse
INT_CCP1_STORE bank02 ; yes, continue and handle CCP1 interrupt request
movf new1cap0,w ; store previous captured values
movwf old1cap0 ;
movf new1cap1,w ;
movwf old1cap1 ;
movf new1cap2,w ;
movwf old1cap2 ;
movf new1cap3,w ;
movwf old1cap3 ;
INT_CCP1_CAPT bank20 ; get capture moment
movf ccpr1l,w ; get first captured byte
bank02 ;
movwf new1cap0 ;
movwf ccp1interval0 ;
bank20 ;
movf ccpr1h,w ; get second captured byte
bank02 ;
movwf new1cap1 ;
movwf ccp1interval1 ;
movf timer1y,w ; get third timer byte
movwf new1cap2 ;
movwf ccp1interval2 ;
movf timer1z,w ; get newly capture values, first get the fourth timer byte
movwf new1cap3 ;
movwf ccp1interval3 ;
INT_CCP1_CHECK bank20
btfss pir1,tmr1if ; do we also have a timer 1 overflow interrupt request ?
goto INT_CCP1_SUB ; no, so it is ok to handle the CCP1 interrupt request
movlw d'128' ; yes, now see if we can use the current timer 1 value (use halfway value)
subwf ccpr1h,w ; any capture from (long) before the timer 1 overflow must use old timer values
skpnc ; was the capture from before the timer 1 overflow ?
goto INT_CCP1_SUB ; yes,
bank02 ;
incf ccp1interval2,f ; no, increase timer 1 bytes 3 and 4
incf new1cap2,f ;
skpnz ;
incf ccp1interval3,f ;
movf ccp1interval3,w ;
movwf new1cap3 ;
INT_CCP1_SUB bank02 ;
movf old1cap0,w ; subtraction, calculate the actual interval
subwf ccp1interval0,f ; store interval byte 0
movf old1cap1,w ;
skpc ;
incfsz old1cap1,w ;
subwf ccp1interval1,f ;
movf old1cap2,w ;
skpc ;
incfsz old1cap2,w ;
subwf ccp1interval2,f ;
movf old1cap3,w ;
skpc ;
incfsz old1cap3,w ;
subwf ccp1interval3,f ;
INT_CCP1_LAST movf ccp1interval0,w ; copy time interval between last two pulses
movwf lastintr0 ;
movf ccp1interval1,w ;
movwf lastintr1 ;
movf ccp1interval2,w ;
movwf lastintr2 ;
movf ccp1interval3,w ;
movwf lastintr3 ;
INT_CCP1_DONE bank20 ;
bcf pir1,ccp1if ; clear capture 1 interrupt flag
return ; we're done here, return in bank2
;--------------------------------------------------------------------------------------------------------------------------------------
; Handle CCP2 capture interrupt
;--------------------------------------------------------------------------------------------------------------------------------------
INT_CCP2 ; since we can have both timer 1 overflow and ccp interrupt request at the same
; time it is important to update the timer 1 extension bytes at the right time,
; now see if we can use the present timer 1 values to calculate the vehicle speed
; from CCP module 2 using the time (t) between two captures, otherwise we exit this
; routine and come back later when timer 1 has been incremented
bcf flags2,nospeed ; we did get a speed pulse
INT_CCP2_STORE bank02 ; yes,
movf new2cap0,w ; store previous captured values
movwf old2cap0 ;
movf new2cap1,w ;
movwf old2cap1 ;
movf new2cap2,w ;
movwf old2cap2 ;
movf new2cap3,w ;
movwf old2cap3 ;
INT_CCP2_CAPT bank20 ; get capture moment
movf ccpr2l,w ; get first captured byte
bank02 ;
movwf new2cap0 ;
movwf ccp2interval0 ;
bank20 ;
movf ccpr2h,w ; get second captured byte
bank02 ;
movwf new2cap1 ;
movwf ccp2interval1 ;
movf timer1y,w ; get third timer byte
movwf new2cap2 ;
movwf ccp2interval2 ;
movf timer1z,w ; get newly capture values, first get the fourth timer byte
movwf new2cap3 ;
movwf ccp2interval3 ;
INT_CCP2_CHECK bank20
btfss pir1,tmr1if ; do we also have a timer 1 overflow interrupt request ?
goto INT_CCP2_SUB ; no, so it is ok to handle the CCP2 interrupt request
movlw d'128' ; yes, now see if we can use the current timer 1 value (use halfway value)
subwf ccpr2h,w ; any capture from (long) before the timer 1 overflow must use old timer values
skpnc ; was the capture from before the timer 1 overflow ?
goto INT_CCP2_SUB ; yes,
bank02 ;
incf ccp2interval2,f ; no, increase timer 1 bytes 3 and 4
incf new2cap2,f ;
skpnz ;
incf ccp2interval3,f ;
movf ccp2interval3,w ;
movwf new2cap3 ;
INT_CCP2_SUB bank02 ;
movf old2cap0,w ; subtraction, the resulting interval is put in denominator
subwf ccp2interval0,f ; store interval byte 0 for use in division routine
movf old2cap1,w ;
skpc ; is capture0 - hold0 < 0 ?
incfsz old2cap1,w ; yes, 'borrow' from more significant byte
subwf ccp2interval1,f ; no, borrow has been skipped, do subtraction and store for use in division
movf old2cap2,w ;
skpc ;
incfsz old2cap2,w ;
subwf ccp2interval2,f ;
movf old2cap3,w ;
skpc ;
incfsz old2cap3,w ;
subwf ccp2interval3,f ;
INT_CCP2_LAST movf ccp2interval0,w ; copy time interval between last two pulses
movwf lastints0 ;
movf ccp2interval1,w ;
movwf lastints1 ;
movf ccp2interval2,w ;
movwf lastints2 ;
movf ccp2interval3,w ;
movwf lastints3 ;
INT_CCP2_DONE bank20 ;
bcf pir2,ccp2if ; clear capture 2 interrupt flag
return ; done, return in bank0
;--------------------------------------------------------------------------------------------------------------------------------------
INT_DROP_RPM ; Drop the speed value (calculate pulse interval) in case we do not get speed pulses anymore
INT_DROP_R_NOW bank2 ;
clrf intervalr0 ; get present timer1 value (now)
clrf intervalr1 ; at the moment of overflow these values became zero
movf timer1y,w ; get third timer byte
movwf intervalr2 ;
movf timer1z,w ;
movwf intervalr3 ; store in rpm interval
INT_DROP_R_SUB movf new1cap0,w ; calculate rpm value when we do not have any rpm pulses on the input
subwf intervalr0,f ; subtraction, calculate the time interval between the last pulse and now
movf new1cap1,w ; store the result in intervalr0-3
skpc ;
incfsz new1cap1,w ;
subwf intervalr1,f ;
movf new1cap2,w ;
skpc ;
incfsz new1cap2,w ;
subwf intervalr2,f ;
movf new1cap3,w ;
skpc ;
incfsz new1cap3,w ;
subwf intervalr3,f ;
INT_DROP_R_TEST movf intervalr0,w ; is the time interval between the last pulse and now larger than the time between
subwf lastintr0,w ; the last two pulses ?
movf intervalr1,w ;
skpc ;
incfsz intervalr1,w ;
subwf lastintr1,w ;
movf intervalr2,w ;
skpc ;
incfsz intervalr2,w ;
subwf lastintr2,w ;
movf intervalr3,w ;
skpc ;
incfsz intervalr3,w ;
subwf lastintr3,w ;
skpnc ;
return ; no, do nothing
INT_DROP_R_YES movf intervalr0,w ; copy the new (longer) interval value so we drop the rpm rate
movwf ccp1interval0 ;
movf intervalr1,w ;
movwf ccp1interval1 ;
movf intervalr2,w ;
movwf ccp1interval2 ;
movf intervalr3,w ;
movwf ccp1interval3 ;
INT_DROP_R_OVR movlw d'255' ; timer1 and the extra bytes will overflow, this will affect the interval value indirectly
subwf ccp1interval3,w ; since the new1cap values will stay the same, so we have to adjust the new1cap values
skpz ; is the value of the interval about to overflow ?
return ; no, done
rlf new1cap3,f ; yes, get the most significant bit of new1cap
skpc ; toggle it's value
setc ; this will move the new1cap value in time
skpnc ; so the interval value will be decreased but still very long to show zero rpm
clrc ;
rrf new1cap3,f ;
return ; done
;--------------------------------------------------------------------------------------------------------------------------------------
; See if we are getting input pulses of rpm and speed, otherwise calculate dropping rpm and speed values
;--------------------------------------------------------------------------------------------------------------------------------------
INT_DROP_SPEED ; calculate dropping rpm and speed values
; Drop the rpm rate (calculate new rpm pulse interval) in case we do not get rpm pulses anymore
INT_DROP_S_NOW bank2 ;
clrf intervals0 ; get present timer1 value (now)
clrf intervals1 ; at the moment of overflow these values became zero
movf timer1y,w ; get third timer byte
movwf intervals2 ;
movf timer1z,w ;
movwf intervals3 ; store in speed interval
INT_DROP_S_SUB movf new2cap0,w ; subtraction, calculate the time interval between the last pulse and now
subwf intervals0,f ; store the result in intervals0-3
movf new2cap1,w ;
skpc ;
incfsz new2cap1,w ;
subwf intervals1,f ;
movf new2cap2,w ;
skpc ;
incfsz new2cap2,w ;
subwf intervals2,f ;
movf new2cap3,w ;
skpc ;
incfsz new2cap3,w ;
subwf intervals3,f ;
INT_DROP_S_TEST movf intervals0,w ; is the time interval between the last pulse and now larger than the time between
subwf lastints0,w ; the last two pulses ?
movf intervals1,w ;
skpc ;
incfsz intervals1,w ;
subwf lastints1,w ;
movf intervals2,w ;
skpc ;
incfsz intervals2,w ;
subwf lastints2,w ;
movf intervals3,w ;
skpc ;
incfsz intervals3,w ;
subwf lastints3,w ;
skpnc ;
return ; no, do nothing
INT_DROP_S_YES movf intervals0,w ; yes, copy the new (longer) interval value so we drop the speed value
movwf ccp2interval0 ;
movf intervals1,w ;
movwf ccp2interval1 ;
movf intervals2,w ;
movwf ccp2interval2 ;
movf intervals3,w ;
movwf ccp2interval3 ;
INT_DROP_S_OVR movlw d'255' ; timer1 and the extra bytes will overflow, this will affect the interval value indirectly
subwf ccp2interval3,w ; since the new2cap values will stay the same, so we have to adjust the new2cap values
skpz ; is the value of the interval about to overflow ?
return ; no, done
rlf new2cap3,f ; yes, get the most significant bit of new1cap
skpc ; toggle it's value
setc ; this will move the new2cap value in time
skpnc ; so the interval value will be decreased but still very long to show zero speed value
clrc ;
rrf new2cap3,f ;
return ; done
;--------------------------------------------------------------------------------------------------------------------------------------
INT_SHIFT ; when the calculated rpm value is more than the given maximum turn on the shift light
; t [s] = 1 / F [Hz]
; t [s] = 60 / RPM
; interval [timer cycles] = 60 * timer1clockrate / RPM
; interval [instruction cycles] = (60 * 3686400/4) MHz / RPM
; interval [instruction cycles] = 55,296,000 / RPM
; (capturevalue - previouscapturevalue) = 55,296,000 / RPM
; (new1cap - old1cap) = 55,296,000 / RPM
; denominator1 (from ccp1 routine) = 55,296,000 / RPM
; so we want to compare the denominator1 value with the four interval bytes:
; interval : rpmmax_3 rpmmax_2 rpmmax_1 rpmmax_0
; which we have calculated at initialization from the two maximum rpm bytes
; rpm max : rpmmax_high rpmmax_low
bank2 ;
movf ccp1interval0,w ; subtraction, when the measured rpm value is more than the set maximum we should turn on the
subwf rpmmax_0,w ; so called 'shift light' to let the driver know it is time to shift gears
movf ccp1interval1,w ; so turn on if: rpmmax - denominator (=interval) > 0
skpc ; is capture0 - hold0 < 0 ?
incfsz ccp1interval1,w ; yes, 'borrow' from more significant byte
subwf rpmmax_1,w ; no, borrow has been skipped, do subtraction
movf ccp1interval2,w ;
skpc ;
incfsz ccp1interval2,w ;
subwf rpmmax_2,w ;
movf ccp1interval3,w ;
skpc ;
incfsz ccp1interval3,w ;
subwf rpmmax_3,w ;
bank20 ;
skpc ; should we turn off the shift light ?
INT_SHIFT_OFF bcf flags2,shlight ; yes, rpm rate is smaller than given maximum, turn shift light (flag) off, flag used in timer
skpc ; again, should we turn off the shift light ?
clrf portd ; yes, turn shift light off
skpnc ; should we turn on the shift light ?
INT_SHIFT_ON bsf flags2,shlight ; yes, with this high rpm rate, turn shift light flag on, used in timer interrupt to flash light
return ; done, return in bank0
;--------------------------------------------------------------------------------------------------------------------------------------
; Handle UART receive interrupt
;--------------------------------------------------------------------------------------------------------------------------------------
; input bytes are stored in buffer for later use when the received command is going to be
; interpreted and executed
; check for command byte sequence, if all bytes of a command have been received set the flag
; bit that allows the command execute routine
; a valid command byte sequence is (in ascii):
; <STX> 1 byte indicator for start of command, value hex 02
; <c1> 1 byte, first letter of command
; <c2> 1 byte, second letter of command
; <data bytes> 0-64 data bytes, all values accepted except ascii codes STX and ETX
; <ETX> 1 byte indicator for end of command, value hex 03
; <checksum> 1 byte checksum, any hex value 00-FF (incl.hex 03 !), last byte of command
; the checksum byte value makes the sum of all bytes from <STX> up to and including <ETX> zero
; used bits for uart in flags register:
; bit 0 = high : STX received, started reception of rest of command
; bit 1 = high : ETX received, now wait for checksum
; bit 2 = high : command has data bytes
; bit 3 = high : all bytes received, now ready for execution of command
INT_RX bank0
bsf portc,cts_out ; clear CTS to hold data stream from computer
btfsc rcsta,oerr ; overrun error ?
goto OVERFLOW_ERR ; yes, we cannot keep up with the input, go discard input
btfsc rcsta,ferr ; no, framing error ?
goto FRAMING_ERR ; yes, go discard input
movlw b'00000001' ; no, select rx9d/parity bit (we use odd parity)
andwf rcsta,w ; store only the parity bit in w register
movwf rx_parity ; copy the value to rx_parity
movf rcreg,w ; deque received byte
movwf rx_byte ; store byte for later use
CHECK_PARITY xorwf rx_parity,f ; use parity bit in calculation
swapf rx_parity,w ; use w and rx_parity register for the calculation
xorwf rx_parity,f ; calculate nibbles
rrf rx_parity,w ;
xorwf rx_parity,f ; at this point the parity values of the nibbles are in bit 2 and bit 0
btfss rx_parity,2 ; if parity one nibble is 1 then whole byte parity equals that of other nibble, skip ahead
incf rx_parity,f ; otherwise, invert bit 0
btfsc rx_parity,0 ; parity error ?
goto PARITY_ERR ; yes
btfsc flags1,command ; no, did we already receive a command or are we busy executing one ?
return ; yes, discard input,seems handshaking is ignored, but we cannot accept a new command right now
VALID_BYTE movf rx_byte,w ; if we get here we have received valid input
sublw h'02' ; ascii code for 'start of text' (STX)
skpnz ; check if byte matches
goto COMMAND_START ; match, go set flags to allow reception of rest of command
STX? btfss flags1,stx ; no match, but did we already receive the STX byte ?
goto NO_ERR ; no, do not use any received byte until STX has been received first
ADD_TO_CS movf rx_byte,w ; yes, received byte is in rx_byte, what was it again ?
addwf rx_checksum,f ; use all bytes including the checksum byte in the checksum calculation
CHECKSUM? btfsc flags1,etx ; did we already receive all bytes of the command and is this the checksum byte ?
goto CHECK_SUM ; yes, go test checksum
ETX? sublw h'03' ; ascii code for 'end of text' (ETX)
skpnz ; did we receive (ETX) and therefore all command and data bytes ?
goto GOT_ETX ; yes, go set flag and exit
OVERFLOW? movlw rx_buffer66 + 1 ; no, check if pointer has passed the maximum allowed address
subwf rx_pointer,w ; see where the pointer is
skpnz ; already there ?
goto ABORT_COMMAND ; yes, too many data bytes: abort receiving this command
STORE movf rx_pointer,w ; get the available address for storage of the newly received data byte
movwf rx_maxpos ; store the pointer, keep the position of the last data byte
movwf fsr ; use value to set the indirect file address
bcf status,irp ; make sure upper bit in address is zero (select register bank 0 and 1)
movf rx_byte,w ; what was the received byte again ?
movwf indf ; store it in the command input buffer
INC_POINTER incf rx_pointer,f ; go point to next position for any following byte
goto NO_ERR ; done handling the received byte, return in bank0
COMMAND_START movlw h'02' ; the value of STX should be
movwf rx_checksum ; the initial checksum value
movlw rx_buffer00 ; first position in the input buffer for command bytes
movwf rx_pointer ; use this value to reset pointer
movwf rx_maxpos ; reset the pointer to the last received data byte
bsf flags1,stx ; set flag 'STX has been received, wait for the rest of the command bytes'
bcf flags1,withdata ; clear flag for data bytes
goto NO_ERR ; done handling the received byte, return in bank0
CHECK_SUM movf rx_checksum,w ; the checksum is the sum of all bytes added together and should be zero
skpz ; is checksum ok ?
goto ABORT_COMMAND ; no, go abort the reception of the command
CHECK_SIZE movlw rx_buffer01 ; yes, second position in buffer
subwf rx_maxpos,w ; test if the last command byte has been placed at the second place or further,
skpc ; so did we get at least two command bytes ?
goto ABORT_COMMAND ; no, go abort the reception of the command
skpz ; did we get more than three bytes ?
bsf flags1,withdata ; yes, set flag 'this is a command with data'
bsf flags1,command ; set flag to get command executed
bcf flags1,stx ; now ok to reset stx flag for a next command
bcf flags1,etx ; now ok to reset etx flag for a next command
return ; bytes have been handled, return without setting CTS to keep data stream blocked, bank0 return
GOT_ETX bsf flags1,etx ; we have received all command and data bytes, now wait for the checksum byte
bcf portc,cts_out ; set CTS to allow computer to send data
return ; done
ABORT_COMMAND bcf flags1,stx ; abort receiving command,
bcf flags1,etx ; clear any command bytes received up to now
movlw h'15' ; negative acknowledgement (NAK)
pagesel TX_BYTE ; make right page selection before call
call TX_BYTE ; send 'command has NOT been accepted', return in bank0
pagesel NO_ERR ; make right page selection before call
goto NO_ERR ; done handling the received byte, return in bank0
OVERFLOW_ERR movf rcreg,w ; deque byte (discard input byte)
movf rcreg,w ; deque byte (discard input byte)
bcf rcsta,cren ; clear overrun error bit
bsf rcsta,cren ; re-enable receive
bcf flags1,stx ; clear any command bytes received up to now
bcf flags1,etx ; clear any command bytes received up to now
bcf portc,cts_out ; set CTS to allow computer to send data
return ; done
FRAMING_ERR movf rcreg,w ; we did not deque received byte yet, do so now (discard input byte), update receive flags
PARITY_ERR bcf flags1,stx ; clear any command bytes received up to now
bcf flags1,etx ; clear any command bytes received up to now
NO_ERR bcf portc,cts_out ; set CTS to allow computer to send data
return ; done
;--------------------------------------------------------------------------------------------------------------------------------------
;--------------------------------------------------------------------------------------------------------------------------------------
; Start of main program
;--------------------------------------------------------------------------------------------------------------------------------------
;--------------------------------------------------------------------------------------------------------------------------------------
INITIALIZE
INIT_INT_OFF clrf intcon ; ensure there are no interrupt requests
bank1 ;
clrf pie1 ; disable all pheripheral interrupts
clrf pie2 ;
INIT_CHECKPIC ; ******* check pic integrity: checksum pic program memory, set/reset sram bits
INIT_PORTA bank0 ; port A will have five analog inputs (note: port A is set as ANALOG i/o as default)
clrf porta ; clear output data latches
bank1 ;
movlw b'00111111' ;
movwf trisa ; make all six pins of port A inputs
; Port A0 = Input, Lambda sensor
; Port A1 = Input, Voltage In
; Port A2 = Input, Thermocouple
; Port A3 = Input, Air Temperature
; Port A4 = Input, clock input 32768 Hz from clock timer chip
; Port A5 = Input, Water Temperature
INIT_PORTB bank0 ; port B
clrf portb ; clear output data latches, make all outputs low
bank1 ;
movlw b'00111111' ;
movwf trisb ; make port B input & outputs
; Port B0 = Input Run
; Port B1 = Input _Mark_
; Port B2 = Input Brake
; Port B3 = Input Laptime
; Port B4 = Input _TC disconnected_
; Port B5 = Input BoardsupplyOn
; Port B6 = Output Status Led Red
; Port B7 = Output Status Led Green
INIT_PORTC bank0 ; inputs, communications port to computer (RS232) realtime clock and serial eeprom (IIC)
clrf portc ; clear output data latches
bank1 ;
movlw b'11111110' ;
movwf trisc ; set port C configuration
; Port C0 = Output computer CTS
; Port C1 = Input Speed
; Port C2 = Input RPM
; Port C3 = Input* SDA for MSSP IIC, configure as input
; Port C4 = Input* SCL for MSSP IIC, configure as input
; Port C5 = Input computer RTS
; Port C6 = Output* computer RX, configure as input
; Port C7 = Input computer TX
INIT_PORTD bank0 ; port D will drive eight pairs of leds (rpm meter/shift light)
clrf portd ; clear output data latches, all outputs low
bank1 ;
movlw b'00000000' ;
movwf trisd ; make port D all outputs
INIT_PORTE bank0 ; port E will have three analog inputs (note: port E is set as ANALOG i/o as default)
clrf porte ; clear output data latches
bank1 ;
movlw b'00000111' ; make port E all inputs
movwf trise ;
; Port E0 = Input Throttle
; Port E1 = Input XAccelleration
; Port E2 = Input YAccelleration
INIT_TIMER0 clrwdt ; clear watchdog timer to prevent unintended device reset
movlw b'10100001' ; we want 32 Hz interrupts using the 32768 Hz output from clock chip, divide by 4,
bank1 ; assign prescaler to timer0, increment from external clock on low-to-high,
movwf optionreg ; pull-ups on portb disabled, interrupt on falling edge of B0/int pin
bcf intcon,t0ie ; disable interrupt from timer 0, it will be enabled only during logging
INIT_TIMER1 bank0 ; use timer 1 as a timer/counter to count instruction cycles
movlw b'00000001' ; use internal instruction cycle clock divided by 1, turn on timer
movwf t1con ; configure timer 1
clrf tmr1l ; start timer at zero value
clrf tmr1h ; start timer at zero value
bank1 ;
bsf pie1,tmr1ie ; enable interrupt from timer 1
INIT_TIMER2 bank0 ;
movlw b'00000000' ;
movwf t2con ; disable timer 2
INIT_CCP1 bank1 ;
bsf trisc,2 ; make sure pin rc2/ccp1 is configured as input
bsf pie1,ccp1ie ; enable interupts from CCP1 module
bank0 ;
movlw b'00000101' ; capture mode, on every rising edge of pin rc2/ccp1
movwf ccp1con ;
INIT_CCP2 bank1 ;
bsf trisc,1 ; make sure pin rc1/t1osi/ccp2 is configured as input
bsf pie2,ccp2ie ; enable interrupt from CCP2 module
bank0 ;
movlw b'00000101' ; capture mode, on every rising edge of pin rc1/t1osi/ccp2
movwf ccp2con ;
INIT_IIC bank1 ; configure the MSSP Module as IIC bus, PIC is bus master
bcf pie1,sspie ; disable interrupt from ssp action
bcf pie2,bclie ; disable interrupt from ssp bus collision
movlw d'9' ; baud rate calculation: x = ((( Fosc / IICbaudrate)/4)-1) (~100 kHz @ 3.6864Mhz)
movwf sspadd ; set IIC baud rate to 100 kHz
bcf sspstat,cke ; select IIC input levels
bsf sspstat,smp ; set slew rate for standard speed mode (100 kHz)
bank0 ;
movlw b'00101000' ; enable serial port, master mode
movwf sspcon ; configure
pagesel IIC_START ; make right program memory page selection
call IIC_START ; send initialization sequence (see Microchip AN709): start, 9 ones, start, stop
bank0 ; to properly reset eeprom devices on iic bus (when circuit has been reset during write)
movlw b'11111111' ; send 8 times 1
movwf sspbuf ; send the slave address
bank1 ;
pagesel INIT_IIC_LOOP1 ; make right program memory page selection
INIT_IIC_LOOP1 btfsc sspstat,r_notw ; is the transmission completed ?
goto INIT_IIC_LOOP1 ; no, wait here until it is completed
bsf sspcon2,ackdt ; select NOACK
bsf sspcon2,acken ; send NOACK, 9th one bit
INIT_IIC_LOOP2 btfsc sspcon2,acken ; send completed ?
goto INIT_IIC_LOOP2 ; no, wait here until send has been completed
pagesel IIC_START ; make right program memory page selection
call IIC_START ;
call IIC_STOP ;
bank1 ;
clrf iicchip ; select first eeprom chip
INIT_AD bank1 ;
movlw b'10000000' ; right justified, five pins of port A and three pins of port E will be analog inputs,
movwf adcon1 ; note: more configuration is to be done in a/d subroutine
bcf pie1,adie ; disable interrupt from a/d module
INIT_UART bank1 ; configure UART for asynchronous communications
movlw d'3' ; value for baudrate (23:9600, 11:19200, 3:57600, 1:115200 baud Fosc=3.6864 MHz)
movwf spbrg ; set UART to this value
movlw b'01000110' ; 9 bit, Tx off, async, high speed (transmit: 8 data bits, odd parity , 1 stop bit)
movwf txsta ; load Tx status register
bank0 ;
movlw b'01010000' ; serial port disabled, 9 bit, Rx off (receive: 8 data bits, odd parity, 1 stop bit)
movwf rcsta ; load Rx status register
bsf rcsta,spen ; enable reception
bank1 ;
bsf txsta,txen ; enable transmission
bsf pie1,rcie ; enable interrupt on receive
bcf portc,cts_out ; set CTS to allow computer to send data
INIT_CLOCK movlw d'7' ; location of clock control byte
bank2 ;
movwf clockaddr ;
movlw b'10000011' ; clock control byte, set square wave at 32768 Hz but still disabled and it's output high
call IIC_WR_CLKCHIP ; write byte to control bytes or static ram of clock chip, returns in bank1
INIT_EEPROM nop ; ****** see if datalogger internal eeprom has ever been intialized, otherwise set defaults
INIT_VARIABLES clrf flags1 ; clear flag register1
clrf flags2 ; clear flag register2
bank2 ;
clrf timer1y ; range extension for timer1 byte three
clrf timer1z ; range extension for timer1 byte four
clrf new1cap0 ; make sure the rpm and speed values start at zero when there are no input pulses
clrf new1cap1 ;
clrf new1cap2 ;
clrf new1cap3 ;
clrf new2cap0 ;
clrf new2cap1 ;
clrf new2cap2 ;
clrf new2cap3 ;
clrf lastintr0 ;
clrf lastintr1 ;
clrf lastintr2 ;
clrf lastintr3 ;
clrf lastints0 ;
clrf lastints1 ;
clrf lastints2 ;
clrf lastints3 ;
bank3 ;
clrf current_rec ; no record selected for download
pagesel COPY_RPMVALUES ; make right program memory page selection
call COPY_RPMVALUES ; call all routines seperately because they have all returns
call COPY_WHEELC ; to be able to use them individually, return with page setting...
call COPY_LOGRATES ;
pagesel COPY_ERRORFLAGS ; restore program memory page selection
call COPY_ERRORFLAGS ; get values maximum rpm rate, wheel circumference, lograte values, error flag bits
INIT_INT_ON bank0
clrf pir1 ; clear all pheripheral interrupt request flags
clrf pir2 ;
bsf intcon,peie ; enable peripheral interrupts
bsf intcon,gie ; enable all interrupts
INIT_READY bsf portb,led_red ; turn on red status led as indication power on and software initialized
INIT_RUN ; empty line to avoid mplab error
pagesel MAIN ; make right program memory page selection
goto MAIN ; all initialization has been done, go run main code
;--------------------------------------------------------------------------------------------------------------------------------------
COPY_RPMVALUES ; reads the maximum rpm value from pic eeprom addresses, from this value the interval is
; calculated that will be used to turn on the shift light when the measured rpm rate is
; larger than the given maximum
; calculation below is for one pulse per revolution
; a correction is done for two pulses per revolution
; engine RPM rate calculation, see also INT_CCP1
; F [Hz] = 1 / t [s]
; RPM = 60 / t [s]
; RPM = 60 * timer1clockrate / interval [timer cycles]
; RPM = (60 * 3686400/4) Mhz / interval [instruction cycles]
; RPM = 55,296,000 / interval [instruction cycles]
; RPM = 55,296,000 / (capturevalue - previouscapturevalue)
; RPM = 55,296,000 / (new1cap - old1cap)
; quotient = numerator / denominator
; see also CALC_RPM and COPY_RPM routines
; calculate interval (input values are 32 bits, the result is 32 bits):
; t [s] = 1 / F [Hz]
; t [s] = 60 / RPMMAX
; interval [timer cycles] = 60 * timer1clockrate / RPMMAX
; interval [instruction cycles] = (60 * 3686400/4) MHz / RPMMAX
; interval [instruction cycles] = 55,296,000 / RPMMAX
; interval [instruction cycles] = 55,296,000 / RPMMAX
; interval [instruction cycles] = 55,296,000 / RPMMAX
; interval [instruction cycles] = 55,296,000 / RPMMAX
; rpmmax_3&2&1&0 = 55,296,000 / rpmmax_high&low
; quotient = numerator / denominator
; numerator : nrator3 nrator2 nrator1 nrator0
; denominator: denom_r3 denom_r2 denom_r1 denomr_0
; quotient : rpmmax_3 rpmmax_2 rpmmax_1 rpmmax_0
; remainder : remain3 remain2 remain1 remain0
; the remainder R is not used
; interval : rpmmax_3 rpmmax_2 rpmmax_1 rpmmax_0
; rpm max : rpmmax_high rpmmax_low
COPY_PULSES movlw d'4' ; pic eeprom address of low byte of rpm maximum
pagesel IEE_READ ; make right program memory page selection
call IEE_READ ; read pic eeprom, address in w, data returned in w, returns in bank2
movwf pulses ; store value
COPY_RPM_READ movlw d'2' ; pic eeprom address of low byte of rpm maximum
call IEE_READ ; read pic eeprom, address in w, data returned in w, returns in bank2
movwf rpmmax_low ; store value, low byte
movlw d'3' ; eeprom address of high byte of rpm maximum
call IEE_READ ; read pic eeprom, address in w, data returned in w, returns in bank2
movwf rpmmax_high ; store value, high byte
pagesel COPY_RPM_DIV ; make right program memory page selection
COPY_CORRECTION btfsc pulses,0 ; do we have one pulse per revolution ?
goto COPY_RPM_DIV ; yes, go use the value as it is
clrc ; no, multiply the maxrpm value by two to correct for the number of pulses per revolution
rlf rpmmax_low ; low byte
rlf rpmmax_high ; high byte
COPY_RPM_DIV bank2 ;
movlw d'3' ; set numerator X = 55,296,000 = 3 : 75 : 192 : 0
movwf nrator3 ;
movlw d'75' ;
movwf nrator2 ;
movlw d'192' ;
movwf nrator1 ;
movlw d'0' ;
movwf nrator0 ;
clrf denom_r3 ; get denominator
clrf denom_r2 ;
movf rpmmax_high,w ;
movwf denom_r1 ;
movf rpmmax_low,w ;
movwf denom_r0 ;
movlw d'32' ; there are 32 bits in this division
movwf divcounter ;
clrf remain0 ; clear remainder
clrf remain1 ;
clrf remain2 ;
clrf remain3 ;
COPY_RPM_LOOP clrc ;
rlf nrator0,f ; shift next bit to remainder (msbit numerator to lsbit remainder)
rlf nrator1,f ;
rlf nrator2,f ;
rlf nrator3,f ;
rlf remain0,f ;
rlf remain1,f ;
rlf remain2,f ;
rlf remain3,f ;
COPY_RPM_TEST movf denom_r3,w ; subtract denominator from remainder, if no borrow then next bit of quotient is 1
subwf remain3,w ; if borrow next bit of the quotient is 0
skpz ; check, is the result of remain3 - denom3 exactly 0 ?
goto COPY_RPM_NOTZ ; no, go check for negative result
movf denom_r2,w ; yes, continue
subwf remain2,w ; do test subtraction
skpz ; check, is the result of remain2 - denom2 exactly 0 ?
goto COPY_RPM_NOTZ ; no, go check for negative result
movf denom_r1,w ; yes, continue
subwf remain1,w ; do test subtraction
skpz ; check, is the result of remain1 - denom1 exactly 0 ?
goto COPY_RPM_NOTZ ; no, go check for negative result
movf denom_r0,w ; yes, continue
subwf remain0,w ; do test subtraction
COPY_RPM_NOTZ skpc ; is the result of any remain - denom less than 0 thus negative ?
goto COPY_RPM_NOGO ; yes, skip subtraction, this quotient bit will be zero
COPY_RPM_SUB2 movf denom_r0,w ; no, start with real subtraction, the resulting interval is put in denominator
subwf remain0,f ;
movf denom_r1,w ;
skpc ; is remain0 - denom0 < 0 ?
incfsz denom_r1,w ; yes, 'borrow' from more significant byte
subwf remain1,f ; no, borrow has been skipped, do subtraction
movf denom_r2,w ;
skpc ; is remain1 - denom1 < 0 ?
incfsz denom_r2,w ; yes, 'borrow' from more significant byte
subwf remain2,f ; no, borrow has been skipped, do subtraction
movf denom_r3,w ;
skpc ; is remain2 - denom2 < 0 ?
incfsz denom_r3,w ; yes, 'borrow' from more significant byte
subwf remain3,f ; no, borrow has been skipped, do subtraction
setc ; this quotient bit is one
COPY_RPM_NOGO rlf rpmmax_0,f ; shift bit into quotient result
rlf rpmmax_1,f ; shift bit into quotient result
rlf rpmmax_2,f ; shift bit into quotient result
rlf rpmmax_3,f ; shift bit into quotient result
decfsz divcounter,f ;
goto COPY_RPM_LOOP ; go do next bit
return ; done
;--------------------------------------------------------------------------------------------------------------------------------------
COPY_WHEELC ; reads the wheel circumference in millimeters from pic eeprom,
; multiplies this value by 3318 and stores the result in speed_const0..3
; please see INT_CCP2 code for details
; to be used in the calculate speed/ccp2 interrupt routine
; calculate product (input values are 16 bits, the result is 32 bits):
; number1 : - - mult_a1 mult_a0
; number2 : - - mult_b1 mult_b0
; result P : speed_const3 speed_const2 speed_const1 speed_const0
; for minimum execution time number1 should be the smaller number of the two
MULT16X16 bank0 ; multiply the two 16-bit numbers 3318 and circumference and store the 32 bit result
movlw d'0' ; eeprom address of low byte of wheel diameter
pagesel IEE_READ ; make right program memory page selection
call IEE_READ ; read pic eeprom, address in w, data returned in w, returns in bank2
movwf mult_a0 ; store first number in multiplication, low byte
bank0 ;
movlw d'1' ; eeprom address of high byte of wheel diameter
call IEE_READ ; read pic eeprom, address in w, data returned in w, returns in bank2
movwf mult_a1 ; store first number in multiplication, high byte
movlw d'246' ; 3318
movwf mult_b0 ; store second number in multiplication, low byte
movlw d'12' ;
movwf mult_b1 ; store second number in multiplication, high byte
clrf speed_const3 ; clear all four bytes of the 32 bit result
clrf speed_const2 ;
clrf speed_const1 ;
clrf speed_const0 ;
bsf speed_const1,7 ; set the 16th bit so we can use the speed_const registers as bit counter
clrc ; start off with clear carry
MULTLOOP rrf mult_a1,f ;
rrf mult_a0,f ;
pagesel MULTSHIFT ; make right program memory page selection
skpc ; was the least significant bit a one ?
goto MULTSHIFT ; no, bypass addition
movf mult_b0,w ; yes, do addition
addwf speed_const2,f ; add first of two bytes
movf mult_b1,w ;
skpnc ; check for overflow after add and
incfsz mult_b1,w ; increment high byte if necessary
addwf speed_const3,f ; add second of the two bytes
MULTSHIFT rrf speed_const3,f ; shift to next position
rrf speed_const2,f ;
rrf speed_const1,f ;
rrf speed_const0,f ;
skpc ; are we done with the multiplication ?
goto MULTLOOP ; no, next
return ; done
;--------------------------------------------------------------------------------------------------------------------------------------
COPY_LOGRATES ; copy the values from pic eeprom addresses to registers
movlw d'16' ; internal eeprom address
pagesel IEE_READ ; make right program memory page selection
call IEE_READ ; read pic eeprom, address in w, data returned in w, returns in bank2
bank3 ;
movwf freq_rpm ; store value
movlw d'17' ;
call IEE_READ ;
bank3 ;
movwf freq_speed ;
movlw d'18' ;
call IEE_READ ;
bank3 ;
movwf freq_lambda ;
movlw d'19' ;
call IEE_READ ;
bank3 ;
movwf freq_voltage ;
movlw d'20' ;
call IEE_READ ;
bank3 ;
movwf freq_tc ;
movlw d'21' ;
call IEE_READ ;
bank3 ;
movwf freq_air ;
movlw d'22' ;
call IEE_READ ;
bank3 ;
movwf freq_water ;
movlw d'23' ;
call IEE_READ ;
bank3 ;
movwf freq_throttle ;
movlw d'24' ;
call IEE_READ ;
bank3 ;
movwf freq_long ;
movlw d'25' ;
call IEE_READ ;
bank3 ;
movwf freq_lat ;
movlw d'26' ;
call IEE_READ ;
bank3 ;
movwf freq_mark ;
movlw d'27' ;
call IEE_READ ;
bank3 ;
movwf freq_brake ;
return ; done
;--------------------------------------------------------------------------------------------------------------------------------------
COPY_ERRORFLAGS ; copy the value of the error flags register from pic eeprom addresses to the register
movlw d'15' ; internal eeprom address
pagesel IEE_READ ; make right program memory page selection
call IEE_READ ; read pic eeprom, address in w, data returned in w, returns in bank2
movwf errors ; store value
return ; done
;---------------------------------------------------------------------------------------------------------------------------
FLASHREAD ; read from a location in the flash program memory
; address should be in eeadrh(bank2) and eeadr(bank2)
; data is returned in eedath(bank2) and eedata(bank2)
bank3 ;
bsf eecon1,eepgd ; point to flash program memory
bsf eecon1,rd ; start read operation
nop ; processor has to wait while data is being read
nop ; wait
return ; return in bank3
;---------------------------------------------------------------------------------------------------------------------------
FLASHWRITE ; write to a location in the flash program memory
; address should be in eeadrh(bank2) and eeadr(bank2)
; data should be in eedath(bank2) and eedata(bank2)
; data will be verified, carry set means error, carry cleared means write was ok
; uses templow and tempmiddle registers during verification
setc ; set error flag, presuming error
bank2 ;
movf eedata,w ; make a copy of the dataword value to use after the write
movwf templow ; to check if the write was ok
movf eedath,w ;
movwf tempmiddle ;
bank3 ;
bsf eecon1,eepgd ; point to flash program memory
bsf eecon1,wren ; enable writes
movlw h'55' ; sequence needed to unlock pic write safety lock,
movwf eecon2 ; used to prevent adverse writes
movlw h'AA' ;
movwf eecon2 ;
bsf eecon1,wr ; start write operation
nop ; two nops allow pic to setup for write and then
nop ; the processor will stop execution for the duration of the entire write cycle
bcf eecon1,wren ; disable writes
call FLASHREAD ; read the value of the flash program memory location into eedata and eedath
bank2 ;
movf eedata,w ; copy the low byte value to w
subwf templow,w ; compare it with the stored value to see if there was a write error
skpz ; was there a write error in the low byte ?
return ; yes, return with error flag (carry) set, return in bank2
movf eedath,w ; no, now check high byte
subwf tempmiddle,w ; compare it with the stored value to see if there was a write error
skpz ; was there a write error in the high byte ?
return ; yes, return with error flag (carry) set, return in bank2
clrc ; reset error flag
return ; return in bank2
;--------------------------------------------------------------------------------------------------------------------------------------
;--------------------------------------------------------------------------------------------------------------------------------------
;--------------------------------------------------------------------------------------------------------------------------------------
org h'0800' ; start of program memory page 1
; Note: The contents of the PCLATH register are unchanged after a RETURN or RETFIE instruction
; is executed ! The user must rewrite the contents of the PCLATH register for any subsequent
; subroutine calls or GOTO instructions
;--------------------------------------------------------------------------------------------------------------------------------------
MAIN ; main program code
MAIN_ONLOOP ; program continously loops to here while rotary switch is set to 'on',
pagesel EXEC_COMMAND ; prepare the right memory page in case we do call
btfsc flags1,command ; commands are received via interrupt routine, has a complete command been put in the buffer ?
call EXEC_COMMAND ; yes, handle it
bank0 ; no, see if the position of the rotary switch has changed from 'on' to 'log'
pagesel MAIN_ONLOOP ; make right program memory page selection
btfss portb,switch ; is the position of the rotary switch currently 'log' ?
goto MAIN_ONLOOP ; no, go back and see if a command has arrived we should handle
btfsc flags1,stx ; yes, are we receiving a command at the moment ?
goto MAIN_ONLOOP ; yes, finish reception first to minimize communication errors
MAIN_LOGTEST bank0 ; we get here if the rotary switch is set to log
bsf portc,cts_out ; clear CTS to hold data stream from computer, we need all processor time for
bcf rcsta,cren ; the logging, so stop reception and ignore any incoming commands
bcf portb,led_red ; turn off the red led
bsf portb,led_green ; turn on the green led
bank3 ; we want to make sure there are channels enabled for logging
movf freq_rpm,w ; so check all channels for their setting
andwf freq_speed,w ;
andwf freq_lambda,w ;
andwf freq_voltage,w ;
andwf freq_tc,w ;
andwf freq_air,w ;
andwf freq_water,w ;
andwf freq_throttle,w ;
andwf freq_long,w ;
andwf freq_lat,w ;
andwf freq_mark,w ;
andwf freq_brake,w ;
andlw b'10000000' ; bit seven will be set if no channels are enabled
skpz ; are there any channels enabled for logging ?
goto MAIN_FLASHLED ; no, all channels are turned off, go flash status led
MAIN_LOGSTART
; **** read errors flag register from eeprom
call MEM_OPEN ; yes, initialize memory, get location for new data, store startdate and -time, bank1 return
bcf flags2,lognow ; before starting actual logging clear this flag, it will be set during every timer0 interrupt
bank0 ;
clrf tmr0 ; clear timer0 register
bcf intcon,t0if ; clear the timer 0 overflow flag
movlw d'7' ; location of control byte in clock chip
bank2 ; turn square wave of real time clock chip on
movwf clockaddr ; select this location
movlw b'10010011' ; clock control byte, set square wave at 32768 Hz, enabled
call IIC_WR_CLKCHIP ; write control byte to clock chip, bank1 return
movlw d'255' ; we want to start logging and get a value stored for
bank3 ; all channels so use 255 as value since the register value
movwf logcounter ; will be increased to zero (log all channels) the first time
MAIN_LOGLOOP movlw d'7' ; show to the user how much room for storing the data there is left
bank1 ; up to ca. 90% (seven out of eight chips) memory filled only green led is on,
subwf mem_chip,w ; above 90% full the red led is also turned on: red and green together (extra bright)
bank0 ; see how many of the eeprom chips we have used so far
skpnc ; have we started filling the last eeprom chip ?
bsf portb,led_red ; yes, turn on the red led as well
btfsc flags2,memfull ; is all memory completely full ?
goto MAIN_LOGSTOP ; yes, exit logloop to do cleanup and then flash red led to notify user
btfsc intcon,t0if ; no, did we get a timer0 overflow interrupt so should we log data ?
call LOGDATA ; yes, actual measurement for the different input channels and the data storage is done here
bank0 ; no,
btfsc portb,switch ; has the position of the rotary switch been changed back to 'on' ?
goto MAIN_LOGLOOP ; no, keep doing the logging stuff
MAIN_LOGSTOP bank2 ; yes, go turn off square wave of real time clock chip to save battery power
movlw d'7' ; location of control byte in clock chip
movwf clockaddr ; point to this location
movlw b'10000011' ; set control value to square wave at 32768 Hz but disabled, output high
call IIC_WR_CLKCHIP ; write control byte to clock chip, bank1 return
call MEM_CLOSE ; write last data block to eeprom, write end position to toc, return in unknown bank
bank0 ; we use this logstop routine in two cases, normal stop and memory full, which one was it ?
btfss portb,switch ; has the position of the rotary switch been changed back to 'on' ?
goto MAIN_CLEANUP ; yes, skip memory full
MAIN_MEMFULL bsf errors,err_mf ; set error flag
MAIN_FLASHLED bank0 ; we will flash red status led at about 2 Hz to notify user that the memory is full
bcf portb,led_green ; turn off the green status led
MAIN_FLASHLOOP bank2
btfsc timer1y,2 ; use timer1y bit for flash rate
bsf portb,led_red ; turn on red status led
btfss timer1y,2 ;
bcf portb,led_red ; turn off red status led
bank0 ;
btfsc portb,switch ; has the position of the rotary switch been changed back to 'on' ?
goto MAIN_FLASHLOOP ; no, keep waiting until the user to switches from 'log' back to 'on' status
MAIN_CLEANUP movlw d'15' ; eeprom address for error flags register
bank2 ;
movwf iee_address ; set internal eeprom address pointer
movf errors,w ; get the value of the error flags register
call IEE_WRITE ; write data to eeprom, address in iee_address, data in w
bank0 ;
bsf rcsta,cren ; re-enable reception for incoming commands
bcf portc,cts_out ; set CTS to re-enable data stream from computer
bcf portb,led_green ; turn off the green status led
bsf portb,led_red ; turn on the red status led
goto MAIN_ONLOOP ; done
;--------------------------------------------------------------------------------------------------------------------------------------
; Handle logging flag (set during Timer0 interrupt) log data to external eeprom
;--------------------------------------------------------------------------------------------------------------------------------------
LOGDATA ; timer0 is used as the timebase for the logging event
; when logging is active this routine is called 32 times every second
; acquire input data and move it to the external eeprom
; check which channels need to be logged
; lograte table:
; 00000000 32 Hz
; 00000001 16 Hz
; 00000011 8 Hz
; 00000111 4 Hz
; 00001111 2 Hz
; 00011111 1 Hz or every second
; 00111111 1/2 Hz or every 2 seconds
; 01111111 1/4 Hz or every 4 seconds
; 1xxxxxxx never
LOG_CLEARFLAG bcf intcon,t0if ; clear the flag that brought us here (timer 0 overflow)
bank3 ;
incf logcounter,f ; counter is increased every time, so the very first time it will be 0
bsf logcounter,7 ; this bit is used to skip channels so we want it always set
LOG_RPM movf freq_rpm,w ;
andwf logcounter,w ; compare counter to the bits of the lograte setting (see above)
skpz ; is the result zero and should we take action ?
goto LOG_SPEED ; no, see if we should log the next channel
call CALC_RPM ; yes, calculate the RPM value from the time interval between the input pulses, return in bank2
movf rpm_low,w ; get low byte value (two bytes altogether for 0..16383 RPM)
movwf numlow ;
movf rpm_high,w ; get high byte value
movwf nummiddle ;
call MEM_ADD14 ; write fourteen bits to the external eeprom
LOG_SPEED bank3 ;
movf freq_speed,w ;
andwf logcounter,w ;
skpz ;
goto LOG_LAMBDA ;
call CALC_SPEED ; calculate speed value from time interval between input pulses, return in bank2
movf speed,w ; get value, maximum speed is limited to 255 km/hr (see ccp2 interrupt)
movwf numlow ; copy value
call MEM_ADD8 ;
LOG_LAMBDA bank3 ;
movf freq_lambda,w ;
andwf logcounter,w ;
skpz ;
goto LOG_VOLTAGE ;
movlw d'0' ; lambda is analog channel 0
call GET_ANALOG ; get value of analog input (selected by w register) into numlow and nummiddle, return in bank1
call MEM_ADD10 ;
LOG_VOLTAGE bank3 ;
movf freq_voltage,w ;
andwf logcounter,w ;
skpz ;
goto LOG_TC ;
movlw d'1' ; 0..5 V voltage input is analog channel 1
call GET_ANALOG ; get value of analog input (selected by w register) into numlow and nummiddle, return in bank1
call MEM_ADD10 ;
LOG_TC bank3 ;
movf freq_tc,w ;
andwf logcounter,w ;
skpz ;
goto LOG_AIRTEMP ;
movlw d'2' ; thermocouple is analog channel 2
bank0 ;
btfss portb,not_tcd ; is the thermocouple disconnected ?
bsf errors,tcdiscon ; yes, set error flag
call GET_ANALOG ; get value of analog input (selected by w register) into numlow and nummiddle, return in bank1
call MEM_ADD10 ;
LOG_AIRTEMP bank3 ;
movf freq_air,w ;
andwf logcounter,w ;
skpz ;
goto LOG_WATERTEMP ;
movlw d'3' ; air temperature is analog channel 3
call GET_ANALOG ; get value of analog input (selected by w register) into numlow and nummiddle, return in bank1
call MEM_ADD10 ;
LOG_WATERTEMP bank3 ;
movf freq_water,w ;
andwf logcounter,w ;
skpz ;
goto LOG_THROTTLE ;
movlw d'4' ; water temperature is analog channel 4
call GET_ANALOG ; get value of analog input (selected by w register) into numlow and nummiddle, return in bank1
call MEM_ADD10 ;
LOG_THROTTLE bank3 ;
movf freq_throttle,w ;
andwf logcounter,w ;
skpz ;
goto LOG_LONG ;
movlw d'5' ; throttle is analog channel 5
call GET_ANALOG ; get value of analog input (selected by w register) into numlow and nummiddle, return in bank1
call MEM_ADD10 ;
LOG_LONG bank3 ;
movf freq_long,w ;
andwf logcounter,w ;
skpz ;
goto LOG_LAT ;
movlw d'6' ; longitudinal acceleration is analog channel 6
call GET_ANALOG ; get value of analog input (selected by w register) into numlow and nummiddle, return in bank1
call MEM_ADD10 ;
LOG_LAT bank3 ;
movf freq_lat,w ;
andwf logcounter,w ;
skpz ;
goto LOG_MARK ;
movlw d'7' ; lateral accelation is analog channel 7
call GET_ANALOG ; get value of analog input (selected by w register) into numlow and nummiddle, return in bank1
call MEM_ADD10 ;
LOG_MARK bank3 ;
movf freq_mark,w ;
andwf logcounter,w ;
skpz ;
goto LOG_BRAKE ;
clrw ; start with clearing w register
bank0 ;
btfss portb,not_mark ; test if mark input is high, use inverse value for not_mark signal
movlw b'00000001' ; change if the value should be 1
call MEM_ADD1 ;
LOG_BRAKE bank3 ;
movf freq_brake,w ;
andwf logcounter,w ;
skpz ;
goto LOG_CHK_OVERFL ;
clrw ; start with clearing w register
bank0 ;
btfsc portb,brake ; test if brake input is high
movlw b'00000001' ; change if the value should be 1
call MEM_ADD1 ;
LOG_CHK_OVERFL btfsc intcon,t0if ; check for log event overflow, we just handled a log event, did we get a new request ?
bsf errors,logoflow ; yes, set flag
LOG_DONE return ; no, done
;--------------------------------------------------------------------------------------------------------------------------------------
; Calculate the RPM rate
;--------------------------------------------------------------------------------------------------------------------------------------
CALC_RPM
; calculate RPM (input values are 32 bits, the result is 16 bits):
; numerator : nrator3 nrator2 nrator1 nrator0
; denominator: denom_r3 denom_r2 denom_r1 denom_r0
; quotient : - - rpm_high rpm_low
; remainder : remain3 remain2 remain1 remain0
; only the lower 16 bits of the 32 bit quotient are used as rpm result
; the remainder R is not used
; after that even more bits are thrown away, a maximum rpm rate of 16383 rpm is
; more than sufficient and only needs 14 bits
; see also INT_CCP1 routine
CALC_RPM_COPY bcf intcon,gie ; disable interrupts during copy
bank2 ;
movf ccp1interval3,w ; copy rpm interval for later use in division routine
movwf denom_r3 ;
movf ccp1interval2,w ;
movwf denom_r2 ;
movf ccp1interval1,w ;
movwf denom_r1 ;
movf ccp1interval0,w ;
movwf denom_r0 ; these values are used to calculate the rpm rate
bsf intcon,gie ; re-enable interrupts
CALC_RPM_DIV movlw d'3' ; set numerator X = 55,296,000 = 3 : 75 : 192 : 0
movwf nrator3 ;
movlw d'75' ;
movwf nrator2 ;
movlw d'192' ;
movwf nrator1 ;
movlw d'0' ;
movwf nrator0 ;
movlw d'32' ; there are 32 bits in this division
movwf divcounter ;
clrf rpm_low ; clear rpm result so we can check for 2 byte overflow (rpm > 65535)
clrf rpm_high ;
clrf remain0 ; clear remainder
clrf remain1 ;
clrf remain2 ;
clrf remain3 ;
CALC_RPM_LOOP clrc ;
rlf nrator0,f ; shift next bit to remainder (msbit numerator to lsbit remainder)
rlf nrator1,f ;
rlf nrator2,f ;
rlf nrator3,f ;
rlf remain0,f ;
rlf remain1,f ;
rlf remain2,f ;
rlf remain3,f ;
CALC_RPM_TEST movf denom_r3,w ; subtract denominator from remainder, if no borrow then next bit of quotient is 1
subwf remain3,w ; if borrow next bit of the quotient is 0
skpz ; check, is the result of remain3 - denom3 exactly 0 ?
goto CALC_RPM_NOTZ ; no, go check for negative result
movf denom_r2,w ; yes, continue
subwf remain2,w ; do test subtraction
skpz ; check, is the result of remain2 - denom2 exactly 0 ?
goto CALC_RPM_NOTZ ; no, go check for negative result
movf denom_r1,w ; yes, continue
subwf remain1,w ; do test subtraction
skpz ; check, is the result of remain1 - denom1 exactly 0 ?
goto CALC_RPM_NOTZ ; no, go check for negative result
movf denom_r0,w ; yes, continue
subwf remain0,w ; do test subtraction
CALC_RPM_NOTZ skpc ; is the result of any remain - denom less than 0 thus negative ?
goto CALC_RPM_NOGO ; yes, skip subtraction, this quotient bit will be zero
CALC_RPM_SUB2 movf denom_r0,w ; no, start with real subtraction
subwf remain0,f ;
movf denom_r1,w ;
skpc ; is remain0 - denom0 < 0 ?
incfsz denom_r1,w ; yes, 'borrow' from more significant byte
subwf remain1,f ; no, borrow has been skipped, do subtraction
movf denom_r2,w ;
skpc ; is remain1 - denom1 < 0 ?
incfsz denom_r2,w ; yes, 'borrow' from more significant byte
subwf remain2,f ; no, borrow has been skipped, do subtraction
movf denom_r3,w ;
skpc ; is remain2 - denom2 < 0 ?
incfsz denom_r3,w ; yes, 'borrow' from more significant byte
subwf remain3,f ; no, borrow has been skipped, do subtraction
setc ; this quotient bit is one
CALC_RPM_NOGO rlf rpm_low,f ; shift bit into quotient result, store lower 16 bits of quotient as result
rlf rpm_high,f ; throw away the upper 16 bits
skpnc ; do we have a rpm rate of more than 65535 rpm ?
goto CALC_RPM_OFLOW ; yes, the result will not fit in the 16 bits of rpm_low and rpm_high, exit
decfsz divcounter,f ;
goto CALC_RPM_LOOP ; go do next bit
CALC_RPM_2PULS btfsc pulses,0 ; do we have only one puls per revolution of the crankshaft ?
goto CALC_RPM_14BIT ; yes, we do not need to adjust the value
rrf rpm_high,f ; no, so we have two pulses per revolution, we have to divide the value by two
rrf rpm_low,f ; no need to clear carry, it was already 'cleared' by previous instructions
CALC_RPM_14BIT movlw b'11000000' ; test value to see if we use the upper 2 bits of the 16 bit rpm value
andwf rpm_high,w ; do the test
skpz ; is the rpm value more than 16383 (the maximum value that will fit in 14 bits) ?
goto CALC_RPM_OFLOW ; yes, go set to maximum value (16383)
return ; no, we're done here, return in bank2
CALC_RPM_OFLOW movlw d'255' ; we have an overflow, set to 14 bit maximum (16383) instead
movwf rpm_low ;
movlw d'63' ;
movwf rpm_high ;
return ; return in bank2
;--------------------------------------------------------------------------------------------------------------------------------------
; Calculate the Speed value
;--------------------------------------------------------------------------------------------------------------------------------------
CALC_SPEED ; since we can have both timer 1 overflow and ccp interrupt request at the same
; time it is important to update the timer 1 extension bytes at the right time,
; now see if we can use the present timer 1 values to calculate the vehicle speed
; from CCP module 2 using the time (t) between two captures, otherwise we exit this
; routine and come back later when timer 1 has been incremented
; we assume there is one puls per revolution of the wheel
; t [s] = time between pulses = 1 / rotation frequency of wheel = 1 / F [Hz]
; c [mm] = circumference wheel = distance per revolution of the wheel
; SPEED [mm/s] = F * c
; SPEED [km/hr] = (F * c * 3600) / 1000000
; SPEED [km/hr] = ((1 / t) * c * 3600) / 1000000
; SPEED [km/hr] = (c * 3600) / (t * 1000000)
; SPEED [km/hr] = (c * 3600) / ((1 / (timer1clockrate / interval)) * 1000000)
; SPEED [km/hr] = (c * 3600) / ((1 / (3686400 Hz / interval)) * 1000000)
; SPEED [km/hr] = (921600 * c * 3600) / (interval * 1000000)
; SPEED [km/hr] = (3318 * c) / interval
; SPEED [km/hr] = (3318 * c) / (capturevalue - previouscapturevalue)
; the value 3318 * c has been calculated during intialization at processor reset
; and is stored in speed_const0..3 (value 3318 is actually 3317.76)
; calculate interval (32 bits):
; current = timer1z timer1y ccpr1h ccpr1l
; new = new2cap3 new2cap2 new2cap1 new2cap0
; old = old2cap3 old2cap2 old2cap1 old2cap0
; interval = denom_s3 denom_s2 denom_s1 denom2_0
; calculate speed (input values are 32 bits, the result is 16 bits):
; quotient Q = numerator X / denominator Y (and remainder R)
; numerator : nrator3 nrator2 nrator1 nrator0
; denominator: denom_s3 denom_s2 denom_s1 denom2_0
; result Q : - - - speed
; remainder : remain3 remain2 remain1 remain0
; only the lower 8 bits of the 32 bit quotient are used as speed result,
; the other bits are thrown away since a top speed of 255 km/hr is more than
; sufficient and can be stored as just one byte
; final speed value (0..255 km/hr) is stored in speed register in bank2
CALC_SPEED_COPY bcf intcon,gie ; disable interrupts during copy
bank2 ;
movf ccp2interval3,w ; copy speed interval for use in division routine
movwf denom_s3 ;
movf ccp2interval2,w ;
movwf denom_s2 ;
movf ccp2interval1,w ;
movwf denom_s1 ;
movf ccp2interval0,w ;
movwf denom_s0 ; use these values to calculate vehicle speed
bsf intcon,gie ; re-enable interrupts
CALC_SPEED_DIV movf speed_const3,w ; set numerator X = 3318 * circumference
movwf nrator3 ;
movf speed_const2,w ;
movwf nrator2 ;
movf speed_const1,w ;
movwf nrator1 ;
movf speed_const0,w ;
movwf nrator0 ;
movlw d'32' ; there are 32 bits in this division
movwf divcounter ;
clrf speed ; clear speed result so we can check for 1 byte overflow (speed > 255)
clrf remain0 ; clear remainder
clrf remain1 ;
clrf remain2 ;
clrf remain3 ;
CALC_SPEED_LOOP clrc ;
rlf nrator0,f ; shift next bit to remainder (msbit numerator to lsbit remainder)
rlf nrator1,f ;
rlf nrator2,f ;
rlf nrator3,f ;
rlf remain0,f ;
rlf remain1,f ;
rlf remain2,f ;
rlf remain3,f ;
CALC_SPEED_TEST movf denom_s3,w ; subtract denominator from remainder, if no borrow then next bit of quotient is 1
subwf remain3,w ; if borrow next bit of the quotient is 0
skpz ; check, is the result of remain3 - denom3 exactly 0 ?
goto CALC_SPEED_NOTZ ; no, go check for negative result
movf denom_s2,w ; yes, continue
subwf remain2,w ; do test subtraction
skpz ; check, is the result of remain2 - denom2 exactly 0 ?
goto CALC_SPEED_NOTZ ; no, go check for negative result
movf denom_s1,w ; yes, continue
subwf remain1,w ; do test subtraction
skpz ; check, is the result of remain1 - denom1 exactly 0 ?
goto CALC_SPEED_NOTZ ; no, go check for negative result
movf denom_s0,w ; yes, continue
subwf remain0,w ; do test subtraction
CALC_SPEED_NOTZ skpc ; is the result of any remain - denom less than 0 thus negative ?
goto CALC_SPEED_NOGO ; yes, skip subtraction, this quotient bit will be zero
CALC_SPEED_RSUB movf denom_s0,w ; no, start with real subtraction
subwf remain0,f ;
movf denom_s1,w ;
skpc ; is remain0 - denom0 < 0 ?
incfsz denom_s1,w ; yes, 'borrow' from more significant byte
subwf remain1,f ; no, borrow has been skipped, do subtraction
movf denom_s2,w ;
skpc ; is remain1 - denom1 < 0 ?
incfsz denom_s2,w ; yes, 'borrow' from more significant byte
subwf remain2,f ; no, borrow has been skipped, do subtraction
movf denom_s3,w ;
skpc ; is remain2 - denom2 < 0 ?
incfsz denom_s3,w ; yes, 'borrow' from more significant byte
subwf remain3,f ; no, borrow has been skipped, do subtraction
setc ; this quotient bit is one
CALC_SPEED_NOGO rlf speed,f ; shift bit into quotient result, store lower 8 bits of quotient as result
skpnc ; do we have a speed of more than 255 km/hr ?
goto CALC_SPEED_OFLW ; yes, the result will not fit in the 8 bits of speed register, exit
decfsz divcounter,f ;
goto CALC_SPEED_LOOP ; go do next bit
return ; we're done here, return in bank2
CALC_SPEED_OFLW movlw d'255' ; we have an overflow, set to 8 bit maximum (255) instead
movwf speed ;
return ; done, return in bank2
;--------------------------------------------------------------------------------------------------------------------------------------
; analog input subroutine
;--------------------------------------------------------------------------------------------------------------------------------------
GET_ANALOG ; get 10 bit value of one analog input
; 3 bit channel number should be in w register (0..7)
; w register content is destroyed
; value is returned in numlow and nummiddle
movwf adtemp ; use adtemp register in channel selection calculation
swapf adtemp,f ; move channel selection bits to the right position
rrf adtemp,w ; move channel selection bits to the right position, no need to clear carry:
andlw b'00111000' ; mask out all but the three bits that hold channel number
iorlw b'01000000' ; Fosc/8 gives 2.2 us bit conversion time@Fosc=3.6864 MHz), channel 0, a/d module off
bank0 ;
movwf adcon0 ; apply settings
bsf adcon0,adon ; activate a/d module
movlw d'7' ; wait the required acquisition time (approx. 20 microseconds), which is
movwf adtemp ; about 19 instruction cycles @ 3.6864 Mhz (1085 ns per instruction)
ADLOOP1 decfsz adtemp,f ; count down from to zero, this instruction is one cycle
goto ADLOOP1 ; this instruction is two cycles, wait until we are done (7*3*1.085=22.8 us)
bsf adcon0,go_notdone ; now start conversion
ADLOOP2 btfsc adcon0,go_notdone ; is conversion done ?
goto ADLOOP2 ; no, wait here until conversion is completed
bcf adcon0,0 ; shut-off a/d module for minimal power consumption
movf adresh,w ; store results, note that this will also give us the neccesary delay of 2 Tad
movwf nummiddle ; between two a/d sampling actions (1.085 us per instruction @ 3.6864 Mhz )
bank1 ;
movf adresl,w ;
movwf numlow ;
return ; return in bank1
;--------------------------------------------------------------------------------------------------------------------------------------
; PIC internal EEPROM read and write routines
;--------------------------------------------------------------------------------------------------------------------------------------
IEE_READ ; reads one byte from PIC EEPROM
; address should be in w
; data byte is returned in w
; returns in bank 2
bank2 ;
movwf eeadr ; register address (0-255 decimal for PIC16F877)
bank3 ;
bcf eecon1,eepgd ; select eeprom data memory for read/write access instead of program memory
bsf eecon1,rd ; set bit to read
bank2 ;
movf eedata,w ;
return ;
;--------------------------------------------------------------------------------------------------------------------------------------
IEE_WRITE ; writes one byte to PIC EEPROM
; address should be in iee_address (bank2), data should be in w
; w content is destroyed
; returns in bank 0
bank3 ;
btfsc eecon1,wr ; is there currently a write cycle busy ?
goto $-1 ; wait here for previous write to finish
bank2 ;
movwf eedata ; set data
movf iee_address,w ; get address
movwf eeadr ; select address
bank3 ;
bcf eecon1,eepgd ; select eeprom data memory for read/write access instead of program memory
bsf eecon1,wren ; write enable
bcf intcon,gie ; disable all interrupts
btfsc intcon,gie ; check if disabled
goto $-2 ; else try again
movlw h'55' ; required sequence
movwf eecon2 ;
movlw h'AA' ;
movwf eecon2 ;
bsf eecon1,wr ; start write
bsf intcon,gie ; re-enable interrupts
bcf eecon1,wren ; disable writes (does not affect current write cycle)
bank0 ;
btfss pir2,eeif ;
goto $-1 ; wait here for the write to complete
bcf pir2,eeif ; clear eeprom write interrupt flag
return ; return in bank0
;--------------------------------------------------------------------------------------------------------------------------------------
; IIC subroutines
;--------------------------------------------------------------------------------------------------------------------------------------
IIC_IDLE bank1 ; use this subroutine for interrupt driven IIC communication
btfsc sspstat,r_notw ; is the IIC bus free (test if a transmission is in progress) ?
goto $-1 ; no, wait until it is free
movf sspcon2,w ; yes, get a copy of sspcon2 to test status bits
andlw b'00011111' ; apply mask to mask out non-status bits
skpz ; test for zero state, if zero bus is idle, bus busy ?
goto $-3 ; yes, test again until bus is free
return ; no, bus is free, exit
;--------------------------------------------------------------------------------------------------------------------------------------
IIC_START bank1 ;
bsf sspcon2,sen ; generate start condition
btfsc sspcon2,sen ; is the start completed ?
goto $-1 ; no, wait until it is completed
return ; yes, exit
;--------------------------------------------------------------------------------------------------------------------------------------
IIC_RESTART bank1 ;
bsf sspcon2,rsen ; generate restart condition
btfsc sspcon2,rsen ; is the restart completed ?
goto $-1 ; no, wait until it is completed
return ; yes, exit
;--------------------------------------------------------------------------------------------------------------------------------------
IIC_STOP bank1 ;
bsf sspcon2,pen ; generate stop condition
btfsc sspcon2,pen ; is the stop completed ?
goto $-1 ; no, wait until it is completed
return ; yes, exit
;--------------------------------------------------------------------------------------------------------------------------------------
IIC_EADDRESS bank1 ; send the control byte, high and low address bytes to eeprom
rlf iicchip,w ; get chip select number in right position, no need to clear carry prior to byte rotation
andlw b'00001110' ; avoid possible errors and make sure we only use the three bit number
iorlw b'10100000' ; control code for eeprom chips, chip number 0, bit 0 clear for write operation
bank0 ;
movwf sspbuf ; send the slave address
bank1 ;
btfsc sspstat,r_notw ; is the transmission completed ?
goto $-1 ; no, wait here until it is completed
btfsc sspcon2,ackstat ; yes, has ACK been received ?
return ; no, exit and deal with error
movf iicahigh,w ; yes, get upper byte of address (**** to do: current eeproms are only 32kB>15 bit number!)
bank0 ;
movwf sspbuf ; send address upper byte
bank1 ;
btfsc sspstat,r_notw ; is the transmission completed ?
goto $-1 ; no, wait here until it is completed
btfsc sspcon2,ackstat ; yes, has ACK been received ?
return ; no, exit and deal with error
movf iicalow,w ; yes, get lower address byte
bank0 ;
movwf sspbuf ; send lower address byte
bank1 ;
btfsc sspstat,r_notw ; is the transmission completed ?
goto $-1 ; no, wait here until it is completed
return ; yes, exit
;--------------------------------------------------------------------------------------------------------------------------------------
IIC_WRBYTE ; write one byte to external IIC eeprom
; address in iicchip, iicalow and iicahigh
; data in iicdata
call IIC_START ;
call IIC_EADDRESS ; send the address and check for any ACK errors
btfsc sspcon2,ackstat ; did we get any ACK error while sending the address ?
goto IIC_STOP ; yes, error, send stop then exit
movf iicdata,w ; no, get data byte
bank0 ;
movwf sspbuf ; send data byte
bank1 ;
btfsc sspstat,r_notw ; is the transmission completed ?
goto $-1 ; no, wait here until it is completed
IIC_POLL1 call IIC_STOP ; start write cycle, then poll eeprom for completion cycle, wait for ACK from eeprom
call IIC_START ; (on completion of the write cycle the eeprom sends an ACK)
bank1 ; send the control byte to eeprom:
rlf iicchip,w ; get chip select number, no need to clear carry prior to byte rotation
andlw b'00001110' ; avoid possible errors and make sure we only use the three bit number
iorlw b'10100000' ; control code for eeprom chips, chip number 0, bit 0 clear for write operation
bank0 ;
movwf sspbuf ; send slave address
bank1 ;
btfsc sspstat,r_notw ; is the transmission completed ?
goto $-1 ; no, wait here until it is completed
btfsc sspcon2,ackstat ; yes, has ACK been received ?
goto IIC_POLL1 ; no, go poll again
goto IIC_STOP ; yes, send stop then exit
;--------------------------------------------------------------------------------------------------------------------------------------
IIC_RDBYTE ; read one byte from external IIC eeprom
; address in iicchip, iicalow and iicahigh
; data is returned in iicdata
call IIC_START ;
call IIC_EADDRESS ; send the address and check for any ACK errors
btfsc sspcon2,ackstat ; did we get any ACK error while sending the address ?
goto IIC_STOP ; yes, send stop then exit
call IIC_RESTART ; no, send restart
bank1 ; send the control byte to eeprom:
rlf iicchip,w ; get chip select number, no need to clear carry prior to byte rotation
andlw b'00001110' ; avoid possible errors and make sure we only use the three bit number
iorlw b'10100001' ; control code for eeprom chips, chip number 0, bit 0 set for read operation
bank0 ;
movwf sspbuf ; send slave address
bank1 ;
btfsc sspstat,r_notw ; is the transmission completed ?
goto $-1 ; no, wait here until it is completed
btfsc sspcon2,ackstat ; yes, has ACK been received ?
goto IIC_STOP ; no, send stop then exit
bsf sspcon2,rcen ; receive data byte from eeprom
btfsc sspcon2,rcen ; have we received the byte ?
goto $-1 ; no, wait here until byte receive is completed
bank0 ;
movf sspbuf,w ; get received data byte
bank1 ;
movwf iicdata ; store the data byte
bsf sspcon2,ackdt ; select NOACK
bsf sspcon2,acken ; send NOACK
btfsc sspcon2,acken ; is the transmission completed ?
goto $-1 ; no, wait here until it is completed
goto IIC_STOP ; yes, send stop then exit
;--------------------------------------------------------------------------------------------------------------------------------------
IIC_WRBLOCK ; writes one block (64 bytes) to the external IIC eeprom memory
; first address in iicchip, iicalow and iicahigh, when address value is not
; the start of a block, the address counter will roll over
; w register content is destroyed
; data is in block buffer in bank1, from address AF hex up to and including EE hex
call IIC_START ; send start condition
call IIC_EADDRESS ; send the address and check for any ACK errors
bank1 ;
btfsc sspcon2,ackstat ; did we get any ACK error while sending the address ?
goto IIC_STOP ; yes, error, send stop then exit
bcf status,irp ; no, make sure upper bit in bank address is zero (select bank 0 and 1)
movlw blockbuff00 ; start position of data in buffer
movwf fsr ; point to this address
IIC_WRITELOOP movf indf,w ; read data byte from buffer
bank0 ;
movwf sspbuf ; send data byte to eeprom
bank1 ;
btfsc sspstat,r_notw ; is the transmission completed ?
goto $-1 ; no, wait here until it is completed
btfsc sspcon2,ackstat ; has ACK been received ?
goto IIC_STOP ; no, send stop then exit
incf fsr,f ; increase pointer to point to next value
movf fsr,w ; use pointer value
sublw blockbuff63 ; was this the last byte ?
skpnc ; let's see
goto IIC_WRITELOOP ; no, do loop again and send next data byte
IIC_POLL2 call IIC_STOP ; yes, start block write, poll eeprom for completion cycle, wait for ACK from eeprom
call IIC_START ; (on completion of the write cycle the eeprom sends an ACK)
bank1 ; send the control byte to eeprom:
rlf iicchip,w ; get chip select number in right position, no need to clear carry prior to byte rotation
andlw b'00001110' ; avoid possible errors and make sure we only use the three bit number
iorlw b'10100000' ; control code for eeprom chips, chip number 0, bit 0 clear for write operation
bank0 ;
movwf sspbuf ; send slave address
bank1 ;
btfsc sspstat,r_notw ; is the transmission completed ?
goto $-1 ; no, wait here until it is completed
btfsc sspcon2,ackstat ; yes, has ACK been received ?
goto IIC_POLL2 ; no, go poll again
goto IIC_STOP ; yes, send stop then exit
;--------------------------------------------------------------------------------------------------------------------------------------
IIC_RDBLOCK ; reads one block (64 bytes) from the external IIC eeprom memory
; first address in iicchip, iicalow and iicahigh
; w register content is destroyed
; data is returned in buffer in bank1, from address 2F hex up to and including 6E hex
; return in bank1
movlw blockbuff00 ; no, start position of data in buffer
movwf fsr ; point to this address
bcf status,irp ; make sure upper bit in address is zero (select bank 0 and 1)
call IIC_START ; read one byte from external IIC eeprom into iicdata, address in iicchip, iicalow and iicahigh
call IIC_EADDRESS ; send the address and check for any ACK errors
btfsc sspcon2,ackstat ; did we get any ACK error while sending the address ?
goto IIC_STOP ; yes, send stop then exit
call IIC_RESTART ; no, send restart
bank1 ; send the control byte to eeprom:
rlf iicchip,w ; get chip select number, no need to clear carry prior to byte rotation
andlw b'00001110' ; avoid possible errors and make sure we only use the three bit number
iorlw b'10100001' ; control code for eeprom chips, chip number 0, bit 0 set for read operation
bank0 ;
movwf sspbuf ; send slave address
bank1 ;
btfsc sspstat,r_notw ; is the transmission completed ?
goto $-1 ; no, wait here until it is completed
btfsc sspcon2,ackstat ; yes, has ACK been received ?
goto IIC_STOP ; no, send stop then exit
IIC_READLOOP bsf sspcon2,rcen ; receive data byte from eeprom
btfsc sspcon2,rcen ; have we received the byte ?
goto $-1 ; no, wait here until byte receive is completed
bank0 ;
movf sspbuf,w ; get received data byte
bank1 ; the buffer is in bank 1
movwf indf ; store the data byte at the right position in the buffer
incf fsr,f ; increase pointer to point to next value
movf fsr,w ; use pointer value
sublw blockbuff63 ; was this the last byte ?
skpc ; let's see
goto IIC_READDONE ; yes, send termination NOACK then exit
bcf sspcon2,ackdt ; select ACK
bsf sspcon2,acken ; send ACK
btfsc sspcon2,acken ; is the transmission completed ?
goto $-1 ; no, wait here until it is completed
goto IIC_READLOOP ; do loop again and read next data byte
IIC_READDONE bsf sspcon2,ackdt ; select NOACK
bsf sspcon2,acken ; send NOACK
btfsc sspcon2,acken ; is the transmission completed ?
goto $-1 ; no, wait here until it is completed
goto IIC_STOP ; yes, send stop then exit
;--------------------------------------------------------------------------------------------------------------------------------------
IIC_RDCLOCK ; read the eight time related bytes from the external IIC clock chip DS1307
; into the buffer in bank2, from address 68 hex up to and including 6F hex
; w register content is destroyed
call IIC_START ;
movlw b'11010000' ; clock control code/slave address, bit 0 is cleared as indication for write
bank0 ;
movwf sspbuf ; send the control byte
bank1 ;
btfsc sspstat,r_notw ; is the transmission completed ?
goto $-1 ; no, wait here until it is completed
btfsc sspcon2,ackstat ; yes, has ACK been received ?
goto IIC_STOP ; no, deal with error: send stop then exit
bank0 ;
clrw ; address byte is zero indicating start of clock memory
movwf sspbuf ; send address byte
bank1 ;
btfsc sspstat,r_notw ; is the transmission completed ?
goto $-1 ; no, wait here until it is completed
btfsc sspcon2,ackstat ; has ACK been received ?
goto IIC_STOP ; no, deal with error: send stop then exit
call IIC_STOP ; yes, continue
call IIC_START ; send start
bank0 ;
movlw b'11010001' ; clock control code/slave address, bit 1 is set as indication for read
movwf sspbuf ; send control byte
bank1 ;
btfsc sspstat,r_notw ; is the transmission completed ?
goto $-1 ; no, wait here until it is completed
btfsc sspcon2,ackstat ; yes, has ACK been received ?
goto IIC_STOP ; no, send stop then exit
movlw h'68' ; no, start position of data in buffer
movwf fsr ; point to this address
bsf status,irp ; make sure upper bit in address is one (select bank 2 and 3)
IIC_CRDLOOP bank1 ;
bsf sspcon2,rcen ; receive data byte from clock chip
btfsc sspcon2,rcen ; have we received the byte ?
goto $-1 ; no, wait here until byte receive is completed
bank0 ;
movf sspbuf,w ; get received data byte
movwf indf ; store the data byte at the right position in the buffer
incf fsr,f ; increase pointer to point to next value
movf fsr,w ; use pointer value
sublw h'6F' ; was this the last byte ?
skpc ; let's see
goto IIC_CRDDONE ; yes, send termination NOACK then exit
bank1 ;
bcf sspcon2,ackdt ; select ACK
bsf sspcon2,acken ; send ACK
btfsc sspcon2,acken ; is the transmission completed ?
goto $-1 ; no, wait here until it is completed
goto IIC_CRDLOOP ; do loop again and read next data byte
IIC_CRDDONE bank1 ;
bsf sspcon2,ackdt ; select NOACK
bsf sspcon2,acken ; send NOACK
btfsc sspcon2,acken ; is the transmission completed ?
goto $-1 ; no, wait here until it is completed
goto IIC_STOP ; yes, send stop then exit
;--------------------------------------------------------------------------------------------------------------------------------------
IIC_WRCLOCK ; write eight time related bytes to the external IIC clock chip DS1307
; uses the data from buffer in bank2, from 68 hex up to and including 6F hex
call IIC_START ;
movlw b'11010000' ; clock chip control code, bit 0 is cleared as indication for write
bank0 ;
movwf sspbuf ; send the control byte
bank1 ;
btfsc sspstat,r_notw ; is the transmission completed ?
goto $-1 ; no, wait here until it has completed
btfsc sspcon2,ackstat ; yes, has ACK been received ?
goto IIC_STOP ; no, deal with error: send stop then exit
bank0 ; yes,
clrw ; address byte is zero indicating start of clock memory
movwf sspbuf ; send address byte
bank1 ;
btfsc sspstat,r_notw ; is the transmission completed ?
goto $-1 ; no, wait here until it has completed
btfsc sspcon2,ackstat ; has ACK been received ?
goto IIC_STOP ; no, deal with error: send stop then exit
movlw h'68' ; yes, start position of data in buffer
movwf fsr ; point to this address
bsf status,irp ; make sure upper bit in address is one (select bank 2 and 3)
IIC_CWRLOOP bank0 ;
movf indf,w ; read data byte from buffer
movwf sspbuf ; send data byte to clock
bank1 ;
btfsc sspstat,r_notw ; is the transmission completed ?
goto $-1 ; no, wait here until it has completed
btfsc sspcon2,ackstat ; has ACK been received ?
goto IIC_STOP ; no, send stop then exit
incf fsr,f ; increase pointer to point to next value
movf fsr,w ; use pointer value
sublw h'6F' ; was this the last byte ?
skpnc ; let's see
goto IIC_CWRLOOP ; no, do loop again and send next data byte
goto IIC_STOP ; yes, send stop then exit
;--------------------------------------------------------------------------------------------------------------------------------------
IIC_RD_CLKCHIP ; read a byte from the control bytes or static ram of the clock chip DS1307
; address in w register (0..63 addresses include the time settings and wrap around !)
; returns with value in w register
bank2 ;
movwf clockaddr ; store the address byte for later use
call IIC_START ;
movlw b'11010000' ; clock control code/slave address, bit 0 is cleared as indication for write
bank0 ;
movwf sspbuf ; send the control byte
bank1 ;
btfsc sspstat,r_notw ; is the transmission completed ?
goto $-1 ; no, wait here until it is completed
btfsc sspcon2,ackstat ; yes, has ACK been received ?
goto IIC_STOP ; no, deal with error: send stop then exit
bank2 ;
movf clockaddr,w ; get the address byte
bank0 ;
movwf sspbuf ; send address byte
bank1 ;
btfsc sspstat,r_notw ; is the transmission completed ?
goto $-1 ; no, wait here until it is completed
btfsc sspcon2,ackstat ; has ACK been received ?
goto IIC_STOP ; no, deal with error: send stop then exit
call IIC_STOP ; yes, continue
call IIC_START ; send start
bank0 ;
movlw b'11010001' ; clock control code/slave address, bit 1 is set as indication for read
movwf sspbuf ; send control byte
bank1 ;
btfsc sspstat,r_notw ; is the transmission completed ?
goto $-1 ; no, wait here until it is completed
btfsc sspcon2,ackstat ; yes, has ACK been received ?
goto IIC_STOP ; no, send stop then exit
IIC_RSTLOOP bank1 ;
bsf sspcon2,rcen ; receive data byte from clock chip
btfsc sspcon2,rcen ; have we received the byte ?
goto $-1 ; no, wait here until byte receive is completed
bank0 ;
movf sspbuf,w ; get received data byte
bank2 ;
movwf clockdata ; store data byte for later use
bank1 ;
bsf sspcon2,ackdt ; select NOACK
bsf sspcon2,acken ; send termination NOACK
btfsc sspcon2,acken ; is the transmission completed ?
goto $-1 ; no, wait here until it is completed
call IIC_STOP ; yes, send stop
bank2 ;
movf clockdata,w ; retrieve data value
return ; return in bank2
;--------------------------------------------------------------------------------------------------------------------------------------
IIC_WR_CLKCHIP ; write one byte to the control bytes or static ram of the clock chip DS1307
; address in clockaddr register (0..63 addresses include the time settings and wrap around !)
; data in w register
; w register content is destroyed
bank2 ;
movwf clockdata ; store data byte for later use
call IIC_START ;
movlw b'11010000' ; clock chip control code, bit 0 is cleared as indication for write
bank0 ;
movwf sspbuf ; send the control byte
bank1 ;
btfsc sspstat,r_notw ; is the transmission completed ?
goto $-1 ; no, wait here until it has completed
btfsc sspcon2,ackstat ; yes, has ACK been received ?
goto IIC_STOP ; no, deal with error: send stop then exit
bank2 ; yes,
movf clockaddr,w ; get address byte
bank0 ;
movwf sspbuf ; send address byte
bank1 ;
btfsc sspstat,r_notw ; is the transmission completed ?
goto $-1 ; no, wait here until it has completed
btfsc sspcon2,ackstat ; has ACK been received ?
goto IIC_STOP ; no, deal with error: send stop then exit
bank2 ; yes,
movf clockdata,w ; get data byte
bank0 ;
movwf sspbuf ; send data byte to clock
bank1 ;
btfsc sspstat,r_notw ; is the transmission completed ?
goto $-1 ; no, wait here until it has completed
goto IIC_STOP ; send stop then exit
;--------------------------------------------------------------------------------------------------------------------------------------
MEMORY ; EEPROMS - measured data is stored in the EXTERNAL eeprom, the location of this data in the
; external eeprom and starttime information are stored in the INTERNAL eeprom, meaning that
; the internal eeprom serves as a table of contents (TOC)
; TOC - the table of contents runs from '@tocstart' up to and including address 255,
; at the start of the logging the start time and date is stored in the toc, when closing the
; record file (after switching back from the 'log' to the 'on' mode) then the address of the
; last used bit in the external memory is stored in the toc (in iicalow, iicahigh and
; iicchip/bitp, so each record in the toc uses 10 bytes:
; 9 high byte upper nibble = bitpointer, lower nibble = iicchip
; 8 middle byte iicahigh
; 7 low byte iicalow
; 6 year start time
; 5 month ,,
; 4 date ,,
; 3 day ,,
; 2 hours ,,
; 1 minutes ,,
; 0 seconds ,,
; BLOCK SIZE - during logging all measured data values are written to the external eeprom and
; the actual writes happen in eeprom block write size (64 bytes, the eeprom datasheet calls
; them pages, but to avoid confusion with pic memory pages we will call them blocks)
; to maximize eeprom lifetime and data throughput, the data is first buffered in pic registers
; and will only written to the external eeprom when the 64 byte size has been reached or when
; the command MEM_CLOSE is executed
; the 64 byte block buffer is in bank 1 from 2F to 6E hex, 6F hex is used as overflow
; there are 512 blocks per eeprom chip (9 bit number), in total there are 4096 blocks
; the following registers hold the first available free block in the external eeprom:
; mem_alow here value is multiple of 64 to always point to start of a block
; mem_ahigh maximum value is 127 since each memory chip is only 32 kbytes in size
; mem_chip value from zero to seven, we have eight eeprom chips
; START POSITION - records always start at the beginning of a new eeprom block (64 bytes)
; and are NOT stored directly after the bits of the first record to make the download routine
; to the computer easier and to prolong the external eeprom lifetime
; the start position for any new record is calculated by the MEM_OPEN command
; note: the start address of the first record is always zero
; WRITES - the following commands are used to store the recorded data in the external
; eeprom chips, there are eight chips of 32k bytes each (total 2^21=2097152 bits)
; MEM_ADD1 digital channels: mark & brake
; MEM_ADD8 speed
; MEM_ADD10 analog channels
; MEM_ADD14 rpm
; writes are not verified by a read !
; after each add command the value of the memfull bit in the flags register
; indicates if there is any room in the memory left, this memfull flag is set at the page
; before the last page so the close command can still write the very last block to the memory
; COMPRESSION - data is stored in compressed form, meaning that a 9 bit value will be stored
; as exactly nine bits in the external eeprom, directly after any previously stored bits
;--------------------------------------------------------------------------------------------------------------------------------------
MEM_CLEAR movlw @tocstart ; clear all toc bytes, both the address and the starttime bytes
bank2 ; location of first byte in table of contents
movwf iee_address ; address for internal eeprom writes should be here
MEM_CLEAR_LOOP clrw ; data for internal eeprom writes should be in w register
call IEE_WRITE ; write data to eeprom, address in iee_address, data in w, bank0 return
bank2 ; iee_address is in bank2
incfsz iee_address,f ; point to next byte and skip when all are done
goto MEM_CLEAR_LOOP ; go clear next byte
movlw d'15' ; eeprom address for error flags register
bank2 ;
movwf iee_address ; set internal eeprom address pointer
clrf errors ; clear error flags register
clrw ; clear all error flags in eeprom as well,
call IEE_WRITE ; write data to eeprom, address in iee_address, data in w, bank0 return
bank3 ;
clrf num_records ; there are no records available anymore
clrf current_rec ; there is no record selected for download anymore
bcf flags2,memfull ; clear flag indicating the table of contents is now empty
return ; return in bank3
;--------------------------------------------------------------------------------------------------------------------------------------
MEM_OPEN ; when both the external memory and the table of contents are not full then:
;
; 1. get the new values for:
; mem_alow
; mem_ahigh
; mem_chip
; bitpointer
; regpointer
; toc_pointer
; memfull flag (set when memory is full, registers above will contain junk)
;
; 2. initialize (clear): blockbuff00
;
; 3. write the current time as starttime (seven bytes) into toc
movlw d'255' ; position of last byte in table of contents, we start at the top and work our way down
bank1 ; empty locations will contain three zero values, first try three most upper bytes
movwf toc_pointer ; point to the top location
MEM_OPEN_SEARCH movf toc_pointer,w ; get copy of tocpointer value, needed in loop
call IEE_READ ; read value from internal eeprom, address in w, data returned in w, return in bank2
bank1 ; pointer registers and eeprom block write buffer are all in bank 1
movwf mem_chip ; store value for later use, but remember this value includes bitpointer in upper nibble
decf toc_pointer,f ; point to lower location in toc
movf toc_pointer,w ; copy the value
call IEE_READ ; read value from internal eeprom, address in w, data returned in w, return in bank2
bank1 ; pointer registers and eeprom block write buffer are all in bank 1
movwf mem_ahigh ; store value for later use
decf toc_pointer,f ; point to lower location in toc
movf toc_pointer,w ; copy the value
call IEE_READ ; read value from internal eeprom, address in w, data returned in w, return in bank2
bank1 ; pointer registers and eeprom block write buffer are all in bank 1
movwf mem_alow ; store value for later use, check all three values of this record to see if they are all zero
iorwf mem_ahigh,w ; since this indicates that this record location is not in use yet,
iorwf mem_chip,w ; so it can be used in the next logging event
skpz ; are all three values zero, meaning this location is not used ?
goto MEM_OPEN_INUSE ; no, use this location to get address, then point to next location which we know is empty
MEM_OPEN_EMTEST movlw d'7' ; yes, go see if the toc is completely empty or if there is already another record
subwf toc_pointer,f ; point to start of time and date bytes for this record
movlw @tocstart ; if toc is empty then we now point to the start of the toc
subwf toc_pointer,w ; see if this is the first record in the toc
skpnz ; will this be the first record in the toc ?
goto MEM_OPEN_1STREC ; yes, go set address of first record to zero to point to bottom of external eeprom memory
MEM_OPEN_DONEXT decf toc_pointer,f ; no, there are lower locations we should test
goto MEM_OPEN_SEARCH ; do loop and try next
MEM_OPEN_INUSE movlw d'255' - d'2' ; when toc is full this is where the pointer will be
subwf toc_pointer,w ; test the value of tocpointer, it the pointer at the last record position
skpnz ; is the current pointer value valid ?
goto MEM_OPEN_TOCFUL ; no, stop looking, the toc is full
MEM_OPEN_POINT movlw d'3' ; point to first time and date byte of next record we know is free to use
addwf toc_pointer,f ; since we have started at the top of the toc
MEM_OPEN_CALC movlw b'11000000' ; use the end address of a previous record to calculate the start of the next record
andwf mem_alow,f ; find the start of the last used block by stripping the lower six bits from the low byte
movlw d'64' ; this value is the size of one block
addwf mem_alow,f ; add one block to point to the next block, which is not used yet
skpnc ; did we get an overflow of the lower byte ?
incf mem_ahigh,f ; yes, also increase high byte
btfsc mem_ahigh,7 ; no, did we get an overflow of the lower seven bits of the high byte (we use 32k eeproms) ?
incf mem_chip,f ; yes, also increase the chip select byte
btfsc mem_ahigh,7 ; no, again, did we get an overflow of the lower seven bits of high byte (we use 32k eeproms) ?
bcf mem_ahigh,7 ; yes, then reset this bit
movlw b'00001111' ; no, the pointer to the last used bit was stored in upper nibble of the chip select byte
andwf mem_chip,f ; we only want the chip select value so strip this nibble
btfsc mem_chip,3 ; see if are we pointing to a non-existing eeprom chip (there are eight eeprom chips)
goto MEM_OPEN_MEMFUL ; yes, we cannot use this non-existing location
MEM_OPEN_TEST movlw d'7' ; no, chip number seven is the last chip
subwf mem_chip,w ; see if we are using the last chip
skpz ; are we pointing to the last chip ?
goto MEM_OPEN_WRTIME ; no, the address is ok
movlw d'127' ; yes, this is the number for last four pages
subwf mem_ahigh,w ; see if we are already there
skpnz ; are we using one of the last four pages ?
goto MEM_OPEN_MEMFUL ; yes, we do not want to use this location
goto MEM_OPEN_WRTIME ; no, the address is ok
MEM_OPEN_1STREC clrf mem_alow ; yes, since there are no records yet we will start at address zero
clrf mem_ahigh ; clear all address pointer bytes
clrf mem_chip ; and start at the bottom of chip zero
MEM_OPEN_WRTIME call IIC_RDCLOCK ; read the time and date bytes to buffer in bank2 (from 68..6E hex), bank1 return
bsf status,irp ; make sure upper bit in bank address is one (select bank 2 and 3)
movlw h'68' ; the time and date bytes were put here
movwf fsr ; point to this address
MEM_OPEN_WRLOOP bank1 ; switch to bank1 because of loop
movf toc_pointer,w ; get a copy of the tocpointer value
bank2 ; register is in bank2
movwf iee_address ; the address byte for internal eeprom write operations
movf indf,w ; get the data value for this address
call IEE_WRITE ; do the actual write operation, return in bank0
bank1 ;
incf toc_pointer,f ; point to next position in toc, after all time writes value will be at first address position
incf fsr,f ; point to the next time and date byte
movlw h'6F' ; position past the last time and date byte
subwf fsr,w ; check if all byte have been written
skpz ; have we written all bytes ?
goto MEM_OPEN_WRLOOP ; no, repeat until all bytes have been written to the toc
MEM_OPEN_INIT clrf blockbuff00 ; yes, clear first byte of blockbuffer as we use logical or operation to add new bits to buffer
clrf bitpointer ; clear pointer for any new write operations, point to first bit
movlw blockbuff00 ; location of first byte in block buffer
movwf regpointer ; use value to set register pointer to start of blockbuffer
bcf flags2,memfull ; clear flag indicating the external eeproms are not full
return ; return in bank1
MEM_OPEN_TOCFUL bsf flags2,tocfull ; set flag indicating the table of contents is full
MEM_OPEN_MEMFUL bsf flags2,memfull ; set flag indicating the memory is full
return ; return in bank1
;--------------------------------------------------------------------------------------------------------------------------------------
MEM_WRITEBLOCK ; write of block buffer to external eeprom, update buffer pointers and memory address pointer
; block buffer should contain the data
; set the memfull flag when the page before the last page is reached, this is done to notify
; to the logging loop that recording should be stopped immediately to allow for a proper
; record file ending (the last page will only be used partially)
; the following registers should hold the address in the external eeprom:
; mem_alow this value is always a multiple of 64 to point to the start of a block
; mem_ahigh maximum value is 127 since each memory chip is only 32 kbytes in size
; mem_chip value from zero to seven, we have eight eeprom chips
movlw blockbuff00 ; location of first byte in block buffer
movwf regpointer ; reset the register pointer for following write events
btfsc mem_chip,3 ; test chip number validity, is there still room in any of the eight external eeproms ?
return ; no, return without writing data or updating pointers, the memfull flag has been set already
movf mem_alow,w ; get address of the next available free block in the memory, select address low byte
movwf iicalow ; copy value to iic write routine
movf mem_ahigh,w ; select address high byte
movwf iicahigh ; copy value to iic write routine
movf mem_chip,w ; select chip
movwf iicchip ; copy value to iic write routine
call IIC_WRBLOCK ; yes, do the actual write operation and copy the block into the external eeprom
movlw d'64' ; now update all adresses for following write operations, this is size of one block
bank1 ; we will add one block to the address
addwf mem_alow,f ; try to point to next block in same chip, low byte of pointer
skpc ; should we also update high byte of pointer ?
return ; no, we're done
incf mem_ahigh,f ; yes, increase high byte of pointer
btfsc mem_ahigh,7 ; is there any room left in this chip ?
goto MEM_WRBL_NEXT ; no, go reset high address byte, we know low byte has wrapped around and is zero
MEM_WRBL_TEST movlw d'7' ; go see if we have to set memfull flag, chip number seven is the last chip
subwf mem_chip,w ; see if we are using the last chip
skpz ; are we pointing to the last chip ?
return ; no, we're done
movlw d'127' ; yes, this is the number for last four pages
subwf mem_ahigh,w ; see if we are already there
skpnz ; are we using one of the last four pages ?
bsf flags2,memfull ; yes, the external eeproms are almost full, set flag to allow for the very last block write
return ; no, we're done, bank1 return
MEM_WRBL_NEXT clrf mem_ahigh ; no, reset high address byte, we know low byte has wrapped around and is zero
incf mem_chip,f ; point to next chip
return ; done
;--------------------------------------------------------------------------------------------------------------------------------------
MEM_ADD1 ; write one bit to the external eeprom
; actual writes are done when the 64 byte block buffer in pic is full
; this is done to optimize eeprom lifetime
; input = bit zero of w register (actually ior of all bits)
bank1 ; block buffer pointer registers and eeprom block write buffer are all in bank 1
bcf status,irp ; make sure upper bit in address is zero (select register bank 0 and 1)
iorlw d'00000000' ; see what value the bit is
skpnz ; is input bit value a zero ?
goto MEM_ADD1_ZERO ; yes, skip bit set code
MEM_ADD1_ONE movf regpointer,w ; no, get the value of the pointer to the register where we will put the bit
movwf fsr ; use indirect addressing
movf bitpointer,w ; convert 3 bit number of bitpointer to eight bit mask, for example 010>00000100
andlw b'00000011' ; first strip upper six bits and continue with only two bits
movwf bitmask ; store the result
incf bitmask,w ; increase by one
btfsc bitmask,1 ; is the first bit in mask a one ?
iorwf bitmask,f ; yes, adjust result
incf bitmask,f ; no, increase by one
btfsc bitpointer,2 ; was bitpointer value larger than three, should we have got a bit in upper nibble ?
swapf bitmask,f ; yes, simply swap nibbles
movf bitmask,w ; no, result is eight bit mask
iorwf indf,f ; use OR operation to set the bit
MEM_ADD1_ZERO nop ; zero bit, we have to do nothing since the new bytes are always cleared
MEM_ADD1_INC incf bitpointer,f ; be ready for next push so increase pointer to point to next bit
btfss bitpointer,3 ; has the bitcounter reached value 8 (bit 3 is now set) ?
return ; no, done
clrf bitpointer ; yes, so we move on to the next byte, let the bitpointer point to the first bit again
incf regpointer,f ; increase the register pointer by one
movlw blockbuff63 + 1 ; one position past buffer, overflow value
subwf regpointer,w ; see if the register pointer has arrived at this overflow register
skpnc ; is the 64 byte block buffer full ?
call MEM_WRITEBLOCK ; yes, write block buffer to external eeprom and reset register pointer, bank1 return
movf regpointer,w ; no, get a copy of the register pointer
movwf fsr ; use this copy to set indirect file pointer to point to this location
clrf indf ; clear every new byte
return ; done
;--------------------------------------------------------------------------------------------------------------------------------------
MEM_ADD8 ; write eight bits (one byte) to the external eeprom
; actual writes are done when the 64 byte block buffer in pic is full
; this is done to optimize eeprom lifetime
; the byte should be in numlow register
; numlow and nummiddle content is not changed
; databyte0 and databyte1 contents are destroyed
MEM_ADD8_SELECT bank1 ; block buffer pointer registers and eeprom block write buffer are all in bank 1
bcf status,irp ; make sure upper bit in address is zero (select register bank 0 and 1)
movf numlow,w ; copy value
movwf databyte0 ;
movf regpointer,w ; get the register location where we will put the data bits
movwf fsr ; use indirect addressing
clrf databyte1 ; use databyte1 as shift register or to clear new locations
movf bitpointer,w ; get the position of the first free bit in this register
skpnz ; is bit number zero the first free bit ?
goto MEM_ADD8_POS0 ; yes, so we can copy the byte directly and don't have to shift any of the bits
MEM_ADD8_POS1_7 movwf bitcounter ; no, we need to shift the bits left, copy value of bitpointer to the loopcounter
clrc ; we only need to clear carry once since databyte1 has been cleared
MEM_ADD8_LOOP rlf databyte0,f ; shift the bits
rlf databyte1,f ; shift the bits
decfsz bitcounter,f ; are all the bits in the right position ?
goto MEM_ADD8_LOOP ; no, go do another shift
MEM_ADD8_POS0 movf databyte0,w ; yes, add the lower bits to the currently selected register of the block buffer
iorwf indf,f ; we use the or operation to add the new bits
incf regpointer,f ; set pointer to the next byte in the buffer
movlw blockbuff63 + 1 ; pointing past buffer, overflow value
subwf regpointer,w ; see if the pointer has arrived at this overflow register
skpnc ; is the 64 byte block buffer full ?
call MEM_WRITEBLOCK ; yes, write block buffer contents to external eeprom and reset register pointer
movf regpointer,w ; no, get the (updated) register location where we will put the data bits
movwf fsr ; use indirect addressing
movf databyte1,w ; get the upper bits or clear byte
movwf indf ; store value
return ; done, we don't have to update bitpointer since we added exactly eight bits
;--------------------------------------------------------------------------------------------------------------------------------------
MEM_ADD10 ; write ten bits to the external eeprom
; actual writes are done when the 64 byte block buffer in pic is full
; this is done to optimize eeprom lifetime
; the bits should be in numlow and nummiddle register
; numlow and nummiddle content stays unchanged
; databyte0, databyte1 and databyte2 content is destroyed
MEM_ADD10_SEL bank1 ; block buffer pointer registers and eeprom block write buffer are all in bank 1
bcf status,irp ; make sure upper bit in address is zero (select register bank 0 and 1)
movf numlow,w ; copy value
movwf databyte0 ;
movf nummiddle,w ; copy value
movwf databyte1 ;
movlw b'00000011' ; make sure we use a 10 bit number
andwf databyte1,f ; strip any excess bits
clrf databyte2 ; use databyte2 as shift register or to clear new locations
movf regpointer,w ; get the register location where we will put the data bits
movwf fsr ; use indirect addressing
movf bitpointer,w ; get the position of the first free bit in this register
skpnz ; is bit number zero the first free bit ?
goto MEM_ADD10_POS0 ; yes, so we can copy the bytes directly and don't have to shift any of the bits
MEM_ADD10_POS17 movwf bitcounter ; no, we need to shift the bits left, copy value of bitpointer to the loopcounter
clrc ; we only need to clear carry once since databyte1 and databyte2 have been cleared
MEM_ADD10_LOOP rlf databyte0,f ; shift the bits
rlf databyte1,f ; shift the bits
rlf databyte2,f ; shift the bits
decfsz bitcounter,f ; are all the bits in the right position ?
goto MEM_ADD10_LOOP ; no, go do another shift
MEM_ADD10_POS0 movf databyte0,w ; yes, add the lower bits to the currently selected register of the block buffer
iorwf indf,f ; we use the or operation to add the new bits
incf regpointer,f ; set pointer to the next byte in the buffer
movlw blockbuff63 + 1 ; pointing past buffer, overflow value
subwf regpointer,w ; see if the pointer has arrived at this overflow register
skpnc ; is the 64 byte block buffer full ?
call MEM_WRITEBLOCK ; yes, write block buffer contents to external eeprom and reset register pointer
movf regpointer,w ; no, get the (updated) register location where we will put the data bits
movwf fsr ; use indirect addressing
movf databyte1,w ; no, get (part of) the upper bits
movwf indf ; store these bits
incf bitpointer,f ; update the position of the bitcounter
incf bitpointer,f ; new bitpointer value = (bitpointer + 10) MOD 8 = bitpointer + 2
btfss bitpointer,3 ; was the start position of the bitcounter bit 6 or bit 7 ?
return ; no, still some room left after storing 10 bit value in two registers, done
MEM_ADD10_THREE bcf bitpointer,3 ; yes, no room left or using three registers, update register pointer, clear overflow bit
incf regpointer,f ; set pointer to the next byte in the buffer
movlw blockbuff63 + 1 ; pointing past buffer, overflow value
subwf regpointer,w ; see if the pointer has arrived at this overflow register
skpnc ; is the 64 byte block buffer full ?
call MEM_WRITEBLOCK ; yes, write block buffer contents to external eeprom and reset register pointer
movf regpointer,w ; get the (updated) register location where we will put the data bits
movwf fsr ; use indirect addressing
movf databyte2,w ; no, get the uppermost bit
movwf indf ; store this bit
return ; done
;--------------------------------------------------------------------------------------------------------------------------------------
MEM_ADD14 ; write fourteen bits to the external eeprom
; actual writes are done when the 64 byte block buffer in pic is full
; this is done to optimize eeprom lifetime
; the bits should be in numlow and nummiddle register
; numlow and nummiddle content stays unchanged
; databyte0, databyte1 and databyte2 content is destroyed
MEM_ADD14_SEL bcf status,irp ; make sure upper bit in address is zero (select register bank 0 and 1)
movf numlow,w ; copy value
bank1 ; block buffer pointer registers and eeprom block write buffer are all in bank 1
movwf databyte0 ;
movf nummiddle,w ; copy value
andlw b'00111111' ; make sure we use a 14 bit number
movwf databyte1 ; store result
movf regpointer,w ; get the register location where we will put the data bits
movwf fsr ; use indirect addressing
bcf status,irp ; make sure upper bit in address is zero (select register bank 0 and 1)
clrf databyte2 ; use databyte2 as shift register or to clear new locations
movf bitpointer,w ; get the position of the first free bit in this register
skpnz ; is bit number zero the first free bit ?
goto MEM_ADD14_POS0 ; yes, so we can copy the bytes directly and don't have to shift any of the bits
MEM_ADD14_POS17 movwf bitcounter ; no, we need to shift the bits left, copy value of bitpointer to the loopcounter
clrc ; we only need to clear carry once since we know value of databyte1 and databyte2
MEM_ADD14_LOOP rlf databyte0,f ; shift the bits
rlf databyte1,f ; shift the bits
rlf databyte2,f ; shift the bits
decfsz bitcounter,f ; are all the bits in the right position ?
goto MEM_ADD14_LOOP ; no, go do another shift
MEM_ADD14_POS0 movf databyte0,w ; yes, add the lower bits to the currently selected register of the block buffer
iorwf indf,f ; we use the or operation to add the new bits
incf regpointer,f ; set pointer to the next byte in the buffer
movlw blockbuff63 + 1 ; pointing past buffer, overflow value
subwf regpointer,w ; see if the pointer has arrived at this overflow register
skpnc ; is the 64 byte block buffer full ?
call MEM_WRITEBLOCK ; yes, write block buffer contents to external eeprom and reset register pointer
movf regpointer,w ; no, get the (updated) register location where we will put the data bits
movwf fsr ; use indirect addressing
movf databyte1,w ; no, get (part of) the upper bits
movwf indf ; store these bits
movlw d'6' ; update the position of the bitcounter
addwf bitpointer,f ; new bitpointer value = (bitpointer + 14) MOD 8 = bitpointer + 6
btfss bitpointer,3 ; was the start position of the bitcounter bit 2 or greater ?
return ; no, still some room left after storing 14 bit value in two registers, done
MEM_ADD14_THREE bcf bitpointer,3 ; yes, no room left or using three registers, update register pointer, clear overflow bit
incf regpointer,f ; set pointer to the next byte in the buffer
movlw blockbuff63 + 1 ; pointing past buffer, overflow value
subwf regpointer,w ; see if the pointer has arrived at this overflow register
skpnc ; is the 64 byte block buffer full ?
call MEM_WRITEBLOCK ; yes, write block buffer contents to external eeprom and reset register pointer
movf regpointer,w ; get the (updated) register location where we will put the data bits
movwf fsr ; use indirect addressing
movf databyte2,w ; no, get the uppermost bits
movwf indf ; store these bits
return ; done
;--------------------------------------------------------------------------------------------------------------------------------------
MEM_CLOSE ; write any bytes that are still in the blockbuffer to the external eeprom, unused bytes are
; cleared first, update the table of contents with the address of the last used bit
; this routine may also be called when we didn't store anything yet, when the memory is full
; or when the table of contents is already full and we are just aborting the logging
; note: during the add commands we use a pointer that points to the next free bit,
; but we store the position of last used bit in the toc
MEM_CL_CHECK1 nop ; **** check if we have added anything to store at all
MEM_CL_CHECK2 btfsc flags2,tocfull ; is the table of contents full and should we ignore the call to this subroutine ?
return ; yes, return without updating the table of contents
MEM_CL_CHECKOK movlw blockbuff00 ; no, start of block buffer location
bank1 ; pointer registers and eeprom block write buffer are all in bank 1
subwf regpointer,w ; see if we are still at the first byte of the block buffer
skpz ; did we write anything into the buffer yet ?
goto MEM_CL_CLRBUFF ; yes, go clear rest of bytes in buffer
movf bitpointer,w ; no, also get value of bitpointer
skpnz ; are we sure the buffer is completely empty ?
goto MEM_CL_DECPOINT ; yes, we don't have to write this block to the external eeprom
MEM_CL_CHECKP movlw blockbuff63 ; no, position of last byte in buffer
subwf regpointer,w ; see if we are pointing to the last byte in the buffer
skpnz ; are we pointing to the last byte ?
goto MEM_CL_ADDR ; yes, there are no bytes to be cleared, skip clear routine
MEM_CL_CLRBUFF incf regpointer,w ; no, start at register pointer location plus one with clearing bytes
movwf fsr ; use indirect addressing
bcf status,irp ; make sure upper bit in address is zero (select register bank 0 and 1)
MEM_CL_CLRLOOP clrf indf ; clear byte of buffer, we want to clear all the rest of the buffer
movlw blockbuff63 ; position of last byte in buffer
incf fsr,f ; point to next byte in buffer for when we do loop
subwf fsr,w ; see if the pointer has arrived at last position
skpz ; have we done the all of the 64 byte block buffer ?
goto MEM_CL_CLRLOOP ; no, do loop to clear next byte
MEM_CL_ADDR movf mem_alow,w ; yes, write contents of block buffer to external eeprom, use address of next free block
movwf iicalow ; copy value to iic write routine
movf mem_ahigh,w ; select address high byte
movwf iicahigh ; copy value to iic write routine
movf mem_chip,w ; select chip
movwf iicchip ; copy value to iic write routine
call IIC_WRBLOCK ; do the actual write operation and copy the block into the external eeprom, bank1 return
MEM_CL_DECPOINT decf bitpointer,f ; don't point to next free bit anymore but to the last used bit, decrement pointer one bit
movlw blockbuff00 ; offset between zero and start address of blockbuffer in bank1
subwf regpointer,w ; get lower six bits of the lower address byte (end position can be anywhere in a block)
iorwf mem_alow,f ; merge with upper two bits of low address byte (which always holds start of a block)
comf bitpointer,w ; since decf does not change the carry we use the complement to see if the result is negative
skpnz ; is the complement zero, meaning the result of the decrement was a negative value ?
decf mem_alow,f ; yes, decrease the lower address byte by one because we have to borrow
movlw b'00000111' ; no, when the result from the decrement of the bitpointer is negative then
andwf bitpointer,f ; set the bitpointer to its maximum value instead
comf mem_alow,w ; since decf does not change the carry we use the complement to see if the result is negative
skpnz ; was the result of the decrement of the low byte negative ?
decf mem_ahigh,f ; yes, decrease the higher address byte by one because we have to borrow
comf mem_ahigh,w ; since decf does not change the carry we use the complement to see if the result is negative
skpnz ; is the complement zero, meaning the result of the decrement was a negative value ?
decf mem_chip,f ; yes, borrow from the chip select byte
movlw b'01111111' ; no, when the result from the decrement of the high address byte is negative then
andwf mem_ahigh,f ; set the high address byte to its maximum value instead (we use 15 bit/32kB eeproms)
MEM_CL_WRITETOC movf toc_pointer,w ; get the location in the toc where we should store the address of the last used bit
bank2 ; so we can determine where data from one record stops and a new record begins
movwf iee_address ; copy value for use in to pic eeprom write routine
bank1 ; pointer registers and eeprom block write buffer are all in bank 1
movf mem_alow,w ; get the low byte of the address pointer
call IEE_WRITE ; write one byte to pic eeprom, address in iee_address(bank2), data in w register, bank0 return
bank2 ;
incf iee_address,f ; point to next byte in toc
bank1 ;
movf mem_ahigh,w ; get the high byte of the address pointer
call IEE_WRITE ; write one byte to pic eeprom, address in iee_address(bank2), data in w register, bank0 return
bank2 ;
incf iee_address,f ; point to next byte in toc
bank1 ;
swapf bitpointer,f ; move bitpointer bits to high nibble
movf mem_chip,w ; since the bitpointer and the chip select value are stored in the same byte
iorwf bitpointer,w ; combine values of bitpointer and chip select
call IEE_WRITE ; write one byte to pic eeprom, address in iee_address(bank2), data in w register, bank0 return
return ; done, return in bank0
;--------------------------------------------------------------------------------------------------------------------------------------
MEM_USAGE nop ; **** get the used space of the external eeprom memory in percent into w register
nop ; **** read end of last record position, do division to get percentage
movlw d'100' ; **** dummy value
return ; **** return in bank?
;--------------------------------------------------------------------------------------------------------------------------------------
MEM_GETNUMRECS ; determine the number of records currently present in the table of contents
; value is stored in num_records in bank3
; registers used: toc_pointer, toc_test, num_records
; returns in bank1 or in bank3
movlw d'255' ; start with number of records is -1
bank3 ;
movwf num_records ;
movlw @tocstart + d'7' ; start address of address bytes of first record in table of contents in internal eeprom
bank1 ;
movwf toc_pointer ; point to this location
MEM_GETNRLOOP bank3 ;
incf num_records,f ; increment number of records by one
movlw @tocstart ; to test for pointer overflow after loop increment see if value of pointer
bank1 ;
subwf toc_pointer,w ; has not wrapped around since the table of contents ends at address 255
skpc ; is the current pointer value valid ?
return ; no, the pointer has wrapped around, exit with number of 20 records, return in bank3
movf toc_pointer,w ; yes, get value of pointer
call IEE_READ ; read value from internal eeprom, address in w, data returned in w, return in bank2
bank1 ;
movwf toc_test ; store value to test if all three bytes are zero
incf toc_pointer,f ; pointer to the next location in the toc
movf toc_pointer,w ; use copy of value
call IEE_READ ; read value from internal eeprom, address in w, data returned in w, return in bank2
bank1 ;
iorwf toc_test,f ; also test this byte for zero value
incf toc_pointer,f ; pointer to the next location in the toc
movf toc_pointer,w ; use copy of value
call IEE_READ ; read value from internal eeprom, address in w, data returned in w, return in bank2
bank1 ;
iorwf toc_test,f ; also test this byte for zero value
movlw d'8' ; records in toc each take ten bytes, we already increased pointer by two
addwf toc_pointer,f ; point to next record in toc in case we do loop
movf toc_test,w ; now we have to test again if all three bytes are zero
skpz ; is the value of all three byte zero ?
goto MEM_GETNRLOOP ; no, this location is already taken, go see if next location is free
return ; yes, return in bank1
;--------------------------------------------------------------------------------------------------------------------------------------
MEM_SELECTREC ; reset the block download pointers pointer_low/high/chip and endpoint_low/high/chip for the
; given record number, update current_rec
; record number should be in w register
; the pointer values are rounded to block size since we can only download complete blocks
bank3 ; this routine is called only with a valid record selection, no need to check record number
movwf current_rec ; the new record number selection is in w register
movlw @tocstart +d'7' ; start off with pointing to the first address byte of the first record in the toc
bank1 ; we will have to look up the right address
movwf toc_pointer ; so we will use this value in the pointer calculation which follows
bank3 ; this routine is called only with a valid record selection, no need to check record number
decf current_rec,w ; copy selected record number minus one to a loopcounter, value is now 0..19
movwf rec_loopcntr ; we may use this counter in the following loop
skpnz ; is the value zero meaning record number one is currently selected ?
goto MEM_SELECTR_ZER ; yes, this is a special case, the starting address is not in the toc since it is always zero
MEM_SELECTR_INC bank3 ; first time we get here is with records 2..20, loopcounter value 1..19
decf rec_loopcntr,f ; count every record
skpnz ; should we increase the pointer to the next record ?
goto MEM_SELECTR_STA ; no, get values
movlw d'10' ; yes, we repeat until the pointer has the right value
bank1 ; each record uses ten bytes in the table of contents
addwf toc_pointer,f ; point to the next record
goto MEM_SELECTR_INC ; repeat until the pointer has the right value
MEM_SELECTR_STA bank1 ; no, read the values from the toc into the pointer registers
movf toc_pointer,w ; use the value of the tocpointer
call IEE_READ ; read value from internal eeprom, address in w, data returned in w, return in bank2
bank3 ;
movwf pointer_low ;
bank1 ;
incf toc_pointer,f ; also for the next byte
movf toc_pointer,w ; get copy of value
call IEE_READ ; read value from internal eeprom, address in w, data returned in w, return in bank2
bank3 ;
movwf pointer_high ;
bank1 ;
incf toc_pointer,f ; and for this byte
movf toc_pointer,w ; get copy of value
call IEE_READ ; read value from internal eeprom, address in w, data returned in w, return in bank2
bank3 ;
movwf pointer_chip ; this value also includes bitpointer
MEM_SELECTR_CAL movlw b'11000000' ; strip the lower six bits from pointer_low value
andwf pointer_low,f ; since we want to start at beginning of the next block
movlw d'64' ; this value is the size of one block
addwf pointer_low,f ; add one block to current block pointer
skpnc ; did we get an overflow of the lower byte ?
incf pointer_high,f ; yes, also increase high byte of pointer
btfsc pointer_high,7 ; no, did we get an overflow of the lower seven bits of the high byte (we use 32k eeproms) ?
incf pointer_chip,f ; yes, also increase the chip select byte
btfsc pointer_high,7 ; no, again, did we get an overflow of the lower seven bits of high byte (we use 32k eeproms) ?
bcf pointer_high,7 ; yes, then reset this bit
movlw b'00001111' ; no, the pointer to the last used bit was stored in upper nibble of the chip select byte
andwf pointer_chip,f ; we only want chip select so strip bitpointer from value
movlw d'8' ; point to end address positions
bank1 ; we already increased the pointer by two positions
addwf toc_pointer,f ; now add the other eight
movf toc_pointer,w ; copy value
MEM_SELECTR_END call IEE_READ ; read value from internal eeprom, address in w, data returned in w, return in bank2
bank3 ;
movwf endpoint_low ;
movlw b'11000000' ; strip the lower six bits from pointer_low value
andwf endpoint_low,f ; since we want to start at beginning of the block
bank1 ;
incf toc_pointer,f ; also for the next byte
movf toc_pointer,w ; get copy of value
call IEE_READ ; read value from internal eeprom, address in w, data returned in w, return in bank2
bank3 ;
movwf endpoint_high ;
bank1 ;
incf toc_pointer,f ; and for this byte
movf toc_pointer,w ; get copy of value
call IEE_READ ; read value from internal eeprom, address in w, data returned in w, return in bank2
bank3 ;
movwf endpoint_chip ; this value also includes bitpointer
movlw b'00001111' ; last free bit pointer is stored in upper nibble
andwf endpoint_chip,f ; we only want chip select so strip bitpointer from value
return ; return in bank3
MEM_SELECTR_ZER clrf pointer_low ; we get here in bank3
clrf pointer_high ; set block pointer to zero for record number one
clrf pointer_chip ; since this record always starts at the bottom of the memory
bank1 ;
movf toc_pointer,w ; get pointer of address bytes of first record
goto MEM_SELECTR_END ; tocpointer was already set to right address for end of record number one
;--------------------------------------------------------------------------------------------------------------------------------------
MEM_GETTIMEDATE ; read the record start time and date of currently selected record from the toc and store
; these in the time and date buffer in bank2
; return in bank2
bsf status,irp ; make sure upper bit in address is one (select bank 2 and 3)
movlw h'68' ; get the time and date bytes in the buffer in bank2
movwf fsr ; since we have to decode them first, set pointer which we will use later
movlw @tocstart -d'10' ; point to the time bytes of first record in the toc,
bank1 ; minus ten bytes since following addition loop runs at least once
movwf toc_pointer ; use this value in the pointer calculation which follows
bank3 ;
movf current_rec,w ; get the currently selected record number and use this number to calculate the offset
movwf rec_loopcntr ; in the table of contents, increment value with one for proper loop end
MEM_GTDFINDLOOP movlw d'10' ; each record uses ten bytes in the table of contents
bank1 ;
addwf toc_pointer,f ; point to the next record
bank3 ;
decfsz rec_loopcntr,f ; should we repeat the addition ?
goto MEM_GTDFINDLOOP ; yes, repeat until the pointer has the right value
MEM_GTDCOPYLOOP bank1 ; no, now go copy the bytes from internal eeprom to buffer
movf toc_pointer,w ; use the value of the tocpointer
call IEE_READ ; read value from internal eeprom, address in w, data returned in w, return in bank2
movwf indf ; store the data value for this address
bank1 ;
incf toc_pointer,f ; point to next position in table of contents
incf fsr,f ; point to the next time and date byte
movlw h'6F' ; position past the last time and date byte
subwf fsr,w ; check if all byte have been written
skpz ; have we copied all bytes ?
goto MEM_GTDCOPYLOOP ; no, repeat until all bytes have been copied to the buffer
return ; return in bank2
;--------------------------------------------------------------------------------------------------------------------------------------
MEM_GETRECSIZE ; store size (in bits) of currently selected record into registers numlow, nummiddle, numhigh
; this routine is called only with a valid record selection, no need to check record number
movlw @tocstart +d'7' ; start off with pointing to the first address byte of the first record in the toc
bank1 ; we will have to look up the right address for the selected record
movwf toc_pointer ; use this value in the pointer calculation which follows
bank3 ; this routine is called only with a valid record selection, no need to check record number
decf current_rec,w ; copy selected record number minus one to a loopcounter, value is now 0..19
movwf rec_loopcntr ; we may use this counter in the following loop
skpnz ; is the value zero meaning record number one is currently selected ?
goto MEM_RECSIZE_ZER ; yes, this is a special case, the starting address is not in the toc since it is always zero
MEM_RECSIZE_INC bank3 ; first time we get here is with records 2..20, loopcounter value 1..19
decf rec_loopcntr,f ; loopcounter
skpnz ; should we repeat the addition ?
goto MEM_RECSIZE_STA ; no, we are at the right position, go use this location as start address of record
movlw d'10' ; yes, we repeat until the pointer has the right value
bank1 ; each record uses ten bytes in the table of contents
addwf toc_pointer,f ; point to the next record
goto MEM_RECSIZE_INC ; do loop until we get right address
MEM_RECSIZE_STA bank1 ; no, read the values from the toc into the pointer registers
movf toc_pointer,w ; use the value of the tocpointer
call IEE_READ ; read value from internal eeprom, address in w, data returned in w, return in bank2
bank3 ;
movwf recsizea0 ; a0/a1/a2 is the smaller number (record n), b0/b1/b2 will be the larger number (record n+1)
bank1 ; so result is record size = b - a [bits]
incf toc_pointer,f ; also for the next byte
movf toc_pointer,w ; get copy of value
call IEE_READ ; read value from internal eeprom, address in w, data returned in w, return in bank2
bank3 ;
movwf recsizea1 ;
bank1 ;
incf toc_pointer,f ; and for this byte
movf toc_pointer,w ; get copy of value
call IEE_READ ; read value from internal eeprom, address in w, data returned in w, return in bank2
bank3 ;
movwf recsizea2 ; this value is combined chipselect (least significant nibble) and bitpointer (upper nibble)
movlw d'8' ; move pointer to end address position bytes
bank1 ; we already increased the pointer by two positions
addwf toc_pointer,f ; now add the other eight
MEM_RECSIZE_CAL bank3 ; we have to adjust the start address
movlw b'11000000' ; use the end address of a previous record to calculate the start of the next record
andwf recsizea0,f ; find the start of the last used block by stripping the lower six bits from the low byte
movlw d'64' ; this value is the size of one block
addwf recsizea0,f ; add one block to point to the next block, which is not used yet
skpnc ; did we get an overflow of the lower byte ?
incf recsizea1,f ; yes, also increase high byte
btfsc recsizea1,7 ; no, did we get an overflow of the lower seven bits of the high byte (we use 32k eeproms) ?
incf recsizea2,f ; yes, also increase the chip select byte
btfsc recsizea1,7 ; no, again, did we get an overflow of the lower seven bits of high byte (we use 32k eeproms) ?
bcf recsizea1,7 ; yes, then reset this bit
movlw b'00001111' ; no, the pointer to the last used bit was stored in upper nibble of the chip select byte
andwf recsizea2,f ; we only want the chip select value so strip this nibble
MEM_RECSIZE_END bank1 ; select bank since we may come from bank3 (record number is zero code below)
movf toc_pointer,w ; get pointer of address bytes of first record
call IEE_READ ; read value from internal eeprom, address in w, data returned in w, return in bank2
bank3 ;
movwf recsizeb0 ;
bank1 ;
incf toc_pointer,f ; also for the next byte
movf toc_pointer,w ; get copy of value
call IEE_READ ; read value from internal eeprom, address in w, data returned in w, return in bank2
bank3 ;
movwf recsizeb1 ;
bank1 ;
incf toc_pointer,f ; and for this byte
movf toc_pointer,w ; get copy of value
call IEE_READ ; read value from internal eeprom, address in w, data returned in w, return in bank2
bank3 ;
movwf recsizeb2 ; this value also includes bitpointer
MEM_RECSIZE_HI rlf recsizeb2,w ; the b recordsize registers hold larger address
movwf numhigh ; calculate address from bitp/chipselect, high address byte and low address byte
rlf numhigh,f ; store address in numlow, nummiddle and numhigh
rlf recsizeb0,w ;
movwf numlow ;
rlf recsizeb1,w ;
movwf nummiddle ;
rlf numhigh,f ;
rlf numlow,f ;
rlf nummiddle,f ;
rlf recsizeb2,f ;
rlf numhigh,f ;
rlf numlow,f ; alow43210 bitp210
rlf nummiddle,f ; ahigh43210 alow765
rlf recsizeb2,w ;
andlw b'00011111' ;
movwf numhigh ; 0 0 0 chip210 ahigh65
MEM_RECSIZE_LO rlf recsizea2,w ; the a recordsize registers hold smaller address
movwf recsizeb2 ; calculate address from bitp/chipselect, high address byte and low address byte
rlf recsizeb2,f ; store address in recsizeb2, recsizeb1, recsizeb0
rlf recsizea0,w ;
movwf recsizeb0 ;
rlf recsizea1,w ;
movwf recsizeb1 ;
rlf recsizeb2,f ;
rlf recsizeb0,f ;
rlf recsizeb1,f ;
rlf recsizea2,f ;
rlf recsizeb2,f ;
rlf recsizeb0,f ;
rlf recsizeb1,f ;
rlf recsizea2,w ;
andlw b'00011111' ;
movwf recsizeb2 ;
MEM_RECSIZE_SUB movf recsizeb0,w ; now do subtraction to get record size
subwf numlow,f ; store result in numlow, nummiddle and numhigh
movf recsizeb1,w ;
skpc ;
incfsz recsizeb1,w ;
subwf nummiddle,f ;
movf recsizeb2,w ;
skpc ;
incfsz recsizeb2,w ;
subwf numhigh,f ;
MEM_RECSIZE_ADD movlw d'1' ; because of the way we store the start and end position of a record in the table of contents
addwf numlow,f ; we have to add one extra bit to the size of the record
skpnc ; a record of one bit will not be found in the toc since the start and end addresses are of
incf nummiddle,f ; the same value, therefore the minimum size of a record is two bits
skpnz ;
incf numhigh,f ;
return ; return in bank3
MEM_RECSIZE_ZER clrf recsizea0 ; set block pointer to zero for record number one
clrf recsizea1 ; since this record always starts at the bottom of the memory
clrf recsizea2 ;
goto MEM_RECSIZE_END ; tocpointer is already at right address
;--------------------------------------------------------------------------------------------------------------------------------------
MEM_RDBLOCK ; copy data block from external eeprom to the block buffer in bank1
; carry set when all blocks done
; return in bank1 or bank3
MEM_RDBL_CHECK2 bank3 ;
movf pointer_low,w ; check for end of record, have we sent all blocks?
subwf endpoint_low,w ; subtract, first low bytes
movf pointer_high,w ; high byte
skpc ; did we have to borrow ?
incfsz pointer_high,w ; yes, adjust value
subwf endpoint_high,w ; no, subtract high bytes
movf pointer_chip,w ; chip select bytes
skpc ; did we have to borrow ?
incfsz pointer_chip,w ; yes, adjust value
subwf endpoint_chip,w ; no, subtract chip select bytes
skpnc ; do we have a valid address ?
goto MEM_RDBL_COPY ; yes, go copy the block from external eeprom to buffer in bank1
setc ; no, set carry as indication that we have already read all blocks of this record
return ; carry has been set, return in bank3
MEM_RDBL_COPY bank3 ; get block pointer
movf pointer_low,w ; address low byte
bank1 ;
movwf iicalow ; copy value
bank3 ;
movf pointer_high,w ; select address high byte
bank1 ;
movwf iicahigh ; copy value
bank3 ;
movf pointer_chip,w ; select chip
bank1 ;
movwf iicchip ; copy value
call IIC_RDBLOCK ; read 64 bytes from external eeprom, address in iicchip, iicalow and iicahigh, bank1 return
clrc ; clear carry
return ; return in bank1
;--------------------------------------------------------------------------------------------------------------------------------------
MEM_INC_RDBLOCK ; auto increment blockpointer, then copy data block from external eeprom to the block
; buffer in bank1
; carry set when all blocks done
; return in bank1 or bank3
MEM_RDBL_CHECK1 bank3 ;
movf pointer_low,w ; check for end of record, have we sent all blocks?
subwf endpoint_low,w ; subtract, first low bytes
movf pointer_high,w ; high byte
skpc ; did we have to borrow ?
incfsz pointer_high,w ; yes, adjust value
subwf endpoint_high,w ; no, subtract high bytes
movf pointer_chip,w ; chip select bytes
skpc ; did we have to borrow ?
incfsz pointer_chip,w ; yes, adjust value
subwf endpoint_chip,w ; no, subtract chip select bytes
skpnc ; do we have a valid address ?
goto MEM_RDBL_INCADD ; yes, go copy the block from external eeprom to buffer in bank1
setc ; no, set carry as indication that we have already read all blocks of this record
return ; carry has been set, return in bank3
MEM_RDBL_INCADD movlw d'64' ; no, this is the size of one memory block in external eeprom
bank3 ;
addwf pointer_low,f ; increment memory block pointer, first low byte
skpc ; do we need to update high byte as well ?
goto MEM_RDBLOCK ; no, go check address again, then copy block
incf pointer_high,f ; yes, do so
btfsc pointer_high,7 ; the current eeprom chips are 32kB (15 bits address), did we get an overflow ?
incf pointer_chip,f ; yes, also increase chip select byte
bcf pointer_high,7 ; no, we don't use bit 7 since we have 32kB eeproms, clear bit
goto MEM_RDBLOCK ; go check address again, then copy block
;--------------------------------------------------------------------------------------------------------------------------------------
;--------------------------------------------------------------------------------------------------------------------------------------
;--------------------------------------------------------------------------------------------------------------------------------------
org h'1000' ; start of program memory page 2
;--------------------------------------------------------------------------------------------------------------------------------------
; RS232 subroutines for transmitting and receiving bytes and numbers (ascii strings)
;--------------------------------------------------------------------------------------------------------------------------------------
; a command byte sequence is (in ascii):
; <STX> 1 byte indicator for start of command, value hex 02
; <c1> 1 byte, first letter of command
; <c2> 1 byte, second letter of command
; <data bytes> 0-64 data bytes, all values accepted except ascii codes STX and ETX
; <checksum> 1 byte checksum (see below), any hex value 00-FF
; <ETX> 1 byte indicator for end of command, value hex 03
; checksum value makes sum of all bytes from <STX> up to and including <ETX> zero
TX_BINVALUE addlw h'30' ; transmit the binary value (0..9) in w register as an ascii character ('0'..'9')
TX_BYTE ; transmit content of w register through RS232 (odd parity)
; checksum calculation is included
; w register content is destroyed
; returns in bank0
bank0 ; select bank, then calculate what the value of the parity bit is going to be:
movwf tx_byte ; w holds the byte to be transmitted, store w for use after the parity calculation
addwf tx_checksum,f ; use byte in checksum calculation
movwf tx_parity ;
swapf tx_parity,w ; use w and tx_parity registers for the calculation
xorwf tx_parity,f ; calculate nibbles
rrf tx_parity,w ;
xorwf tx_parity,f ; at this point the parity values of the nibbles are in bit 2 and bit 0
btfss tx_parity,2 ; if parity one nibble is 1 then the byte parity is that of other nibble, skip ahead
incf tx_parity,f ; otherwise, invert bit 0
bank1 ;
bcf txsta,tx9d ; first assume we should reset the parity bit that will be send
bank0 ;
btfss tx_parity,0 ; was it the right decision to reset the bit ?
goto TX_LOOP ; yes, go send the byte
bank1 ;
bsf txsta,tx9d ; no, first set the parity bit and then send the byte
bank0 ;
TX_LOOP btfss pir1,txif ; yes, ready to send ? (bit set means transmit buffer is empty)
goto TX_LOOP ; no, keep trying
TX_POLL_RTS btfsc portc,rts_in ; is the PC input buffer empty, so are we allowed to send data ? (RTS serves as a CTS here)
goto TX_POLL_RTS ; no, keep trying
; **** to do: we would like to have some timeout testing here....
movf tx_byte,w ; yes, move character to w
movwf txreg ; transmit byte
;nop ; there is a delay of one instruction cycle after writing to txreg, before txif gets cleared
return ; line above, nop, is just information for of possible future changes, return in bank0
;--------------------------------------------------------------------------------------------------------------------------------------
TX_COMMANDSTART ; send ACK, STX and command mnemonics
movlw h'06' ; Acknowledgement (ACK)
call TX_BYTE ; send 'command has been accepted', return in bank0
clrf tx_checksum ; start off with clear checksum value
movlw h'02' ; STX
call TX_BYTE ; return in bank0
movf rx_buffer00,w ; location of first letter of mnemonic in RS232 buffer
call TX_BYTE ;
movf rx_buffer01,w ; location of second letter of mnemonic in RS232 buffer
call TX_BYTE ;
return ;
;--------------------------------------------------------------------------------------------------------------------------------------
TX_COMMANDEND ; send checksum and ETX, clear command status byte
bank0 ;
movlw h'03' ; ETX
call TX_BYTE ; transmit, return in bank0
movf tx_checksum,w ; get checksum value
sublw d'0' ; calculate right value (all command bytes added should result in zero value)
call TX_BYTE ; transmit, return in bank0
bcf flags1,command ; now command has been handled we can clear the execute flag
bcf portc,cts_out ; set CTS to re-enable data stream from computer
bsf portb,led_red ; turn on red status led
bcf portb,led_green ; turn off green status led
return ; return in bank0
;--------------------------------------------------------------------------------------------------------------------------------------
TX_NUMBER ; this routine transmits the 8 bit number (0-255) in the w register via RS232 as
; (up to three) ascii characters without preceding zeros (23 will be sent as '23'
; and not as '023'), transmit checksum is calculated
; w register content is destroyed
; returns in bank0
; 255 byte value
;
; 100 100
; 10 10
; 1 1
bank0
movwf tx_numberl ; store the value of the number for use in the calculation
movlw b'10000000' ; use the upper bit of the counter as a flag for preceding zeros,
movwf tx_counter ; 1 means skip characters
movlw d'100' ; how many hundreds ?
call TX_DIGIT ; send digit
movlw b'10000000' ; select flag bit
andwf tx_counter,f ; clear counter but keep preceding zeros flag bit
movlw d'10' ; how many tens ?
call TX_DIGIT ; send digit
clrf tx_counter ; the last character always has to be sent so clear counter and flag bit too
movlw d'1' ; how many ones ?
goto TX_DIGIT ; goto instead of call because we are done here
TX_NLOOP bcf tx_counter,7 ; this character is non-zero, so begin sending characters
incf tx_counter,f ; increment counter
TX_DIGIT subwf tx_numberl,f ; subtract
skpnc ; is the result negative ?
goto TX_NLOOP ; no, repeat
addwf tx_numberl,f ; undo negative result, we need the right value for the rest of the calculation
btfsc tx_counter,7 ; can we still skip the preceding zero characters ?
return ; yes, skip those characters
movlw a'0' ; in ascii numbers start at 30 hex
addwf tx_counter,w ; so add the counter to get the right ascii code
call TX_BYTE ; send the ascii character stored in register w, return in bank0
return ; return in bank0
;--------------------------------------------------------------------------------------------------------------------------------------
TX_LNUMBER ; this routine transmits the 16 bit number (0-65535) in the registers numlow and
; nummiddle via RS232 as (up to five) ascii characters without preceding zeros
; transmit checksum is calculated
; w register content is destroyed
; returns in bank0
; 65,535 high_byte low_byte
;
; 10,000 39 16
; 1,000 3 232
; 100 0 100
; 10 0 10
; 1 0 1
movf numlow,w ; get lower byte of number
bank0 ;
movwf tx_numberl ; store it for use during calculation
movf nummiddle,w ; get upper byte of number
movwf tx_numberh ; store it for use during calculation
movlw b'10000000' ; use the upper bit of the counter as a flag for preceding zeros,
movwf tx_counter ; 1 means skip characters
movlw d'16' ; first count number of 10,000's, lower byte of binary number 10,000
movwf templow ;
movlw d'39' ; upper byte of binary number 10,000
movwf temphigh ;
call TX_LDIGIT ;
movlw d'232' ; count number of 1,000's, lower byte of binary number 1,000
movwf templow ;
movlw d'3' ; upper byte of binary number 1,000
movwf temphigh ;
call TX_LDIGIT ;
movlw d'100' ; count number of 100's, lower byte of binary number 100
movwf templow ;
clrf temphigh ; high byte is zero
call TX_LDIGIT ;
movlw d'10' ; count number of 10's, lower byte of binary number 10
movwf templow ;
clrf temphigh ; high byte is zero
call TX_LDIGIT ;
movlw d'1' ; count number of 1's, lower byte of binary number 1
movwf templow ;
clrf temphigh ; high byte is zero
clrf tx_counter ; the last character always has to be sent so clear counter and flag bit too
goto TX_LDIGIT ; goto instead of call because we are done here
TX_LDIGIT movlw b'10000000' ; select flag bit
andwf tx_counter,f ; clear counter but keep flag bit for preceding zeros
TX_LTEST movf templow,w ; check if subtraction will give positive or zero result
subwf tx_numberl,w ; compare lower byte
movf temphigh,w ;
skpc ;
incfsz temphigh,w ;
subwf tx_numberh,w ; high byte
skpc ; is the result >= zero ?
goto TX_LSEND ; no, result is negative, go send character
TX_LSUBTR bcf tx_counter,7 ; yes, clear flag bit since we skipped possible zero character, now start sending characters
incf tx_counter,f ; increment counter
movf templow,w ; subtract
subwf tx_numberl,f ; low byte
movf temphigh,w ;
skpc ;
incfsz temphigh,w ; high byte
subwf tx_numberh,f ;
goto TX_LTEST ; next
TX_LSEND btfsc tx_counter,7 ; can we still skip the preceding zero characters ?
return ; yes, skip those characters
movlw a'0' ; in ascii numbers start at 30 hex
addwf tx_counter,w ; so add the counter to get the right ascii code
call TX_BYTE ; send the ascii character stored in register w,return in bank0
return ; return in bank0
;--------------------------------------------------------------------------------------------------------------------------------------
TX_WNUMBER ; this routine transmits the 24 bit number (0-16777215)('wide' number) in the registers
; numlow(lsB), nummiddle (middle byte) and numhigh (msB) (word extended) via RS232 as
; (up to eight) ascii characters without preceding zeros, a transmit checksum is calculated
; w register content is destroyed
; returns in bank0
;
; 16,777,215 upper_byte middle_byte lower_byte
; 10,000,000 152 150 128
; 1,000,000 15 66 64
; 100,000 1 134 160
; 10,000 0 39 16
; 1,000 0 3 232
; 100 0 0 100
; 10 0 0 10
; 1 0 0 1
bank0 ;
movf numlow,w ; get lower byte of number
movwf tx_numberl ; store it for use during calculation
movf nummiddle,w ; get middle byte of number
movwf tx_numberm ; store it for use during calculation
movf numhigh,w ; get upper byte of number
movwf tx_numberh ; store it for use during calculation
movlw b'10000000' ; use the upper bit of the counter as a flag for preceding zeros,
movwf tx_counter ; 1 means skip characters
movlw d'128' ; first count number of 10,000,000's, lower byte
movwf templow ;
movlw d'150' ; middle byte
movwf tempmiddle ;
movlw d'152' ; upper byte
movwf temphigh ;
call TX_WDIGIT ;
movlw d'64' ; count number of 1,000,000's, lower byte
movwf templow ;
movlw d'66' ; middle byte
movwf tempmiddle ;
movlw d'15' ; upper byte
movwf temphigh ;
call TX_WDIGIT ;
movlw d'160' ; count number of 100,000's, lower byte
movwf templow ;
movlw d'134' ; middle byte
movwf tempmiddle ;
movlw d'1' ; upper byte
movwf temphigh ;
call TX_WDIGIT ;
movlw d'16' ; count number of 10,000's, lower byte
movwf templow ;
movlw d'39' ; middle byte
movwf tempmiddle ;
clrf temphigh ; upper byte is zero
call TX_WDIGIT ;
movlw d'232' ; number of 1,000's
movwf templow ;
movlw d'3' ; middle byte
movwf tempmiddle ;
clrf temphigh ; upper byte is zero
call TX_WDIGIT ;
movlw d'100' ; number of 100's
movwf templow ;
clrf tempmiddle ; middle byte is zero
clrf temphigh ; upper byte is zero
call TX_WDIGIT ;
movlw d'10' ; count number of 10's
movwf templow ;
clrf tempmiddle ; middle byte is zero
clrf temphigh ; upper byte is zero
call TX_WDIGIT ;
movlw b'00000001' ; count number of 1's
movwf templow ;
clrf tempmiddle ; middle byte is zero
clrf temphigh ; high byte is zero
clrf tx_counter ; the last character always has to be sent so clear counter and flag bit too
goto TX_WDIGIT ; goto instead of call because we are done here
TX_WDIGIT movlw b'10000000' ; select flag bit
andwf tx_counter,f ; clear counter but keep flag bit for preceding zeros
TX_WTEST movf templow,w ; check if subtraction will give positive or zero result
subwf tx_numberl,w ; compare lower byte
movf tempmiddle,w ;
skpc ;
incfsz tempmiddle,w ;
subwf tx_numberm,w ; middle byte
movf temphigh,w ;
skpc ;
incfsz temphigh,w ; high byte
subwf tx_numberh,w ;
skpc ; is the result >= zero ?
goto TX_WSEND ; no, result is negative, go send character
TX_WSUBTR bcf tx_counter,7 ; yes, clear flag bit since we skipped possible zero character, now start sending characters
incf tx_counter,f ; increment counter
movf templow,w ; subtract
subwf tx_numberl,f ; low byte
movf tempmiddle,w ;
skpc ;
incfsz tempmiddle,w ;
subwf tx_numberm,f ; middle byte
movf temphigh,w ;
skpc ;
incfsz temphigh,w ; high byte
subwf tx_numberh,f ;
goto TX_WTEST ; next
TX_WSEND btfsc tx_counter,7 ; can we still skip the preceding zero characters ?
return ; yes, skip those characters
movlw a'0' ; in ascii numbers start at 30 hex
addwf tx_counter,w ; so add the counter to get the right ascii code
call TX_BYTE ; send the ascii character stored in register w,return in bank0
return ; return in bank0
;--------------------------------------------------------------------------------------------------------------------------------------
TX_TIMEANDDATE ; this routine transmits the time and date values from the buffer in bank2 as a string
; use Windows format: 'DD/MM/YY HH:MM:SS'
; transmit checksum is calculated
; w register content is destroyed
; returns in bank2
bank2 ;
swapf date,w ; get date/ 10 date
andlw b'00000011' ; strip date
call TX_BINVALUE ; transmit the binary value (0..9) in w register as an ascii character ('0'..'9')
bank2 ;
movf date,w ; get date/ 10 date
andlw b'00001111' ; strip 10 date
call TX_BINVALUE ; transmit the binary value (0..9) in w register as an ascii character ('0'..'9')
movlw a'/' ;
call TX_BYTE ;
bank2 ;
swapf month,w ; get months/ 10 month
andlw b'00000001' ; strip months
call TX_BINVALUE ; transmit the binary value (0..9) in w register as an ascii character ('0'..'9')
bank2 ;
movf month,w ; get months/ 10 month
andlw b'00001111' ; strip 10 month
call TX_BINVALUE ; transmit the binary value (0..9) in w register as an ascii character ('0'..'9')
movlw a'/' ;
call TX_BYTE ;
bank2 ;
swapf year,w ; get year/ 10 year
andlw b'00001111' ; strip year
call TX_BINVALUE ; transmit the binary value (0..9) in w register as an ascii character ('0'..'9')
bank2 ;
movf year,w ; get year/ 10 year
andlw b'00001111' ; strip 10 year
call TX_BINVALUE ; transmit the binary value (0..9) in w register as an ascii character ('0'..'9')
movlw a' ' ;
call TX_BYTE ;
bank2 ;
swapf hours,w ; get hours/ 10 hours
andlw b'00000011' ; strip hours
call TX_BINVALUE ; transmit the binary value (0..9) in w register as an ascii character ('0'..'9')
bank2 ;
movf hours,w ; get 10 hours/ hours
andlw b'00001111' ; strip 10 hours
call TX_BINVALUE ; transmit the binary value (0..9) in w register as an ascii character ('0'..'9')
movlw a':' ;
call TX_BYTE ;
bank2 ;
swapf minutes,w ; get minutes/ 10 minutes
andlw b'00000111' ; strip minutes
call TX_BINVALUE ; transmit the binary value (0..9) in w register as an ascii character ('0'..'9')
bank2 ;
movf minutes,w ; get 10 minutes/ minutes
andlw b'00001111' ; strip 10 minutes
call TX_BINVALUE ; transmit the binary value (0..9) in w register as an ascii character ('0'..'9')
movlw a':' ;
call TX_BYTE ;
bank2 ;
swapf seconds,w ; get seconds/ 10 seconds / oscillator enable
andlw b'00000111' ; strip seconds / oscillator enable
call TX_BINVALUE ; transmit the binary value (0..9) in w register as an ascii character ('0'..'9')
bank2 ;
movf seconds,w ; get seconds/ 10 seconds / oscillator enable
andlw b'00001111' ; strip 10 seconds / oscillator enable
call TX_BINVALUE ; transmit the binary value (0..9) in w register as an ascii character ('0'..'9')
return ; return in bank2
;--------------------------------------------------------------------------------------------------------------------------------------
TX_FREQVALSTR ; w register should hold the frequency settings value
; this routine transmits the corresponding value from the table below as an ascii string
; 00000000 32
; 00000001 16
; 00000011 8
; 00000111 4
; 00001111 2
; 00011111 1
; 00111111 0.5
; 01111111 0.25
; 11111111 0
TX_FREQ_32 bank0 ;
movwf tx_numberl ;
movlw b'00000000' ;
subwf tx_numberl,w ;
skpz ;
goto TX_FREQ_16 ;
movlw a'3' ;
call TX_BYTE ; transmit byte , return in bank0
movlw a'2' ;
call TX_BYTE ; transmit byte , return in bank0
return ; return in bank0
TX_FREQ_16 movlw b'00000001' ;
subwf tx_numberl,w ;
skpz ;
goto TX_FREQ_8 ;
movlw a'1' ;
call TX_BYTE ;
movlw a'6' ;
call TX_BYTE ;
return ;
TX_FREQ_8 movlw b'00000011' ;
subwf tx_numberl,w ;
skpz ;
goto TX_FREQ_4 ;
movlw a'8' ;
call TX_BYTE ;
return ;
TX_FREQ_4 movlw b'00000111' ;
subwf tx_numberl,w ;
skpz ;
goto TX_FREQ_2 ;
movlw a'4' ;
call TX_BYTE ;
return ;
TX_FREQ_2 movlw b'00001111' ;
subwf tx_numberl,w ;
skpz ;
goto TX_FREQ_1 ;
movlw a'2' ;
call TX_BYTE ;
return ;
TX_FREQ_1 movlw b'00011111' ;
subwf tx_numberl,w ;
skpz ;
goto TX_FREQ_0.5 ;
movlw a'1' ;
call TX_BYTE ;
return ;
TX_FREQ_0.5 movlw b'00111111' ;
subwf tx_numberl,w ;
skpz ;
goto TX_FREQ_0.25 ;
movlw a'0' ;
call TX_BYTE ;
movlw a'.' ;
call TX_BYTE ;
movlw a'5' ;
call TX_BYTE ;
return ;
TX_FREQ_0.25 movlw b'01111111' ;
subwf tx_numberl,w ;
skpz ;
goto TX_FREQ_0 ;
movlw a'0' ;
call TX_BYTE ;
movlw a'.' ;
call TX_BYTE ;
movlw a'2' ;
call TX_BYTE ;
movlw a'5' ;
call TX_BYTE ;
return ;
TX_FREQ_0 movlw b'11111111' ;
subwf tx_numberl,w ;
skpz ;
return ;
movlw a'0' ;
call TX_BYTE ;
return ;
;--------------------------------------------------------------------------------------------------------------------------------------
GET_NUMBER ; reads the ascii characters of the RS232 input buffer and converts this data to an
; eight bit number (0-255), preceding zeros are accepted
; returns in bank 0 with:
; w holds number - carry is cleared - read OK
; w holds junk - carry is set - error: not a number
bank0 ;
clrf templow ; we will add the values of each character to this register, so start with zero
movlw (rx_buffer00+2) ; start position of data in buffer (runs up to and includes maxpos)
movwf fsr ; point to this address
bcf status,irp ; make sure upper bit in address is zero (select bank 0 and 1)
READVALUE movlw h'3A' ; subtract 3A hex from the buffer number
subwf indf,w ; if the character is a number, the result should be negative
skpnc ; is the result negative?
HIGHERR return ; no, so it is not an ascii number, we cannot handle this data, exit with carry set, error
addlw h'0A' ; yes, if the character is a number then the result of this addition should be positive
skpc ; is the result positive ?
LOWERR goto GN_ERROR ; no, we cannot handle this data
ADDVALUE addwf templow,f ; yes, store the resulting value
skpnc ; is the resulting number greater than 255 decimal ?
return ; yes, so it won't fit in 1 byte, we cannot handle this data, exit with carry set, error
POINTER incf fsr,f ; no, increase pointer to point to next value
movf fsr,w ; use pointer value
subwf rx_maxpos,w ; was this the last character ?
skpc ; let's see
goto READOK ; yes, end loop and go to exit with carry cleared as indication for no errors
TESTSIZE movlw d'26' ; the value should be smaller than 26 because 25 times 10 will be 250
subwf templow,w ; and we know there is a next character, result should fit in one byte
skpnc ; will the result times 10 be larger than 250 ?
return ; yes, we cannot handle this data, exit with carry set, error
TIMES10 clrc ; no, go multiply the current value by ten, start off with clear carry bit:
rlf templow,f ; first times two,
movf templow,w ; store in w,
rlf templow,f ; and this makes times 4,
rlf templow,f ; and this times 8,
addwf templow,f ; plus 2 is 10 times
goto READVALUE ; next, do loop again and add new numbers
GN_ERROR setc ; indication for not a number, error
return ; done
READOK movf templow,w ; what is the resulting number value of the ascii characters ?
return ; done, exit with carry cleared as indication for no errors
;--------------------------------------------------------------------------------------------------------------------------------------
GET_LNUMBER ; reads the ascii characters of the RS232 input buffer and converts this data to an
; a number from 0-65535, preceding zeros are accepted
; returns in bank 0 with:
; numlow & nummiddle hold number - carry is cleared - read OK
; numlow & nummiddle hold junk - carry is set - error: not a number
bank0 ;
clrf numlow ; we will add the values of each character to these registers,
clrf nummiddle ; so start with zero
movlw (rx_buffer00+2) ; start position of data in buffer (runs up to and includes maxpos)
movwf fsr ; point to this address
bcf status,irp ; make sure upper bit in address is zero (select bank 0 and 1)
LREADVALUE movlw h'3A' ; subtract 3A hex from the buffer number
subwf indf,w ; if the character is a number, the result should be negative
skpnc ; is the result negative?
LHIGHERR goto LGN_ERROR ; no, so it is not an ascii number we cannot handle this data
addlw h'0A' ; yes, if the character is a number then the result of this addition should be positive
skpc ; is the result positive ?
LLOWERR goto LGN_ERROR ; no, we cannot handle this data
LADDVALUE addwf numlow,f ; yes, store the resulting value
skpc ; is the resulting number greater than 255 decimal ?
goto LPOINTER ; no, go get next character
incf nummiddle,f ; yes, add one count to upper byte of number (perform two byte add with carry)
skpnz ; is the resulting number greater than 65535 decimal ?
goto LGN_ERROR ; yes, so it won't fit in 2 bytes, we cannot handle this data
LPOINTER incf fsr,f ; no, increase pointer to point to next value
movf fsr,w ; use pointer value
subwf rx_maxpos,w ; was this the last character ?
skpc ; let's see
return ; yes, end loop and exit with carry cleared as indication for no errors
LTESTSIZE movlw d'231' ; the value of the upper byte should not be more than 25 because 25 times 10 will be 250
addwf nummiddle,w ; and we know there is a next character, the result should fit in two bytes
skpnc ; is the value of the upper byte equal or more than 25 ?
goto LTESTMORE ; yes, go see how much it is exactly and see how much is the lower byte
LTIMES10 bcf status,c ; yes, go multiply the current value by ten, start off with clear carry bit:
rlf numlow,f ; first times two,
movf numlow,w ;
movwf templow ; store value temporarily
rlf nummiddle,f ; first times two, use carry bit from lower byte
movf nummiddle,w ;
movwf temphigh ; store value temporarily
rlf numlow,f ; and this makes times 4, lower byte
rlf nummiddle,f ; and this makes times 4, upper byte, use carry bit of lower byte
rlf numlow,f ; and this makes times 8, lower byte
rlf nummiddle,f ; and this makes times 8, upper byte, use carry bit of lower byte
movf templow,w ;
addwf numlow,f ; plus 2 is 10 times, lower byte
skpnc ;
incf nummiddle,f ; transport carry bit
movf temphigh,w ;
addwf nummiddle,f ; plus 2 is 10 times, upper byte
goto LREADVALUE ; next, do loop again and add new numbers
LTESTMORE skpz ; is the value exactly 25 ?
return ; no, number won't fit in two bytes therefore we cannot handle this data, exit with carry set
movlw d'154' ; yes, lower byte should be smaller than 154 because the total value should be less than 6553
subwf numlow,w ;
skpc ; is the number represented by the two bytes smaller than 6554 ?
goto LTIMES10 ; yes, continue and multiply by 10 and then fetch next character
LGN_ERROR setc ; set error flag
return ; no, number won't fit in two bytes therefore we cannot handle this data, exit with carry set
;--------------------------------------------------------------------------------------------------------------------------------------
GET_TIMEANDDATE ; convert the 17 byte Windows format input string 'DD/MM/YY HH:MM:SS' into eight bytes for
; clock, store the values in the time and date buffer in bank2
; w register content destroyed
; returns in bank2
bank0 ; DATE
movlw h'30' ; ascii to binary offset
subwf rx_buffer03,w ; get Day and convert from ascii character to binary value
bank2 ;
movwf date ; move value to clock buffer
bank0 ;
movlw h'30' ;
subwf rx_buffer02,f ; get 10 Days and convert from ascii character to binary value
swapf rx_buffer02,w ; place result in upper four bits
bank2 ;
iorwf date,f ;
bank0 ; MONTH
movlw h'30' ; ascii to binary offset
subwf rx_buffer06,w ; get Months and convert from ascii character to binary value
bank2 ;
movwf month ; move value to clock buffer
bank0 ;
movlw h'30' ;
subwf rx_buffer05,f ; get 10 Months and convert from ascii character to binary value
swapf rx_buffer05,w ; place result in upper four bits
bank2 ;
iorwf month,f ;
bank0 ; YEAR
movlw h'30' ; ascii to binary offset
subwf rx_buffer09,w ; get Years and convert from ascii character to binary value
bank2 ;
movwf year ; move value to clock buffer
bank0 ;
movlw h'30' ;
subwf rx_buffer08,f ; get 10 Years and convert from ascii character to binary value
swapf rx_buffer08,w ; place result in upper four bits
bank2 ;
iorwf year,f ;
bank0 ; HOURS
movlw h'30' ; ascii to binary offset
subwf rx_buffer12,w ; get Hours and convert from ascii character to binary value
bank2 ;
movwf hours ; move value to clock buffer
bank0 ;
movlw h'30' ;
subwf rx_buffer11,f ; get 10 Hours and convert from ascii character to binary value
swapf rx_buffer11,w ; place result in upper four bits
bank2 ;
iorwf hours,f ;
bcf hours,6 ; select 24 hour clock
bank0 ; MINUTES
movlw h'30' ; ascii to binary offset
subwf rx_buffer15,w ; get Minutes and convert from ascii character to binary value
bank2 ;
movwf minutes ; move value to clock buffer
bank0 ;
movlw h'30' ;
subwf rx_buffer14,f ; get 10 Minutes and convert from ascii character to binary value
swapf rx_buffer14,w ; place result in upper four bits
bank2 ;
iorwf minutes,f ;
bank0 ; SECONDS
movlw h'30' ; ascii to binary offset
subwf rx_buffer18,w ; get Seconds and convert from ascii character to binary value
bank2 ;
movwf seconds ; move value to clock buffer
bank0 ;
movlw h'30' ;
subwf rx_buffer17,f ; get 10 Seconds and convert from ascii character to binary value
swapf rx_buffer17,w ; place result in upper four bits
bank2 ;
iorwf seconds,f ;
bcf seconds,7 ; set bit to enable clock oscillator, is CH (clock halt) bit in clock chip
movlw d'1' ; day 1
movwf day ; just start DAY start at day 1, Sunday, although we don't know this for sure, doesn't matter
movlw b'10000011' ; set square wave at 32768 Hz but disabled and it's output high
movwf clockctrl ; CONTROL
return ; return in bank2
;--------------------------------------------------------------------------------------------------------------------------------------
GET_FREQVALNUM ; convert string data that goes with the logfrequency setting commands to the right lograte
; value according to the following table (example for 16 Hz: 16 > 00000001)
; carry cleared - value is ok
; carry set - error reading number
; 32 00000000
; 16 00000001
; 8 00000011
; 4 00000111
; 2 00001111
; 1 00011111
; 0.5 00111111
; 0.25 01111111
; 0 11111111 (actually 1xxxxxxx is used since only bit 7 is tested)
F_32 bank0 ;
movlw a'3' ; test first data byte
subwf rx_buffer02,w ;
skpz ;
goto F_16 ;
movlw a'2' ;
subwf rx_buffer03,w ; test second data byte
skpz ;
goto F_ERROR ;
movlw rx_buffer03 ;
subwf rx_maxpos,w ; test length
skpz ;
goto F_ERROR ;
movlw b'00000000' ;
clrc ; clear carry to indicate result is ok
return
F_16 movlw a'1' ; test first data byte
subwf rx_buffer02,w ;
skpz ;
goto F_8 ;
movlw rx_buffer03 ;
subwf rx_maxpos,w ; test length first, we not only have the value 16 but also the value 1
skpz ;
goto F_1 ;
movlw a'6' ;
subwf rx_buffer03,w ; test second data byte
skpz ;
goto F_ERROR ;
movlw b'00000001' ;
clrc ; clear carry to indicate result is ok
return
F_8 movlw a'8' ; test first data byte
subwf rx_buffer02,w ;
skpz ;
goto F_4 ;
movlw rx_buffer02 ;
subwf rx_maxpos,w ; test length
skpz ;
goto F_ERROR ;
movlw b'00000011' ;
clrc ; clear carry to indicate result is ok
return
F_4 movlw a'4' ; test first data byte
subwf rx_buffer02,w ;
skpz ;
goto F_2 ;
movlw rx_buffer02 ;
subwf rx_maxpos,w ; test length
skpz ;
goto F_ERROR ;
movlw b'00000111' ;
clrc ; clear carry to indicate result is ok
return
F_2 movlw a'2' ; test first data byte
subwf rx_buffer02,w ;
skpz ;
goto F_1 ;
movlw rx_buffer02 ;
subwf rx_maxpos,w ; test length
skpz ;
goto F_ERROR ;
movlw b'00001111' ;
clrc ;
return ; clear carry to indicate result is ok
F_1 movlw a'1' ; test first data byte
subwf rx_buffer02,w ;
skpz ;
goto F_0 ;
movlw rx_buffer02 ;
subwf rx_maxpos,w ; test length
skpz ;
goto F_ERROR ;
movlw b'00011111' ;
clrc ; clear carry to indicate result is ok
return
F_0 movlw a'0' ; test first data byte
subwf rx_buffer02,w ;
skpz ;
goto F_ERROR ;
movlw rx_buffer02 ;
subwf rx_maxpos,w ; test length
skpz ;
goto F_0.5 ;
movlw b'11111111' ;
clrc ; clear carry to indicate result is ok
return
F_0.5 movlw a'.' ; test second data byte
subwf rx_buffer03,w ;
skpz ;
goto F_ERROR ;
movlw a'5' ; test third data byte
subwf rx_buffer04,w ;
skpz ;
goto F_0.25 ;
movlw rx_buffer04 ;
subwf rx_maxpos,w ; test length
skpz ;
goto F_ERROR ;
movlw b'00111111' ;
clrc ; clear carry to indicate result is ok
return
F_0.25 movlw a'2' ; test third data byte
subwf rx_buffer04,w ;
skpz ;
goto F_ERROR ;
movlw a'5' ; test fourth data byte
subwf rx_buffer05,w ;
skpz ;
goto F_ERROR ;
movlw rx_buffer05 ;
subwf rx_maxpos,w ; test length
skpz ;
goto F_ERROR ;
movlw b'01111111' ;
clrc ; clear carry to indicate result is ok
return
F_ERROR setc ; none of the values matched
return ; return with carry set as indication result is not ok
;--------------------------------------------------------------------------------------------------------------------------------------
; Commands:
;--------------------------------------------------------------------------------------------------------------------------------------
COMMANDS ; label to allow use of pagesel
;--------------------------------------------------------------------------------------------------------------------------------------
EXIT_ACK movlw h'06' ; acknowledgement (ACK)
call TX_BYTE ; send 'command has been accepted', return in bank0
bcf flags1,command ; now command has been handled we can clear the execute flag, ready for next command
bcf portc,cts_out ; set CTS to re-enable data stream from computer
bsf portb,led_red ; turn on red status led
bcf portb,led_green ; turn off green status led
return ; done
;--------------------------------------------------------------------------------------------------------------------------------------
EXIT_NAK movlw h'15' ; unknown command, exit, send negative acknowledgement (NAK)
call TX_BYTE ; send 'command has NOT been accepted', return in bank0
bcf flags1,command ; clear any command bytes received up to now get, ready for next command
bcf portc,cts_out ; set CTS to re-enable data stream from computer
bsf portb,led_red ; turn on red status led
bcf portb,led_green ; turn off green status led
return ; go back to main program loop see what else we can do
;--------------------------------------------------------------------------------------------------------------------------------------
AU ; read the string with the name of the author of this software uses a table read that works
; anywhere in the memory. A normal table read using just the 'addwf pcl' instruction will only
; work if the program code and table code are entirely in the first 8-bit page of program
; memory = the first 256 memory locations !
; a write opens the backdoor for the serial number write (command SE)
btfsc flags1,withdata ; is there any data ?
goto AU_COMMAND ; yes, so we should use this data
call TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
bank3 ;
clrf tablepointer ; start the reading of the table at the first position
AU_LOOP movlw high AU_TABLE ; see where the assembler will put the table in PIC memory and get the high byte
movwf pclath ; now move this into pclath
movlw low AU_TABLE+1 ; load w with the memory address low byte of the first piece of data in the table
addwf tablepointer,w ; calculate the offset address and see if it overflows
skpnc ; did it overflow ?
incf pclath,f ; yes, so increase pclath one count
AU_GET_CHAR call AU_TABLE ; no, lookup value in table, returns ascii character in w register
xorlw d'00' ; zero indicates end of table
skpnz ; are we at the end of the table ?
goto TX_COMMANDEND ; yes, exit: send checksum and ETX, clear command flag
call TX_BYTE ; returns in bank0
bank3 ;
incf tablepointer,f ; point to next position in table
goto AU_LOOP ;
AU_TABLE movwf pcl ; w contains low program counter byte and points to the next location plus counter
retlw a'A' ; offset text in table is 'Andries C. Tip'
retlw a'n' ;
retlw a'd' ; Note: in a Microchip application note it states that it is neccesary to disable the
retlw a'r' ; interrupts before executing the instruction movwf pcl because an interrupt during
retlw a'i' ; this instruction may cause the program to jump to an unknown address. It turnes
retlw a'e' ; out to be not true, so table reads do not have to disable any interrupts.
retlw a's' ;
retlw a' ' ;
retlw a'C' ;
retlw a'.' ;
retlw a' ' ;
retlw a'T' ;
retlw a'i' ;
retlw a'p' ;
retlw d'00' ; end of message
AU_COMMAND bsf flags1,changese ; backdoor to allow serial number to be changed
goto EXIT_NAK ; exit and be ready for next command
;--------------------------------------------------------------------------------------------------------------------------------------
CA ; read/write value of static ram address (value 0..63)
; write: input is ascii string with 8 bit number
btfsc flags1,withdata ; is there any data ?
goto CA_COMMAND ; yes, so we should use this data
CA_REQUEST call TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
bank2 ;
movf clockaddr,w ;
call TX_NUMBER ; transmits the value of w as ascii characters
goto TX_COMMANDEND ; send checksum and ETX, clear command status byte
CA_COMMAND call GET_NUMBER ; read the data input number that goes with the command into the w register
skpnc ; errors reading number ?
goto EXIT_NAK ; yes, exit and be ready for next command
andlw b'11000000' ; the maximum address for the clock chip static ram and control bytes is 63
skpz ; is the address value valid ?
goto EXIT_NAK ; no, exit and be ready for next command
call GET_NUMBER ; again, read the data input number that goes with the command into the w register
bank2 ;
movwf clockaddr ;
goto EXIT_ACK ; done
;--------------------------------------------------------------------------------------------------------------------------------------
CD ; IIC clock chip serial control bytes and static ram read/ write
btfsc flags1,withdata ; is there any data ?
goto CD_COMMAND ; yes, so we should use this data
CD_REQUEST call TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
pagesel IIC_RD_CLKCHIP ; make right program memory page selection
call IIC_RD_CLKCHIP ; get data byte from clock chip into w register
pagesel TX_NUMBER ; make right program memory page selection
call TX_NUMBER ; transmits the value of w as ascii characters
goto TX_COMMANDEND ; send checksum and ETX, clear command status byte
CD_COMMAND call GET_NUMBER ; read the data input number that goes with the command into the w register
skpnc ; errors reading number ?
goto EXIT_NAK ; yes, exit and be ready for next command
pagesel IIC_WR_CLKCHIP ; make right program memory page selection
call IIC_WR_CLKCHIP ; write byte to static ram
pagesel EXIT_ACK ; make right program memory page selection
goto EXIT_ACK ; done
;--------------------------------------------------------------------------------------------------------------------------------------
CL ; read/set clock time and data in the following format HH:MM:SS MM/DD/YY
; external clock chip is Dallas DS1307 (www.dalsemi.com)
btfsc flags1,withdata ; is there any data ?
goto CL_COMMAND ; yes, so we should use this data to write to the clock chip through IIC
CL_REQUEST call TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
pagesel IIC_RDCLOCK ; make right program memory page selection
call IIC_RDCLOCK ; read the eight time related bytes from external clock chip into buffer (bank2, 68..6F hex)
pagesel TX_TIMEANDDATE ; make right program memory page selection
call TX_TIMEANDDATE ; send time and date values from the buffer in bank2 as a string, bank2 return
goto TX_COMMANDEND ; send checksum and ETX, clear command flag
CL_COMMAND call GET_TIMEANDDATE ; convert string serial input buffer, store in time and date buffer in bank2, bank2 return
pagesel IIC_WRCLOCK ; make right program memory page selection
call IIC_WRCLOCK ; write the eight time related bytes from buffer in bank2 (68..6F) to clock
pagesel EXIT_ACK ; make right program memory page selection
goto EXIT_ACK ; done
;--------------------------------------------------------------------------------------------------------------------------------------
EA ; set eeprom read/write address
; write: input string is 8 bit address
btfsc flags1,withdata ; is there any data ?
goto EA_COMMAND ; yes, so we should use this data to write to the eeprom location
EA_REQUEST call TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
bank2 ;
movf iee_address,w ; get address of eeprom location
bank0 ;
call TX_NUMBER ; transmits the value of w as ascii characters
goto TX_COMMANDEND ; send checksum and ETX, clear command status byte
EA_COMMAND call GET_NUMBER ; read the data input number that goes with the command into the w register
skpnc ; errors reading number ?
goto EXIT_NAK ; yes, exit and be ready for next command
bank2 ;
movwf iee_address ; store the number for later use with command ED
bank0 ;
goto EXIT_ACK ; done
;--------------------------------------------------------------------------------------------------------------------------------------
ED ; read/write eeprom memory location
; write: input is ascii string with 8 bit number
btfsc flags1,withdata ; is there any data ?
goto ED_COMMAND ; yes, so we should use this data to write to the eeprom location
ED_REQUEST call TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
bank2 ;
movf iee_address,w ; get address of eeprom location
bank0 ;
pagesel IEE_READ ; make right program memory page selection
call IEE_READ ; read value from internal eeprom, address in w, data returned in w, return in bank2
pagesel TX_NUMBER ; make right program memory page selection
call TX_NUMBER ; transmits the value of w as ascii characters
goto TX_COMMANDEND ; send checksum and ETX, clear command status byte
ED_COMMAND call GET_NUMBER ; read the data input number that goes with the command into the w register
skpnc ; errors reading number ?
goto EXIT_NAK ; yes, exit and be ready for next command
pagesel IEE_WRITE ; no, make right program memory page selection
call IEE_WRITE ; write data to eeprom, address in iee_address, data in w
pagesel EXIT_ACK ; make right program memory page selection
goto EXIT_ACK ; done
;--------------------------------------------------------------------------------------------------------------------------------------
ER ; read/write error flags byte
; write: input is ascii string with 8 bit number
btfsc flags1,withdata ; is there any data ?
goto ER_COMMAND ; yes, so we should use this data to write to the eeprom location
ER_REQUEST call TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
bank3 ;
movf errors,w ; get value of error flags register
call TX_NUMBER ; transmits the value of w as ascii characters
goto TX_COMMANDEND ; send checksum and ETX, clear command status byte
ER_COMMAND movlw d'15' ; eeprom address for error flags register
bank2 ;
movwf iee_address ; set internal eeprom address pointer
call GET_NUMBER ; read the data input number that goes with the command into the w register
skpnc ; errors reading number ?
goto EXIT_NAK ; yes, exit and be ready for next command
movwf errors ; write value to error flags register
pagesel IEE_WRITE ; make right program memory page selection
call IEE_WRITE ; write data to eeprom, address in iee_address, data in w
pagesel EXIT_ACK ; make right program memory page selection
goto EXIT_ACK ; done
;--------------------------------------------------------------------------------------------------------------------------------------
FA ; read/write air temperature logging frequency from/to pic eeprom
btfsc flags1,withdata ; is there any data ?
goto FA_COMMAND ; yes, so we should use this data to write to the eeprom location
FA_REQUEST call TX_COMMANDSTART ; send ACK, STX and command mnemonics
bank3 ;
movf freq_air,w ; get value of lograte for air temperature channel
call TX_FREQVALSTR ; transmit value as string, return in bank0
goto TX_COMMANDEND ; send checksum and ETX, clear command status byte
FA_COMMAND movlw d'21' ; eeprom address for log frequency setting
bank2 ;
movwf iee_address ; set internal eeprom address pointer
call GET_FREQVALNUM ; read the value that goes with the command, convert it and put it in w register
skpnc ; were there any errors reading the value ?
goto EXIT_NAK ; yes, exit
bank3 ; no,
movwf freq_air ; copy value
pagesel IEE_WRITE ; make right program memory page selection
call IEE_WRITE ; write data to eeprom, address in iee_address, data in w
pagesel EXIT_ACK ; make right program memory page selection
goto EXIT_ACK ; done
;--------------------------------------------------------------------------------------------------------------------------------------
FB ; read/write brake switch logging frequency from/to pic eeprom
btfsc flags1,withdata ; is there any data ?
goto FB_COMMAND ; yes, so we should use this data to write to the eeprom location
FB_REQUEST call TX_COMMANDSTART ; send ACK, STX and command mnemonics
bank3 ;
movf freq_brake,w ; get value of lograte for brake switch channel
call TX_FREQVALSTR ; transmit value as string, return in bank0
goto TX_COMMANDEND ; send checksum and ETX, clear command status byte
FB_COMMAND movlw d'27' ; eeprom address for log frequency setting
bank2 ;
movwf iee_address ; set internal eeprom address pointer
call GET_FREQVALNUM ; read the value that goes with the command, convert it and put it in w register
skpnc ; were there any errors reading the value ?
goto EXIT_NAK ; yes, exit
bank3 ; no,
movwf freq_brake ; copy value
pagesel IEE_WRITE ; make right program memory page selection
call IEE_WRITE ; write data to eeprom, address in iee_address, data in w
pagesel EXIT_ACK ; make right program memory page selection
goto EXIT_ACK ; done
;--------------------------------------------------------------------------------------------------------------------------------------
FE ; read/write lateral acceleration logging frequency from/to pic eeprom
btfsc flags1,withdata ; is there any data ?
goto FE_COMMAND ; yes, so we should use this data to write to the eeprom location
FE_REQUEST call TX_COMMANDSTART ; send ACK, STX and command mnemonics
bank3 ;
movf freq_lat,w ; get value of lograte for lateral acceleration channel
call TX_FREQVALSTR ; transmit value as string, return in bank0
goto TX_COMMANDEND ; send checksum and ETX, clear command status byte
FE_COMMAND movlw d'25' ; eeprom address for log frequency setting
bank2 ;
movwf iee_address ; set internal eeprom address pointer
call GET_FREQVALNUM ; read the value that goes with the command, convert it and put it in w register
skpnc ; were there any errors reading the value ?
goto EXIT_NAK ; yes, exit
bank3 ; no,
movwf freq_lat ; copy value
pagesel IEE_WRITE ; make right program memory page selection
call IEE_WRITE ; write data to eeprom, address in iee_address, data in w
pagesel EXIT_ACK ; make right program memory page selection
goto EXIT_ACK ; done
;--------------------------------------------------------------------------------------------------------------------------------------
FH ; read/write thermocouple logging frequency from/to pic eeprom
btfsc flags1,withdata ; is there any data ?
goto FH_COMMAND ; yes, so we should use this data to write to the eeprom location
FH_REQUEST call TX_COMMANDSTART ; send ACK, STX and command mnemonics
bank3 ;
movf freq_tc,w ; get value of lograte for thermocouple channel
call TX_FREQVALSTR ; transmit value as string, return in bank0
goto TX_COMMANDEND ; send checksum and ETX, clear command status byte
FH_COMMAND movlw d'20' ; eeprom address for log frequency setting
bank2 ;
movwf iee_address ; set internal eeprom address pointer
call GET_FREQVALNUM ; read the value that goes with the command, convert it and put it in w register
skpnc ; were there any errors reading the value ?
goto EXIT_NAK ; yes, exit
bank3 ; no,
movwf freq_tc ; copy value
pagesel IEE_WRITE ; make right program memory page selection
call IEE_WRITE ; write data to eeprom, address in iee_address, data in w
pagesel EXIT_ACK ; make right program memory page selection
goto EXIT_ACK ; done
;--------------------------------------------------------------------------------------------------------------------------------------
FL ; read/write lambda logging frequency from/to pic eeprom
btfsc flags1,withdata ; is there any data ?
goto FL_COMMAND ; yes, so we should use this data to write to the eeprom location
FL_REQUEST call TX_COMMANDSTART ; send ACK, STX and command mnemonics
bank3 ;
movf freq_lambda,w ; get value of lograte for lambda channel
call TX_FREQVALSTR ; transmit value as string, return in bank0
goto TX_COMMANDEND ; send checksum and ETX, clear command status byte
FL_COMMAND movlw d'18' ; eeprom address for log frequency setting
bank2 ;
movwf iee_address ; set internal eeprom address pointer
call GET_FREQVALNUM ; read the value that goes with the command, convert it and put it in w register
skpnc ; were there any errors reading the value ?
goto EXIT_NAK ; yes, exit
bank3 ; no,
movwf freq_lambda ; copy value
pagesel IEE_WRITE ; make right program memory page selection
call IEE_WRITE ; write data to eeprom, address in iee_address, data in w
pagesel EXIT_ACK ; make right program memory page selection
goto EXIT_ACK ; done
;--------------------------------------------------------------------------------------------------------------------------------------
FM ; read/write mark switch logging frequency from/to pic eeprom
btfsc flags1,withdata ; is there any data ?
goto FM_COMMAND ; yes, so we should use this data to write to the eeprom location
FM_REQUEST call TX_COMMANDSTART ; send ACK, STX and command mnemonics
bank3 ;
movf freq_mark,w ; get value of lograte for mark switch channel
call TX_FREQVALSTR ; transmit value as string, return in bank0
goto TX_COMMANDEND ; send checksum and ETX, clear command status byte
FM_COMMAND movlw d'26' ; eeprom address for log frequency setting
bank2 ;
movwf iee_address ; set internal eeprom address pointer
call GET_FREQVALNUM ; read the value that goes with the command, convert it and put it in w register
skpnc ; were there any errors reading the value ?
goto EXIT_NAK ; yes, exit
bank3 ; no,
movwf freq_mark ; copy value
pagesel IEE_WRITE ; make right program memory page selection
call IEE_WRITE ; write data to eeprom, address in iee_address, data in w
pagesel EXIT_ACK ; make right program memory page selection
goto EXIT_ACK ; done
;--------------------------------------------------------------------------------------------------------------------------------------
FO ; read/write longitudinal acceleration logging frequency from/to pic eeprom
btfsc flags1,withdata ; is there any data ?
goto FO_COMMAND ; yes, so we should use this data to write to the eeprom location
FO_REQUEST call TX_COMMANDSTART ; send ACK, STX and command mnemonics
bank3 ;
movf freq_long,w ; get value of lograte for longitudinal acceleration channel
call TX_FREQVALSTR ; transmit value as string, return in bank0
goto TX_COMMANDEND ; send checksum and ETX, clear command status byte
FO_COMMAND movlw d'24' ; eeprom address for log frequency setting
bank2 ;
movwf iee_address ; set internal eeprom address pointer
call GET_FREQVALNUM ; read the value that goes with the command, convert it and put it in w register
skpnc ; were there any errors reading the value ?
goto EXIT_NAK ; yes, exit
bank3 ; no,
movwf freq_long ; copy value
pagesel IEE_WRITE ; make right program memory page selection
call IEE_WRITE ; write data to eeprom, address in iee_address, data in w
pagesel EXIT_ACK ; make right program memory page selection
goto EXIT_ACK ; done
;--------------------------------------------------------------------------------------------------------------------------------------
FR ; read/write rpm logging frequency from/to pic eeprom
btfsc flags1,withdata ; is there any data ?
goto FR_COMMAND ; yes, so we should use this data to write to the eeprom location
FR_REQUEST call TX_COMMANDSTART ; send ACK, STX and command mnemonics
bank3 ;
movf freq_rpm,w ; get value of lograte for rpm channel
call TX_FREQVALSTR ; transmit value as string, return in bank0
goto TX_COMMANDEND ; send checksum and ETX, clear command status byte
FR_COMMAND movlw d'16' ; eeprom address for log frequency setting
bank2 ;
movwf iee_address ; set internal eeprom address pointer
call GET_FREQVALNUM ; read the value that goes with the command, convert it and put it in w register
skpnc ; were there any errors reading the value ?
goto EXIT_NAK ; yes, exit
bank3 ; no,
movwf freq_rpm ; copy value
pagesel IEE_WRITE ; make right program memory page selection
call IEE_WRITE ; write data to eeprom, address in iee_address, data in w
pagesel EXIT_ACK ; make right program memory page selection
goto EXIT_ACK ; done
;--------------------------------------------------------------------------------------------------------------------------------------
FS ; read/write speed logging frequency from/to pic eeprom
btfsc flags1,withdata ; is there any data ?
goto FS_COMMAND ; yes, so we should use this data to write to the eeprom location
FS_REQUEST call TX_COMMANDSTART ; send ACK, STX and command mnemonics
bank3 ;
movf freq_speed,w ; get value of lograte for speed channel
call TX_FREQVALSTR ; transmit value as string, return in bank0
goto TX_COMMANDEND ; send checksum and ETX, clear command status byte
FS_COMMAND movlw d'17' ; eeprom address for log frequency setting
bank2 ;
movwf iee_address ; set internal eeprom address pointer
call GET_FREQVALNUM ; read the value that goes with the command, convert it and put it in w register
skpnc ; were there any errors reading the value ?
goto EXIT_NAK ; yes, exit
bank3 ; no,
movwf freq_speed ; copy value
pagesel IEE_WRITE ; make right program memory page selection
call IEE_WRITE ; write data to eeprom, address in iee_address, data in w
pagesel EXIT_ACK ; make right program memory page selection
goto EXIT_ACK ; done
;--------------------------------------------------------------------------------------------------------------------------------------
FT ; read/write throttle logging frequency from/to pic eeprom
btfsc flags1,withdata ; is there any data ?
goto FT_COMMAND ; yes, so we should use this data to write to the eeprom location
FT_REQUEST call TX_COMMANDSTART ; send ACK, STX and command mnemonics
bank3 ;
movf freq_throttle,w ; get value of lograte for throttle channel
call TX_FREQVALSTR ; transmit value as string, return in bank0
goto TX_COMMANDEND ; send checksum and ETX, clear command status byte
FT_COMMAND movlw d'23' ; eeprom address for log frequency setting
bank2 ;
movwf iee_address ; set internal eeprom address pointer
call GET_FREQVALNUM ; read the value that goes with the command, convert it and put it in w register
skpnc ; were there any errors reading the value ?
goto EXIT_NAK ; yes, exit
bank3 ; no,
movwf freq_throttle ; copy value
pagesel IEE_WRITE ; make right program memory page selection
call IEE_WRITE ; write data to eeprom, address in iee_address, data in w
pagesel EXIT_ACK ; make right program memory page selection
goto EXIT_ACK ; done
;--------------------------------------------------------------------------------------------------------------------------------------
FV ; read/write voltage logging frequency from/to pic eeprom
btfsc flags1,withdata ; is there any data ?
goto FV_COMMAND ; yes, so we should use this data to write to the eeprom location
FV_REQUEST call TX_COMMANDSTART ; send ACK, STX and command mnemonics
bank3 ;
movf freq_voltage,w ; get value of lograte for voltage channel
call TX_FREQVALSTR ; transmit value as string, return in bank0
goto TX_COMMANDEND ; send checksum and ETX, clear command status byte
FV_COMMAND movlw d'19' ; eeprom address for log frequency setting
bank2 ;
movwf iee_address ; set internal eeprom address pointer
call GET_FREQVALNUM ; read the value that goes with the command, convert it and put it in w register
skpnc ; were there any errors reading the value ?
goto EXIT_NAK ; yes, exit
bank3 ; no,
movwf freq_voltage ; copy value
pagesel IEE_WRITE ; make right program memory page selection
call IEE_WRITE ; write data to eeprom, address in iee_address, data in w
pagesel EXIT_ACK ; make right program memory page selection
goto EXIT_ACK ; done
;--------------------------------------------------------------------------------------------------------------------------------------
FW ; read/write water temperature logging frequency from/to pic eeprom
btfsc flags1,withdata ; is there any data ?
goto FW_COMMAND ; yes, so we should use this data to write to the eeprom location
FW_REQUEST call TX_COMMANDSTART ; send ACK, STX and command mnemonics
bank3 ;
movf freq_water,w ; get value of lograte for water temperature channel
call TX_FREQVALSTR ; transmit value as string, return in bank0
goto TX_COMMANDEND ; send checksum and ETX, clear command status byte
FW_COMMAND movlw d'22' ; eeprom address for log frequency setting
bank2 ;
movwf iee_address ; set internal eeprom address pointer
call GET_FREQVALNUM ; read the value that goes with the command, convert it and put it in w register
skpnc ; were there any errors reading the value ?
goto EXIT_NAK ; yes, exit
bank3 ; no,
movwf freq_water ; copy value
pagesel IEE_WRITE ; make right program memory page selection
call IEE_WRITE ; write data to eeprom, address in iee_address, data in w
pagesel EXIT_ACK ; make right program memory page selection
goto EXIT_ACK ; done
;--------------------------------------------------------------------------------------------------------------------------------------
ID ; read the identification string explaining the application of this PIC chip
; uses a table read that works anywhere in the memory.
; A normal table read using just the 'addwf pcl' instruction will only work if the
; program code and table code are entirely in the first 8-bit page of program
; memory = the first 256 memory locations !
btfsc flags1,withdata ; is there any data ?
goto EXIT_NAK ; yes, exit
call TX_COMMANDSTART ; send ACK, STX and command mnemonics
bank3 ;
clrf tablepointer ; start the reading of the table at the first position
ID_LOOP movlw high ID_TABLE ; see where the assembler will put the table in PIC memory and get the high byte
movwf pclath ; now move this into pclath
movlw low ID_TABLE+1 ; load w with the memory address low byte of the first piece of data in the table
addwf tablepointer,w ; calculate the offset address and see if it overflows
skpnc ; did it overflow ?
incf pclath,f ; yes, so increase pclath one count
ID_GET_CHAR call ID_TABLE ; no, lookup value in table, returns ascii character in w register
xorlw d'00' ; zero indicates end of table
skpnz ; are we at the end of the table ?
goto TX_COMMANDEND ; yes, exit: send checksum and ETX, clear command status byte
call TX_BYTE ; returns in bank0
bank3 ;
incf tablepointer,f ; point to next position in table
goto ID_LOOP ;
ID_TABLE movwf pcl ; w contains low program counter byte and points to the next location plus counter
retlw a'M' ; offset text in table is 'Motorcycle Datalogger'
retlw a'o' ;
retlw a't' ; Note: in a Microchip application note it states that it is neccesary to disable the
retlw a'o' ; interrupts before executing the instruction movwf pcl because an interrupt during
retlw a'r' ; this instruction may cause the program to jump to an unknown address. It turnes
retlw a'c' ; out to be not true, so table reads do not have to disable any interrupts.
retlw a'y' ;
retlw a'c' ;
retlw a'l' ;
retlw a'e' ;
retlw a' ' ;
retlw a'D' ;
retlw a'a' ;
retlw a't' ;
retlw a'a' ;
retlw a'l' ;
retlw a'o' ;
retlw a'g' ;
retlw a'g' ;
retlw a'e' ;
retlw a'r' ;
retlw d'00' ; end of message
;--------------------------------------------------------------------------------------------------------------------------------------
KC ; read/write wheel circumference [mm] to/from pic eeprom
; value is used in speed calculation
; write: input is ascii string with 16 bit number
btfsc flags1,withdata ; is there any data ?
goto KC_COMMAND ; yes, so we should use this data
KC_REQUEST call TX_COMMANDSTART ; send ACK, STX and command mnemonics
movlw d'0' ; eeprom address 0
bank2 ;
movwf iee_address ; set address of pic eeprom location
pagesel IEE_READ ; make right program memory page selection
call IEE_READ ; read value from internal eeprom, address in w, data returned in w, return in bank2
movwf numlow ;
movlw d'1' ; eeprom address 1
movwf iee_address ; set address of pic eeprom location
call IEE_READ ; read value from internal eeprom, address in w, data returned in w, return in bank2
movwf nummiddle ;
pagesel TX_LNUMBER ; make right program memory page selection
call TX_LNUMBER ; transmits the value of numlow and nummiddle as ascii characters
goto TX_COMMANDEND ; send checksum and ETX, clear command status byte
KC_COMMAND call GET_LNUMBER ; read the data input number that goes with the command into the numlow and nummiddle register
skpnc ; errors reading number ?
goto EXIT_NAK ; yes, exit and be ready for next command
movlw d'0' ;
bank2 ;
movwf iee_address ; set address of pic eeprom location
movf numlow,w ;
pagesel IEE_WRITE ; make right program memory page selection
call IEE_WRITE ; write data to eeprom, address in iee_address, data in w
movlw d'1' ;
bank2 ;
movwf iee_address ; set address of pic eeprom location
movf nummiddle,w ;
call IEE_WRITE ; write data to eeprom, address in iee_address, data in w
pagesel COPY_WHEELC ; make right program memory page selection
call COPY_WHEELC ; *** do not update while logging/calculate constant from value
pagesel EXIT_ACK ; make right program memory page selection
goto EXIT_ACK ; done
;--------------------------------------------------------------------------------------------------------------------------------------
KP ; read/write pulses per revolution to/from pic eeprom
; value is used in rpm calculation
; write: input is ascii string with 8 bit number (only values 1 and 2 are allowed)
btfsc flags1,withdata ; is there any data ?
goto KP_COMMAND ; yes, so we should use this data
KP_REQUEST call TX_COMMANDSTART ; send ACK, STX and command mnemonics
movlw d'4' ; eeprom address 4
bank2 ;
movwf iee_address ; set address of pic eeprom location
pagesel IEE_READ ; make right program memory page selection
call IEE_READ ; read value from internal eeprom, address in w, data returned in w, return in bank2
pagesel TX_NUMBER ; make right program memory page selection
call TX_NUMBER ; transmits the value of w register as ascii characters
goto TX_COMMANDEND ; send checksum and ETX, clear command status byte
KP_COMMAND call GET_NUMBER ; read the data input number that goes with the command into the w register
skpnc ; errors reading number ?
goto EXIT_NAK ; yes, exit and be ready for next command
sublw d'1' ; no, the number should be one or two
KP_TESTONE skpnz ; is the value one ?
goto KP_VALUEOK ; yes, go store value
call GET_NUMBER ; no, read the data input number that goes with the command into the w register
sublw d'2' ; the number should be one or two
KP_TESTTWO skpz ; is the value two ?
goto EXIT_NAK ; no, exit and be ready for next command
KP_VALUEOK movlw d'4' ; yes, eeprom address 4
bank2 ;
movwf iee_address ; set address of pic eeprom location
call GET_NUMBER ; read the data input number that goes with the command into the w register
pagesel IEE_WRITE ; make right program memory page selection
call IEE_WRITE ; write data to eeprom, address in iee_address, data in w
pagesel COPY_RPMVALUES ; make right program memory page selection
call COPY_RPMVALUES ; copy the value from eeprom to pic registers for use during the ccp interrupt
pagesel EXIT_ACK ; make right program memory page selection
goto EXIT_ACK ; done
;--------------------------------------------------------------------------------------------------------------------------------------
KR ; set or read the maximum RPM rate, above this RPM rate, the shift light will be
; turned on when the measured rpm value is more than this maximum, otherwise the shift
; light is off
; write: input is ascii string with a number of not more than 14 bits (0..16383)
btfsc flags1,withdata ; is there any data ?
goto KR_COMMAND ; yes, so we should use this data
KR_REQUEST call TX_COMMANDSTART ; send ACK, STX and command mnemonics
movlw d'2' ; eeprom address for rpm maximum low byte
pagesel IEE_READ ; make right program memory page selection
call IEE_READ ; read value from internal eeprom, address in w, data returned in w, return in bank2
movwf numlow ;
movlw d'3' ; eeprom address for rpm maximum high byte
call IEE_READ ; read value from internal eeprom, address in w, data returned in w, return in bank2
movwf nummiddle ;
pagesel TX_LNUMBER ; make right program memory page selection
call TX_LNUMBER ; transmits the value of numlow and nummiddle as ascii characters
goto TX_COMMANDEND ; send checksum and ETX, clear command status byte
KR_COMMAND call GET_LNUMBER ; read the data input number that goes with the command into the numlow and nummiddle register
skpnc ; errors reading number ?
goto EXIT_NAK ; yes, exit and be ready for next command
movlw b'11000000' ; value for test
andwf nummiddle,w ; test if the upper two bits of the received number are ones
skpz ; is the received number larger than 16383 ?
goto EXIT_NAK ; yes, so it will not fit in 14 bits, exit and be ready for next command
movlw d'2' ; no, continue, this is the eeprom address for the rpm maximum low byte
bank2 ;
movwf iee_address ; select eeprom write address
movf numlow,w ;
pagesel IEE_WRITE ; make right program memory page selection
call IEE_WRITE ; write data to eeprom, address in iee_address, data in w
movlw d'3' ; eeprom address for the rpm maximum high byte
bank2 ;
movwf iee_address ; select eeprom write address
movf nummiddle,w ;
call IEE_WRITE ; write data to eeprom, address in iee_address, data in w
pagesel COPY_RPMVALUES ; make right program memory page selection
call COPY_RPMVALUES ; copy the value from eeprom to pic registers for use during the ccp interrupt
pagesel EXIT_ACK ; make right program memory page selection
goto EXIT_ACK ; done
;--------------------------------------------------------------------------------------------------------------------------------------
LS ; **** select start condition for logging (RPM, mark button...)
; input value is eight bit number
btfsc flags1,withdata ; is there any data ?
goto LS_COMMAND ; yes, so we should use this data
LS_REQUEST call TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
movlw d'0' ; **** dummy value
call TX_NUMBER ; transmits the value of w as ascii characters
goto TX_COMMANDEND ; send checksum and ETX, clear command status byte
LS_COMMAND call GET_NUMBER ; read the data input number that goes with the command into the w register
skpnc ; errors reading number ?
goto EXIT_NAK ; yes, exit and be ready for next command
nop ; **** store the value in the proper place
goto EXIT_ACK ; done
;--------------------------------------------------------------------------------------------------------------------------------------
LV ; **** select value for start condition for logging (when RPM, Speed...)
; input value is 16 bit, use 14 bits for RPM or 8 bits for speed
btfsc flags1,withdata ; is there any data ?
goto LV_COMMAND ; yes, so we should use this data
LV_REQUEST call TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
movlw d'0' ; **** dummy value
movwf numlow ;
movwf nummiddle ;
call TX_LNUMBER ; transmits the value of numlow and nummiddle as ascii characters
goto TX_COMMANDEND ; send checksum and ETX, clear command status byte
LV_COMMAND call GET_LNUMBER ; read the data input number that goes with the command into numlow and nummiddle registers
skpnc ; errors reading number ?
goto EXIT_NAK ; yes, exit and be ready for next command
;nop ; **** store the value in the proper place
goto EXIT_ACK ; done
;--------------------------------------------------------------------------------------------------------------------------------------
RC ; clear all record memory
btfsc flags1,withdata ; is there any data ?
goto EXIT_NAK ; yes, exit
RC_COMMAND ; empty line to prevent illegal label error from MPLAB IDE
pagesel MEM_CLEAR ; make right program memory page selection
call MEM_CLEAR ; clear all bytes of the table of contents, return in bank3
pagesel EXIT_ACK ; make right program memory page selection
goto EXIT_ACK ; done
;--------------------------------------------------------------------------------------------------------------------------------------
RD ; download record data in blocks of 64 bytes
btfsc flags1,withdata ; is there any data ?
goto EXIT_NAK ; yes, exit
RD_REQUEST bank3 ; check if there are any records
movf current_rec,w ; get currently selected record number, zero for no selection yet or when no records available
skpnz ; are there any records and has there been a record selected yet ?
goto EXIT_NAK ; no, exit
pagesel MEM_RDBLOCK ; yes, make right program memory page selection
call MEM_RDBLOCK ; copy data block to buffer in bank1, increase blocknumber, carry set when all blocks were done
pagesel EXIT_NAK ; make right program memory page selection
RD_RI_CODE skpnc ; have we sent all blocks ? (from here same code for RD and RI commands)
goto EXIT_NAK ; yes, exit
RD_TRANSMIT call TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
bcf status,irp ; make sure upper bit in address is zero (select bank 0 and 1)
movlw blockbuff00 ; start position of data in buffer in bank1
movwf fsr ; point to this address
RD_LOOP bank1 ; the buffer is in bank 1
movf indf,w ; read the data byte at the right position in the buffer
call TX_BYTE ; transmit the value of the w register, return in bank0
incf fsr,f ; increase pointer to point to next value
movf fsr,w ; get pointer value
sublw blockbuff63 ; was this the last byte ?
skpnc ; let's see
goto RD_LOOP ; no, do loop again and read next data byte
goto TX_COMMANDEND ; no, send checksum and ETX, clear command status byte
;--------------------------------------------------------------------------------------------------------------------------------------
RI ; increment block number, then download record data in blocks of 64 bytes
btfsc flags1,withdata ; is there any data ?
goto EXIT_NAK ; yes, exit
RI_REQUEST bank3 ; check if there are any records
movf current_rec,w ; get currently selected record number, zero for no selection yet or when no records available
skpnz ; are there any records and has there been a record selected yet ?
goto EXIT_NAK ; no, exit
pagesel MEM_INC_RDBLOCK ; yes, make right program memory page selection
call MEM_INC_RDBLOCK ; copy data block to buffer in bank1, increase blocknumber, carry set when all blocks were done
pagesel RD_RI_CODE ; make right program memory page selection
goto RD_RI_CODE ; from here same code as for RD command, save space
;--------------------------------------------------------------------------------------------------------------------------------------
RM ; return the percentage of memory that is used
; value 0..100%
btfsc flags1,withdata ; is there any data ?
goto EXIT_NAK ; yes, exit
RM_REQUEST call TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
pagesel MEM_USAGE ; make right program memory page selection
call MEM_USAGE ; **** get the percentage of used space of external eeprom memory into w register, bank? return
pagesel TX_NUMBER ; make right program memory page selection
call TX_NUMBER ; transmits the value of w as ascii characters
goto TX_COMMANDEND ; send checksum and ETX, clear command status byte
;--------------------------------------------------------------------------------------------------------------------------------------
RN ; number of records in datalogger memory
; value 0..20
btfsc flags1,withdata ; is there any data ?
goto EXIT_NAK ; yes, exit
RN_REQUEST call TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
pagesel MEM_GETNUMRECS ; make right program memory page selection
call MEM_GETNUMRECS ; get the number of records stored in memory, value stored in num_records, bank1/3 return
bank3 ;
movf num_records,w ; copy the number of records
pagesel TX_NUMBER ; make right program memory page selection
call TX_NUMBER ; transmits the value of w as ascii characters
goto TX_COMMANDEND ; send checksum and ETX, clear command status byte
;--------------------------------------------------------------------------------------------------------------------------------------
RS ; select one record for downloading and reset the block download position to the start of
; this record
; valid value range is 1..number of available records (maximum 20)
btfsc flags1,withdata ; is there any data ?
goto RS_COMMAND ; yes, so we should use this data
RS_REQUEST call TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
bank3 ;
movf current_rec,w ; get the number of the currently selected record
call TX_NUMBER ; transmits the value of w as ascii characters
goto TX_COMMANDEND ; send checksum and ETX, clear command status byte
RS_COMMAND ; empty line to prevent illegal label error from MPLAB IDE
pagesel MEM_GETNUMRECS ; make right program memory page selection
call MEM_GETNUMRECS ; get the number of records stored in memory, value stored in num_records, bank1/3 return
pagesel GET_NUMBER ; make right program memory page selection
call GET_NUMBER ; read the data input number that goes with the command into the w register
skpnc ; errors reading number ?
goto EXIT_NAK ; yes, exit and be ready for next command
iorlw d'0' ; see if number is zero without changing w register, the number should not be less than one
skpnz ; is the number zero ?
goto EXIT_NAK ; yes, exit
bank3 ; no, continue
subwf num_records,w ; see if the new value is valid
skpc ; is the new value more than the available number of records ?
goto EXIT_NAK ; yes, do not update value and exit, be ready for next command
call GET_NUMBER ; no, again read the data input number that goes with the command into the w register
pagesel MEM_SELECTREC ; make right program memory page selection
call MEM_SELECTREC ; select record for downloading by value in w register and reset the block download pointer
pagesel EXIT_ACK ; make right program memory page selection
goto EXIT_ACK ; done
;--------------------------------------------------------------------------------------------------------------------------------------
RT ; read start time and date of the currently selected record
; in the following format HH:MM:SS MM/DD/YY
btfsc flags1,withdata ; is there any data ?
goto EXIT_NAK ; yes, exit
RT_REQUEST bank3 ; no, check current record selection
movf current_rec,w ; test for zero value
skpnz ; are there any records and is there already a valid record selected ?
goto EXIT_NAK ; no, exit
call TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
pagesel MEM_GETTIMEDATE ; make right program memory page selection
call MEM_GETTIMEDATE ; store start time and date of currently selected record in the time and date buffer in bank2
pagesel TX_TIMEANDDATE ; make right program memory page selection
call TX_TIMEANDDATE ; send time and date values from the buffer in bank2 as a string, bank2 return
goto TX_COMMANDEND ; send checksum and ETX, clear command flag
;--------------------------------------------------------------------------------------------------------------------------------------
RZ ; get record size (in bits !) of currently selected record
btfsc flags1,withdata ; is there any data ?
goto EXIT_NAK ; yes, exit
RZ_REQUEST bank3 ; no, check current record selection
movf current_rec,w ; test for zero value
skpnz ; are there any records and is there already a valid record selected ?
goto EXIT_NAK ; no, exit
call TX_COMMANDSTART ; yes, send ACK, STX and command mnemonics
pagesel MEM_GETRECSIZE ; make right program memory page selection
call MEM_GETRECSIZE ; store size (in bits) of currently selected record into registers numlow, nummiddle, numhigh
pagesel TX_WNUMBER ; make right program memory page selection
call TX_WNUMBER ; transmit the 24 bit number in numlow(lsB), nummiddle and numhigh(msB), bank0 return
goto TX_COMMANDEND ; send checksum and ETX, clear command status byte
;--------------------------------------------------------------------------------------------------------------------------------------
SE ; get serial number of device from location in bootloader area
; the four topmost words in PIC flash memory are reserved for storing serial numbers etc.
; after the write backdoor is closed again
btfsc flags1,withdata ; is there any data ?
goto SE_COMMAND ; yes, so we should use this data
SE_REQUEST call TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
call SE_LOADADDRESS ; bank2 return
pagesel FLASHREAD ; make right program memory page selection
call FLASHREAD ; read flash program memory, address in eeadr/eeadrh, data in eedata/eedath, bank3 return
bank2 ;
movf eedata,w ; get data value into wordlow and wordmiddle
movwf numlow ;
movf eedath,w ;
movwf nummiddle ;
pagesel TX_LNUMBER ; make right program memory page selection
call TX_LNUMBER ; transmits the value of numlow and nummiddle as ascii characters
goto TX_COMMANDEND ; send checksum and ETX, clear command status byte
SE_COMMAND call GET_LNUMBER ; read the data input number that goes with the command into the numlow and nummiddle register
skpnc ; errors reading number ?
goto EXIT_NAK ; yes, exit and be ready for next command
btfss flags1,changese ; check the backdoor flag that allows the serial number to be changed
goto EXIT_NAK ; yes, exit and be ready for next command
movlw b'00111111' ; words of pic program memory are 14 bits wide, strip upper two bits
andwf nummiddle,f ;
movf numlow,w ; copy data
bank2 ;
movwf eedata ; low
movf nummiddle,w ;
movwf eedath ; high
bcf flags1,changese ; clear backdoor flag that allows serial number to be changed
call SE_LOADADDRESS ; bank2 return
pagesel FLASHWRITE ; make right program memory page selection
call FLASHWRITE ; write flash memory,addr. in eeadr/eeadrh,data in eedata/eedath, carry set on error, bank2 ret.
pagesel EXIT_ACK ; make right program memory page selection
skpnc ; was the data written ok ?
goto EXIT_NAK ; exit, there was a data write verification error
goto EXIT_ACK ; done
SE_LOADADDRESS bank2 ;
movlw h'FF' ; get low byte value
movwf eeadr ; low byte
movlw h'1F' ; get low byte value
movwf eeadrh ; high byte
return ; return in bank2
;-------------------------------------------------------------------------------------------------------------------------------------
SV ; read firmware build version (for example: 1.66)
btfsc flags1,withdata ; is there any data ?
goto EXIT_NAK ; yes, exit
SV_REQUEST call TX_COMMANDSTART ; send ACK, STX and command mnemonics
movlw fwv0 ; version bytes, defined at the very top of this program
call TX_BYTE ;
movlw a'.' ;
call TX_BYTE ;
movlw fwv1 ;
call TX_BYTE ;
movlw fwv2 ;
call TX_BYTE ;
goto TX_COMMANDEND ; send checksum and ETX, clear command status byte
;--------------------------------------------------------------------------------------------------------------------------------------
V0 ; get values of all inputs (read only), separated by a horizonal tab (456 22 1014 ...)
; actual command mnemonic is V$, but we cannot use the dollar character in labels
; transmit order:
; RPM,speed,lambda,voltage,thermocouple,air,water,throttle,longitudinal,lateral,digital
btfsc flags1,withdata ; is there any data ?
goto EXIT_NAK ; yes, exit
V0_REQUEST call TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
V0_RPM ; prevent mplab ide errror
pagesel CALC_RPM ; make right program memory page selection
call CALC_RPM ; yes, calculate the RPM value from the time interval between the input pulses, return in bank2
movf rpm_low,w ; get low byte value
movwf numlow ;
movf rpm_high,w ; get high byte value
movwf nummiddle ;
pagesel TX_LNUMBER ; make right program memory page selection
call TX_LNUMBER ; transmits the value of numlow and nummiddle as ascii characters
V0_TAB0 movlw d'9' ; seperator character is ascii horizontal tab character
call TX_BYTE ; send separator character
V0_SPEED ; prevent mplab ide errror
pagesel CALC_SPEED ; make right program memory page selection
call CALC_SPEED ; calculate speed value from time interval between input pulses, return in bank2
movf speed,w ; get value
pagesel TX_NUMBER ; make right program memory page selection
call TX_NUMBER ; transmits the value of w register as ascii characters
V0_TAB1 movlw d'9' ; seperator character is ascii horizontal tab character
call TX_BYTE ; send separator character
V0_ANALOG_CHAN clrf adchannel ; start at channel zero
V0_LOOP movf adchannel,w ; select channel
pagesel GET_ANALOG ; make right program memory page selection
call GET_ANALOG ; get value of analog input (selected by w register) into numlow and nummiddle, return in bank1
pagesel TX_LNUMBER ; make right program memory page selection
call TX_LNUMBER ; transmit value of numlow and nummiddle as ascii characters
V0_TAB2 movlw d'9' ; seperator character is ascii horizontal tab character
call TX_BYTE ; send separator character
incf adchannel,f ; select next channel
movlw d'8' ; there's eight analog channels
subwf adchannel,w ; see if we have done all
skpz ; was this the last measurement ?
goto V0_LOOP ; no, next
V0_DIGITAL bank0 ; yes,
movf portb,w ; get the status of portb
call TX_NUMBER ; transmits the value of w as ascii characters
goto TX_COMMANDEND ; exit, send checksum and ETX, clear command flag
;--------------------------------------------------------------------------------------------------------------------------------------
VA ; get value of air temperature analog input (read only)
btfsc flags1,withdata ; is there any data ?
goto EXIT_NAK ; yes, exit
VA_REQUEST call TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
movlw d'3' ; analog input channel 3
pagesel GET_ANALOG ; make right program memory page selection
call GET_ANALOG ; get value of analog input (selected by w register) into numlow and nummiddle, return in bank1
pagesel TX_LNUMBER ; make right program memory page selection
call TX_LNUMBER ; transmit value of numlow and nummiddle as ascii characters
goto TX_COMMANDEND ; send checksum and ETX, clear command flag
;--------------------------------------------------------------------------------------------------------------------------------------
VD ; get the value of digital portb, which are the digital inputs (upper two bits are outputs)
; read: bit7=green led, red led, brdsuppl,tcdisc,laptime,brake,mark,_run_/log=bit0
; write: only upper two bits (the leds) are written
btfsc flags1,withdata ; is there any data ?
goto VD_COMMAND ; yes, so we should use this data to write to the eeprom location
VD_REQUEST call TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
bank0 ;
movf portb,w ; get the status of portb
call TX_NUMBER ; transmits the value of w as ascii characters
goto TX_COMMANDEND ; send checksum and ETX, clear command flag
VD_COMMAND call GET_NUMBER ; read the data input number that goes with the command into the w register
skpnc ; errors reading number ?
goto EXIT_NAK ; yes, exit and be ready for next command
bank0
xorwf portb,w ; calculate the difference between new data and the actual status
andlw b'11000000' ; mask, we only should only change bits 6 and 7 which are outputs
xorwf portb,f ; flip bits if they are not the same and write the new value to portb
goto EXIT_ACK ; done
;--------------------------------------------------------------------------------------------------------------------------------------
VE ; get value of lateral g-sensor analog input (read only)
btfsc flags1,withdata ; is there any data ?
goto EXIT_NAK ; yes, exit
VE_REQUEST call TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
movlw d'7' ; analog input channel 7
pagesel GET_ANALOG ; make right program memory page selection
call GET_ANALOG ; get value of analog input (selected by w register) into numlow and nummiddle, return in bank1
pagesel TX_LNUMBER ; make right program memory page selection
call TX_LNUMBER ; transmit value of numlow and nummiddle as ascii characters
goto TX_COMMANDEND ; send checksum and ETX, clear command flag
;--------------------------------------------------------------------------------------------------------------------------------------
VH ; get value of throttle (handlebar) analog input (read only)
btfsc flags1,withdata ; is there any data ?
goto EXIT_NAK ; yes, exit
VH_REQUEST call TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
movlw d'5' ; analog input channel 5
pagesel GET_ANALOG ; make right program memory page selection
call GET_ANALOG ; get value of analog input (selected by w register) into numlow and nummiddle, return in bank1
pagesel TX_LNUMBER ; make right program memory page selection
call TX_LNUMBER ; transmit value of numlow and nummiddle as ascii characters
goto TX_COMMANDEND ; send checksum and ETX, clear command flag
;--------------------------------------------------------------------------------------------------------------------------------------
VI ; set or read the eight leds of port D (shift light leds)
btfsc flags1,withdata ; is there any data ?
goto VI_COMMAND ; yes, so we should use this data
VI_REQUEST call TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
bank0 ;
movf portd,w ; get the current leds status
call TX_NUMBER ; transmits the value of w as ascii characters
goto TX_COMMANDEND ; send checksum and ETX, clear command status byte
VI_COMMAND call GET_NUMBER ; read the data input number that goes with the command into the w register
skpnc ; errors reading number ?
goto EXIT_NAK ; yes, exit and be ready for next command
bank0 ;
movwf portd ; set leds to new value
goto EXIT_ACK ; done
;--------------------------------------------------------------------------------------------------------------------------------------
VL ; get value of lambda analog input (read only)
btfsc flags1,withdata ; is there any data ?
goto EXIT_NAK ; yes, exit
VL_REQUEST call TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
movlw d'0' ; analog input channel 0
pagesel GET_ANALOG ; make right program memory page selection
call GET_ANALOG ; get value of analog input (selected by w register) into numlow and nummiddle, return in bank1
pagesel TX_LNUMBER ; make right program memory page selection
call TX_LNUMBER ; transmit value of numlow and nummiddle as ascii characters
goto TX_COMMANDEND ; send checksum and ETX, clear command flag
;--------------------------------------------------------------------------------------------------------------------------------------
VO ; get value of longitudinal g-sensor analog input (read only)
btfsc flags1,withdata ; is there any data ?
goto EXIT_NAK ; yes, exit
VO_REQUEST call TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
movlw d'6' ; analog input channel 6
pagesel GET_ANALOG ; make right program memory page selection
call GET_ANALOG ; get value of analog input (selected by w register) into numlow and nummiddle, return in bank1
pagesel TX_LNUMBER ; make right program memory page selection
call TX_LNUMBER ; transmit value of numlow and nummiddle as ascii characters
goto TX_COMMANDEND ; send checksum and ETX, clear command flag
;--------------------------------------------------------------------------------------------------------------------------------------
VR ; returns value of engine rpm, calculated from pulses on CCP1 input
btfsc flags1,withdata ; is there any data ?
goto EXIT_NAK ; yes, exit
VR_REQUEST call TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
pagesel CALC_RPM ; make right program memory page selection
call CALC_RPM ; calculate the RPM value from the time interval between the input pulses, return in bank2
movf rpm_low,w ; get low byte value
bank0 ;
movwf numlow ;
bank2 ;
movf rpm_high,w ; get high byte value
bank0 ;
movwf nummiddle ;
pagesel TX_LNUMBER ; make right program memory page selection
call TX_LNUMBER ; transmits the value of numlow and nummiddle as ascii characters
goto TX_COMMANDEND ; send checksum and ETX, clear command status byte
;--------------------------------------------------------------------------------------------------------------------------------------
VS ; returns value of vehicle speed in km/hr, calculated from pulses on CCP2 input
btfsc flags1,withdata ; is there any data ?
goto EXIT_NAK ; yes, exit
VS_REQUEST call TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
pagesel CALC_SPEED ; make right program memory page selection
call CALC_SPEED ; calculate speed value from time interval between input pulses, return in bank2
movf speed,w ; get value
pagesel TX_NUMBER ; make right program memory page selection
call TX_NUMBER ; transmits the value of w register as ascii characters
goto TX_COMMANDEND ; send checksum and ETX, clear command status byte
;--------------------------------------------------------------------------------------------------------------------------------------
VT ; get value of thermocouple analog input (read only)
btfsc flags1,withdata ; is there any data ?
goto EXIT_NAK ; yes, exit
VT_REQUEST call TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
movlw d'2' ; analog input channel 2
pagesel GET_ANALOG ; make right program memory page selection
call GET_ANALOG ; get value of analog input (selected by w register) into numlow and nummiddle, return in bank1
pagesel TX_LNUMBER ; make right program memory page selection
call TX_LNUMBER ; transmit value of numlow and nummiddle as ascii characters, return in bank0
goto TX_COMMANDEND ; send checksum and ETX, clear command flag
;--------------------------------------------------------------------------------------------------------------------------------------
VV ; get value of voltage analog input (read only)
btfsc flags1,withdata ; is there any data ?
goto EXIT_NAK ; yes, exit
VV_REQUEST call TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
movlw d'1' ; analog input channel 1
pagesel GET_ANALOG ; make right program memory page selection
call GET_ANALOG ; get value of analog input (selected by w register) into numlow and nummiddle, return in bank1
pagesel TX_LNUMBER ; make right program memory page selection
call TX_LNUMBER ; transmit value of numlow and nummiddle as ascii characters
goto TX_COMMANDEND ; send checksum and ETX, clear command flag
;--------------------------------------------------------------------------------------------------------------------------------------
VW ; get value of water temperature analog input (read only)
btfsc flags1,withdata ; is there any data ?
goto EXIT_NAK ; yes, exit
VW_REQUEST call TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
movlw d'4' ; analog input channel 4
pagesel GET_ANALOG ; make right program memory page selection
call GET_ANALOG ; get value of analog input (selected by w register) into numlow and nummiddle, return in bank1
pagesel TX_LNUMBER ; make right program memory page selection
call TX_LNUMBER ; transmit value of numlow and nummiddle as ascii characters
goto TX_COMMANDEND ; send checksum and ETX, clear command flag
;--------------------------------------------------------------------------------------------------------------------------------------
XA ; set external eeprom address, input is ascii string with 16 bit number
btfsc flags1,withdata ; is there any data ?
goto XA_COMMAND ; yes, so we should use this data
XA_REQUEST call TX_COMMANDSTART ; send ACK, STX and command mnemonics
bank1 ;
movf iicalow,w ; copy iic address registers to rs232 number receive/transmit registers
bank0 ;
movwf numlow ;
bank1 ;
movf iicahigh,w ;
bank0 ;
movwf nummiddle ;
call TX_LNUMBER ; transmits the value of numlow and nummiddle as ascii characters
goto TX_COMMANDEND ; send checksum and ETX, clear command status byte
XA_COMMAND call GET_LNUMBER ; read the data input number that goes with the command into the numlow and nummiddle register
skpnc ; errors reading number ?
goto EXIT_NAK ; yes, exit and be ready for next command
btfsc nummiddle,7 ; size of the eeproms is 32k x 8 (256kbit), therefore the address maximum is 32767
goto EXIT_NAK ;
movf numlow,w ; store address in iic address registers
bank1 ;
movwf iicalow ;
bank0 ;
movf nummiddle,w ;
bank1 ;
movwf iicahigh ;
goto EXIT_ACK ; done
;--------------------------------------------------------------------------------------------------------------------------------------
XC ; select external eeprom chip, input is ascii character with 8 bit number (0..7)
btfsc flags1,withdata ; is there any data ?
goto XC_COMMAND ; yes, so we should use this data
XC_REQUEST call TX_COMMANDSTART ; send ACK, STX and command mnemonics
bank1 ;
movf iicchip,w ; get number of selected chip
call TX_NUMBER ; transmits the value w register as ascii characters
goto TX_COMMANDEND ; send checksum and ETX, clear command status byte
XC_COMMAND call GET_NUMBER ; read the data input number that goes with the command into w register
skpnc ; errors reading number ?
goto EXIT_NAK ; yes, exit and be ready for next command
sublw d'7' ; maximum number of eeprom chips is eight, therefore the number should be from 0..7
skpc ; is the number larger than 7 ?
goto EXIT_NAK ; yes, exit and be ready for next command
sublw d'7' ; no, restore w register content to its previous value
bank1 ;
movwf iicchip ; store new chip select value
goto EXIT_ACK ; done
;--------------------------------------------------------------------------------------------------------------------------------------
XD ; read/write data byte from external eeprom value
; write: input is ascii string with 8 bit number
btfsc flags1,withdata ; is there any data ?
goto XD_COMMAND ; yes, so we should use this data
XD_REQUEST call TX_COMMANDSTART ; send ACK, STX and command mnemonics
pagesel IIC_RDBYTE ; make right program memory page selection
call IIC_RDBYTE ; read data byte from external eeprom, address is in iicchip, iicalow and iicahigh
bank1 ;
movf iicdata,w ; store data byte in w register
bank0 ;
pagesel TX_NUMBER ; make right program memory page selection
call TX_NUMBER ; transmit the value of the w register as ascii characters
goto TX_COMMANDEND ; send checksum and ETX, clear command status byte
XD_COMMAND call GET_NUMBER ; read the data input number that goes with the command into the w register
skpnc ; errors reading number ?
goto EXIT_NAK ; yes, exit and be ready for next command
bank1 ;
movwf iicdata ; store data byte from w register
pagesel IIC_WRBYTE ; make right program memory page selection
call IIC_WRBYTE ; write byte in iicdata to external eeprom, address in iicchip, iicalow and iicahigh
pagesel EXIT_ACK ; make right program memory page selection
goto EXIT_ACK ; done
;--------------------------------------------------------------------------------------------------------------------------------------
XP ; read/write data 1 block of 64 bytes from external eeprom
; write 64 bytes of data from RS232 input buffer
; **** the receive routine prevents the character h'03' to be included in the data !
btfsc flags1,withdata ; is there any data ?
goto XP_COMMAND ; yes, so we should use this data
XP_REQUEST call TX_COMMANDSTART ; no, send ACK, STX and command mnemonics
pagesel IIC_RDBLOCK ; make right program memory page selection
call IIC_RDBLOCK ; read 64 data bytes from external eeprom, address in iicchip, iicalow and iicahigh
bcf status,irp ; make sure upper bit in bank address is zero (select bank 0 and 1)
movlw h'AF' ; start position of data in buffer in bank1
movwf fsr ; point to this address
XP_LOOP1 movf indf,w ; read the data byte at the right position in the buffer
pagesel TX_BYTE ; make right program memory page selection
call TX_BYTE ; transmit the value of the w register
incf fsr,f ; increase pointer to point to next value
movf fsr,w ; use pointer value
sublw h'EE' ; was this the last byte ?
skpnc ; let's see
goto XP_LOOP1 ; no, do loop again and read next data byte
goto TX_COMMANDEND ; send checksum and ETX, clear command status byte
XP_COMMAND bank0 ; check that we actually got 64 bytes
movlw rx_buffer65 ; position of last data byte
subwf rx_maxpos,w ; test length
skpz ; did we get exactly 64 data bytes ?
goto EXIT_NAK ; no, exit and be ready for next command
movlw h'2F' ; yes, copy the 64 databytes from databuffer in bank0 to the block buffer in bank1
movwf fsr ; point to the start position of the data buffers in bank0 and bank1 (!)
bcf status,irp ; make sure upper bit in bank address is zero (select bank 0 and 1)
XP_LOOP2 movf indf,w ; read the data byte at the right position in the buffer
bsf fsr,7 ; the block buffer is in bank1 (hex 80 to hex FF)
movwf indf ; write the data byte
incf fsr,f ; increase pointer to point to next value
bcf fsr,7 ; the receive databuffer is in bank 0 (hex 00 to hex 7F)
movf fsr,w ; use pointer value
sublw h'6E' ; was this the last byte ?
skpnc ; let's see
goto XP_LOOP2 ; no, do loop again and read next data byte
pagesel IIC_WRBLOCK ; make right program memory page selection
call IIC_WRBLOCK ; yes, write 64 bytes to external eeprom, address in iicchip, iicalow and iicahigh
pagesel EXIT_ACK ; make right program memory page selection
goto EXIT_ACK ; done
;--------------------------------------------------------------------------------------------------------------------------------------
;--------------------------------------------------------------------------------------------------------------------------------------
;--------------------------------------------------------------------------------------------------------------------------------------
org h'1800' ; start of program memory page 3
;--------------------------------------------------------------------------------------------------------------------------------------
; command interpreter: find and go execute selected command
;--------------------------------------------------------------------------------------------------------------------------------------
;
; to do commands are marked with a '>' character
; non optimized commands are marked with '***'
;
; mnemonic read/ value range or description
; write example
;
;
; AU R(/W) Andries C. Tip returns software author, a write opens the backdoor to change serial number
;
; CL R/W 05/31/04 11:23:47 clock time and date in the following format MM/DD/YY HH:MM:SS
; CA R/W 0..63 clock static ram address byte (address wraps around)
; CD R/W 0..255 clock static ram data byte at address specified by CA
;
; EA R/W 0..255 address location for PIC internal eeprom
; ED R/W 0..255 data of the PIC internal eeprom at the address specified by EA
; ER R/W 0..255 error flag bits, bits will be set when errors occur during logging
;
; FA R/W 32 air temperature logging frequency
; FB R/W 16 brake switch logging frequency
; FE R/W 8 lateral acceleration logging frequency
; FH R/W 0 thermocouple logging frequency
; FL R/W 2 lambda logging frequency
; FM R/W 1 mark switch logging frequency
; FO R/W 0.5 longitudinal acceleration logging frequency
; FR R/W 0.25 rpm logging frequency
; FS R/W 0 speed logging frequency
; FT R/W 4 throttle logging frequency
; FV R/W 0 voltage logging frequency
; FW R/W 0 water temperature logging frequency
;
; ID R Motorcycle Datalogger returns device identification string
;
; >KA R/W 0..16383 engine stationary RPM adjust, left half of leds are on, 0=off
; >KB R/W 0..16383 engine high RPM adjust. right half of leds are on, 0=off
; KC R/W 1..65535 wheel circumference [millimeters] used in speed calculation
; >KN R/W 1..255 puls count setting: Number of notches/pulses per revolution of wheel
; >KP R/W 2 pulses per crankshaft revolution, used in rpm calculation
; KR R/W 0..16383 max RPM rate, leds will flash above this rate
;
; >LS R/W 0..255 select logging start condition (Rotary switch, Mark Button, Speed, RPM)
; >LV R/W 0..16363 select value for logging start condition (when RPM/Speed)
;
; >PA R/W 1..7/13 select the desired logging resolution of a channel
;
; RN R 0..20 number of records in datalogger memory
; RS R/W 0..19 select a record for downloading
; RT R 07/28/04 12:32:04 record start time and date in the following format MM/DD/YY HH:MM:SS
; RZ R 93645543 get record size (in bits !) and reset download position
; RD R 6%gr'@:sE.... download record data in blocks of one block (64 bytes)
; RI R 6%gr'@:sE.... increment block, then download record data in blocks of one block (64 bytes)
; RC W clear all record memory
; >RM R 0..100 memory usage/maximum memory address ??
;
; >SD R 14/28/04 device programming date, stored in flash program memory of bootloader
; SE R(/W) 0..16383 device serial number in top of flash program memory, see AU command
; >ST R 0..255 datalogger status (busy, idle, full, error, etc.)
; SV R 1.55 returns firmware version
;
; V$ R 0..1023 values of all inputs separated by a tab (234 444 ...)
; VA R 0..1023 analog input channel 3: air temperature (NTC)
; VD R/W 0..255 status of digital portb
; VE R 0..1023 analog input channel 7: lateral G-sensor
; VH R 0..1023 analog input channel 5: throttle (0..5V)
; VI R/W 0..255 status of leds at portd functioning as shift light and rpm counter
; VL R 0..1023 analog input channel 0: lambda sensor
; VO R 0..1023 analog input channel 6: longitudinal G-sensor
; VR R 0..16383 RPM rate (ccp1 pulse input)
; VS R 0..255 Speed [km/hr] from ccp2 pulse input
; VT R 0..1023 analog input channel 2: K-type thermocouple
; VV R 0..1023 analog input channel 1: voltage in 0..5 V
; VW R 0..1023 analog input channel 4: water temperature (NTC)
;
; XA R/W 0..32767 external eeprom address
; XC R/W 0..7 select external eeprom chip
; XD R/W 0..255 data byte of the external eeprom at the position specified by XA and XC
; XP R/W ABC@%432_;!... block size read or write (64 bytes) external eeprom
;
;--------------------------------------------------------------------------------------------------------------------------------------
EXEC_COMMAND ; go look in buffer to find out which command was sent and has to be executed
; a command starts with two letters which may be followed by one or more data bytes
; remember CTS has already been cleared to hold data stream from computer
; CTS will be set again after command execution to re-enable data reception
bank0 ;
bcf portb,led_red ; turn off red status led
bsf portb,led_green ; turn on green status led
A? movlw a'A' ;
subwf rx_buffer00,w ;
skpnz ;
goto A_ ;
C? movlw a'C' ; does the command start with the letter 'C' ?
subwf rx_buffer00,w ; compare to first command character (first byte in buffer)
skpnz ; let's see
goto C_ ; yes it does, go to all commands starting with the letter 'C'
E? movlw a'E' ;
subwf rx_buffer00,w ;
skpnz ;
goto E_ ;
F? movlw a'F' ;
subwf rx_buffer00,w ;
skpnz ;
goto F_ ;
I? movlw a'I' ;
subwf rx_buffer00,w ;
skpnz ;
goto I_ ;
K? movlw a'K' ;
subwf rx_buffer00,w ;
skpnz ;
goto K_ ;
L? movlw a'L' ;
subwf rx_buffer00,w ;
skpnz ;
goto L_ ;
R? movlw a'R' ;
subwf rx_buffer00,w ;
skpnz ;
goto R_ ;
S? movlw a'S' ;
subwf rx_buffer00,w ;
skpnz ;
goto S_ ;
V? movlw a'V' ;
subwf rx_buffer00,w ;
skpnz ;
goto V_ ;
X? movlw a'X' ;
subwf rx_buffer00,w ;
skpnz ;
goto X_ ;
pagesel EXIT_NAK ; make right program memory page selection
goto EXIT_NAK ; no command mnemonic matched, exit
;--------------------------------------------------------------------------------------------------------------------------------------
A_ ; empty line to prevent mplab editor error
pagesel COMMANDS ; make right program memory page selection
movlw a'U' ;
subwf rx_buffer01,w ;
skpnz ;
goto AU ;
goto EXIT_NAK ; exit_nak is on same page as commands so pagesel is valid
C_ ; empty line to prevent mplab editor error
pagesel COMMANDS ;
movlw a'A' ;
subwf rx_buffer01,w ;
skpnz ;
goto CA ;
movlw a'D' ;
subwf rx_buffer01,w ;
skpnz ;
goto CD ;
movlw a'L' ;
subwf rx_buffer01,w ;
skpnz ;
goto CL ;
goto EXIT_NAK ;
E_ ;
pagesel COMMANDS ;
movlw a'A' ;
subwf rx_buffer01,w ;
skpnz ;
goto EA ;
movlw a'D' ;
subwf rx_buffer01,w ;
skpnz ;
goto ED ;
movlw a'R' ;
subwf rx_buffer01,w ;
skpnz ;
goto ER ;
goto EXIT_NAK ;
F_ ;
pagesel COMMANDS ;
movlw a'A' ;
subwf rx_buffer01,w ;
skpnz ;
goto FA ;
movlw a'B' ;
subwf rx_buffer01,w ;
skpnz ;
goto FB ;
movlw a'E' ;
subwf rx_buffer01,w ;
skpnz ;
goto FE ;
movlw a'H' ;
subwf rx_buffer01,w ;
skpnz ;
goto FH ;
movlw a'M' ;
subwf rx_buffer01,w ;
skpnz ;
goto FM ;
movlw a'L' ;
subwf rx_buffer01,w ;
skpnz ;
goto FL ;
movlw a'O' ;
subwf rx_buffer01,w ;
skpnz ;
goto FO ;
movlw a'R' ;
subwf rx_buffer01,w ;
skpnz ;
goto FR ;
movlw a'S' ;
subwf rx_buffer01,w ;
skpnz ;
goto FS ;
movlw a'T' ;
subwf rx_buffer01,w ;
skpnz ;
goto FT ;
movlw a'V' ;
subwf rx_buffer01,w ;
skpnz ;
goto FV ;
movlw a'W' ;
subwf rx_buffer01,w ;
skpnz ;
goto FW ;
goto EXIT_NAK ;
I_ ;
pagesel COMMANDS ;
movlw a'D' ;
subwf rx_buffer01,w ;
skpnz ;
goto ID ;
goto EXIT_NAK ;
K_ ;
pagesel COMMANDS ;
movlw a'C' ;
subwf rx_buffer01,w ;
skpnz ;
goto KC ;
movlw a'P' ;
subwf rx_buffer01,w ;
skpnz ;
goto KP ;
movlw a'R' ;
subwf rx_buffer01,w ;
skpnz ;
goto KR ;
goto EXIT_NAK ;
L_ ;
pagesel COMMANDS ;
movlw a'S' ;
subwf rx_buffer01,w ;
skpnz ;
goto LS ;
movlw a'V' ;
subwf rx_buffer01,w ;
skpnz ;
goto LV ;
goto EXIT_NAK ;
R_ ;
pagesel COMMANDS ;
movlw a'C' ;
subwf rx_buffer01,w ;
skpnz ;
goto RC ;
movlw a'D' ;
subwf rx_buffer01,w ;
skpnz ;
goto RD ;
movlw a'I' ;
subwf rx_buffer01,w ;
skpnz ;
goto RI ;
movlw a'M' ;
subwf rx_buffer01,w ;
skpnz ;
goto RM ;
movlw a'N' ;
subwf rx_buffer01,w ;
skpnz ;
goto RN ;
movlw a'S' ;
subwf rx_buffer01,w ;
skpnz ;
goto RS ;
movlw a'T' ;
subwf rx_buffer01,w ;
skpnz ;
goto RT ;
movlw a'Z' ;
subwf rx_buffer01,w ;
skpnz ;
goto RZ ;
goto EXIT_NAK ;
S_ ;
pagesel COMMANDS ;
movlw a'E' ;
subwf rx_buffer01,w ;
skpnz ;
goto SE ;
movlw a'V' ;
subwf rx_buffer01,w ;
skpnz ;
goto SV ;
goto EXIT_NAK ;
V_ ;
pagesel COMMANDS ;
movlw a'$' ;
subwf rx_buffer01,w ;
skpnz ;
goto V0 ;
movlw a'R' ;
subwf rx_buffer01,w ;
skpnz ;
goto VR ;
movlw a'S' ;
subwf rx_buffer01,w ;
skpnz ;
goto VS ;
movlw a'A' ;
subwf rx_buffer01,w ;
skpnz ;
goto VA ;
movlw a'D' ;
subwf rx_buffer01,w ;
skpnz ;
goto VD ;
movlw a'E' ;
subwf rx_buffer01,w ;
skpnz ;
goto VE ;
movlw a'H' ;
subwf rx_buffer01,w ;
skpnz ;
goto VH ;
movlw a'I' ;
subwf rx_buffer01,w ;
skpnz ;
goto VI ;
movlw a'L' ;
subwf rx_buffer01,w ;
skpnz ;
goto VL ;
movlw a'O' ;
subwf rx_buffer01,w ;
skpnz ;
goto VO ;
movlw a'T' ;
subwf rx_buffer01,w ;
skpnz ;
goto VT ;
movlw a'V' ;
subwf rx_buffer01,w ;
skpnz ;
goto VV ;
movlw a'W' ;
subwf rx_buffer01,w ;
skpnz ;
goto VW ;
goto EXIT_NAK ;
X_ ;
pagesel COMMANDS ;
movlw a'A' ;
subwf rx_buffer01,w ;
skpnz ;
goto XA ;
movlw a'C' ;
subwf rx_buffer01,w ;
skpnz ;
goto XC ;
movlw a'D' ;
subwf rx_buffer01,w ;
skpnz ;
goto XD ;
movlw a'P' ;
subwf rx_buffer01,w ;
skpnz ;
goto XP ;
goto EXIT_NAK ;
;--------------------------------------------------------------------------------------------------------------------------------------
org h'1EA4' ; start of bootloader version 1.1 from file boot31.asm is at h'1EA5' !!!
nop ; force assembler to notice the start of this page
; we switched off the assember error messages but we want to see if we cross boundaries
;--------------------------------------------------------------------------------------------------------------------------------------
end ; tell the assembler to stop
file: /Techref/member/AT-planet-T9/datalogger.htm, 425KB, , updated: 2011/2/23 22:13, local time: 2024/8/14 15:16,
owner: AT-planet-T9,
|
| ©2024 These pages are served without commercial sponsorship. (No popup ads, etc...).Bandwidth abuse increases hosting cost forcing sponsorship or shutdown. This server aggressively defends against automated copying for any reason including offline viewing, duplication, etc... Please respect this requirement and DO NOT RIP THIS SITE. Questions? <A HREF="http://piclist.com/Techref/member/AT-planet-T9/datalogger.htm"> Datalogger</A> |
Did you find what you needed?
|