MX Foundation 4
can_fd.c
/*******************************************************************************
//
// File:
// can_fd.c
//
// Copyright (c) MAX Technologies Inc. 1988-2020, 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 CAN FD. 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 FD channel connected on the same bus.
//
*******************************************************************************/
#include "example.h"
//#define LOCAL
#define BUFFER_SIZE (512*1024) // 512KB
#define MAX_TX_RECORDS_TO_TRANSMIT 8
uint32 RXCanbusReadAcquisitionData(HMXF_BUFFER rxBuffer, MXF_CANBUS_DATAREC *recCanbus);
uint32 TXCanbusStartAperiodicTransmissionDefault(HMXF_BUFFER txBuffer, MXF_CANBUS_DATAREC *recCanbus);
uint32 TXCanbusStartAperiodicTransmissionRecordAbsolute(HMXF_DEVICE device, HMXF_BUFFER txBuffer, MXF_CANBUS_DATAREC *recCanbus);
/***************************************************************************************************************/
// Main
/***************************************************************************************************************/
int main(void)
{
uint32 rc;
HMXF_SERVER server;
HMXF_DEVICE device=0;
HMXF_MODULE module=0;
uint64 count=0;
HMXF_CHANNEL channel[2]={0};
HMXF_BUFFER rxBuffer=0;
HMXF_BUFFER txBuffer=0;
MXF_CANBUS_DATAREC *recCanbus=NULL;
uint64 status, txErrorCnt, rxErrorCnt;
int chn;
// Connect to services local or remote
#ifdef LOCAL
rc = mxfServerConnect("0.0.0.0", "", "", FALSE, &server);
#else
rc = mxfServerConnect("192.168.0.1", "admin", "admin", FALSE, &server);
#endif
if (rc)
{
printf("Failed to connect; rc=0x%08x", rc);
printf("\nPress a key to terminate\n");
getchar();
return 0;
}
// Initialize the server
printf("\nStarting\n");
rc = mxfSystemInit(server);
if(rc == MAXT_ERROR_ANOTHER_PROCESS_RUNNING)
rc = mxfSystemResourcesInit(server, 0);
// Get the first device handle
if (!rc)
rc = mxfSystemDeviceGet(server, 0, &device);
if (!rc)
rc = mxfDeviceModuleAllGet(device, MXF_MODULE_CANBUS, 1, &count, &module);
// Obtain the first two canbus channel
// First channel will will transmit data, the second channel will receive
if (!rc && count)
rc = mxfModuleChannelAllGet(module, MXF_CLASS_CANBUS, MXF_SCLASS_ALL, 2, &count, channel);
// If module or channel not found, return an error
if(!rc && !count)
rc = MAXT_ERROR_NOT_FOUND;
// Set timebase to 64-bit nanoseconds
if (!rc)
rc = mxfSystemTimeBaseSet(server, MXF_TIMEBASE_DEVICE_NSEC);
// Set the module to CAN FD
if(!rc)
rc = mxfAttributeUint64Set(module, KMXF_CANBUS_MODULE_MODE, VMXF_CANBUS_MODULE_MODE_CAN_FD);
for(chn=0; chn<2 && !rc; chn++)
{
// Set the channel to ISO CAN FD
if (!rc)
rc = mxfAttributeUint64Set(channel[chn], KMXF_CANBUS_FD_MODE, VMXF_CANBUS_FD_MODE_ISO);
// Set the channel speed to 500Kbps with sampling point at 80% for nominal phase and 1Mbps with sampling point at 80% for data phase
if (!rc)
{
timing.options = 0; // reserved
timing.reserved = 0; // reserved
timing.nominal.brp = 4; // nominal prescaler (clock 80MHz)
timing.nominal.tseg1 = 31; // nominal quanta before sampling point
timing.nominal.tseg2 = 8; // nominal quanta after sampling point
timing.nominal.sjw = 8; // nominal sjw
timing.data.brp = 2; // data prescaler (clock 80MHz)
timing.data.tseg1 = 31; // data quanta before sampling point
timing.data.tseg2 = 8; // data quanta after sampling point
timing.data.sjw = 8; // data sjw
rc = mxfCanBusFdTimingSet(channel[chn], &timing);
}
// Set bus state
if (!rc)
rc = mxfCanBusStateSet(channel[chn], MXF_CANBUS_BUS_STATE_ON);
}
// Allocate RX acquisition buffer
if (!rc)
rc = mxfRxAcqBufferAlloc(channel[1], BUFFER_SIZE, &rxBuffer, NULL);
// Allocate Aperiodic TX static buffer for priority queue 0 (highest)
if (!rc)
rc = mxfTxAperiodicBufferAlloc(channel[0], 0, BUFFER_SIZE, &txBuffer, NULL);
// Allocate host buffer
if (!rc)
{
recCanbus = (MXF_CANBUS_DATAREC*)malloc(BUFFER_SIZE);
if (!recCanbus)
rc = MAXT_ERROR_MEM;
}
//Enable CAN #0 discrete trigger on error (discrete #1)
if (!rc)
rc = mxfChannelDiscreteOutputTriggerEnableSet(channel[0], MXF_CANBUS_DISCRETE_OUTPUT_TRIG_ON_BUS_ERROR, VMXF_ENABLE, 1);
// Start the acquisition process
if (!rc)
rc = mxfRxAcqModeSet(rxBuffer, MXF_RXACQ_MODE_LINEAR);
if (!rc)
{
rc = mxfRxAcqStart(rxBuffer, MXF_RXACQ_FLAG_DEFAULT, 0, 0);
if (!rc)
printf("\nAcquisition started\n\r");
}
if (!rc)
{
// Start CAN FD data transmission
rc = TXCanbusStartAperiodicTransmissionDefault(txBuffer, recCanbus);
if (!rc)
rc = RXCanbusReadAcquisitionData(rxBuffer, recCanbus);
if (!rc)
rc = TXCanbusStartAperiodicTransmissionRecordAbsolute(device, txBuffer, recCanbus);
if (!rc)
rc = RXCanbusReadAcquisitionData(rxBuffer, recCanbus);
}
// Stop and flush unread data
if (!rc)
rc = mxfRxAcqStop(rxBuffer);
if (!rc)
{
rc = mxfRxAcqClear(rxBuffer);
if (!rc)
printf("\nAcquisition stopped\n\r");
}
// Get bus status
for (chn = 0; chn<2 && !rc; chn++)
{
rc = mxfCanBusStatusGet(channel[chn], &status, &txErrorCnt, &rxErrorCnt);
if(!rc)
{
char s[128];
if(status & MXF_CANBUS_BUS_STATUS_ERROR_OFF)
strcpy(s, "OFF (Error)");
else if (status & MXF_CANBUS_BUS_STATUS_ERROR_PASSIVE)
strcpy(s, "Error Passive");
else if (status & MXF_CANBUS_BUS_STATUS_ON)
strcpy(s, "ON");
else
strcpy(s, "OFF");
printf("txErrorCnt=%llu, rxErrorCnt=%llu, status=%s\n", txErrorCnt, rxErrorCnt, s);
}
// Get bus stats
rc = mxfCanBusStatisticGet(channel[chn], &stats);
if (!rc)
printf("txFrame=%u, rxFrame=%u, txArbLost=%u, rxFrameError=%u\n", stats.txFrame, stats.rxFrame, stats.txArbLost, stats.rxFrameError);
}
// Catch any previous error
if (rc)
{
char buffer[256];
rc = mxfSystemErrorStringGet(server, rc, sizeof(buffer), buffer);
printf("%s\n", buffer);
}
printf("\nTerminating\n");
// 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)
mxfRxAcqBufferFree(rxBuffer);
if (txBuffer)
if (recCanbus)
free(recCanbus);
printf("\nPress enter to terminate\n");
getchar();
return rc;
}
uint32 RXCanbusReadAcquisitionData(HMXF_BUFFER rxBuffer, MXF_CANBUS_DATAREC *recCanbus)
{
MXF_CANBUS_DATAREC* recPtr= recCanbus;
uint64 status, msgsCount, bytesCount;
uint64 index;
uint32 rc, byte;
// Read and display records
rc = mxfCanBusRxAcqRead(rxBuffer, 0, BUFFER_SIZE, &status, &msgsCount, &bytesCount, recCanbus);
for (index =0; index<msgsCount && !rc; index++)
{
printf(" %03llu: Timetag=%012llu, Size=%u", index, recPtr->timeTag, recPtr->dataSize);
if (!recPtr->control)
printf(" OK");
else
printf(" Error");
printf("\n Data=");
for (byte=0; byte<recPtr->dataSize; byte++)
{
printf("%02X ", recPtr->data[byte]);
}
printf("\n");
rc = mxfCanBusNextDataRecordPtrGet(recPtr, &recPtr);
}
return rc;
}
uint32 TXCanbusStartAperiodicTransmissionDefault(HMXF_BUFFER txBuffer, MXF_CANBUS_DATAREC *recCanbus)
{
uint32 record;
uint64 delay100ms = 100*1000*1000;
uint32 rc;
uint32 i;
// In the example below the record are sent back-to-back 100 ms in the future.
// The CAN FD frame is used without the bit rate switch (nominal speed for the duration of the transmission)
printf("\nAperiodic transmission (Relative Start Time-Default)\n");
// Set the record
rec = recCanbus;
for (record=0; record<MAX_TX_RECORDS_TO_TRANSMIT; record++)
{
// Set each record: ID Extended 0x080A0000, DLC=12 (24 bytes)
rec->timeTag = 0;
rec->control = 0;
rec->repeatCount = 1;
rec->errorInj = 0;
rec->dataSize = 24;
rec->id = 0x080A0000;
rec->info = MXF_CANBUS_REC_INFO_EXTENDED | MXF_CANBUS_REC_INFO_FD | 12;
for(i=0; i<rec->dataSize; i++)
rec->data[i] = (uint8)i;
}
// Transmit the array of records
rc = mxfCanBusTxAperiodicWrite(txBuffer, MXF_TXAPERIODIC_FLAG_DEFAULT, delay100ms, MAX_TX_RECORDS_TO_TRANSMIT, recCanbus);
// Wait TX completion
if (!rc)
mxfSleep(250);
return rc;
}
uint32 TXCanbusStartAperiodicTransmissionRecordAbsolute(HMXF_DEVICE device, HMXF_BUFFER txBuffer, MXF_CANBUS_DATAREC *recCanbus)
{
uint32 record;
uint64 currentTime;
uint32 rc;
uint32 i;
// 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.
// Record #4 is sent with a CRC error
// CAN FD frame is used with bit rate switch
printf("\nAperiodic transmission (Absolute with timetag)\n");
// Get the current time
rc = mxfDeviceTimerGet(device, &currentTime);
if (rc)
return rc;
currentTime+=100*1000*1000;
// Set the record
rec = recCanbus;
for (record=0; record<MAX_TX_RECORDS_TO_TRANSMIT; record++)
{
// Set each record: ID Extended 0x08820000, DLC=10 (16 bytes)
// 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->repeatCount = 1;
rec->control = 0;
rec->errorInj = 0;
if (record == 4)
{
rec->control = MXF_CANBUS_TX_REC_CTRL_ERROR_INJ | MXF_CANBUS_TX_REC_CTRL_SINGLE_SHOT;
mxfCanBusErrorInjectionSet(MXF_CANBUS_ERRORID_CRC, 0, &rec->errorInj);
}
rec->dataSize = 16;
rec->id = 0x08820000;
rec->info = MXF_CANBUS_REC_INFO_EXTENDED | MXF_CANBUS_REC_INFO_FD | MXF_CANBUS_REC_INFO_FD_BRS | 10;
for (i = 0; i<rec->dataSize; i++)
rec->data[i] = (uint8)i;
}
// Transmit the array of records
rc = mxfCanBusTxAperiodicWrite(txBuffer, MXF_TXAPERIODIC_FLAG_USE_RECORD_ABSOLUTE_TIME, 0, MAX_TX_RECORDS_TO_TRANSMIT, recCanbus);
// Wait TX completion
if (!rc)
mxfSleep(250);
return rc;
}
Updated 10/23/2023