;-------------------------------------------------------------------------------------------------------------------------------------- ; ; MOTORCYCLE DATA LOGGER - ASSEMBLY SOURCE CODE FOR THE MICROCHIP PIC 16F877 ; ; ; All rights reserved - Copyright (C) 2004-2023 Sivisti / A. Tip ; ; Start of programming : May 2002 ; Last Revision Date : January 14, 2007 ; ; 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 The order used to store the status register in the interrupt routine can be changed ; 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€ á PõÔÙCà 0€ á PõÔÙCà 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 clrf status ; bank 0, regardless of current bank, clears irp,rp1,rp0 (for other than 16F877) movwf status_temp ; save status register to status_temp register 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): ; 1 byte indicator for start of command, value hex 02 ; 1 byte, first letter of command ; 1 byte, second letter of command ; 0-64 data bytes, all values accepted except ascii codes STX and ETX ; 1 byte indicator for end of command, value hex 03 ; 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 up to and including 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): ; 1 byte indicator for start of command, value hex 02 ; 1 byte, first letter of command ; 1 byte, second letter of command ; 0-64 data bytes, all values accepted except ascii codes STX and ETX ; 1 byte checksum (see below), any hex value 00-FF ; 1 byte indicator for end of command, value hex 03 ; checksum value makes sum of all bytes from up to and including 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 'Motortoer 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't' ; out to be not true, so table reads do not have to disable any interrupts. retlw a'o' ; retlw a'e' ; retlw a'r' ; 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 Motortoer 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