CAN Part 10 - Running Code on XNet Hardware (continued CRC and Counter)

NI-CAN hardware was useful enough when it first came out.  You could read and write CAN frames, and with some hardware read and write CAN signals, and that was about all that was asked for from developers.  But as time went on NI saw the need to come up with a more flexible set of API drivers, and I'm guessing they thought this would be a good opportunity for a hardware refresh too.  This is where XNet came in and while I've already covered several topics on XNet, I know of one new topic that hasn't been covered anywhere because it is an undocumented, and incomplete feature going back to the the LabVIEW 8.6 era.  And I'm going to peel back just a bit of the curtain here in the hopes that others will find it useful and possibly so NI will put efforts into making it an official feature.

...So What Is It?

Okay so let me drop what seemed like a bomb shell to me, when I first heard about it.  All XNet hardware (as far as I know) has the ability to run arbitrary code that can be loaded at run-time from within LabVIEW.  What this means is XNet hardware has the ability to function as it always does, reading and writing data and working within the confines of the XNet API.  But in addition to that, it has the ability to load compiled binaries, and run code on specific events.

...Restrictions?

Well I don't want to go into all of the details but lets just say there are lots of restrictions.  The code that can be ran is not LabVIEW, it is texted based code, compiled to the specific microcontroller on the XNet hardware.  In addition to that, the events that code are ran on are pretty limited.  The code that you write does not run periodically and has pretty limited access to global information.  

I was hoping an entire protocol like ISO 15765, or XCP, could be loaded on the hardware so that the host software wouldn't need to handle that, but that isn't currently possible.  I also thought it might be useful to have the hardware always reply to a specific frame with a specific frame.  But again the limitations at the moment mean this isn't possible.

...So What Good Is It?

At the moment, the only real thing I see that is useful with this functionality, is to perform an action, just before a frame is to be sent out.  This means just before a frame is to be sent out, an action can be done.  The most common thing I can think of is to modify the payload of the frame that is to be sent out, but I think other things could be done like preventing a frame from going out.  This event is triggered when the hardware is preparing to send the frame, and not necessarily when the XNet Write function is called.  This means a periodic frame that is sent out every 10ms, will call the custom code every 10ms, after the first XNet Write call.

Great How Does It Work?

Okay so remember when I said this was undocumented?  Well this is where I start holding back on what I know.  NI unofficially said I could post this information publicly, if I didn't go into the whole process of how to setup the tool chain, write code, compile code, deploy it, and open up the properties that allow this to work.  The reason for this is because this functionality is relatively old, and not many in NI know it exists, let alone how to support it.  If a flood of people start calling NI for support on tool chain issues, and debugging deploying code to hardware that shouldn't be deployed to, then NI will regret ever allowing me to talk about this publicly.  

Uh...So What Can You Tell Us?

Here is the good news.  Even though I can't show how to setup code, and compile it, I can share already compiled code, and demonstrate how it works for a couple of common uses.

CRCs and Counters Again?

Yes...again.  So back in Part 9 we talk about CRCs and Counter signals inside a payload of a frame.  Here we talk about how some devices require a counter to change value with every frame being sent, and we discuss the bucket filling problem, which ensures the values are correct, but rely on the hardware to send them at the specified rate.  Well this is the perfect application to our custom XNet code.  Take a look at this example, which is part of the CAN XNet Tools package, found in the CAN Drivers download:



So here we see that we setup the frame out single point session like normal.  Except we have an initialize and configure function that I wrote which is called before starting the session.  These two VIs are password protected (by NI's request) but I can describe what is in them, since it isn't very helpful.  The configure function takes a premade binary that NI has generated, which tells the XNet how to perform incrementing counters, and CRCs.  This binary is downloaded to the XNet using a hidden property node, and it then sends some config information.  The second subVI just sends down more config information using hidden property nodes.  The secret sauce in all of this is really the binary which can't be modified without the whole tool chain and source.

So after running these two functions, and starting the interface, a frame will be sent out every 10ms with a new value for the counter, and a new value for the CRC.  In the demo the counter is the first byte of the payload, and the CRC is the last byte in the payload.  The write function just updates the values of the other 6 bytes in the payload.  You can download the source and run it yourself.  I didn't make a package for this and am just uploading a zip with the VIs in it that are needed. Download can be found here.  The only dependency is on LabVIEW 2015 or newer, and you need the XNet drivers installed.

Part 11...

Now that we've gotten through most of the large topics I hope to step back and show some more specific examples of programming techniques I've used in the past.  The next one is a short and simple example on detecting a signal drop out, using the XNet Trigger and Single Point signal type.  The idea is that we often want to know what a signals value is, but also hold that value for some amount of time before reverting to a value that lets us know the signal hasn't been seen.  This example shows the most efficient method I know of accomplishing this.