mirror of
https://gitee.com/Lamdonn/varch.git
synced 2025-12-06 08:46:42 +08:00
490 lines
20 KiB
C
490 lines
20 KiB
C
/*********************************************************************************************************
|
|
* ------------------------------------------------------------------------------------------------------
|
|
* file description
|
|
* ------------------------------------------------------------------------------------------------------
|
|
* \file cant.c
|
|
* \unit cant
|
|
* \brief This is a can test for C language
|
|
* \author Lamdonn
|
|
* \version v0.1.0
|
|
* \license GPL-2.0
|
|
* \copyright Copyright (C) 2023 Lamdonn.
|
|
********************************************************************************************************/
|
|
#include "cant.h"
|
|
|
|
/* Frame data base length. */
|
|
// Constant representing the base number of bits for a CAN standard frame's data.
|
|
// It is set to 47 bits and is used to define the basic length of a CAN standard frame.
|
|
static const uint32_t CanStdFrmBaseBits = 47; /**< CAN standard frame data base length. */
|
|
// Constant representing the base number of bits for a CAN extended frame's data.
|
|
// It is set to 67 bits and is used to define the basic length of a CAN extended frame.
|
|
static const uint32_t CanExtFrmBaseBits = 67; /**< CAN extended frame data base length. */
|
|
// Constant representing the base number of bits for a CANFD (Controller Area Network Flexible Data Rate) standard frame's data.
|
|
// Calculated as the sum of different parts of the frame (17(SOF~BRS) + 12(ACK~IFS) + 5(ESI~DLC) + 18(CRC~DEL) + dlc * 8).
|
|
// This defines the basic length of a CANFD standard frame.
|
|
static const uint32_t CanFDStdFrmBaseBits = 17 + 12 + 5 + 18; /**< CANFD standard frame data base length, 17(SOF~BRS) + 12(ACK~IFS) + 5(ESI~DLC) + 18(CRC~DEL) + dlc * 8 */
|
|
// Constant representing the base number of bits for a CANFD extended frame's data.
|
|
// Calculated as the sum of different parts of the frame (36(SOF~BRS) + 12(ACK~IFS) + 5(ESI~DLC) + 18(CRC~DEL) + dlc * 8).
|
|
// This defines the basic length of a CANFD extended frame.
|
|
static const uint32_t CanFDExtFrmBaseBits = 36 + 12 + 5 + 18; /**< CANFD extended frame data base length, 36(SOF~BRS) + 12(ACK~IFS) + 5(ESI~DLC) + 18(CRC~DEL) + dlc * 8 */
|
|
// Constant representing the additional number of bits for CANFD when the Data Length Code (DLC) is greater than 16.
|
|
// In this case, the CRC changes from 17 bits to 23 bits, and this value represents that additional length.
|
|
static const uint32_t CanFDAppendBits = 4; /**< CANFD append length, when DLC is greater than 16, crc changes from 17bits to 23bits. */
|
|
|
|
// Array of baud rates available for the CAN bus.
|
|
// It contains different baud rate values such as 125000 (125K baud), 250000 (250K baud), etc.
|
|
// The size of the array is determined by CANT_BAUDRATE_MAX (although it's not fully clear from this code what that is exactly).
|
|
static const uint32_t baudrate_list[CANT_BAUDRATE_MAX] =
|
|
{
|
|
125000, // CANT_BAUDRATE_125K
|
|
250000, // CANT_BAUDRATE_250K
|
|
500000, // CANT_BAUDRATE_500K
|
|
800000, // CANT_BAUDRATE_800K
|
|
1000000, // CANT_BAUDRATE_1000K
|
|
// CANT_BAUDRATE_MAX,
|
|
};
|
|
|
|
/**
|
|
* \brief Calculates the CRC8 (Cyclic Redundancy Check with 8 bits) value for a given data buffer.
|
|
* \param data: Pointer to the data buffer for which the CRC8 is to be calculated.
|
|
* \param len: The length of the data buffer.
|
|
* \return The calculated CRC8 value as a uint8_t.
|
|
*
|
|
* This function calculates the CRC8 value for the input data buffer. It iterates through each byte of the data.
|
|
* For each byte, it XORs the byte with the current CRC value and then performs a series of shift and XOR operations
|
|
* based on the most significant bit of the CRC value to update the CRC. After processing all bytes, it returns the final CRC8 value.
|
|
*/
|
|
static uint8_t crc8(uint8_t* data, uint32_t len)
|
|
{
|
|
uint8_t i;
|
|
uint8_t crc = 0;
|
|
while (len--)
|
|
{
|
|
crc ^= *data++;
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
if (crc & 0x80) crc = (crc << 1) ^ 0x07;
|
|
else crc <<= 1;
|
|
}
|
|
}
|
|
return crc;
|
|
}
|
|
|
|
/**
|
|
* \brief Updates the bus load statistics for a CAN (Controller Area Network) device.
|
|
* \param cant: Pointer to the CANT structure representing the CAN device.
|
|
* \param length: The length of the data frame in bytes.
|
|
* \return CANT_E_OK on success, indicating that the statistics have been updated.
|
|
*
|
|
* This function calculates the number of bits transmitted in a CAN FD (Flexible Data Rate) standard frame
|
|
* and adds it to the `periodbits` member of the CANT structure. If the data length is greater than 16 bytes,
|
|
* it also adds the additional bits due to the change in CRC length from 17 bits to 23 bits.
|
|
*/
|
|
static int cant_statistics_busload(CANT *cant, uint16_t length)
|
|
{
|
|
// Check if the configured baud rate is within the valid range
|
|
if (cant->config.baundrate < CANT_BAUDRATE_MAX)
|
|
{
|
|
// Calculate the total number of bits in the frame and add it to the periodbits
|
|
cant->periodbits += (CanFDStdFrmBaseBits + length * 8);
|
|
|
|
/* When DLC is greater than 16, crc changes from 17bits to 23bits. */
|
|
if (length > 16)
|
|
{
|
|
// Add the additional bits due to the CRC change
|
|
cant->periodbits += CanFDAppendBits;
|
|
}
|
|
}
|
|
|
|
return CANT_E_OK;
|
|
}
|
|
|
|
/**
|
|
* \brief Calculates the bus load percentage for a CAN device.
|
|
* \param cant: Pointer to the CANT structure representing the CAN device.
|
|
* \return CANT_E_OK on success, indicating that the bus load has been calculated.
|
|
*
|
|
* This function calculates the bus load percentage based on the total number of bits transmitted
|
|
* during a period and the configured baud rate. It then updates the `busload` member of the CANT structure.
|
|
* If the calculated bus load exceeds 100%, it is capped at 100%. Finally, it resets the `periodbits` member.
|
|
*/
|
|
static int cant_calculate_busload(CANT *cant)
|
|
{
|
|
// Check if the configured baud rate is within the valid range
|
|
if (cant->config.baundrate < CANT_BAUDRATE_MAX)
|
|
{
|
|
/* Count the amount of data transmitted during this cycle, including sent and received. */
|
|
// Calculate the bus load as a percentage and scale it by 10000
|
|
cant->busload = (uint16_t)((double)cant->periodbits / baudrate_list[cant->config.baundrate] * 10000);
|
|
// Cap the bus load at 100%
|
|
if (cant->busload > 10000) cant->busload = 10000;
|
|
// Reset the periodbits for the next cycle
|
|
cant->periodbits = 0;
|
|
}
|
|
|
|
return CANT_E_OK;
|
|
}
|
|
|
|
/**
|
|
* \brief Updates the dummy data parameters based on the bus load in the CAN device.
|
|
* \param cant: Pointer to the CANT structure representing the CAN device.
|
|
* \return CANT_E_OK if the update is successful, CANT_E_DCANID if the CAN ID in the dummy data is 0.
|
|
*
|
|
* This function first checks if the CAN ID in the dummy data is valid (not 0). If it is valid,
|
|
* it compares the current bus load with the target load in the dummy data. If the bus load is less
|
|
* than the target load, it increases the 'gapcount' by a fixed resolution value. If the bus load
|
|
* is greater than the target load, it decreases the 'gapcount' by the same resolution value.
|
|
* Then it calculates the 'gapbase' as the integer part of 'gapcount' divided by 100,
|
|
* and the 'compression' as the fractional part of 'gapcount' divided by 100 minus 'gapbase'.
|
|
* Finally, it sets the 'rate' to the 'compression' value.
|
|
*/
|
|
static int cant_dummy_update(CANT *cant)
|
|
{
|
|
const uint32_t resolution = 5;
|
|
|
|
if (cant->dummy.canid == 0) return CANT_E_DCANID;
|
|
|
|
/* Update the deviation value and record it in 'cant->dummy.gapcount' */
|
|
if (cant->busload < cant->dummy.tarload)
|
|
{
|
|
if (cant->dummy.gapcount + resolution > cant->dummy.gapcount)
|
|
{
|
|
cant->dummy.gapcount += resolution;
|
|
}
|
|
}
|
|
else if (cant->busload > cant->dummy.tarload)
|
|
{
|
|
if (cant->dummy.gapcount >= resolution)
|
|
{
|
|
cant->dummy.gapcount -= resolution;
|
|
}
|
|
}
|
|
|
|
/* Update the basic sending number and compression rate of the sending point */
|
|
cant->dummy.gapbase = (uint32_t)(cant->dummy.gapcount / 100);
|
|
cant->dummy.compression = (double)cant->dummy.gapcount / 100.0 - cant->dummy.gapbase;
|
|
cant->dummy.rate = cant->dummy.compression;
|
|
|
|
return CANT_E_OK;
|
|
}
|
|
|
|
/**
|
|
* \brief Executes the dummy data sending process in the CAN device.
|
|
* \param cant: Pointer to the CANT structure representing the CAN device.
|
|
* \return CANT_E_OK if the execution is successful.
|
|
*
|
|
* This function first calculates the number of messages to send based on the 'gapbase'
|
|
* and 'compression' values in the dummy data. It adds the 'compression' value to the 'rate'.
|
|
* If the 'rate' exceeds 1.0, it subtracts 1.0 from the 'rate' and increments the number of messages to send.
|
|
* If there are messages to send, it constructs the dummy data buffer with relevant information such as
|
|
* the current count and bus load, calculates the CRC8 value for a part of the data buffer,
|
|
* and then calls the 'cant_transmit' function to send the dummy data. After sending each message,
|
|
* it increments the current count in the dummy data.
|
|
*/
|
|
static int cant_dummy_execute(CANT *cant)
|
|
{
|
|
uint32_t send = 0;
|
|
|
|
/* Calculate how many messages the current sending point needs to send */
|
|
send = cant->dummy.gapbase;
|
|
cant->dummy.rate += cant->dummy.compression;
|
|
if (cant->dummy.rate > 1.0)
|
|
{
|
|
cant->dummy.rate -= 1.0;
|
|
send++;
|
|
}
|
|
|
|
/* Evenly 'gapCount' the difference to each sending point for sending */
|
|
if (send > 0)
|
|
{
|
|
for (int i = 0; i < send; i++)
|
|
{
|
|
cant->dummy.data[2] = (cant->dummy.curcount >> 24) & 0xFF;
|
|
cant->dummy.data[3] = (cant->dummy.curcount >> 16) & 0xFF;
|
|
cant->dummy.data[4] = (cant->dummy.curcount >> 8) & 0xFF;
|
|
cant->dummy.data[5] = (cant->dummy.curcount) & 0xFF;
|
|
|
|
cant->dummy.data[6] = (cant->busload >> 8) & 0xFF;
|
|
cant->dummy.data[7] = (cant->busload) & 0xFF;
|
|
|
|
cant->dummy.data[1] = crc8(&(cant->dummy.data[2]), sizeof(cant->dummy.data) - 2);
|
|
|
|
cant_transmit(cant, cant->dummy.canid, cant->dummy.data, sizeof(cant->dummy.data));
|
|
|
|
cant->dummy.curcount++;
|
|
}
|
|
}
|
|
|
|
return CANT_E_OK;
|
|
}
|
|
|
|
/**
|
|
* \brief Verifies the received dummy data in the CAN device.
|
|
* \param cant: Pointer to the CANT structure representing the CAN device.
|
|
* \param canid: The CAN ID of the received data.
|
|
* \param data: Pointer to the received data buffer.
|
|
* \param length: The length of the received data buffer.
|
|
* \return CANT_E_OK if the verification is successful, CANT_E_DCANID if the CAN ID is invalid.
|
|
*
|
|
* This function first checks if the CAN ID in the verification settings is valid (not 0)
|
|
* and if it matches the received CAN ID. If not, it returns an error code. Then it extracts
|
|
* the type of the received data. If the type is 0x00, it extracts the expected CRC value,
|
|
* the count value, and the loading value from the data buffer. It calculates the CRC value
|
|
* for the relevant part of the data buffer and compares it with the expected CRC value.
|
|
* If they don't match, it increments the error count. It also checks if the received count
|
|
* value is consistent with the current count value in the verification settings. If not,
|
|
* it updates the last count value. If the verification is successful (no errors), it increments
|
|
* the verification count and updates the current count value.
|
|
*/
|
|
static int cant_dummy_verify(CANT *cant, uint32_t canid, uint8_t *data, uint16_t length)
|
|
{
|
|
uint8_t type = 0;
|
|
uint8_t calcrc = 0;
|
|
uint8_t expcrc = 0;
|
|
uint8_t verifyfail = 0;
|
|
uint16_t loadding = 0;
|
|
uint32_t count = 0;
|
|
|
|
if (cant->verify.canid == 0 || canid != cant->verify.canid) return CANT_E_DCANID;
|
|
|
|
type = data[0];
|
|
|
|
if (type == 0x00)
|
|
{
|
|
expcrc = data[1];
|
|
count = data[2] << 24 | data[3] << 16 | data[4] << 8 | data[5];
|
|
loadding = data[6] << 8 | data[7];
|
|
|
|
calcrc = crc8(&data[2], length - 2);
|
|
if (calcrc != expcrc)
|
|
{
|
|
cant->verify.errcount++;
|
|
verifyfail++;
|
|
}
|
|
|
|
if (count != cant->verify.curcount + 1 && cant->verify.vercount != 0)
|
|
{
|
|
int32_t lstcount = count - cant->verify.curcount - 1;
|
|
if (lstcount > 0) cant->verify.lstcount += lstcount;
|
|
}
|
|
|
|
if (verifyfail == 0) cant->verify.vercount++;
|
|
|
|
cant->verify.curcount = count;
|
|
}
|
|
|
|
return CANT_E_OK;
|
|
}
|
|
|
|
/**
|
|
* \brief Sets the target bus load for the CAN device's dummy data.
|
|
* \param cant: Pointer to the CANT structure representing the CAN device.
|
|
* \param load: The target bus load value to be set.
|
|
* \return CANT_E_OK if the setting is successful, CANT_E_INVALID if the pointer is NULL.
|
|
*
|
|
* This function checks if the provided pointer is valid. If so, it ensures that the load
|
|
* value is within the valid range (less than or equal to 10000) and then sets the target
|
|
* bus load value in the dummy data part of the CANT structure.
|
|
*/
|
|
int cant_set_busload(CANT *cant, uint16_t load)
|
|
{
|
|
if (!cant) return CANT_E_INVALID;
|
|
if (load >= 10000) load = 10000;
|
|
|
|
cant->dummy.tarload = load;
|
|
|
|
return CANT_E_OK;
|
|
}
|
|
|
|
/**
|
|
* \brief Retrieves the target bus load for the CAN device's dummy data.
|
|
* \param cant: Pointer to the CANT structure representing the CAN device.
|
|
* \param load: Pointer to a variable where the target bus load will be stored.
|
|
* \return CANT_E_OK if the retrieval is successful, CANT_E_INVALID if the pointer is NULL,
|
|
* CANT_E_LOAD if the provided load pointer is NULL.
|
|
*
|
|
* This function checks if the provided pointers are valid. If so, it copies the target
|
|
* bus load value from the dummy data part of the CANT structure to the provided variable.
|
|
*/
|
|
int cant_get_busload(CANT *cant, uint16_t *load)
|
|
{
|
|
if (!cant) return CANT_E_INVALID;
|
|
if (!load) return CANT_E_LOAD;
|
|
|
|
*load = cant->dummy.tarload;
|
|
|
|
return CANT_E_OK;
|
|
}
|
|
|
|
/**
|
|
* \brief Sets the CAN ID for the CAN device's dummy data.
|
|
* \param cant: Pointer to the CANT structure representing the CAN device.
|
|
* \param canid: The CAN ID value to be set.
|
|
* \return CANT_E_OK if the setting is successful, CANT_E_INVALID if the pointer is NULL,
|
|
* CANT_E_ECANID if the provided CAN ID is 0.
|
|
*
|
|
* This function checks if the provided pointer is valid. If so, it checks if the provided
|
|
* CAN ID is not 0. If valid, it sets the CAN ID in the dummy data part of the CANT structure.
|
|
*/
|
|
int cant_set_dummy_canid(CANT *cant, uint32_t canid)
|
|
{
|
|
if (!cant) return CANT_E_INVALID;
|
|
if (canid == 0) return CANT_E_ECANID;
|
|
|
|
cant->dummy.canid = canid;
|
|
|
|
return CANT_E_OK;
|
|
}
|
|
|
|
/**
|
|
* \brief Sets the CAN ID for the verification settings in the CAN device.
|
|
* \param cant: Pointer to the CANT structure representing the CAN device.
|
|
* \param canid: The CAN ID value to be set.
|
|
* \return CANT_E_OK if the setting is successful, CANT_E_INVALID if the pointer is NULL,
|
|
* CANT_E_ECANID if the provided CAN ID is 0.
|
|
*
|
|
* This function checks if the provided pointer is valid. If so, it checks if the provided
|
|
* CAN ID is not 0. If valid, it sets the CAN ID in the verification part of the CANT structure.
|
|
*/
|
|
int cant_set_verify_canid(CANT *cant, uint32_t canid)
|
|
{
|
|
if (!cant) return CANT_E_INVALID;
|
|
if (canid == 0) return CANT_E_ECANID;
|
|
|
|
cant->verify.canid = canid;
|
|
|
|
return CANT_E_OK;
|
|
}
|
|
|
|
/**
|
|
* \brief Transmits data over the CAN bus using the provided CAN device configuration.
|
|
* \param cant: Pointer to the CANT structure representing the CAN device.
|
|
* \param canid: The CAN ID to which the data will be transmitted.
|
|
* \param data: Pointer to the data buffer to be transmitted.
|
|
* \param length: The length of the data buffer.
|
|
* \return CANT_E_OK if the transmission is successful, appropriate error codes otherwise.
|
|
* CANT_E_INVALID if the pointer to the CANT structure is NULL.
|
|
* CANT_E_DATA if the data pointer is NULL.
|
|
* CANT_E_TRANSMIT if the transmission function pointer in the configuration is NULL.
|
|
* CANT_E_TRANSFAIL if the actual transmission function returns a non-zero value.
|
|
*
|
|
* This function first checks the validity of the input parameters. If all parameters are valid,
|
|
* it calls the configured transmission function to send the data. If the transmission function
|
|
* returns a non-zero value, it indicates a transmission failure. After a successful transmission,
|
|
* it updates the bus load statistics for the CAN device.
|
|
*/
|
|
int cant_transmit(CANT *cant, uint32_t canid, uint8_t *data, uint16_t length)
|
|
{
|
|
if (!cant) return CANT_E_INVALID;
|
|
if (!data) return CANT_E_DATA;
|
|
if (!cant->config.transmit) return CANT_E_TRANSMIT;
|
|
|
|
if (cant->config.transmit(canid, data, length) != 0) return CANT_E_TRANSFAIL;
|
|
|
|
cant_statistics_busload(cant, length);
|
|
|
|
return CANT_E_OK;
|
|
}
|
|
|
|
/**
|
|
* \brief Receives data over the CAN bus and performs related operations.
|
|
* \param cant: Pointer to the CANT structure representing the CAN device.
|
|
* \param canid: The CAN ID from which the data is received.
|
|
* \param data: Pointer to the buffer where the received data will be stored.
|
|
* \param length: The length of the received data.
|
|
* \return CANT_E_OK if the operation is successful, CANT_E_INVALID if the pointer to the CANT structure is NULL.
|
|
*
|
|
* This function first checks the validity of the input pointer. If valid, it verifies the received
|
|
* dummy data using the 'cant_dummy_verify' function. Then it updates the bus load statistics.
|
|
* If the receive function pointer in the configuration is valid, it calls the receive function
|
|
* to handle the received data.
|
|
*/
|
|
int cant_receive(CANT *cant, uint32_t canid, uint8_t *data, uint16_t length)
|
|
{
|
|
if (!cant) return CANT_E_INVALID;
|
|
cant_dummy_verify(cant, canid, data, length);
|
|
cant_statistics_busload(cant, length);
|
|
if (cant->config.receive) cant->config.receive(canid, data, length);
|
|
return CANT_E_OK;
|
|
}
|
|
|
|
/**
|
|
* \brief Initializes the CANT structure representing the CAN device.
|
|
* \param cant: Pointer to the CANT structure to be initialized.
|
|
* \return CANT_E_OK if the initialization is successful, CANT_E_INVALID if the pointer is NULL.
|
|
*
|
|
* This function initializes various fields in the CANT structure. It sets the bus load, period bits,
|
|
* and timestamp to 0. It also initializes the dummy data and verification related fields in the
|
|
* CANT structure to their default values. For the dummy data, it sets the CAN ID, compression,
|
|
* rate, target load, gap base, gap count, current count, and initializes the data buffer. For
|
|
* the verification part, it sets the CAN ID, error count, current count, verification count,
|
|
* and last count to 0.
|
|
*/
|
|
int cant_init(CANT *cant)
|
|
{
|
|
if (!cant) return CANT_E_INVALID;
|
|
|
|
cant->busload = 0;
|
|
cant->periodbits = 0;
|
|
cant->timestamp = 0;
|
|
|
|
/* Dummy data init */
|
|
cant->dummy.canid = 0;
|
|
cant->dummy.compression = 0.0;
|
|
cant->dummy.rate = 0.0;
|
|
cant->dummy.tarload = 0;
|
|
cant->dummy.gapbase = 0;
|
|
cant->dummy.gapcount = 0;
|
|
cant->dummy.curcount = 0;
|
|
cant->dummy.data[0] = 0x00;
|
|
for (uint8_t i = 8; i < sizeof(cant->dummy.data); i++)
|
|
{
|
|
cant->dummy.data[i] = i;
|
|
}
|
|
|
|
cant->verify.canid = 0;
|
|
cant->verify.errcount = 0;
|
|
cant->verify.curcount = 0;
|
|
cant->verify.vercount = 0;
|
|
cant->verify.lstcount = 0;
|
|
|
|
return CANT_E_OK;
|
|
}
|
|
|
|
/**
|
|
* \brief The main task function for the CAN device.
|
|
* \param cant: Pointer to the CANT structure representing the CAN device.
|
|
* \return CANT_E_OK if the task is executed successfully, CANT_E_INVALID if the pointer is NULL.
|
|
*
|
|
* This function is the main task handler for the CAN device. It updates the timestamp of the
|
|
* CAN device. Based on the timestamp value, it performs different operations at specific intervals.
|
|
* Every 1000 units of the timestamp, it calculates the bus load using the 'cant_calculate_busload'
|
|
* function. Every 10 units of the timestamp, it executes the dummy data sending operation using
|
|
* the 'cant_dummy_execute' function. Every 1000 units of the timestamp, it updates the dummy
|
|
* data parameters using the 'cant_dummy_update' function.
|
|
*/
|
|
int cant_task(CANT *cant)
|
|
{
|
|
if (!cant) return CANT_E_INVALID;
|
|
|
|
cant->timestamp += cant->config.period;
|
|
if (cant->timestamp >= 252000000) cant->timestamp = 0;
|
|
|
|
if (cant->timestamp % 1000 == 0)
|
|
{
|
|
cant_calculate_busload(cant);
|
|
}
|
|
|
|
if (cant->timestamp % 10 == 0)
|
|
{
|
|
cant_dummy_execute(cant);
|
|
}
|
|
|
|
if (cant->timestamp % 1000 == 0)
|
|
{
|
|
cant_dummy_update(cant);
|
|
}
|
|
|
|
return CANT_E_OK;
|
|
}
|