Shop OBEX P1 Docs P2 Docs Learn Events
Schematic to use with Chris Gadd's OBDII object. — Parallax Forums

Schematic to use with Chris Gadd's OBDII object.

sccoupesccoupe Posts: 118
edited 2014-07-28 13:01 in Propeller 1
Does anyone have a good schematic available that is tested to work with Chris Gadd's OBD2 object? There is one in the Spin code, but I cannot seem to make it work. I added a MCP2551 to a Propellor proto board but cannot get it to work. Its not connected to a vehicle but to a Microchip can bus analyzer. I should see CAN packets when the PID list is requested but I only get error frames. Ive checked that the analyzer is working correctly by hooking it up to a J1939 simulator and all is well. Thoughts, ideas?

Thanks

Comments

  • ChrisGaddChrisGadd Posts: 310
    edited 2013-11-10 10:58
    The schematic in the Spin code is pretty much it, I only ever constructed it on a breadboard. The Tx is transmit from the Prop to the 2551, Rx is from the 2551 to the Prop, power the 2551 with 5V ... do you have termination resistors on the other end of the lines as required for high-speed? I used this CAN primer from KEIL as the basis for the circuit.
    The transmitter is hard-coded to 500Kbps, 11-bit standard frame, only ever tested with a 2009 Chevy Cobalt. It shouldn't ever send an error frame, as the writer inserts a stuffed bit after five consecutive 1's or 0's, never getting to the sixth that triggers the error.

    If you just want something to look at, I'd use something like:
    CON
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
    
      CAN_tx   = 0
      CAN_rx   = 1
    
    OBJ
      Writer: "CANbus Writer 1.0"
    
    PUB Main
      writer.start(CAN_tx,CAN_rx)
      repeat
        writer.send(%11111011111,8,11,22,33,44,55,66,77,88)       ' as an example
        waitcnt(cnt + clkfreq)
    
    That should give you the most control over what it sends, and hopefully what you see on the analyzer.
  • sccoupesccoupe Posts: 118
    edited 2013-11-10 11:16
    Thanks Chris. As soon as you mentioned 500k baud, I knew what was wrong. I was set at 250k on the analyzer for the J1939 simulator. As soon as it switched over to 500k, the frames displayed correctly. Your code will be very helpful in playing around here. My end game is to use the receiver to create a new array for every new ID that shows up on the bus and continually update the data from each ID in its own array. Then, I can use a seperate microprocessor to request each ID via rs232 as needed. This allows the propeller to do all the hard work. Your code is well documented so I'm anxious to start modifying it allow for all ID's and not just the couple that it looks for to handle standard OBD2 stuff.
  • sccoupesccoupe Posts: 118
    edited 2013-11-11 06:56
    What can you recommend for getting the frame ID from the CAN reader back to the main program? I've altered the reader so far to send the data part of the frame for all ID's and it works. It seems like the time to do it is where the ID is stored in t1 when it was compared to reset if not the two OBD2 ID's. A mov function will be involved but I don't know enough about assembly to get it into a variable.
  • ChrisGaddChrisGadd Posts: 310
    edited 2013-11-12 13:15
    I wanted to test some things before answering, and realized that more modifications were needed to make it a general-purpose reader. I added support for standard-frame and extended-frame messages – other frame types are also possible. Added support for an arbitrary number of data bytes, including zero-byte messages, and realized that the DLC also needed to be stored in order to be useful. I moved all of the Spin variables inside the object to make it as self-contained as possible, though a bit of Spin code in the parent object is still necessary to deal with the mix of data types – can't store the ID in a byte-sized register. I also created two versions of the reader, one with a single array and one with a circular buffer with space for eight messages, which gives you more time to process the messages in case several arrive in a burst.

    I tested them as much as I could without an actual CANbus to connect to.
    CANbus Monitor - array - Archive.zip
    CANbus Monitor - single - Archive.zip
  • sccoupesccoupe Posts: 118
    edited 2013-11-12 15:42
    Wow! Thanks a million Chris, you done all of the leg work. I cant wait to give this a try. Thanks again for your time on this!
  • ChrisGaddChrisGadd Posts: 310
    edited 2013-11-13 05:39
    I got around to testing with my writer object and discovered a couple bugs:
    In the main method of the CANbus monitor-array object, Data needs to equal ID + 4 * 8
    In the PASM section of both reader objects, under parse_DLC_loop, 'rcl t1,#1' needs to be changed to 'rcl Temp_DLC,#1'

    One more: again in the PASM section of the readers, under the Parse CRC_loop there needs to be an instruction between the movd and the instruction that it modifies. Simply move the add instruction down one line.
                 movd      :loop,#Temp_Data
                 add        t1,#1
    :loop      wrbyte    0-0,t1
    
  • sccoupesccoupe Posts: 118
    edited 2013-11-13 11:48
    Chris,

    So far no luck with this, but still playing around. The new reader and main program (even with latest fix) do not send anything to the terminal program and cause passive CAN errors as soon as I send anything from the Microchip CAN Analyzer. If I use the code below for the main program and the original can read object(modified to not reset on other ID's that are not 7E8 and 7DF), I can send frames though the Microchip Analyzer and the Prop will send the 8 data bytes to the terminal program with no CAN errors.
    OBJ
      Reader: "CANbus Reader 1.0"
      FDS:  "FullDuplexSerial" 
      
    PUB Main | x
      reader.Start(CAN_tx,CAN_rx,@Receive_Buffer)
      FDS.start(31, 30, 0, 115200)
      waitcnt(clkfreq + cnt)
    
    repeat
      FDS.str(@Receive_buffer)
    
  • ChrisGaddChrisGadd Posts: 310
    edited 2013-11-14 06:09
    My only guess is that the analyzer requires acknowledgements, as that's the only time the reader ever drives the bus. So with that in mind, I've made some minimal changes to the basic reader routine to have it return the ident, and I've also modified the 8-message version to send acks.
    I'd just assumed that any acks would come from other devices on the bus, and that the analyzer would carry on regardless.

    CANbus Monitor - basic - Archive.zip
    CANbus Monitor - array 0.1 - Archive.zip
  • sccoupesccoupe Posts: 118
    edited 2013-12-08 09:55
    Thanks Chris, this has been working well and seems pretty stable. One thing that i'm running into is that it seems like if I stop emptying the buffer, it fills up and the acks from the prop quit. Is this by design or do I have something else wrong on my end?

    Thanks
  • ChrisGaddChrisGadd Posts: 310
    edited 2013-12-08 10:58
    Hmm, looks like I did. In the Parse_Acknowledge routine, the line if_nz jmp #$-1 causes it to loop until there's room in the buffer before continuing. Comment that line out to have the routine overwrite old data. Or change it if_nz jmp #Parse_EOF to have it discard the new data.
  • redheadedrodredheadedrod Posts: 78
    edited 2014-01-16 13:30
    sccoupe, are you still working on this? I am working on a project that I need to do this too so I would like to maybe share notes or something.

    Thanks!

    Rodney
  • sccoupesccoupe Posts: 118
    edited 2014-05-30 06:24
    Hello Chris,

    This code seems to be really stable and testing has been going great. Now messing with different speeds and running into issues. Changing the frqa move from 500khz to 250khz is causing a lot of bus errors. I've tried tweaking the 250khz long value in small increments in case it was just a slight timing issue but with no good results. Your calculation for the frequency (Frequency = 2^32 * 12.5ns * bitrate) i'm having troubles fully understanding.

    Frequency = 500
    2^32 = 4294967296
    12.5 = 12.5
    bitrate = 500000

    None of this calculates so I have something wrong above.

    It should be worth noting that if I leave everything at the 500khz and set the PLL to 8 from 16, everything works great at 250kbps. Also, what are your thoughts on 1mbit bus support? Your notes say not to expect much faster than 500kbps. Is this a running out of time (prop not fast enough) problem or a stability problem. Here I've tried just doubling the 500khz long value with no success.

    Also, one other thing just to clarify.... A long address like 0x000CB800 will not be handled as the lower 11 bits are zero, but 0x000CB801 would be handled correctly?

    Thanks again for your help Chris, this is some very good code you have put together here.
  • ChrisGaddChrisGadd Posts: 310
    edited 2014-05-30 18:33
    Great to hear that you're getting good use out of it!

    The writer object uses a waitcnt instruction to establish a 500Kbps transmit speed. 1 / 500K = 2us, 2us / 12.5ns = 160. In order to have the writer work at 250Kbps, change the delay to 320.
    The reader object uses a counter set to 500KHz. 2^32 * 12.5ns * 500_000 = 26_843_546. For 250KHz, use 13_421_773. The reader begins monitoring the input pin when the counter passes 270 degrees, and samples when a transition occurs or when the counter rolls over 0 degrees, so that should work the same regardless of the frequency.
    In the Parse_Acknowledge section, I use a djnz loop to create a 2us delay for sending the acknowledge bit. For 250Kbps mov Delay_counter,#40 needs to be changed to mov Delay_counter,#80. 50ns * 80 = 4us.
    Been a while since I looked at this code, but I don't see any other obvious gotchas.

    As for the top speed, that's due to the Prop not being fast enough to sample the pin on a transition, determine if it's a real or a stuffed bit, decrement or reset the stuffed-bit counter, parse the bit, and update the CRC, or if there was no transition and the counter rolled over to 0, determine if the bit should've been stuffed, and still do everything else.
    It could be made faster by storing every bit in a buffer and processing it afterward, though the acknowledge bit would be tricky; it'd either have to ack every message or no message. I spent a good bit of time optimizing it, though it's still entirely possible that one of the Prop gurus could find a more efficient routine.

    A long address like 0x000CB800 would be transmitted as 00000000000, 0x000CB801 would be sent as 00000000001. The reader supports 29-bit IDs, but the writer object would need to be modified. Following the Add_IdentA section, add two 1's for SRR and IDE to indicate an extended frame, then add eighteen bits of Ident_B. Everything after that should work the same.
  • sccoupesccoupe Posts: 118
    edited 2014-06-04 18:11
    Thanks Chris. PM Sent.
  • ChrisGaddChrisGadd Posts: 310
    edited 2014-06-07 16:25
    I hadn't really thought through the comment about buffering and acking everything; the unpredictability of stuffed bits would make that impossible, but it turns out that there's a better way. By re-visiting an earlier version of the reader, which splits the job of reading between two cogs, I managed to get it working at 1Mbps. One cog produces a 1MHz clock signal that is re-synced whenever the receive pin transitions, the second cog samples the receive pin whenever the 1MHz clock transitions high to low, parses the bits, removes stuffed bits, calculates the CRC, and sends an ack if everything checks out. Limited testing shows that it is also able to be down-clocked to 500KHz, just halve the frequency of the syncer and double the delay in the reader parse_acknowledge. This is just the single message reader, which stores the ID in a long, and the data-length followed by the received data in up to nine bytes.

    CANbus 1Mbps Main - Archive.zip
  • sccoupesccoupe Posts: 118
    edited 2014-06-07 19:03
    Your a heck of a guy Chris. Running across someone that wants to help for the sake of helping seems to be more and more rare. All is much appreciated.

    On this new reader.... Does the CAN side keep updating the variables with ID and Data regardless of the FDS reading those variables? In your other code, if the buffer is full the new data is dumped until a buffer spot is open, but its difficult for me to find such a thing in the new code so, I'm unsure of how it is operating even though the new code doesn't have a buffer. The ID's seem to come though great at 1Mbit and the data appears to come though as well, but seems to be less reliable. I wonder if the reader is writing over the data variables before the FDS transmits the entire string or is this even possible?

    Thanks again!


    Some further info added here. In the attached picture, the top portion is the terminal showing only the 0x444 ID. The lower portion shows the full bus info. The data '15 13 00 00 07 00 DA 25' is the correct data for it, but strangely it appear to more often than not, use the correct first two bytes and then use the last 6 bytes from ID 0x111. I suspect that the over writing is occurring but am also confused that it is not more random in nature.

    Untitled.jpg
    1024 x 359 - 94K
  • sccoupesccoupe Posts: 118
    edited 2014-06-08 06:15
    Messing around with the reader here a bit and think I'm on the right track but no go yet. Comparing what is going on the the other code, a new variable t3 has been created though its noted that this is a throwaway, presumably to allow for a 'rdlong' command to be fulfilled. Not sure what the 'wz' is for unless this is the variable that the 'rdlong' is stored into, though i dont see 'wz' in the variable list. The jump command after it is to skip all of the writing (ident and data) if the current ident variable is not 0. Not sure if the 'if_nz' is not zero or perhaps relates to the 'wz' in the previous line as if maybe the z is the variable. Thanks for your patience, i'm just thinking out loud a little and looking for corrections on what part of my thinking is incorrect.

    Parse_Acknowledge
                            or        dira,Tx_mask                                  ' Transmit an acknowledge (low for 2us)
                            mov       Delay_counter,#20                             ' 2us
                            djnz      Delay_counter,#$
                            andn      dira,Tx_mask
                            rdlong    t3,t1                       wz
              if_nz         jmp       #Parse_EOF
                            wrlong    temp_Ident,Ident_address
                            wrbyte    Temp_DLC,Hub_buffer_base
                            movd      :loop,#Temp_data                              ' Transfer data from the temporary buffer in to the hub buffer
                            mov       t1,Hub_buffer_base
                            add       t1,#1
                            mov       t2,Temp_DLC
    :loop                   wrbyte    0-0,t1
                            add       :loop,Bit_9
                            add       t1,#1
                            djnz      t2,#:loop
    
  • ChrisGaddChrisGadd Posts: 310
    edited 2014-06-08 15:54
    That last file was just to make it was going to work at the most basic level, here's one with the 8-message array added.

    CANbus 1Mbps Main 0.05 - Archive.zip

    The rdlong operation checks for room in the buffer, setting the zero flag if there's room. The following line jmps to the end if there isn't room.
  • sccoupesccoupe Posts: 118
    edited 2014-06-08 18:22
    Ahhh, I see. This looks pretty good. The 11 bit ID's are working fine, but the 29bit ID's are not coming through though. Also, another question... I see that there is a TX pin assigned but not sent to another object. The reader object also shows that ACK's are still to be sent. How does it know which pin to send the ACK to if the TX pin designation isnt sent anywhere?

    Many thanks again Chris. This is all great work!
  • ChrisGaddChrisGadd Posts: 310
    edited 2014-06-08 19:53
    D'oh, I re-purposed the tx_pin for use as a sync pin, and then forgot about it as I'm using a pattern generator to create the test messages, which didn't use acks.
    Dunno what to make of the extended frames.

    CANbus 1Mbps reader 0.06 - Archive.zip
  • ChrisGaddChrisGadd Posts: 310
    edited 2014-06-13 13:03
    I squashed a sneaky bug in the 1Mbps reader object that only affected certain messages. While I was at it, I added a spin handler to allow bitrates to be calculated by the objects, and also added methods to retrieve ID and data bytes more easily. Feeling like I was on a roll, I modified the CANbus writer to also support 1Mbps, and for an encore I created a couple Frankenstein objects combining a reader and writer. The combined reader/writers cannot receive while transmitting, and won't read any incoming message that wins arbitration over an outgoing message, but they require one fewer cog than the stand-alone routines.

    Check it out at the Obex
  • sccoupesccoupe Posts: 118
    edited 2014-06-13 17:52
    Great job Chris! What was the sneaky bug and how did it effect what messages?

    Thank.
  • sccoupesccoupe Posts: 118
    edited 2014-07-23 10:45
    More questions for you Chris. Its seems that some transmitting CAN devices are unhappy with how the Propeller is reading the bus, while most are OK. When in a scenario where there are only two devices, one transmitting, and the Propeller receiving, some devices seem to run away with the data speeds and acting erratic. I can see this if I hook up a CAN analyzer in listen only mode. If I switch to normal mode on the analyzer (the analyzer now sends ACK's) everthing works fine. This doesnt happen with all devices but I have seen the problem with both 500k and 1mb speeds. Since the offended devices seem happy when the CAN analyzer is sending ACK's and unhappy when ONLY the Propeller sends ACK's, there must be some timing problem there but only a slight one because other devices seem quite happy with the Propeller ACK's. Based on your assembly comments, the "mov Delay_counter, #40" is what causes a 2us ACK (at 500k). Is this the length of the pulse or a delay until a 2us pulse is sent. Since I dont know exactly what is wrong i've been playing around with the #40 with #38 or #42 for instance to try to lengthen or shorten the pulse or move it slightly forward or backward to see if the offended devices become happy with the ACK as they are when the CAN analyzer is supplying them. It would seem that its currently either slightly off timing wise or not quite long enough. If it were too long then the issue would still be there with the CAN Analyzer hooked up. Any thoughts on some adjustment techniques or other ideas to narrow this down?

    Thanks!
  • ChrisGaddChrisGadd Posts: 310
    edited 2014-07-23 13:21
    These things are so fussy with the acks. I was having ack problems with the new 1Mbps reader when I tried it with the car, and that reader uses a different technique. I think I fixed that one but haven't verified it; I'm awaiting delivery of a MCP2515 so I can hopefully figure this out once and for all.
    For the version that you're working with:
    '                       mov       Delay_counter,#20                             ' Wait for the current (CRC delimiter) bit to time out
    '                       djnz      Delay_counter,#$                              '  -Instruction overhead takes care of the delay
                            or        dira,Tx_mask                                  ' Assert a low
                            mov       Delay_counter,#40                             '  Set up time delay 40 * 50ns = 2us
                            djnz      Delay_counter,#$                              '  Wait for time delay to expire 
                            andn      dira,Tx_mask                                  '  Release the line
    
    The 40 is the length of the acknowledge pulse, you might try uncommenting the top lines and tweaking the #20 and seeing if that makes the other devices are happy. Also try the 1Mbps reader again, in addition to the earlier bug--caused by resetting the stuffed bit counter to 5 rather than 4 following a stuffed bit, which only became a problem in certain bitstreams, and during testing only showed up in the CRC--I also found a total noob mistake of using ina in the destination field. The hopefully fixed version is in the obex.
  • sccoupesccoupe Posts: 118
    edited 2014-07-23 16:04
    Chris,

    What i've seen so far is that #26 seems to get it much closer to 2us at 500k. Interesting is that this time seems to swing wildly from as low as 0.5us to 2.7us from the Propeller, but is much more stable at 2.25us to 2.5us when measured from the CAN Analyzer. Funny you mention the MCP2515, I just had a few arrive today for this purpose. If it comes down to it, I'll add one just to do the ACK. Crazy to do so but it would work.
  • ChrisGaddChrisGadd Posts: 310
    edited 2014-07-28 13:01
    I received my MCP2515 and had a chance to play today. I had the Prop connected to the MCP2515 through SPI, the 2515 connected to a 2551 transceiver, the 2551 connected to a second 2551 through CAN, and the second 2551 connected directly back to the Prop. My 500Kbps reader was sending an ack about a quarter bit-width too early, but that was easily fixed by swapping a couple instructions in code. The 1Mbps reader might be cutting the ack a couple hundred nanoseconds short, but it should still be withing spec. If I padded it to get it spot on at 1Mbps, it'd be off even more at 500Kbps. Here're some comparisons at the two bitrates using a simple message (ID $78F, 1 data byte, $55):

    1Mbps comparison.jpg
    500Kbps comparison.jpg


    No idea why some devices are having problems with that.
    1024 x 346 - 63K
    1024 x 476 - 82K
Sign In or Register to comment.