MX Foundation 4
canbus.c
/*******************************************************************************
//
// File:
// canbus.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 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.
//
*******************************************************************************/
#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 TXCanbusStartAperiodicTransmissionAbsolute(HMXF_DEVICE device, HMXF_BUFFER txBuffer, MXF_CANBUS_DATAREC *recCanbus);
uint32 TXCanbusStartAperiodicTransmissionRecordAbsolute(HMXF_DEVICE device, HMXF_BUFFER txBuffer, MXF_CANBUS_DATAREC *recCanbus);
uint32 TXCanbusStartAperiodicTransmissionRecordRelative(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 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);
for (chn = 0; chn<2 && !rc; 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], &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 data transmission
rc = TXCanbusStartAperiodicTransmissionDefault(txBuffer, recCanbus);
if (!rc)
rc = RXCanbusReadAcquisitionData(rxBuffer, recCanbus);
if (!rc)
rc = TXCanbusStartAperiodicTransmissionAbsolute(device, txBuffer, recCanbus);
if (!rc)
rc = RXCanbusReadAcquisitionData(rxBuffer, recCanbus);
if (!rc)
rc = TXCanbusStartAperiodicTransmissionRecordAbsolute(device, txBuffer, recCanbus);
if (!rc)
rc = RXCanbusReadAcquisitionData(rxBuffer, recCanbus);
if (!rc)
rc = TXCanbusStartAperiodicTransmissionRecordRelative(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=%"PRIu64", rxErrorCnt=%"PRIu64", 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(" %03"PRIu64": Timetag=%012"PRIu64", 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.
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=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] = (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 TXCanbusStartAperiodicTransmissionAbsolute(HMXF_DEVICE device, HMXF_BUFFER txBuffer, MXF_CANBUS_DATAREC *recCanbus)
{
uint32 record, rc;
uint64 delay100ms=100*1000*1000;
uint64 currentTime;
uint32 i;
// Get the current time
rc = mxfDeviceTimerGet(device, &currentTime);
if (rc)
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.
printf("\nAperiodic transmission (Absolute)\n");
rec = recCanbus;
for (record=0; 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, &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] = (uint8)i;
}
// Transmit the array of records
rc = mxfCanBusTxAperiodicWrite(txBuffer, MXF_TXAPERIODIC_FLAG_ABSOLUTE_START_TIME, currentTime+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.
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=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] = (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;
}
uint32 TXCanbusStartAperiodicTransmissionRecordRelative(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_RELATIVE_TIME.
// The relative time between each records is set in the
// timetag field of the record array.
printf("\nAperiodic transmission (Record Relative)\n");
// Set the record
rec = recCanbus;
for (record=0, currentTime=100*1000*1000; 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] = (uint8)i;
}
// Transmit the array of records
rc = mxfCanBusTxAperiodicWrite(txBuffer, MXF_TXAPERIODIC_FLAG_USE_RECORD_RELATIVE_TIME, 0, MAX_TX_RECORDS_TO_TRANSMIT, recCanbus);
// Wait TX completion
if (!rc)
mxfSleep(250);
return rc;
}
Updated 10/23/2023