MX Foundation 4
canbus.cs
/*******************************************************************************
//
// File:
// canbus.cs
//
// Copyright (c) MAX Technologies Inc. 1988-2019, All Rights Reserved.
// CONFIDENTIAL AND PROPRIETARY INFORMATION WHICH IS THE
// PROPERTY OF MAX TECHNOLOGIES INC.
//
// This example shows how to perform a basic aperiodic transmission and
// acquisition with canbus. It also demonstrates how to inject errors and
// trig a discrete line using the CAN record control field.
//
// Hardware Requirements:
// - MAXT Flex with two CAN channel connected on the same bus.
//
*******************************************************************************/
//#define LOCAL
using System;
using static MAXT.MXFoundation.mxf;
using System.Runtime.InteropServices;
using System.Text;
namespace DevelopmentAPICSharp
{
class canbus
{
const int BUFFER_SIZE = (512 * 1024); // 512KB
const int MAX_TX_RECORDS_TO_TRANSMIT = 8;
static void Main(string[] args)
{
UInt32 rc;
UInt64 server;
UInt64 device = 0;
var module = new UInt64[1];
UInt64 count = 0;
var channel = new UInt64[2];
UInt64 rxBuffer = 0;
UInt64 txBuffer = 0;
IntPtr recCanbus = IntPtr.Zero;
UInt64 status, txErrorCnt, rxErrorCnt;
int chn;
// Connect to services local or remote
#if LOCAL
rc = mxfServerConnect("0.0.0.0", "", "", FALSE, &server);
#else
rc = mxfServerConnect("192.168.0.1", "admin", "admin", Convert.ToUInt64(false), out server);
#endif
if (rc != MAXT_SUCCESS)
{
Console.WriteLine("Failed to connect; rc=0x{0:x8}", rc);
Console.WriteLine("Press a key to terminate");
Console.Read();
return;
}
// Initialize the server
Console.WriteLine();
Console.WriteLine("Starting");
rc = mxfSystemInit(server);
if (rc == MAXT_ERROR_ANOTHER_PROCESS_RUNNING)
rc = mxfSystemResourcesInit(server, 0);
// Get the first device handle
if (rc == MAXT_SUCCESS)
rc = mxfSystemDeviceGet(server, 0, out device);
if (rc == MAXT_SUCCESS)
rc = mxfDeviceModuleAllGet(device, MXF_MODULE_CANBUS, 1, out count, module);
// Obtain the first two canbus channel
// First channel will transmit data, the second channel will receive
if ((rc == MAXT_SUCCESS) && (count != 0))
rc = mxfModuleChannelAllGet(module[0], MXF_CLASS_CANBUS, MXF_SCLASS_ALL, 2, out count, channel);
// If module or channel not found, return an error
if ((rc == MAXT_SUCCESS) && (count == 0))
rc = MAXT_ERROR_NOT_FOUND;
// Set timebase to 64-bit nanoseconds
if (rc == MAXT_SUCCESS)
rc = mxfSystemTimeBaseSet(server, MXF_TIMEBASE_DEVICE_NSEC);
for (chn = 0; chn < 2 && (rc == MAXT_SUCCESS); chn++)
{
// Set the channel speed to 500Kbps with sampling point at 75%
timing.options = 0; // reserved
timing.sjw = 1; // sync each bit
timing.sampTriple = 0; // sample once each bit
timing.brp = 4; // prescaler (clock 64MHz)
timing.tseg1 = 11; // quanta before sampling point
timing.tseg2 = 4; // quanta after sampling point
rc = mxfCanBusTimingSet(channel[chn], ref timing);
// Set bus state
if (rc == MAXT_SUCCESS)
{
rc = mxfCanBusStateSet(channel[chn], MXF_CANBUS_BUS_STATE_ON);
}
}
// Allocate RX acquisition buffer
if (rc == MAXT_SUCCESS)
rc = mxfRxAcqBufferAlloc(channel[1], BUFFER_SIZE, out rxBuffer, IntPtr.Zero);
// Allocate Aperiodic TX static buffer for priority queue 0 (highest)
if (rc == MAXT_SUCCESS)
rc = mxfTxAperiodicBufferAlloc(channel[0], 0, BUFFER_SIZE, out txBuffer, IntPtr.Zero);
// Allocate host buffer
if (rc == MAXT_SUCCESS)
{
try
{
recCanbus = Marshal.AllocHGlobal(BUFFER_SIZE);
}
catch (OutOfMemoryException)
{
rc = MAXT_ERROR_MEM;
}
}
//Enable CAN #0 discrete trigger on error (discrete #1)
if (rc == MAXT_SUCCESS)
rc = mxfChannelDiscreteOutputTriggerEnableSet(channel[0], MXF_CANBUS_DISCRETE_OUTPUT_TRIG_ON_BUS_ERROR, VMXF_ENABLE, 1);
// Start the acquisition process
if (rc == MAXT_SUCCESS)
rc = mxfRxAcqModeSet(rxBuffer, MXF_RXACQ_MODE_LINEAR);
if (rc == MAXT_SUCCESS)
{
rc = mxfRxAcqStart(rxBuffer, MXF_RXACQ_FLAG_DEFAULT, 0, 0);
if (rc == MAXT_SUCCESS)
{
Console.WriteLine();
Console.WriteLine("Acquisition started");
}
}
if (rc == MAXT_SUCCESS)
{
// Start CAN data transmission
rc = TXCanbusStartAperiodicTransmissionDefault(txBuffer, recCanbus);
if (rc == MAXT_SUCCESS)
rc = RXCanbusReadAcquisitionData(rxBuffer, recCanbus);
if (rc == MAXT_SUCCESS)
rc = TXCanbusStartAperiodicTransmissionAbsolute(device, txBuffer, recCanbus);
if (rc == MAXT_SUCCESS)
rc = RXCanbusReadAcquisitionData(rxBuffer, recCanbus);
if (rc == MAXT_SUCCESS)
rc = TXCanbusStartAperiodicTransmissionRecordAbsolute(device, txBuffer, recCanbus);
if (rc == MAXT_SUCCESS)
rc = RXCanbusReadAcquisitionData(rxBuffer, recCanbus);
if (rc == MAXT_SUCCESS)
rc = TXCanbusStartAperiodicTransmissionRecordRelative(txBuffer, recCanbus);
if (rc == MAXT_SUCCESS)
rc = RXCanbusReadAcquisitionData(rxBuffer, recCanbus);
}
// Stop and flush unread data
if (rc == MAXT_SUCCESS)
rc = mxfRxAcqStop(rxBuffer);
if (rc == MAXT_SUCCESS)
{
rc = mxfRxAcqClear(rxBuffer);
if (rc == MAXT_SUCCESS)
{
Console.WriteLine();
Console.WriteLine("Acquisition stopped");
}
}
for (chn = 0; chn < 2 && (rc == MAXT_SUCCESS); chn++)
{
// Get bus status
rc = mxfCanBusStatusGet(channel[chn], out status, out txErrorCnt, out rxErrorCnt);
if (rc == MAXT_SUCCESS)
{
StringBuilder sb = new StringBuilder();
if ((status & MXF_CANBUS_BUS_STATUS_ERROR_OFF) == MXF_CANBUS_BUS_STATUS_ERROR_OFF)
sb.Append("OFF (Error)");
else if ((status & MXF_CANBUS_BUS_STATUS_ERROR_PASSIVE) == MXF_CANBUS_BUS_STATUS_ERROR_PASSIVE)
sb.Append("Error Passive");
else if ((status & MXF_CANBUS_BUS_STATUS_ON) == MXF_CANBUS_BUS_STATUS_ON)
sb.Append("ON");
else
sb.Append("OFF");
Console.WriteLine("txErrorCnt={0}, rxErrorCnt={1}, status={2}", txErrorCnt, rxErrorCnt, sb);
}
// Get bus stats
if (rc == MAXT_SUCCESS)
{
rc = mxfCanBusStatisticGet(channel[chn], out stats);
if (rc == MAXT_SUCCESS)
{
Console.WriteLine("txFrame={0}, rxFrame={1}, txArbLost={2}, rxFrameError={3}", stats.txFrame, stats.rxFrame, stats.txArbLost, stats.rxFrameError);
}
}
}
// Catch any previous error
if (rc != MAXT_SUCCESS)
{
StringBuilder buffer = new StringBuilder(256);
if (mxfSystemErrorStringGet(server, rc, 256, buffer) != MAXT_SUCCESS)
buffer.Append(string.Format("ERROR # 0x{0:x8}", rc));
Console.WriteLine(buffer);
}
Console.WriteLine();
Console.WriteLine("Terminating");
// Disconnect from the bus
for (chn = 0; chn < 2; chn++)
mxfCanBusStateSet(channel[chn], MXF_CANBUS_BUS_STATE_OFF);
// Free all buffers and terminate
if (rxBuffer != 0)
mxfRxAcqBufferFree(rxBuffer);
if (txBuffer != 0)
if (recCanbus != IntPtr.Zero)
Marshal.FreeHGlobal(recCanbus);
Console.WriteLine();
Console.WriteLine("Press enter to terminate");
Console.Read();
return;
}
private static UInt32 RXCanbusReadAcquisitionData(UInt64 rxBuffer, IntPtr recCanbus)
{
IntPtr recPtr = recCanbus;
UInt64 status, msgsCount, bytesCount;
UInt64 index;
UInt32 rc, Byte;
rec.data = new byte[64];
// Read and display records
rc = mxfCanBusRxAcqRead(rxBuffer, 0, BUFFER_SIZE, out status, out msgsCount, out bytesCount, recCanbus);
for (index = 0; (index < msgsCount) && (rc == MAXT_SUCCESS); index++)
{
rec = (MXF_CANBUS_DATAREC)Marshal.PtrToStructure(recPtr, typeof(MXF_CANBUS_DATAREC));
Console.Write(" {0:D3}: Timetag={1:D12}, Size={2}", index, rec.timeTag, rec.dataSize);
if (rec.control == 0)
Console.Write(" OK");
else
Console.Write(" Error");
Console.Write("\n Data=");
for (Byte = 0; Byte < rec.dataSize; Byte++)
{
Console.Write("{0:x2} ", rec.data[Byte]);
}
Console.WriteLine();
rc = mxfCanBusNextDataRecordPtrGet(recPtr, out recPtr);
}
return rc;
}
private static UInt32 TXCanbusStartAperiodicTransmissionDefault(UInt64 txBuffer, IntPtr recCanbus)
{
IntPtr recPtr = recCanbus;
UInt32 record;
UInt64 delay100ms = 100 * 1000 * 1000;
UInt32 rc = 0;
UInt32 i;
rec.data = new byte[72];
// In the example below the record are sent back-to-back 100 ms in the future.
Console.WriteLine();
Console.WriteLine("Aperiodic transmission (Relative Start Time-Default)");
// Set the record
for (record = 0; (rc == MAXT_SUCCESS) && (record < MAX_TX_RECORDS_TO_TRANSMIT); record++)
{
// Set each record: ID Extended 0x080A0000, DLC=8
rec.timeTag = 0;
rec.control = 0;
rec.repeatCount = 1;
rec.errorInj = 0;
rec.dataSize = 8;
rec.id = 0x080A0000;
rec.info = MXF_CANBUS_REC_INFO_EXTENDED | 8;
for (i = 0; i < rec.dataSize; i++)
rec.data[i] = (byte)i;
Marshal.StructureToPtr(rec, recPtr, false);
rc = mxfCanBusNextDataRecordPtrGet(recPtr, out recPtr);
}
// Transmit the array of records
if (rc == MAXT_SUCCESS)
rc = mxfCanBusTxAperiodicWrite(txBuffer, MXF_TXAPERIODIC_FLAG_DEFAULT, delay100ms, MAX_TX_RECORDS_TO_TRANSMIT, recCanbus);
// Wait TX completion
if (rc == MAXT_SUCCESS)
mxfSleep(250);
return rc;
}
private static UInt32 TXCanbusStartAperiodicTransmissionAbsolute(UInt64 device, UInt64 txBuffer, IntPtr recCanbus)
{
IntPtr recPtr = recCanbus;
UInt32 record, rc = 0;
UInt64 delay100ms = 100 * 1000 * 1000;
UInt64 currentTime;
UInt32 i;
rec.data = new byte[72];
// Get the current time
rc = mxfDeviceTimerGet(device, out currentTime);
if (rc != MAXT_SUCCESS)
return rc;
// In the example below the option MXF_TXAPERIODIC_FLAG_ABSOLUTE_START_TIME
// is used. In this case the records are sent back-to-back
// with a start transmission time based on the device clock + 100ms in the future.
// Second record will have a CRC error.
Console.WriteLine();
Console.WriteLine("Aperiodic transmission (Absolute)");
for (record = 0; (rc == MAXT_SUCCESS) && (record < MAX_TX_RECORDS_TO_TRANSMIT); record++)
{
// Set each record: ID Extended 0x08120000, DLC=4
rec.timeTag = 0;
rec.control = 0;
rec.dataSize = 4;
rec.errorInj = 0;
if (record == 1)
{
rec.control |= MXF_CANBUS_TX_REC_CTRL_ERROR_INJ | MXF_CANBUS_TX_REC_CTRL_SINGLE_SHOT;
mxfCanBusErrorInjectionSet(MXF_CANBUS_ERRORID_CRC, 0, out rec.errorInj);
}
rec.repeatCount = 1;
rec.id = 0x08120000;
rec.info = MXF_CANBUS_REC_INFO_EXTENDED | 4;
for (i = 0; i < rec.dataSize; i++)
rec.data[i] = (byte)i;
Marshal.StructureToPtr(rec, recPtr, false);
rc = mxfCanBusNextDataRecordPtrGet(recPtr, out recPtr);
}
// Transmit the array of records
if (rc == MAXT_SUCCESS)
rc = mxfCanBusTxAperiodicWrite(txBuffer, MXF_TXAPERIODIC_FLAG_ABSOLUTE_START_TIME, currentTime + delay100ms, MAX_TX_RECORDS_TO_TRANSMIT, recCanbus);
// Wait TX completion
if (rc == MAXT_SUCCESS)
mxfSleep(250);
return rc;
}
private static UInt32 TXCanbusStartAperiodicTransmissionRecordAbsolute(UInt64 device, UInt64 txBuffer, IntPtr recCanbus)
{
IntPtr recPtr = recCanbus;
UInt32 record;
UInt64 currentTime;
UInt32 rc = 0;
UInt32 i;
rec.data = new byte[72];
// In the example below the timetag in the record is set
// and the option MXF_TXAPERIODIC_FLAG_USE_RECORD_ABSOLUTE_TIME
// In this case the records are sent based on the absolute clock
// timing specified in the timetag field for each records.
Console.WriteLine();
Console.WriteLine("Aperiodic transmission (Absolute with timetag)");
// Get the current time
rc = mxfDeviceTimerGet(device, out currentTime);
if (rc != MAXT_SUCCESS)
return rc;
currentTime += 100 * 1000 * 1000;
// Set the record
for (record = 0; (rc == MAXT_SUCCESS) && (record < MAX_TX_RECORDS_TO_TRANSMIT); record++)
{
// Set each record: ID Extended 0x08820000, DLC=2
// The scheduling timetag is as follow:
// clock current time + 100 ms
// Each record is then sent with a 10 ms interval.
rec.timeTag = currentTime + ((record + 1) * 10 * 1000 * 1000);
rec.control = 0;
rec.repeatCount = 1;
rec.errorInj = 0;
rec.dataSize = 2;
rec.id = 0x08820000;
rec.info = MXF_CANBUS_REC_INFO_EXTENDED | 2;
for (i = 0; i < rec.dataSize; i++)
rec.data[i] = (byte)i;
Marshal.StructureToPtr(rec, recPtr, false);
rc = mxfCanBusNextDataRecordPtrGet(recPtr, out recPtr);
}
// Transmit the array of records
if (rc == MAXT_SUCCESS)
rc = mxfCanBusTxAperiodicWrite(txBuffer, MXF_TXAPERIODIC_FLAG_USE_RECORD_ABSOLUTE_TIME, 0, MAX_TX_RECORDS_TO_TRANSMIT, recCanbus);
// Wait TX completion
if (rc == MAXT_SUCCESS)
mxfSleep(250);
return rc;
}
private static UInt32 TXCanbusStartAperiodicTransmissionRecordRelative(UInt64 txBuffer, IntPtr recCanbus)
{
IntPtr recPtr = recCanbus;
UInt32 record;
UInt64 currentTime;
UInt32 rc = 0;
UInt32 i;
rec.data = new byte[72];
// In the example below the timetag in the record is set
// and the option MXF_TXAPERIODIC_FLAG_USE_RECORD_RELATIVE_TIME.
// The relative time between each records is set in the
// timetag field of the record array.
Console.WriteLine();
Console.WriteLine("Aperiodic transmission (Record Relative)");
// Set the record
for (record = 0, currentTime = 100 * 1000 * 1000; (rc == MAXT_SUCCESS) && (record < MAX_TX_RECORDS_TO_TRANSMIT); record++)
{
// Set each record: ID 0x0111, DLC=2
// The scheduling timetag is as follow:
// 100 ms offset before first record.
// Each record is then sent with a 10 ms interval.
rec.timeTag = currentTime + ((record + 1) * 10 * 1000 * 1000);
rec.control = 0;
rec.repeatCount = 1;
rec.errorInj = 0;
rec.dataSize = 2;
rec.id = 0x0111;
rec.info = 2;
for (i = 0; i < rec.dataSize; i++)
rec.data[i] = (byte)i;
Marshal.StructureToPtr(rec, recPtr, false);
rc = mxfCanBusNextDataRecordPtrGet(recPtr, out recPtr);
}
// Transmit the array of records
if (rc == MAXT_SUCCESS)
rc = mxfCanBusTxAperiodicWrite(txBuffer, MXF_TXAPERIODIC_FLAG_USE_RECORD_RELATIVE_TIME, 0, MAX_TX_RECORDS_TO_TRANSMIT, recCanbus);
// Wait TX completion
if (rc == MAXT_SUCCESS)
mxfSleep(250);
return rc;
}
}
}
Updated 10/23/2023