-- File: VPWM.jal -- Author: Shawn Standfast -- Version 1.00.2 -- 11/1/2005 -- Library for OBDII Variable Pulse Width Modulation on PIC16F877(A) @ 20MHz -- -- *Note - This library utilizes the CCPX Module, Timer 1, Timer 2 W/ Interrupts -- Also, be sure to be in Bank 0 before using any procedures in this -- library. -- -- This library is designed around the SAE J1850 VPWM standard. It is assumed -- that the bus is a CSMA/CR (Carrier Sense, Multiple Access, Collision Resolution) -- type bus. Ultimate bus control is determined by bit-by-bit arbitration. -- The general message format used is -- SOF + 1 or 3 Header Bytes + 10 or 8 Data Bytes + 1 CRC Byte + EOD + EOF. --- Max Frame Length = 12 Bytes -- -- Nominal Pulse widths are as follows: -- --------------------------- -- | SYMBOL | Pulse Width | -- --------------------------- -- | Active 1 | 64uS | -- | Passive 0 | 64uS | -- | | -- | Passive 1 | 128uS | -- | Active 0 | 128uS | -- | | -- | SOF | 200uS | -- | EOD | 200uS | -- | | -- | EOF | 280-300uS | -- --------------------------- -- Functions: vpw_send(byte in data) return bit -- Procedures: vpw_receive() -- get_frame(byte in frame_num) -- -- I/O Pin Definitions and Directions -- pin_c2_direction = input --- vpwin -- pin_c1_direction = output -- vpwout -- Required Libraries: include jpic -- Set Bounds for Buffer Arrays const array_check_indices = true const array0_start = 0xA0 -- Output Array Start const array0_end = 0xAC -- Output Array End const array1_start = 0xAD -- Input Buffer Array 1 Start const array1_end = 0xB9 -- Input Buffer Array 1 End const array2_start = 0xBA -- Input Buffer Array 2 Start const array2_end = 0xC6 -- Input Buffer Array 2 End const array3_start = 0xC7 -- Input Buffer Array 3 Start const array3_end = 0xD3 -- Input Buffer Array 3 End const array4_start = 0xD4 -- Processing Buffer Array Start const array4_end = 0xE0 -- Processing Buffer Array End include bank_arrays -- Other Constants const rising = 0x05 ----- configure for capture on rising edge const falling = 0x04 --- configure for capture on falling edge const per_short = 0x50 -- number of cycles for 64uS for tmr1 const per_long = 0xA0 -- number of cycles for 128uS for tmr1 const per_sof = 0xFA -- number of cycles for 200uS for tmr1 const per_eof = 0xAF ---- #cycles 280uS, must use 1:8 prescaler for EOF detection const comp_drive_low = 0x09 --- for ccp2con to drive CCP2 pin low on compare const comp_drive_high = 0x08 -- for ccp2con to drive CCP2 pin high on compare const comp_only = 0x0A -------- for ccp2con to flag only -- I/O Pin Definitions and Directions pin_c2_direction = input pin_c1_direction = output var bit vpwin is pin_c2 var bit vpwout is pin_c1 vpwout = false -- Some Variables -- Byte Variables RESERVED FOR INTERRUPT ROUTINE var byte tmr1_low_old = 0x00, tmr1_high_old = 0x00 -- stores old tmr1 values var byte tmr1_low_new = 0x00, tmr1_high_new = 0x00 -- stores new tmr1 values var byte delta_low = 0x00, delta_high = 0x00 -- stores the difference between -- tmr1_old & tmr1_new var bit pulse_rec = off -- Configure ccpXmodule ---------------------------------------------------------- f877_ccp1con = 0x00 f877_ccp2con = 0x00 f877_ccpr2l = 0x00 f877_ccpr2h = 0x00 f877_ccpr1l = 0x00 f877_ccpr1h = 0x00 pir1_ccp1if = false -- clear both CCP interrupt flags pir2_ccp2if = false -- Initialize Timer1------------------------------------------------------------ -- -- max frame width = SOF + 11*(8 Data Bits) + 1 CRC Byte + EOF -- SOF = 200uS-- EOD = 200Us -- EOF = 300uS -- Max Byte Width 1.024 ms = (longest byte = 0xAA = 0b_1010_1010) = 128uS*8 -- Max Data Width = 12* 1.024 mS = 12.288mS -- max frame width = 12.988mS = 0.2 + 12.288 + 0.2 + 0.3 = SOF + DATA + EOD + EOF -- tmr1 overflows w/ a 1:4 prescaler @ 52.4288mS ample room for an entire frame -- -- ----------------------------------------------------------------------------- f877_t1con = 0x00 -- 1:4 prescaler for timer1 t1ckps1 = on tmr1if = false -- clear interupt flag f877_tmr1h = 0x00 f877_tmr1l = 0x00 -- Initialize Timer 2 ---------------------------------------------------------- f877_t2con = 0x02 -- 1:16 prescaler 1:1 postscaler for timer2 f877_tmr2 = 0x00 pir1_tmr2if = false -- Initialize Periphial Interrupts --------------------------------------------- asm BSF STATUS, 5 -- bank 1 tmr1ie = off -- disable tmr1 interupt flag pie1_ccp1ie = on -- enable ccp1 interrupt flag pie2_ccp2ie = off -- disable ccp2 interrupt flag pie1_tmr2ie = off -- disable timer2 interrupts f877_pr2 = 0x58 asm BCF STATUS, 5 -- bank 0 -- disable interrupts ----------------------------------------------------------- intcon_gie = off intcon_peie = off -- vpw_idle_chk is for internal use to check for inactivity on the Com. Bus procedure vpw_idle_chk is tmr1on = false vpwout = false -- make sure only transmitting a low signal pir2_ccp2if = false t1ckps1 = on t1ckps0 = on -- set 1:8 prescaler for timer1 f877_tmr1l = 0x00 -- reset timer one high and low bytes f877_tmr1h = 0x00 f877_ccpr2l = per_eof -- set period for compare to be ~280uS f877_ccpr2h = 0x00 f877_ccp2con = comp_only -- set CCP2 module to only flag when timer times out while vpwin loop end loop -- loop until bus transisitions low tmr1on = on -- start timer while ! pir2_ccp2if loop -- wait while bus is low if vpwin then -- if activity detected on bus restart timer f877_tmr1l = 0x00 -- no need to reset high byte end if end loop pir2_ccp2if = false -- clear interrupt flag tmr1on = false -- stop timer 1 f877_tmr1l = 0x00 -- reset timer 1 f877_tmr1h = 0x00 t1ckps0 = false -- 1:4 prescaler for timer1 end procedure procedure crc_calc(byte in data, byte in out crc ) is -- calculates the CRC byte var byte bit_point = 0x80 -- that is appended to the end of the OBD message Frame var byte poly -- NOTE* Procedure is to be called continuously for each for 8 loop -- byte that is transmitted. However, once final byte is if (bit_point & data) != 0 then -- passed through the CRC routine the final result in crc_reg if (crc & 0x80) != 0 then -- must be complimented before appending to the message. poly = 0x01 -- For all messages, paramater CRC should be initialized to else -- 0xFF for the first itteration. poly = 0x1C end if crc = ((crc << 1) | 1) ^ poly -- original C Code for CRC posted on http://obddiagnostics.com by B. Roadman else -- adapted to JAL by Shawn Standfast poly = 0x00 if (crc & 0x80) != 0 then poly = 0x1D end if crc = (crc << 1) ^ poly end if bit_point = bit_point >> 1 end loop end procedure -- Function vpw_send(byte in data) shifts out "data" one bit at a time MSB first. -- Arbritration is taken care of during transmission. If the transmission is successful then the function returns true -- else it returns false. Call procedure repeatedly to send multiple bytes. -- just be sure to only send one start of frame symbol per message frame. -- Example Usage: *success is a var of type bit -- Send Single byte of data: success = vpw_send(data,true) -- tmr1on = false -- Send Multiple bytes in the same frame: success = vpw_send(data1,true) -- if success then -- success = vpw_send(data2,false) -- end if -- tmr1on = false -- Send "Array" of data: -- var bit send_sof = true -- var byte count = 0x00 -- while ((success) & (count < arrayX_put_index)) loop -- temp = arrayX -- success = vpw_send(temp,send_sof) -- send_sof = false -- count = count + 1 -- end loop -- tmr1on = false function vpw_send (byte in data, bit in send_sof) return bit is asm bcf intcon_gie -- disables interrupts while sending. otherwise we can get asm bcf intcon_peie -- into all sorts of trouble. :) f877_ccp1con = 0x00 var bit error_flag = false var volatile bit bit_out at data : 7 -- select bit to be transmitted var bit dom_pass = false -- keep track of active or passive symbol var byte timer_low_byte, timer_high_byte, prtc_buf var volatile byte next_bit = 0x00 -- sets next pulse width for 8 loop -- shift data out one bit at a time; MSB first if bit_out then -- if bit to be sent is a One if dom_pass then -- send "Active" One next_bit = per_short else -- send "Passive" One next_bit = per_long end if else -- Bit to be sent is a Zero if dom_pass then -- send active zero next_bit = per_long else -- send "Passive" zero next_bit = per_short end if end if -- start of frame takes care of period adjustment for first bit. If current -- itteration is not sending a SOF, must adjust new period by adding pulse width -- to current ccp2H:ccp2L registers. if send_sof then -- need to send a start of frame first? -- send start of frame plus first bit vpw_idle_chk -- verify bus is idle before beginning transmission f877_ccp2con = comp_drive_low -- bus will be high for SOF. Drive bus low -- when pulse width is achieved. should also -- drive output pin high beginning transmission tmr1on = on -- begin timing bus position f877_ccpr2l = per_sof -- set period for compare to be ~200uS f877_ccpr2h = 0x00 pir2_ccp2if = false error_flag = false -- assume success unless failure occurs while ! pir2_ccp2if loop end loop -- wait until timer2 times out. Once -- this loop is exited our SOF symbol -- will have finished and our output -- pin will be low. -- If the bus is still active then that means another node is still -- transmitting. The only other allowed active symbol that lasts for -- this duration is a BREAK symbol. So we poll the bus for the shortest -- allowed BREAK time. If this is passed then we will cease our attempt -- to transmit. while vpwin loop -- allows for nodes with slower clocks to finish their assembler -- SOF‘s. local shorter movf f877_tmr1h,w -- The new starting point for the compare register movwf f877_ccpr2h -- is updated while the other nodes finish. movf f877_tmr1l,w movwf f877_ccpr2l movf f877_tmr1h,w -- ensures proper loading of CCP2 with the current movwf f877_ccpr2h -- timer value bcf status_Z movlw 0x01 subwf f877_ccpr2h,w -- magnitude comparison of the current high byte BTFSS status_Z -- if the current timer value is greater than GOTO shorter -- 0x128 then the maximum allowed transmission movlw 0x28 -- length for a SOF has been reached. If it isnt bsf status_C subwf f877_ccpr2l,w -- then we need to poll the bus again and repeat. BTFSS status_C GOTO shorter bsf error_flag shorter: end assembler if error_flag then vpwout = false tmr1on = false f877_tmr1l = 0x00 f877_tmr1h = 0x00 f877_ccp2con = 0x00 return false end if end loop -- SOF is always followed by data bytes. Prep for first bit to be sent assembler -- add next period to compare reference, 8-bit + 16-bit bank MOVF next_bit,W bank ADDWF f877_ccpr2l,f BTFSC status_C bank INCF f877_ccpr2h,f end assembler asm clrf f877_ccp2con -- first bit to be transmitted is always low f877_ccp2con = comp_drive_high -- set ccp2 to drive output high on match pir2_ccp2if = false -- reset interrupt flag send_sof = false -- no more start of frame symbols to transmit else -- just send the remaining bits assembler -- add next period to compare reference. 8-bit + 16-bit bank MOVF next_bit,W bank ADDWF f877_ccpr2l,f BTFSC status_C bank INCF f877_ccpr2h,f end assembler end if while ! pir2_ccp2if loop -- wait until end of period, when loop is -- exited bus transition should be done. prtc_buf = port_c -- Check for arbitration. If output does not prtc_buf = prtc_buf & 0x06 -- match input then arbitration may be lost. if ((prtc_buf == 0x04) & (! pir2_ccp2if)) then assembler bcf tmr1on -- Comparison between the bus transition time -- and the time remaining before bus expected -- transition is used to compensate for clock -- mismatch. Therefore false "lost arbritration" -- is avoided. This portion only checks for nodes -- that transition early. Nodes that transition -- later are dealt with after this portion. bsf status_C bcf status_Z local EXITE, EXIT MOVF f877_tmr1l,W -- determine delta between expected transition SUBWF f877_ccpr2l,w -- and actual transition time. movwf timer_low_byte -- timer_X_byte = ccpr2X - f877_tmr1X MOVF f877_tmr1h,W BTFSS status_C INCF f877_tmr1h,W SUBWF f877_ccpr2h,w movwf timer_high_byte btfss status, status_z -- the difference between high bytes must be zero goto EXITE movlw 0x14 -- to be within tolerance the difference must be less bsf status_C -- than 20 instructions. Setting the Carry/Borrow bit subwf timer_low_byte,w -- makes it available to borrow. BTFSS status_C -- If the low byte is >= 20 then a borrow goto EXIT -- will not have occured and status_c will still be set. BTFSC status_Z -- If the result is 0 then transition occured right on goto EXIT -- the boundary and is still valid. EXITE: -- check somewhere has not passed. bsf error_flag EXIT: end assembler if (error_flag) then -- bus dominance lost. vpwout = false -- "Shut up and try again later" tmr1on = false f877_tmr1l = 0x00 f877_tmr1h = 0x00 f877_ccp2con = 0x00 return false else -- early transitioning node transitioned within specs. Change f877_tmr1l = f877_ccpr2l -- output and move on. f877_tmr1h = f877_ccpr2h tmr1on = on end if end if if ((prtc_buf == 0x02) & (! pir2_ccp2if)) then -- bus fault has been detected. Try again later. vpwout = false tmr1on = false f877_tmr1l = 0x00 f877_tmr1h = 0x00 f877_ccp2con = 0x00 return false end if end loop pir2_ccp2if = false -- reset ccp2 interrupt flag if dom_pass then -- switch transition directions -- first must allow for nodes that are transmitting their active signals -- for just a little bit longer than we are. For the short pulse, the -- maximum allowed overshoot in time is 32uS and for the long pulse the -- maximum allowed overshoot in time is 34uS. Splitting the difference -- we will check for 33uS. However, I believe this to be incorrect and used 16uS. if vpwin then -- bus is still active assembler local loop1, done, exita movlw comp_only movwf f877_ccp2con MOVlW 0x14 bank ADDWF f877_ccpr2l,f BTFSC status_C bank INCF f877_ccpr2h,f loop1: BTFSS vpwin -- loops until either the input pin changes goto done -- or our "added" time expires. BTFSS pir2_ccp2if goto loop1 bcf tmr1on -- if the program gets here then that means the input bcf vpwout -- is still set and our timer period has expired. If clrf f877_tmr1l -- we were sending a short pulse then it means arbitration clrf f877_tmr1h -- has been lost. If it is a long pulse then it could clrf f877_ccp2con -- mean a break symbol is being transmitted. Either way bsf error_flag -- we need to stop transmitting and exit. bcf pir2_ccp2if goto exita done: clrf f877_ccp2con -- updates the compare registers with new movf f877_tmr1h,w -- starting values movwf f877_ccpr2h movf f877_tmr1l,w movwf f877_ccpr2l movf f877_tmr1h,w movwf f877_ccpr2h bcf pir2_ccp2if exita: end assembler if error_flag then return false end if end if f877_ccp2con = comp_drive_high -- if previously sending an active signal dom_pass = false -- next symbol will be passive. Ergo need else -- to drive bus high on next compare. f877_ccp2con = comp_drive_low -- Same logic here just opposite results dom_pass = on -- No need to check for devices with "longer" end if -- periods here because we have driven the -- bus high already. data = data << 1 -- shift in next bit to be sent end loop return true end function -- procedure vpw_receive configures the ccp1 module to capture timer 1 and timer2 -- to time inactivity. First ccp1 is configured to interrupt on the rising -- edge. On the first rising edge, timer 1 is started and the ccp1 module is -- reset for falling edge int. On the following edge interrupts, the two different -- captured timer 1 values are subtracted from each other to determine the -- pulse witdh. Then based upon which direction of the interrupting edge, the -- symbol is decoded. Once a SOF character has been decoded timer2 is enabled to start -- counting out 280uS to determine if IFS has been satisfied. Timer2 is reset -- on each valid pulse received. Therefore, timer2 clocks 280uS from the last bit received. -- Once a bit is received, it is placed in a bit buffer that is shifted left -- after each bit. When 8 bits have been received, that byte is placed into -- the first available frame buffer array. -- ***NOTE*** In-Frame Response has NOT been implimented yet!! If you are using -- this library for a VPW system that requires IFR then you will need to add it. -- I will get around to adding it sometime in the near future but right now I do -- not need it (GM doesn‘t use IFR). -- ---------------------------------------------------------------------------- function vpw_receive return bit is var byte delta_low_old = 0x00, delta_high_old = 0x00 -- buffers delta bytes var volatile byte rec_buff = 0x00 -- buffers the current received byte before -- loading into the buffer array var byte bit_count = 0x00 -- counts number of bits received var volatile bit bit_rec = false -- holds current received bit var volatile bit data_rec = false -- determines whether data has been received var volatile bit bit_buff at rec_buff : 0 -- stores current received bit var volatile bit long_short = false -- flag indicating long or short pulse var volatile bit brk_rec = false -- flag indicating a BRK symbol has been received var volatile byte ccpconfig = 0x00 -- stores current ccp1 configuration var volatile bit sof_rec = false f877_tmr1l = 0x00 -- reset timer 1 f877_tmr1h = 0x00 tmr1_low_old = 0x00 -- reset tmr1 storage variables tmr1_high_old = 0x00 array4_put_index = 0x00 -- reset processing buffer index to 0 pulse_rec = false f877_ccp2con = 0x00 intcon_gie = on -- enable interrupts intcon_peie = on pir1_tmr2if = false vpw_idle_chk -- wait until beginning of next frame before starting to receive asm clrf f877_ccp1con f877_ccp1con = rising while ! pir1_tmr2if loop while ! pulse_rec loop end loop pulse_rec = false delta_low_old = delta_low -- buffer the delta values incase interrupt occurs delta_high_old = delta_high -- before we finish working with this pulse ccpconfig = f877_ccp1con -- buffer ccp1 configuration incase interrupt -- occurs before we finish working with this pulse if ((delta_high_old == 0x00) ) then if delta_low_old >= 0xCC then sof_rec = on tmr2on = on bit_count = 0x00 f877_tmr2 = 0x00 array4_put_index = 0x00 elsif ((delta_low_old <= 0xCB) & (delta_low_old >= 0x78)) then long_short = on data_rec = on f877_tmr2 = 0x00 elsif ((delta_low_old <= 0x77) & (delta_low_old >= 0x2A)) then long_short = off data_rec = on f877_tmr2 = 0x00 else data_rec = false end if elsif ((delta_high_old == 0x01) ) then if ((delta_low_old >= 0x2B) & (! pir1_tmr2if)) then brk_rec = on sof_rec = false data_rec = false long_short = false tmr1on = false tmr2on = false f877_tmr1l = 0x00 f877_tmr1h = 0x00 f877_tmr2 = 0x00 array4_put_index = 0x00 tmr1_low_old = 0x00 tmr1_high_old = 0x00 delta_low_old = 0x00 delta_high_old = 0x00 asm clrf f877_ccp1con f877_ccp1con = rising elsif delta_low_old < 0x2B then sof_rec = on tmr2on = on pir1_tmr2if = false bit_count = 0x00 f877_tmr2 = 0x00 array4_put_index = 0x00 end if end if if data_rec & sof_rec then if ccpconfig == rising then -- if the next interrupt edge will be rising if long_short then -- then that means the previous pulse was active. bit_rec = off -- that means a long pulse = 0 and a short pulse = 1 else bit_rec = on end if else -- next interrupt edge will be falling, indicating the if long_short then -- previous pulse was passive. long_pulse = 1 & short_pulse = 0 bit_rec = on else bit_rec = off end if end if rec_buff = rec_buff << 1 bit_buff = bit_rec data_rec = false bit_count = bit_count + 1 end if if bit_count == 0x08 then array4 = rec_buff bit_count = 0x00 end if end loop tmr1on = false tmr2on = false f877_ccp1con = 0x00 f877_tmr1l = 0x00 f877_tmr1h = 0x00 f877_tmr2 = 0x00 pir1_tmr2if = false pir1_ccp1if = false intcon_gie = false intcon_peie = false return brk_rec end function -- Table for determining pulse widths from delta values: -- |long pulse| short pulse| SOF | break | -- | max | min | max | min | max | min | max | min | -- delta_high |0x00 | 0x00| 0x00 | 0x00| 0x01| 0x00| 0x01| 0x01| -- delta_low |0xCC | 0x78| 0x78 | 0x2A| 0x2B| 0xCC| 0x2B| 0x2B| -- -- Logic Flow: If delta_high > 0 then either SOF or BREAK -- if delta_low - 0x2B > 0 & delta_high > 0 then it is a break -- if delta_low - 0x2B <= 0 & delta_high > 0 then it is a SOF -- cases for delta_high == 0 -- IF delta_low - 0xCC > 0 then it is a SOF -- If delta_low - 0x78 >= 0 & delta_low - 0xCC < 0 then it is a long pulse -- if delta_low - 0x2A >= 0 & delta_low - 0x78 < 0 then it is a short pulse -- if delta_low - 0x2A < 0 then it is too short procedure interrupt_handler is -- edge has been detected on ccp1 pragma interrupt tmr1_low_new = f877_ccpr1l tmr1_high_new = f877_ccpr1h -- tmr1_new - tmr1_old = delta assembler -- 16-bit subtraction of old values from the new btfss tmr1on bsf tmr1on bank MOVF tmr1_low_old,W bank SUBWF tmr1_low_new,W bank MOVWF delta_low bank MOVF tmr1_high_old,W BTFSS status_C INCFSZ tmr1_high_old,W bank SUBWF tmr1_high_new,W bank MOVWF delta_high end assembler tmr1_low_old = tmr1_low_new -- replace old values with new ones tmr1_high_old = tmr1_high_new if ccp1m0 then -- rising edge interrupt has been detected on ccp1 assembler clrf f877_ccp1con -- configure for falling edge movlw falling movwf f877_ccp1con end assembler else -- falling edge detected assembler clrf f877_ccp1con movlw rising movwf f877_ccp1con end assembler end if pir1_ccp1if = false pulse_rec = on end procedure
时间: 2024-10-04 19:22:26