CAN Bus Simulation
TEMU provides support for CAN bus based devices.
The bus model interfaces are available in: temu-c/Bus/Can.h
.
In addition to the interfaces one CAN bus model is provided.
As CAN is a multi-node bus, a bus model object is needed to route messages to the relevant destination.
There are two types of CAN classes that can be created, firstly bus models and secondly device models. A bus model is responsible for routing messages to the device models, and the device models implement CAN message reception logic.
The standard SimpleCANBus
bus model, provides fairly simple logic.
It routes a sent message to all devices connected to the CAN bus
(except the sender device).
However, CAN devices often implements filtering of message IDs in hardware,
and this filtering (which is typically based on a mask and code pair)
can be used to define a smart CAN bus model.
Such a model would route frames using internal routing tables.
A smart CAN bus model will by definition be system specific. It’s routing will depend on the system specific allocation of message IDs. |
Interfaces
The interesting interfaces are defined in the temu-c/Bus/Can.h
header.
This header also define inline functions to help construct CAN frames.
typedef struct {
uint8_t Data[8];
uint32_t Flags;
uint8_t Length;
uint8_t Error;
} temu_CanFrame;
struct temu_CanDevIface {
void (*connected)(void *Dev, temu_CanBusIfaceRef Bus);
void (*disconnected)(void *Dev);
void (*receive)(void *Dev, temu_CanFrame *Frame);
};
struct temu_CanBusIface {
void (*connect)(void *Bus, temu_CanDevIfaceRef Dev);
void (*send)(void *Bus, void *Sender, temu_CanFrame *Frame);
void (*enableSendEvents)(void *Bus);
void (*disableSendEvents)(void *Bus);
void (*reportStats)(void *Bus);
void (*setFilter)(void *Bus, temu_CanDevIfaceRef Dev, int FilterID,
uint32_t Mask, uint32_t Code);
};
The CAN frame is central to the transmission of CAN data. It is not a bit by bit representation of the CAN protocol, rather it is a simplified format that omit bits that are implicit and ensures that relevant bits such as RTR is fixed in location.
If a real CAN frame is needed, the frame struct need to be transformed to the needed representation. Note that the struct is optimised for performance (e.g. Data is first and can be bitcopied as a uint64).
Device models are typically simple, they implement the connected
,
disconnected
and receive
functions.
If the device needs registers and MMIO handling, it tends to get more complex.
The device and bus interface support connect
and disconnect
events.
The purpose of these are to support hot-plugging of CAN devices.
As these connect
and disconnect
events are supported,
the normal connect
command should not be used when connecting a CAN
device, rather the can-connect
command is to be used.
Commands
Two CAN bus related commands are provided:
Name | Description |
---|---|
can-connect |
Connect a CAN device to a CAN bus. |
can-disconnect |
Disconnect CAN device from a CAN bus. |
Classes
The SimpleCAN bus class provides a CAN bus model. In the SimpleCANBus class, messages are forwarded to all connected devices (except the sending one). If this results in performance issues, it is possible to write a filtering CAN bus model.
SimpleCANBus Reference
Properties
Name | Type | Description |
---|---|---|
Class |
*void |
Class object |
Component |
*void |
Pointer to component object if part of component |
LoggingFlags |
uint64_t |
Flags for logging info |
Name |
*char |
Object name |
TimeSource |
*void |
Time source object |
devices |
temu_IfaceRefArray |
CAN devices attached to bus |
stats.lastReportSentBits |
uint64_t |
Statistics |
stats.sentBits |
uint64_t |
Statistics |
Examples
This example shows how to create a simple CAN device and connect it to a bus model.
exec ut700.temu
import MyCanDevice
# Create a can bus
create class=SimpleCANBus name=canbus0
create class=MyCANClass name=mycan0
can-connect bus=canbus0:CanBusIface dev=occan0:CanDevIface # From ut700
can-connect bus=canbus0:CanBusIface dev=mycan0:CanDevIface
The next example shows how to implement a simple CAN device
#include "temu-c/Bus/Can.h"
#include "temu-c/Bus/Objsys.h"
// This is a device / RTU model, it needs to know about its CAN bus
typedef struct MyCanDevice {
temu_Object Super;
temu_CanBusIfaceRef Bus;
} MyCanDevice;
void*
create(const char *Name, int Argc, const temu_CreateArg *Argv)
{
MyCanDevice *Dev = malloc(sizeof(MyCanDevice));
memset(Dev, 0, sizeof(MyCanDevice);
return Dev;
}
void
dispose(void *Obj)
{
MyCanDevice *Dev = (MyCanDevice*)Obj;
free(Dev);
}
// Implement the CAN Device interface
void
connected(void *Obj, temu_CanBusIfaceRef Bus)
{
MyCanDevice *Dev = (MyCanDevice*)Obj;
Dev->Bus = Bus;
temu_logInfo(Dev, "connected to CAN bus");
}
void
disconnected(void *Obj)
{
MyCanDevice *Dev = (MyCanDevice*)Obj;
Dev->Bus = {NULL, NULL};
// NOTE: This should also stop any pending events related to
// message transmissions
temu_logInfo(Dev, "disconnected from CAN bus");
}
void
receive(void *Dev, temu_CanFrame *Frame)
{
temu_logInfo(Dev, "received CAN message with msg id %u",
temu_canGetIdent(Frame));
}
temu_CanDevIface CanIface = {
connected,
disconnected,
receive,
};
TEMU_PLUGIN_INIT
{
temu_Class *cls = temu_registerClass("MyCANClass", create, dispose);
temu_addProperty(cls, "CANBus", teTY_IfaceRef, 1);
temu_addInterface(cls, "CanDevIface", "CanDevIface", &CanIface);
}