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