Simulated Microcontroller CAN
The MxV uC library simulates a CAN sub-system of a typical microcontroller (µC). The library provides an interface to a simulated µC CAN used for:
•Sending and receiving CAN messages with different IDs on different CAN buses
•Generating receive and transmit interrupt requests
As with all MxV uC sub-systems, the CAN sub-system must be initialized before it is used. To initialize the CAN sub-system, add a call to: void MxVuC_CAN_Initialize(void) in your application initialization code or add it to the following function in the 'AppIF.c' file: void SUT_Init(void) The next step is to open a CAN bus and retrieve a handle for that bus: MxVuC_CAN_Device MxVuC_CAN_Open(int bus_id) where 'bus_id' is the bus number (0 corresponds to the first bus in the list of buses). This function returns a handle which is used later when closing the bus or when transmitting CAN messages; therefore, this handle needs to be retained for later use by other functions (don't throw away the returned value). When using transport CAN messages, the following function: void MxVuC_CAN_TP_Tick10ms(void) must be called every 10ms (10ms in terms of the application time, not MS Windows time) for the built-in Transport CAN stacks to function. This required function call can be placed in: void SUT_Tick(void) in the 'AppIF.c' file. The CAN sub-system can be configured to generate interrupt requests when CAN messages are received and when CAN messages get transmitted. The SUT vector table 'MxVuC_VectorTableSUT' at offsets 'MxVuC_Vector_CAN_Rx' and 'MxVuC_Vector_CAN_Tx' must be set up before enabling CAN interrupts. To set up the vector table, the addresses of the Rx and Tx interrupt service routines must be set in the vector table: MxVuC_VectorTableSUT[MxVuC_Vector_CAN_Rx] = CAN_Rx_Interrupt MxVuC_VectorTableSUT[MxVuC_Vector_CAN_Tx] = CAN_Tx_Interrupt where CAN_Rx_Interrupt is the CAN receive interrupt service routine and CAN_Tx_Interrupt is the CAN transmit interrupt service routine. CAN_Rx_Interrupt is called on interrupt when a message is sent from MxVDev. Because of that, avoid making any other calls that can cause other interrupts, for example, SetEvent() in OSEK. When the CAN receive interrupt service routine is serviced, the following variables are set: •MxVuC_CAN_Rx_Bus •MxVuC_CAN_Rx_Device •MxVuC_CAN_Rx_Message 'MxVuC_CAN_Rx_Bus' is the bus on which the CAN message was received. 'MxVuC_CAN_Rx_Device' is the handle of the bus that the CAN message was received on. 'MxVuC_CAN_Rx_Message' is a structure that contains all the information about the received CAN message. The CAN receive interrupt must make use of these variables to distinguish CAN messages coming from different buses with different IDs. A typical CAN Rx interrupt service routine looks like: void CAN_Rx_Interrupt(void) { // handle different CAN buses switch(MxVuC_CAN_Rx_Bus) { // deal with all buses that we're not handling default: { } break;
// first CAN bus case 0: { // handle different CAN message IDs on the first bus switch(MxVuC_CAN_Rx_Message.id) { // deal with all message IDs that we're not handling default: { } break;
// handle CAN message ID 0x412 case 0x412: { // do something here // make use of 'MxVuC_CAN_Rx_Message.flags' // check the message length 'MxVuC_CAN_Rx_Message.dlc' // read the received message 'MxVuC_CAN_Rx_Message.payload' } break; } } break;
// second CAN bus case 1: { // handle different CAN message IDs on the second bus switch(MxVuC_CAN_Rx_Message.id) { // deal with all message IDs that we're not handling default: { } break;
// handle CAN message ID 0x700 case 0x700: { // do something here // make use of 'MxVuC_CAN_Rx_Message.flags' // check the message length 'MxVuC_CAN_Rx_Message.dlc' // read the received message 'MxVuC_CAN_Rx_Message.payload' } break; } } break; } } When the CAN transmit interrupt service routine is serviced, the following variables are set: •MxVuC_CAN_Tx_Bus •MxVuC_CAN_Tx_Device •MxVuC_CAN_Tx_Message 'MxVuC_CAN_Tx_Bus' is the bus on which the CAN message was transmitted. 'MxVuC_CAN_Tx_Device' is the handle of the bus that the CAN message was transmitted on. 'MxVuC_CAN_Tx_Message' is a structure that contains all the information about the transmitted CAN message. The CAN transmit interrupt must make use of these variables to distinguish CAN messages being transmitted on different buses with different IDs. A typical CAN Tx interrupt service routine should be very similar to that of the Rx interrupt service routine. Note that Tx interrupt service routine is serviced after the CAN message is fully transmitted. Once the interrupt service routines and the SUT vector table are set up, the CAN bus needs to be enabled to enable receiving and transmitting interrupts. This can be accomplished by calling: void MxVuC_CAN_Enable(MxVuC_CAN_Device hbus, mxvBool Enable) where 'hbus' is the bus handle that needs to be enabled (this handle was returned by 'MxVuC_CAN_Open'); 'Enable' controls enabling or disabling of the bus ('mxvTrue' enables the bus, 'mxvFalse' disabled the bus). Also note that global interrupts must be enabled for the CAN sub-system to generate interrupt requests. void MxVuC_EnableInterrupts(void) Enables global interrupts. At this point the CAN sub-system is ready to receive, transmit, and handle CAN messages. Note that opened CAN buses can be closed by calling: void MxVuC_CAN_Close(MxVuC_CAN_Device hbus) where 'hbus' is the handle return by 'MxVuC_CAN_Open'. |
The baud rate of a CAN bus can be configured by calling: void MxVuC_CAN_SetBaud(MxVuC_CAN_Device hbus, unsigned int baudRate) where 'hbus' is the handle of the bus of interest and 'baudRate' is the new desired baud rate. |
CAN messages (received or transmitted) have the following structure: typedef struct { mxvU32 id; mxvU8 flags; mxvU8 dlc; mxvU8 payload[8]; } MxVuC_CAN_Message; where: 'id' is the CAN message ID, 'flags' are defined below, 'dlc' is the length of the data of the CAN message in bytes (must be between 0 and 8), and 'payload' is an array of 8 bytes that contain the data of the CAN Message. Flags are used to indicate if you have an Extended ID CAN messages or a wake up message: mxvU8 flags |
Once the CAN sub-system is initialized, the application can start transmitting CAN messages. Transmitting CAN messages is very easy and can be accomplished by calling: void MxVuC_CAN_Tx(MxVuC_CAN_Device hbus, const MxVuC_CAN_Message* message) which transmits 'message' on 'hbus'. 'hbus' is the handle of the bus on which the message is to be transmitted. 'message' is the address of an allocated CAN message structure. All fields of 'message' must be set up prior to calling this function. A helper function exists which can be called prior to calling the transmit function to help set up the CAN message structure: void MxVuC_CAN_MakeMessage(MxVuC_CAN_Message* message, mxvU32 id, mxvU8 dlc, mxvU8 flags, const mxvU8* data); where 'message' is the address of an allocated CAN message structure (this is the structure which is populated based on the other function arguments), 'id' is the ID of the transmitted CAN message, 'dlc' is the length of the data of the CAN message in bytes (must be between 0 and 8), 'flags' can be used to specify extended CAN messages, and 'data' is the address of an array of 8 bytes that contains the data to be transmitted. When this helper function returns, 'message' is set up and ready to be transmitted. If the CAN sub-system is set up to generate Tx CAN interrupt requests, the Tx interrupt service routine is serviced after the CAN message has been fully transmitted. |
// in the 'AppIF.c' file
// global variable used to retain the bus handle
MxVuC_CAN_Device can_bus_handle;
// CAN Rx interrupt service routine
void CAN_Rx_Int(void);
// used to transmit CAN messages
void SendCANMessage(void);
void SUT_Init(void)
{
//Initializes microcontroller simulation and starts to execute main()
MxVuC_Initialize();
// initialize the CAN sub-system
MxVuC_CAN_Initialize();
// get a handle to bus 0
can_bus_handle = MxVuC_CAN_Open(0);
// setup the SUT vector table
MxVuC_VectorTableSUT[MxVuC_Vector_CAN_Rx] = CAN_Rx_Int;
// enable the bus
MxVuC_CAN_Enable(can_bus_handle, mxvTrue);
// enable interrupts
MxVuC_EnableInterrupts();
MxVuC_Tick();
}
// this function is set in MxV to run every 10ms
void SUT_Tick(void)
{
MxVuC_CAN_TP_Tick10ms(); // this function must be called every 10ms
MxVuC_Tick();
}
void CAN_Rx_Int(void)
{
// handle different CAN buses
switch(MxVuC_CAN_Rx_Bus)
{
// deal with all buses that we're not handling
default:
{
} break;
// first CAN bus
case 0:
{
// handle different CAN message IDs on the first bus
switch(MxVuC_CAN_Rx_Message.id)
{
// deal with all message IDs that we're not handling
default:
{
} break;
// handle CAN message ID 0x412
case 0x412:
{
// read the data from MxVuC_CAN_Rx_Message.payload
} break;
}
} break;
}
}
void SendCANMessage(void)
{
MxVuC_CAN_Message can_tx_msg = {0};
uint8 data = 0x7F;
MxVuC_CAN_MakeMessage(&can_tx_msg, 0x700, sizeof(data), 0, &data);
MxVuC_CAN_Tx(can_bus_handle, &can_tx_msg);
}
Linking your code with the MxVMC