CAN Terminals
CAN is a protocol, very common in automotive systems. It is also being used in several spacecraft and by hobbyists. CAN is a multi-node bus, meaning that a bus model is needed for it. TEMU comes with a model of the OpenCores CAN controller (with the AMBA P&P extensions for GRLIB compatibility) and a simple bus model. With simple, it is meant that the bus model broadcasts every message and does not provide any built-in routing or filtering. Broadcasting is potentially expensive for large CAN networks, and for these cases it is possible to replace the simple CAN bus model with a custom one.
You need to implement three functions in a CAN device:
connected, disconnected and receive.
The temu_CanDevIface
is defined in temu-c/Bus/Can.h
.
The CAN connected function is called when the device has been connected to a CAN bus, it needs to ensure that the bus interface reference is saved in the device model:
void
connected(void *obj, temu_CanBusIfaceRef bus)
{
MyDevice *dev = (MyDevice *)obj;
dev->bus = bus;
const char *busName = temu_nameForObject(bus.Obj);
temu_logInfo(obj, "connected to CAN bus %s", busName);
}
The disconnected function is called whenever the user disconnects a CAN device from the bus (e.g. to simulate someone/something severing the cable). This function should clear the CAN bus property:
void
disconnected(void *obj)
{
MyDevice *dev = (MyDevice *)obj;
// Stop any in-flight events
// Disconnect the bus iface reference
dev->bus.Iface = NULL;
dev->bus.Obj = NULL;
temu_logInfo(obj, "disconnected");
}
The third function is the receive function which is responsible for handling incoming frames, note that the device probably need to filter out messages:
void
receive(void *obj, temu_CanFrame *frame)
{
MyDevice *dev = (MyDevice *)obj;
int rtr = temu_canIsRemoteTransmissionRequest(frame);
int len = frame->Length;
temu_logInfo(obj, "received CAN frame RTR = %d, len = %d", rtr,
(int)frame->Length);
if (len > 8)
len = 8;
if (!rtr) {
for (int i = 0; i < len; ++i) {
temu_logInfo(obj, "\tFrame data %u", (unsigned)frame->Data[i]);
}
}
temu_canSetAck(frame);
}
Finally, the interface struct should be constructed and it needs to be registered in the CAN device plugin initialisation function:
static temu_CanDevIface CanIface = {
connected,
disconnected,
receive,
};
Plug-in init:
TEMU_PLUIGIN_INIT
{
temu_Class *c = temu_registerClass("MyCANDevice", create, dispose);
temu_addProperty(c, "bus", offsetof(MyDevice, bus), teTY_IfaceRef, NULL, NULL,
"CAN bus object");
temu_addInterface(c, "can_a", TEMU_CAN_DEV_IFACE_TYPE, &CanIface, 0,
"CAN interface");
}
When running TEMU, there are two items that must be done in order to use the CAN bus and the new CAN device, the first is to create a CAN bus object (CAN is a multi-node bus and therefore needs a bus object for message routing). The SimpleCANBus class is available in the BusModels plugin:
temu> import BusModels
temu> object-create class=SimpleCANBus name=canbus0
Then instantiate your CAN device and connect it and the canoc0 controller (created by the ut700.temu
-script) to the bus, this connection is done using the can-connect command:
temu> object-create class=MyCANDevice name=candev0
temu> can-connect bus=canbus0:CanBusIface dev=candev0:can_a
temu> can-connect bus=canbus0:CanBusIface
dev=canoc0:CanDevIface
The last thing is to have some embedded software that can be used to control the CAN OC controller:
struct CAN_OC_Device {
uint8_t Ctrl;
uint8_t Command;
uint8_t Status;
uint8_t Interrupt;
uint8_t AcceptCode;
uint8_t AcceptMask;
uint8_t BusTiming[2];
uint8_t unused0[2];
uint8_t TxID[2];
uint8_t TxData[8];
uint8_t RxID[2];
uint8_t RxData[8];
uint8_t unused1[1];
uint8_t ClockDivider;
};
rtems_task
Init(rtems_task_argument ignored)
{
volatile struct CAN_OC_Device *CAN_OC
= (struct CAN_OC_Device*)0xfff20000;
printk("OBSW: Will send a CAN message\n");
CAN_OC->Ctrl = 0; // Exit reset mode and disable Irqs
CAN_OC->TxID[0] = 0xaa;
CAN_OC->TxID[1] = 0xe1; // Length is 1
CAN_OC->TxData[0] = 42; // Payload
CAN_OC->Command = 1; // Transmission request
sleep(1);
printk("OBSW: Just sent the CAN message\n");
exit( 0 );
}
Now run the emulator and see the logging done by the OBSW and the CAN device.