mirror of
https://gitee.com/Lamdonn/varch.git
synced 2025-12-06 16:56:42 +08:00
Add the source code of the initial version slup and cant
This commit is contained in:
parent
1846fbab1b
commit
c97a43dd6f
489
source/06_performance/cant.c
Normal file
489
source/06_performance/cant.c
Normal file
@ -0,0 +1,489 @@
|
|||||||
|
/*********************************************************************************************************
|
||||||
|
* ------------------------------------------------------------------------------------------------------
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
126
source/06_performance/cant.h
Normal file
126
source/06_performance/cant.h
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
/*********************************************************************************************************
|
||||||
|
* ------------------------------------------------------------------------------------------------------
|
||||||
|
* file description
|
||||||
|
* ------------------------------------------------------------------------------------------------------
|
||||||
|
* \file cant.h
|
||||||
|
* \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.
|
||||||
|
********************************************************************************************************/
|
||||||
|
#ifndef __cant_H
|
||||||
|
#define __cant_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* Version infomation */
|
||||||
|
#define CANT_V_MAJOR 0
|
||||||
|
#define CANT_V_MINOR 1
|
||||||
|
#define CANT_V_PATCH 0
|
||||||
|
|
||||||
|
#define CANT_BAUDRATE_125K 0
|
||||||
|
#define CANT_BAUDRATE_250K 1
|
||||||
|
#define CANT_BAUDRATE_500K 2
|
||||||
|
#define CANT_BAUDRATE_800K 3
|
||||||
|
#define CANT_BAUDRATE_1000K 4
|
||||||
|
#define CANT_BAUDRATE_MAX 5
|
||||||
|
|
||||||
|
#define CANT_E_OK 0 // Success
|
||||||
|
#define CANT_E_INVALID 1 // Invalid `cant` pointer
|
||||||
|
#define CANT_E_DATA 2 // Invalid `data` pointer
|
||||||
|
#define CANT_E_LEN 3 // Invalid legth
|
||||||
|
#define CANT_E_NTEST 4 // CANT load testing has not started
|
||||||
|
#define CANT_E_OVER 5 // CANT load exceeds range
|
||||||
|
#define CANT_E_LINK 6 // Invalid `link` pointer
|
||||||
|
#define CANT_E_RATE 7 // Invalid `rate` pointer
|
||||||
|
#define CANT_E_TRANSMIT 8 // Invalid `transmit` pointer
|
||||||
|
#define CANT_E_TRANSFAIL 9 // Invalid `transmit` pointer
|
||||||
|
#define CANT_E_LOAD 10 // Invalid `load` pointer
|
||||||
|
#define CANT_E_DCANID 11 // Dummy canids that do not match
|
||||||
|
#define CANT_E_ECANID 12 // Error canid
|
||||||
|
|
||||||
|
// Function pointer type for functions related to CAN transfer.
|
||||||
|
// It takes a CAN ID (uint32_t), a pointer to data (uint8_t), and the length of the data (uint16_t) as parameters
|
||||||
|
// and returns an int. It can be used for functions like CAN transmission or reception.
|
||||||
|
typedef int (*cant_transfer_t)(uint32_t canid, uint8_t *data, uint16_t length);
|
||||||
|
|
||||||
|
// Structure definition for the CANT (presumably related to CAN bus) structure.
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
// Inner structure for configuration related to the CAN functionality.
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
// Channel number of the CAN bus, represented as a uint8_t.
|
||||||
|
uint8_t channel;
|
||||||
|
// Baud rate of the CAN bus, represented as a uint8_t.
|
||||||
|
uint8_t baundrate;
|
||||||
|
// Call period for the 'cant_task()' function, represented as a uint16_t.
|
||||||
|
uint16_t period; // `cant_task()` call period
|
||||||
|
// Function pointer for CAN transmission. It points to a function that can transmit data on the CAN bus.
|
||||||
|
cant_transfer_t transmit; // can transmit the drive function
|
||||||
|
// Function pointer for CAN reception. It points to a function that can handle received data on the CAN bus.
|
||||||
|
cant_transfer_t receive; // can receive hook functions
|
||||||
|
} config;
|
||||||
|
|
||||||
|
// Bus load value of the CAN bus, represented as a uint16_t.
|
||||||
|
uint16_t busload;
|
||||||
|
// Value related to period bits (specific meaning depends on the context), represented as a uint32_t.
|
||||||
|
uint32_t periodbits;
|
||||||
|
// Timestamp value, represented as a uint32_t.
|
||||||
|
uint32_t timestamp;
|
||||||
|
|
||||||
|
// Inner structure for dummy data related to CAN operations.
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
// CAN ID, represented as a uint32_t.
|
||||||
|
uint32_t canid;
|
||||||
|
// Compression factor, represented as a float.
|
||||||
|
float compression;
|
||||||
|
// Rate value, represented as a float.
|
||||||
|
float rate;
|
||||||
|
// Target load value, represented as a uint16_t.
|
||||||
|
uint16_t tarload;
|
||||||
|
// Base value for the gap (specific meaning depends on the context), represented as a uint32_t.
|
||||||
|
uint32_t gapbase;
|
||||||
|
// Count value for the gap (specific meaning depends on the context), represented as a uint32_t.
|
||||||
|
uint32_t gapcount;
|
||||||
|
// Current count value, represented as a uint32_t.
|
||||||
|
uint32_t curcount;
|
||||||
|
// Array to store data, with a size of 64 bytes.
|
||||||
|
uint8_t data[64];
|
||||||
|
} dummy;
|
||||||
|
|
||||||
|
// Inner structure for verification related data in CAN operations.
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
// CAN ID, represented as a uint32_t.
|
||||||
|
uint32_t canid;
|
||||||
|
// Error count value, represented as a uint32_t.
|
||||||
|
uint32_t errcount;
|
||||||
|
// Current count value, represented as a uint32_t.
|
||||||
|
uint32_t curcount;
|
||||||
|
// Verification count value, represented as a uint32_t.
|
||||||
|
uint32_t vercount;
|
||||||
|
// Last count value, represented as a uint32_t.
|
||||||
|
uint32_t lstcount;
|
||||||
|
} verify;
|
||||||
|
} CANT;
|
||||||
|
|
||||||
|
int cant_set_dummy_canid(CANT *cant, uint32_t canid);
|
||||||
|
int cant_set_verify_canid(CANT *cant, uint32_t canid);
|
||||||
|
|
||||||
|
int cant_set_busload(CANT *cant, uint16_t load);
|
||||||
|
int cant_get_busload(CANT *cant, uint16_t *load);
|
||||||
|
|
||||||
|
int cant_transmit(CANT *cant, uint32_t canid, uint8_t *data, uint16_t length);
|
||||||
|
int cant_receive(CANT *cant, uint32_t canid, uint8_t *data, uint16_t length);
|
||||||
|
|
||||||
|
int cant_init(CANT *cant);
|
||||||
|
int cant_task(CANT *cant);
|
||||||
|
|
||||||
|
#endif
|
||||||
780
source/06_performance/slup.c
Normal file
780
source/06_performance/slup.c
Normal file
@ -0,0 +1,780 @@
|
|||||||
|
/*********************************************************************************************************
|
||||||
|
* ------------------------------------------------------------------------------------------------------
|
||||||
|
* file description
|
||||||
|
* ------------------------------------------------------------------------------------------------------
|
||||||
|
* \file slup.c
|
||||||
|
* \unit slup
|
||||||
|
* \brief This is a simple serial link universal protocol for C language
|
||||||
|
* \author Lamdonn
|
||||||
|
* \version v0.1.0
|
||||||
|
* \license GPL-2.0
|
||||||
|
* \copyright Copyright (C) 2023 Lamdonn.
|
||||||
|
********************************************************************************************************/
|
||||||
|
#include "slup.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
| HEAD | SN | FrameType | Length | Data | Check | TAIL |
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Macro to send a character and perform some additional operations.
|
||||||
|
// It calls the putc function pointer in the slup structure to send the character 'c'.
|
||||||
|
// It also logs the character in hexadecimal format using SLUP_DEBUG and updates the upload statistics.
|
||||||
|
#define sputc(c) do { (slup->putc)(c); SLUP_DEBUG("%02x ", (c)); slup_statis_upload(slup); } while (0)
|
||||||
|
|
||||||
|
// Macro to access the i-th element of the head mask in the SLUP configuration.
|
||||||
|
#define head(i) (slup->cfg.head[(i)])
|
||||||
|
|
||||||
|
// Macro to access the i-th element of the tail mask in the SLUP configuration.
|
||||||
|
#define tail(i) (slup->cfg.tail[(i)])
|
||||||
|
|
||||||
|
// Macro to access the i-th byte of the sequence number (sn) in the slup structure.
|
||||||
|
#define sn(i) (((uint8_t *)(&(slup->sn)))[(i)])
|
||||||
|
|
||||||
|
// Macro to access the i-th byte of the length variable.
|
||||||
|
#define len(i) (((uint8_t *)(&(length)))[(i)])
|
||||||
|
|
||||||
|
// Define states for the SLUP receive state machine.
|
||||||
|
#define SLUP_RX_STATE_HEAD 0
|
||||||
|
#define SLUP_RX_STATE_SN 1
|
||||||
|
#define SLUP_RX_STATE_FRAMETYPE 2
|
||||||
|
#define SLUP_RX_STATE_LENGTH 3
|
||||||
|
#define SLUP_RX_STATE_DATA 4
|
||||||
|
#define SLUP_RX_STATE_CHECK 5
|
||||||
|
#define SLUP_RX_STATE_TAIL 6
|
||||||
|
|
||||||
|
// Debug macro, currently defined as an empty operation.
|
||||||
|
// Can be redefined to use printf for debugging purposes.
|
||||||
|
#define SLUP_DEBUG(...)
|
||||||
|
// #define SLUP_DEBUG printf
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Pushes a character into the SLUP queue.
|
||||||
|
* \param queue: Pointer to the SLUP queue structure.
|
||||||
|
* \param data: The character to be pushed into the queue.
|
||||||
|
* \return 1 if the push operation is successful, 0 otherwise.
|
||||||
|
*
|
||||||
|
* This function adds a character to the SLUP queue. If the queue is not full,
|
||||||
|
* it simply adds the character to the tail of the queue and updates the tail index and size.
|
||||||
|
* If the queue is full, it overwrites the oldest element and updates both the head and tail indices.
|
||||||
|
*/
|
||||||
|
static int slup_queue_push(SLUP_QUEUE *queue, char data)
|
||||||
|
{
|
||||||
|
if (!queue) return 0;
|
||||||
|
|
||||||
|
if (queue->size < SLUP_RXQUE_SIZE)
|
||||||
|
{
|
||||||
|
queue->base[queue->tail] = data;
|
||||||
|
queue->tail = (queue->tail + 1) % SLUP_RXQUE_SIZE;
|
||||||
|
queue->size++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
queue->base[queue->tail] = data;
|
||||||
|
queue->tail = (queue->tail + 1) % SLUP_RXQUE_SIZE;
|
||||||
|
queue->head = (queue->head + 1) % SLUP_RXQUE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Pops a specified number of elements from the SLUP queue.
|
||||||
|
* \param queue: Pointer to the SLUP queue structure.
|
||||||
|
* \param count: The number of elements to be popped from the queue.
|
||||||
|
* \return 1 if the pop operation is successful, 0 otherwise.
|
||||||
|
*
|
||||||
|
* This function removes a specified number of elements from the SLUP queue.
|
||||||
|
* If the number of elements to be popped is greater than the current size of the queue,
|
||||||
|
* it will only pop the existing elements in the queue.
|
||||||
|
*/
|
||||||
|
static int slup_queue_pop(SLUP_QUEUE *queue, uint32_t count)
|
||||||
|
{
|
||||||
|
if (!queue) return 0;
|
||||||
|
if (queue->size == 0) return 0;
|
||||||
|
if (count == 0) return 0;
|
||||||
|
|
||||||
|
if (count > queue->size) count = queue->size;
|
||||||
|
|
||||||
|
queue->head = (queue->head + count) % SLUP_RXQUE_SIZE;
|
||||||
|
queue->size -= count;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Retrieves an element at a specified index from the SLUP queue.
|
||||||
|
* \param queue: Pointer to the SLUP queue structure.
|
||||||
|
* \param index: The index of the element to be retrieved.
|
||||||
|
* \return The character at the specified index if the queue is valid and not empty, 0 otherwise.
|
||||||
|
*
|
||||||
|
* This function returns the element at a given index in the SLUP queue.
|
||||||
|
* The index is calculated relative to the head of the queue.
|
||||||
|
*/
|
||||||
|
static char slup_queue_at(SLUP_QUEUE *queue, uint32_t index)
|
||||||
|
{
|
||||||
|
if (!queue) return 0;
|
||||||
|
if (queue->size == 0) return 0;
|
||||||
|
return queue->base[((queue->head + index) % SLUP_RXQUE_SIZE)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Update the upload statistics of the SLUP structure.
|
||||||
|
* \param slup: Pointer to the SLUP structure.
|
||||||
|
*
|
||||||
|
* This function increments the 'upbits' field in the SLUP structure by 8,
|
||||||
|
* indicating that 8 bits of data have been uploaded. It is called within the 'sputc' macro
|
||||||
|
* when sending data to keep track of the amount of uploaded data.
|
||||||
|
*/
|
||||||
|
static void slup_statis_upload(SLUP *slup)
|
||||||
|
{
|
||||||
|
slup->upbits += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Update the download statistics of the SLUP structure.
|
||||||
|
* \param slup: Pointer to the SLUP structure.
|
||||||
|
*
|
||||||
|
* This function increments the 'downbits' field in the SLUP structure by 8,
|
||||||
|
* indicating that 8 bits of data have been downloaded. It is used to track
|
||||||
|
* the amount of data received in the SLUP communication process.
|
||||||
|
*/
|
||||||
|
static void slup_statis_download(SLUP *slup)
|
||||||
|
{
|
||||||
|
slup->downbits += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Receive and process an SLUP package.
|
||||||
|
* \param slup: Pointer to the SLUP structure.
|
||||||
|
* \return SLUP_E_OK: Indicates that the package has been successfully received and processed.
|
||||||
|
*
|
||||||
|
* This function first checks the 'frame' field in the 'parser' of the SLUP structure.
|
||||||
|
* If the 'frame' is 0x00, it extracts the link information from the first byte of the 'buffer'.
|
||||||
|
* Depending on the result of the bitwise AND operation between the link information and SLUP_LINK_RX,
|
||||||
|
* it updates the 'link' field in the SLUP structure. If the 'frame' is not 0x00 and the 'receive'
|
||||||
|
* function pointer in the SLUP structure is valid, it calls the 'receive' function to handle
|
||||||
|
* the received data in the 'buffer' with the specified size 'bsize'.
|
||||||
|
*/
|
||||||
|
static int slup_receive_package(SLUP *slup)
|
||||||
|
{
|
||||||
|
if (slup->parser.frame == 0x00)
|
||||||
|
{
|
||||||
|
uint8_t link = 0;
|
||||||
|
|
||||||
|
link = slup->buffer[0];
|
||||||
|
|
||||||
|
if (link & SLUP_LINK_RX)
|
||||||
|
{
|
||||||
|
slup->link |= SLUP_LINK_TX;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
slup->link &= (~SLUP_LINK_TX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (slup->receive)
|
||||||
|
{
|
||||||
|
(slup->receive)(slup->buffer, slup->bsize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return SLUP_E_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Send an SLUP package.
|
||||||
|
* \param slup: Pointer to the SLUP structure.
|
||||||
|
* \param frame: The frame type of the package.
|
||||||
|
* \param data: Pointer to the data buffer of the package.
|
||||||
|
* \param length: The length of the data in the package.
|
||||||
|
* \return SLUP_E_OK: Indicates that the package has been successfully sent.
|
||||||
|
* SLUP_E_INVALID: Returned if the 'slup' pointer is NULL.
|
||||||
|
* SLUP_E_DATA: Returned if the 'data' pointer is NULL.
|
||||||
|
* SLUP_E_LEN: Returned if the 'length' is 0.
|
||||||
|
*
|
||||||
|
* This function constructs and sends an SLUP package. It first validates the input parameters.
|
||||||
|
* Then it sends each part of the package in order: the head, sequence number (SN), frame type,
|
||||||
|
* data length, data, checksum, and tail. The sequence number is incremented after being sent.
|
||||||
|
* The checksum is calculated using the 'check' function pointer in the SLUP structure.
|
||||||
|
*/
|
||||||
|
static int slup_send_package(SLUP *slup, uint8_t frame, uint8_t *data, uint16_t length)
|
||||||
|
{
|
||||||
|
uint32_t i = 0;
|
||||||
|
uint32_t check = 0;
|
||||||
|
|
||||||
|
if (!slup) return SLUP_E_INVALID;
|
||||||
|
if (!data) return SLUP_E_DATA;
|
||||||
|
if (length == 0) return SLUP_E_LEN;
|
||||||
|
|
||||||
|
/* Send the HEAD part of the package */
|
||||||
|
for (i = 0; i < slup->cfg.hsize; i++)
|
||||||
|
{
|
||||||
|
sputc(head(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send the SN (Sequence Number) part of the package */
|
||||||
|
sputc(sn(0));
|
||||||
|
sputc(sn(1));
|
||||||
|
sputc(sn(2));
|
||||||
|
sputc(sn(3));
|
||||||
|
slup->sn++;
|
||||||
|
|
||||||
|
/* Send the FrameType part of the package */
|
||||||
|
sputc(frame);
|
||||||
|
|
||||||
|
/* Send the Length part of the package */
|
||||||
|
sputc(len(0));
|
||||||
|
sputc(len(1));
|
||||||
|
|
||||||
|
/* Calculate the checksum of the data */
|
||||||
|
check = (slup->check)(data, length);
|
||||||
|
|
||||||
|
/* Send the Data part of the package */
|
||||||
|
for (i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
sputc(data[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send the Check part of the package */
|
||||||
|
for (i = 0; i < slup->cfg.csize; i++)
|
||||||
|
{
|
||||||
|
sputc((((uint8_t *)(&(check)))[(i)]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send the TAIL part of the package */
|
||||||
|
for (i = 0; i < slup->cfg.tsize; i++)
|
||||||
|
{
|
||||||
|
sputc(tail(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
return SLUP_E_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Parses the received data in the SLUP queue and processes it according to the SLUP protocol.
|
||||||
|
* \param slup: Pointer to the SLUP structure.
|
||||||
|
*
|
||||||
|
* This function is responsible for parsing the data in the SLUP queue. It iterates through all the
|
||||||
|
* data in the queue when there is data available. For each byte of data, it uses a state machine
|
||||||
|
* (implemented with a switch statement) to process the data based on the current state of the parser.
|
||||||
|
* The states represent different parts of the SLUP package such as the head, sequence number (SN),
|
||||||
|
* frame type, length, data, checksum, and tail. Once a complete package is parsed and verified,
|
||||||
|
* it calls the slup_receive_package function to handle the received package and resets the buffer
|
||||||
|
* and parser state for the next package.
|
||||||
|
*/
|
||||||
|
static void slup_parse_task(SLUP *slup)
|
||||||
|
{
|
||||||
|
uint32_t i = 0;
|
||||||
|
char data = 0;
|
||||||
|
|
||||||
|
if (slup->queue.size > 0)
|
||||||
|
{
|
||||||
|
for (i = 0; i < slup->queue.size; i++)
|
||||||
|
{
|
||||||
|
data = slup_queue_at(&slup->queue, i);
|
||||||
|
|
||||||
|
SLUP_DEBUG("%02x ", data);
|
||||||
|
|
||||||
|
switch (slup->parser.state)
|
||||||
|
{
|
||||||
|
case SLUP_RX_STATE_HEAD:
|
||||||
|
{
|
||||||
|
// If the current index for the head (hindex) is less than the configured head size
|
||||||
|
if (slup->parser.hindex < slup->cfg.hsize)
|
||||||
|
{
|
||||||
|
// If the received data byte matches the expected head byte at the current index
|
||||||
|
if (data == slup->cfg.head[slup->parser.hindex])
|
||||||
|
{
|
||||||
|
// Increment the head index
|
||||||
|
slup->parser.hindex++;
|
||||||
|
|
||||||
|
// If the head index reaches the configured head size, it means the head is fully received
|
||||||
|
if (slup->parser.hindex == slup->cfg.hsize)
|
||||||
|
{
|
||||||
|
slup->parser.hindex = 0;
|
||||||
|
SLUP_DEBUG(" [SLUP_RX_STATE_HEAD] OK\r\n");
|
||||||
|
// Move to the next state which is to receive the sequence number (SN)
|
||||||
|
slup->parser.state = SLUP_RX_STATE_SN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If the received byte doesn't match, reset the head index
|
||||||
|
slup->parser.hindex = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case SLUP_RX_STATE_SN:
|
||||||
|
{
|
||||||
|
// If the current index for the sequence number (sindex) is less than the configured SN size
|
||||||
|
if (slup->parser.sindex < slup->cfg.ssize)
|
||||||
|
{
|
||||||
|
// Store the received data byte into the appropriate position of the parser's SN
|
||||||
|
((uint8_t *)(&(slup->parser.sn)))[slup->parser.sindex] = data;
|
||||||
|
|
||||||
|
// Increment the sequence number index
|
||||||
|
slup->parser.sindex++;
|
||||||
|
|
||||||
|
// If the sequence number index reaches the configured SN size, it means the SN is fully received
|
||||||
|
if (slup->parser.sindex == slup->cfg.ssize)
|
||||||
|
{
|
||||||
|
slup->parser.sindex = 0;
|
||||||
|
SLUP_DEBUG(" [SLUP_RX_STATE_SN] OK\r\n");
|
||||||
|
// Move to the next state which is to receive the frame type
|
||||||
|
slup->parser.state = SLUP_RX_STATE_FRAMETYPE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case SLUP_RX_STATE_FRAMETYPE:
|
||||||
|
{
|
||||||
|
// Store the received data byte as the frame type
|
||||||
|
slup->parser.frame = data;
|
||||||
|
SLUP_DEBUG(" [SLUP_RX_STATE_FRAMETYPE] OK\r\n");
|
||||||
|
// Move to the next state which is to receive the length
|
||||||
|
slup->parser.state = SLUP_RX_STATE_LENGTH;
|
||||||
|
} break;
|
||||||
|
case SLUP_RX_STATE_LENGTH:
|
||||||
|
{
|
||||||
|
// If the current index for the length (lindex) is less than the size of a uint16_t (since length is uint16_t)
|
||||||
|
if (slup->parser.lindex < sizeof(uint16_t))
|
||||||
|
{
|
||||||
|
// Store the received data byte into the appropriate position of the parser's length
|
||||||
|
((uint8_t *)(&(slup->parser.length)))[slup->parser.lindex] = data;
|
||||||
|
|
||||||
|
// Increment the length index
|
||||||
|
slup->parser.lindex++;
|
||||||
|
|
||||||
|
// If the length index reaches the size of a uint16_t, it means the length is fully received
|
||||||
|
if (slup->parser.lindex == sizeof(uint16_t))
|
||||||
|
{
|
||||||
|
slup->parser.lindex = 0;
|
||||||
|
SLUP_DEBUG(" [SLUP_RX_STATE_LENGTH] OK\r\n");
|
||||||
|
// Move to the next state which is to receive the data
|
||||||
|
slup->parser.state = SLUP_RX_STATE_DATA;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case SLUP_RX_STATE_DATA:
|
||||||
|
{
|
||||||
|
// Store the received data byte into the buffer and increment the buffer size
|
||||||
|
slup->buffer[slup->bsize++] = data;
|
||||||
|
// If the current index for the data (dindex) is less than the received length
|
||||||
|
if (slup->parser.dindex < slup->parser.length)
|
||||||
|
{
|
||||||
|
// Increment the data index
|
||||||
|
slup->parser.dindex++;
|
||||||
|
|
||||||
|
// If the data index reaches the received length, it means the data is fully received
|
||||||
|
if (slup->parser.dindex == slup->parser.length)
|
||||||
|
{
|
||||||
|
slup->parser.dindex = 0;
|
||||||
|
SLUP_DEBUG(" [SLUP_RX_STATE_DATA] OK\r\n");
|
||||||
|
// Move to the next state which is to receive the checksum
|
||||||
|
slup->parser.state = SLUP_RX_STATE_CHECK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case SLUP_RX_STATE_CHECK:
|
||||||
|
{
|
||||||
|
// If the current index for the checksum (cindex) is less than the configured checksum size
|
||||||
|
if (slup->parser.cindex < slup->cfg.csize)
|
||||||
|
{
|
||||||
|
// Store the received data byte into the appropriate position of the parser's checksum
|
||||||
|
((uint8_t *)(&(slup->parser.check)))[slup->parser.cindex] = data;
|
||||||
|
|
||||||
|
// Increment the checksum index
|
||||||
|
slup->parser.cindex++;
|
||||||
|
|
||||||
|
// If the checksum index reaches the configured checksum size, it means the checksum is fully received
|
||||||
|
if (slup->parser.cindex == slup->cfg.csize)
|
||||||
|
{
|
||||||
|
slup->parser.cindex = 0;
|
||||||
|
|
||||||
|
uint32_t cvalue = 0;
|
||||||
|
|
||||||
|
// Calculate the checksum of the received data in the buffer
|
||||||
|
cvalue = (slup->check)(slup->buffer, slup->bsize);
|
||||||
|
|
||||||
|
// Compare the calculated checksum with the received checksum
|
||||||
|
if (memcmp(&cvalue, &slup->parser.check, slup->cfg.csize) == 0)
|
||||||
|
{
|
||||||
|
SLUP_DEBUG(" [SLUP_RX_STATE_CHECK] OK\r\n");
|
||||||
|
// Move to the next state which is to receive the tail
|
||||||
|
slup->parser.state = SLUP_RX_STATE_TAIL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If the checksums don't match, reset the buffer size for the next reception
|
||||||
|
slup->bsize = 0;
|
||||||
|
// Reset the parser state to start receiving a new package from the head
|
||||||
|
slup->parser.state = SLUP_RX_STATE_HEAD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case SLUP_RX_STATE_TAIL:
|
||||||
|
{
|
||||||
|
// If the current index for the tail (tindex) is less than the configured tail size
|
||||||
|
if (slup->parser.tindex < slup->cfg.tsize)
|
||||||
|
{
|
||||||
|
// If the received data byte matches the expected tail byte at the current index
|
||||||
|
if (data == slup->cfg.tail[slup->parser.tindex])
|
||||||
|
{
|
||||||
|
// Increment the tail index
|
||||||
|
slup->parser.tindex++;
|
||||||
|
|
||||||
|
// If the tail index reaches the configured tail size, it means the tail is fully received
|
||||||
|
if (slup->parser.tindex == slup->cfg.tsize)
|
||||||
|
{
|
||||||
|
slup->parser.tindex = 0;
|
||||||
|
SLUP_DEBUG(" [SLUP_RX_STATE_TAIL] OK\r\n");
|
||||||
|
// Call the function to handle the received package
|
||||||
|
slup_receive_package(slup);
|
||||||
|
// Reset the buffer size for the next reception
|
||||||
|
slup->bsize = 0;
|
||||||
|
// Reset the parser state to start receiving a new package from the head
|
||||||
|
slup->parser.state = SLUP_RX_STATE_HEAD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If the received byte doesn't match, reset the tail index and the parser state to start from the head
|
||||||
|
slup->parser.tindex = 0;
|
||||||
|
slup->parser.state = SLUP_RX_STATE_HEAD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pop all the data from the queue after processing
|
||||||
|
slup_queue_pop(&slup->queue, 0xFFFFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Calculate the upload and download rates and reset the bit counters.
|
||||||
|
* \param slup: Pointer to the SLUP structure.
|
||||||
|
*
|
||||||
|
* This function is responsible for calculating the upload and download rates.
|
||||||
|
* It copies the current values of 'upbits' and 'downbits' to 'uprate' and 'downrate' respectively.
|
||||||
|
* Then it resets 'upbits' and 'downbits' to zero, preparing for the next rate calculation.
|
||||||
|
*/
|
||||||
|
static void slup_calrate_task(SLUP *slup)
|
||||||
|
{
|
||||||
|
slup->uprate = slup->upbits;
|
||||||
|
slup->downrate = slup->downbits;
|
||||||
|
slup->upbits = 0;
|
||||||
|
slup->downbits = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Update the dummy data parameters based on the upload rate.
|
||||||
|
* \param slup: Pointer to the SLUP structure.
|
||||||
|
*
|
||||||
|
* This function updates the 'gapcount', 'gapbase', 'compression', and 'rate' fields
|
||||||
|
* in the 'dummy' structure of the SLUP object. It first calculates the deviation
|
||||||
|
* between the upload rate and the target rate. If the upload rate is less than the target,
|
||||||
|
* it increases the 'gapcount' by a fixed resolution value. If the upload rate is greater
|
||||||
|
* than the target, 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 void slup_dummy_update(SLUP *slup)
|
||||||
|
{
|
||||||
|
const uint32_t resolution = 5;
|
||||||
|
|
||||||
|
/* Update the deviation value and record it in 'slup->dummy.gapcount' */
|
||||||
|
if (slup->uprate < slup->dummy.target)
|
||||||
|
{
|
||||||
|
if (slup->dummy.gapcount + resolution > slup->dummy.gapcount)
|
||||||
|
{
|
||||||
|
slup->dummy.gapcount += resolution;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (slup->uprate > slup->dummy.target)
|
||||||
|
{
|
||||||
|
if (slup->dummy.gapcount >= resolution)
|
||||||
|
{
|
||||||
|
slup->dummy.gapcount -= resolution;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update the basic sending number and compression rate of the sending point */
|
||||||
|
slup->dummy.gapbase = (uint32_t)(slup->dummy.gapcount / 100);
|
||||||
|
slup->dummy.compression = (double)slup->dummy.gapcount / 100.0 - slup->dummy.gapbase;
|
||||||
|
slup->dummy.rate = slup->dummy.compression;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Execute the dummy data sending process based on the calculated parameters.
|
||||||
|
* \param slup: Pointer to the SLUP structure.
|
||||||
|
*
|
||||||
|
* This function calculates the number of messages to send based on the 'gapbase'
|
||||||
|
* and 'compression' values in the 'dummy' structure of the SLUP object. 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 calls the 'slup_send' function to send the dummy data
|
||||||
|
* in the 'dummy' structure 'send' times.
|
||||||
|
*/
|
||||||
|
static void slup_dummy_execute(SLUP *slup)
|
||||||
|
{
|
||||||
|
uint32_t send = 0;
|
||||||
|
|
||||||
|
/* Calculate how many messages the current sending point needs to send */
|
||||||
|
send = slup->dummy.gapbase;
|
||||||
|
slup->dummy.rate += slup->dummy.compression;
|
||||||
|
if (slup->dummy.rate > 1.0)
|
||||||
|
{
|
||||||
|
slup->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++)
|
||||||
|
{
|
||||||
|
slup_send(slup, slup->dummy.data, sizeof(slup->dummy.data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Sends a heartbeat message in the SLUP protocol.
|
||||||
|
* \param slup: Pointer to the SLUP structure.
|
||||||
|
* \return SLUP_E_OK if the message is successfully sent, appropriate error code otherwise.
|
||||||
|
*
|
||||||
|
* This function constructs a heartbeat message. It sets the first byte of the data buffer
|
||||||
|
* to the current link status from the SLUP structure. Then it calls the slup_send_package
|
||||||
|
* function with a frame type of 0x00 to send the message.
|
||||||
|
*/
|
||||||
|
static int slup_send_heart(SLUP *slup)
|
||||||
|
{
|
||||||
|
uint8_t data[SLUP_SN_MAX];
|
||||||
|
uint16_t length = 0;
|
||||||
|
|
||||||
|
data[0] = slup->link;
|
||||||
|
|
||||||
|
length = 1;
|
||||||
|
|
||||||
|
return slup_send_package(slup, 0x00, data, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Initializes the SLUP structure.
|
||||||
|
* \param slup: Pointer to the SLUP structure.
|
||||||
|
* \return SLUP_E_OK if the initialization is successful, SLUP_E_INVALID if the pointer is NULL.
|
||||||
|
*
|
||||||
|
* This function initializes various fields in the SLUP structure. It sets the buffer size (bsize),
|
||||||
|
* sequence number (sn), and timestamp to 0. It also initializes the queue's head, tail, and size to 0,
|
||||||
|
* and clears the parser structure using memset.
|
||||||
|
*/
|
||||||
|
int slup_init(SLUP *slup)
|
||||||
|
{
|
||||||
|
if (!slup) return SLUP_E_INVALID;
|
||||||
|
|
||||||
|
// memset(slup, 0, sizeof(SLUP));
|
||||||
|
|
||||||
|
slup->bsize = 0;
|
||||||
|
slup->sn = 0;
|
||||||
|
slup->timestamp = 0;
|
||||||
|
|
||||||
|
/* Init queue */
|
||||||
|
slup->queue.head = 0;
|
||||||
|
slup->queue.tail = 0;
|
||||||
|
slup->queue.size = 0;
|
||||||
|
|
||||||
|
/* Init parser */
|
||||||
|
memset(&slup->parser, 0, sizeof(SLUP_PARSER));
|
||||||
|
|
||||||
|
return SLUP_E_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Sends data in the SLUP protocol with a frame type of 0x01.
|
||||||
|
* \param slup: Pointer to the SLUP structure.
|
||||||
|
* \param data: Pointer to the data buffer to be sent.
|
||||||
|
* \param length: The length of the data buffer.
|
||||||
|
* \return SLUP_E_OK if the data is successfully sent, appropriate error code otherwise.
|
||||||
|
*
|
||||||
|
* This function simply calls the slup_send_package function with a frame type of 0x01
|
||||||
|
* to send the provided data.
|
||||||
|
*/
|
||||||
|
int slup_send(SLUP *slup, uint8_t *data, uint16_t length)
|
||||||
|
{
|
||||||
|
return slup_send_package(slup, 0x01, data, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Handles the reception of a single character in the SLUP system.
|
||||||
|
* \param slup: Pointer to the SLUP structure.
|
||||||
|
* \param c: The received character.
|
||||||
|
*
|
||||||
|
* This function updates the link status to indicate a received character (sets SLUP_LINK_RX),
|
||||||
|
* clears the silent flag, updates the download statistics, and pushes the received character
|
||||||
|
* into the queue.
|
||||||
|
*/
|
||||||
|
void slup_getc(SLUP *slup, char c)
|
||||||
|
{
|
||||||
|
if (!slup) return;
|
||||||
|
|
||||||
|
slup->link |= SLUP_LINK_RX;
|
||||||
|
slup->silent = 0;
|
||||||
|
|
||||||
|
slup_statis_download(slup);
|
||||||
|
|
||||||
|
slup_queue_push(&slup->queue, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Retrieves the current link status from the SLUP structure.
|
||||||
|
* \param slup: Pointer to the SLUP structure.
|
||||||
|
* \param link: Pointer to a variable where the link status will be stored.
|
||||||
|
* \return SLUP_E_OK if the retrieval is successful, appropriate error code otherwise.
|
||||||
|
*
|
||||||
|
* This function checks if the provided pointers are valid. If so, it copies the current
|
||||||
|
* link status from the SLUP structure to the provided variable.
|
||||||
|
*/
|
||||||
|
int slup_link_status(SLUP *slup, uint8_t *link)
|
||||||
|
{
|
||||||
|
if (!slup) return SLUP_E_INVALID;
|
||||||
|
if (!link) return SLUP_E_LINK;
|
||||||
|
|
||||||
|
*link = slup->link;
|
||||||
|
|
||||||
|
return SLUP_E_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Retrieves the upload rate from the SLUP structure.
|
||||||
|
* \param slup: Pointer to the SLUP structure.
|
||||||
|
* \param rate: Pointer to a variable where the upload rate will be stored.
|
||||||
|
* \return SLUP_E_OK if the retrieval is successful, appropriate error code otherwise.
|
||||||
|
*
|
||||||
|
* This function checks if the provided pointers are valid. If so, it copies the current
|
||||||
|
* upload rate from the SLUP structure to the provided variable.
|
||||||
|
*/
|
||||||
|
int slup_upload_rate(SLUP *slup, uint32_t *rate)
|
||||||
|
{
|
||||||
|
if (!slup) return SLUP_E_INVALID;
|
||||||
|
if (!rate) return SLUP_E_RATE;
|
||||||
|
|
||||||
|
*rate = slup->uprate;
|
||||||
|
|
||||||
|
return SLUP_E_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Retrieves the download rate from the SLUP structure.
|
||||||
|
* \param slup: Pointer to the SLUP structure.
|
||||||
|
* \param rate: Pointer to a variable where the download rate will be stored.
|
||||||
|
* \return SLUP_E_OK if the retrieval is successful, appropriate error code otherwise.
|
||||||
|
*
|
||||||
|
* This function checks if the provided pointers are valid. If so, it copies the current
|
||||||
|
* download rate from the SLUP structure to the provided variable.
|
||||||
|
*/
|
||||||
|
int slup_download_rate(SLUP *slup, uint32_t *rate)
|
||||||
|
{
|
||||||
|
if (!slup) return SLUP_E_INVALID;
|
||||||
|
if (!rate) return SLUP_E_RATE;
|
||||||
|
|
||||||
|
*rate = slup->downrate;
|
||||||
|
|
||||||
|
return SLUP_E_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Sets the target rate for the dummy data in the SLUP structure.
|
||||||
|
* \param slup: Pointer to the SLUP structure.
|
||||||
|
* \param rate: The target rate value to be set.
|
||||||
|
* \return SLUP_E_OK if the setting is successful, SLUP_E_INVALID if the pointer is NULL.
|
||||||
|
*
|
||||||
|
* This function checks if the provided pointer is valid. If so, it sets the target rate
|
||||||
|
* in the dummy data part of the SLUP structure to the provided value.
|
||||||
|
*/
|
||||||
|
int slup_set_dummy(SLUP *slup, uint32_t rate)
|
||||||
|
{
|
||||||
|
if (!slup) return SLUP_E_INVALID;
|
||||||
|
|
||||||
|
slup->dummy.target = rate;
|
||||||
|
|
||||||
|
return SLUP_E_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief The main task function for the SLUP system.
|
||||||
|
* \param slup: Pointer to the SLUP structure.
|
||||||
|
*
|
||||||
|
* This function is the main task handler for the SLUP system. It updates the timestamp,
|
||||||
|
* and based on the timestamp value, performs various operations such as sending heartbeat
|
||||||
|
* messages, calculating rates, executing dummy data sending, and updating dummy data parameters.
|
||||||
|
* It also calls the slup_parse_task function to parse the received data in the queue.
|
||||||
|
*/
|
||||||
|
void slup_task(SLUP *slup)
|
||||||
|
{
|
||||||
|
if (!slup) return;
|
||||||
|
|
||||||
|
slup->timestamp += slup->period;
|
||||||
|
if (slup->timestamp >= 252000000) slup->timestamp = 0;
|
||||||
|
|
||||||
|
if (slup->timestamp % 500 == 0)
|
||||||
|
{
|
||||||
|
if (slup->silent < 3)
|
||||||
|
{
|
||||||
|
slup->silent++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
slup->link &= (~SLUP_LINK_RX);
|
||||||
|
}
|
||||||
|
|
||||||
|
slup_send_heart(slup);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slup->timestamp % 1000 == 0)
|
||||||
|
{
|
||||||
|
slup_calrate_task(slup);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slup->timestamp % 10 == 0)
|
||||||
|
{
|
||||||
|
slup_dummy_execute(slup);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slup->timestamp % 1000 == 0)
|
||||||
|
{
|
||||||
|
slup_dummy_update(slup);
|
||||||
|
}
|
||||||
|
|
||||||
|
slup_parse_task(slup);
|
||||||
|
}
|
||||||
|
|
||||||
205
source/06_performance/slup.h
Normal file
205
source/06_performance/slup.h
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
/*********************************************************************************************************
|
||||||
|
* ------------------------------------------------------------------------------------------------------
|
||||||
|
* file description
|
||||||
|
* ------------------------------------------------------------------------------------------------------
|
||||||
|
* \file slup.h
|
||||||
|
* \unit slup
|
||||||
|
* \brief This is a simple serial link universal protocol for C language
|
||||||
|
* \author Lamdonn
|
||||||
|
* \version v0.1.0
|
||||||
|
* \license GPL-2.0
|
||||||
|
* \copyright Copyright (C) 2023 Lamdonn.
|
||||||
|
********************************************************************************************************/
|
||||||
|
#ifndef __slup_H
|
||||||
|
#define __slup_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* Version infomation */
|
||||||
|
#define SLUP_V_MAJOR 0
|
||||||
|
#define SLUP_V_MINOR 1
|
||||||
|
#define SLUP_V_PATCH 0
|
||||||
|
|
||||||
|
// Maximum size of the head part in the SLUP structure, set to 4 bytes.
|
||||||
|
#define SLUP_HEAD_MAX 4
|
||||||
|
// Maximum size of the tail part in the SLUP structure, set to 4 bytes.
|
||||||
|
#define SLUP_TAIL_MAX 4
|
||||||
|
// Maximum size of the check code part in the SLUP structure, set to 4 bytes.
|
||||||
|
#define SLUP_CHECK_MAX 4
|
||||||
|
// Maximum size of the sequence number (SN) part in the SLUP structure, set to 4 bytes.
|
||||||
|
#define SLUP_SN_MAX 4
|
||||||
|
// Maximum size of the frame in the SLUP structure, set to 4095 bytes.
|
||||||
|
#define SLUP_FRAME_MAX 4095
|
||||||
|
// Size of the receive queue in the SLUP structure, set to 1024 bytes.
|
||||||
|
#define SLUP_RXQUE_SIZE 1024
|
||||||
|
|
||||||
|
// Success code, indicating that an operation has been completed successfully.
|
||||||
|
#define SLUP_E_OK 0
|
||||||
|
// Error code, indicating that the 'slup' pointer is invalid.
|
||||||
|
#define SLUP_E_INVALID 1
|
||||||
|
// Error code, indicating that the 'data' pointer is invalid.
|
||||||
|
#define SLUP_E_DATA 2
|
||||||
|
// Error code, indicating that the length value is invalid.
|
||||||
|
#define SLUP_E_LEN 3
|
||||||
|
// Error code, indicating that the SLUP load testing has not started.
|
||||||
|
#define SLUP_E_NTEST 4
|
||||||
|
// Error code, indicating that the SLUP load exceeds the defined range.
|
||||||
|
#define SLUP_E_OVER 5
|
||||||
|
// Error code, indicating that the 'link' pointer is invalid.
|
||||||
|
#define SLUP_E_LINK 6
|
||||||
|
// Error code, indicating that the 'rate' pointer is invalid.
|
||||||
|
#define SLUP_E_RATE 7
|
||||||
|
|
||||||
|
// Link status value representing the link is in the down state.
|
||||||
|
#define SLUP_LINK_DOWN 0x00
|
||||||
|
// Link status value representing the link is in the transmit (TX) state.
|
||||||
|
#define SLUP_LINK_TX 0x01
|
||||||
|
// Link status value representing the link is in the receive (RX) state.
|
||||||
|
#define SLUP_LINK_RX 0x02
|
||||||
|
// Link status value representing the link is in the up state.
|
||||||
|
#define SLUP_LINK_UP 0x03
|
||||||
|
|
||||||
|
// Function pointer type for a function that writes a character.
|
||||||
|
// It takes a character 'c' as an argument and returns an 'int'.
|
||||||
|
typedef int (*slup_putc_t)(char c);
|
||||||
|
|
||||||
|
// Function pointer type for a function that checks data.
|
||||||
|
// It takes a pointer to an array of 'uint8_t' data and its length (in 'uint16_t') as arguments
|
||||||
|
// and returns a 'uint32_t'.
|
||||||
|
typedef uint32_t (*slup_check_t)(uint8_t *data, uint16_t length);
|
||||||
|
|
||||||
|
// Function pointer type for a function that receives data.
|
||||||
|
// It takes a pointer to an array of 'uint8_t' data and its length (in 'uint16_t') as arguments
|
||||||
|
// and returns an 'int'.
|
||||||
|
typedef int (*slup_receive_t)(uint8_t *data, uint16_t length);
|
||||||
|
|
||||||
|
// Structure definition for SLUP configuration.
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
// Array to store the head mask, with a maximum size defined by SLUP_HEAD_MAX.
|
||||||
|
uint8_t head[SLUP_HEAD_MAX]; /**< head mask */
|
||||||
|
// Array to store the tail mask, with a maximum size defined by SLUP_TAIL_MAX.
|
||||||
|
uint8_t tail[SLUP_TAIL_MAX]; /**< tail mask */
|
||||||
|
// Size of the head part, stored in 4 bits.
|
||||||
|
uint8_t hsize : 4; /**< head size */
|
||||||
|
// Size of the tail part, stored in 4 bits.
|
||||||
|
uint8_t tsize : 4; /**< tail size */
|
||||||
|
// Size of the check code part, stored in 4 bits.
|
||||||
|
uint8_t csize : 4; /**< check code size */
|
||||||
|
// Size of the sequence number (sn) part, stored in 4 bits.
|
||||||
|
uint8_t ssize : 4; /**< sn size */
|
||||||
|
} SLUP_CFG;
|
||||||
|
|
||||||
|
/* queue type define */
|
||||||
|
// Structure definition for an SLUP queue.
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
// Array that serves as the base address for the data in the queue,
|
||||||
|
// with a size defined by SLUP_RXQUE_SIZE.
|
||||||
|
char base[SLUP_RXQUE_SIZE]; /* base address of data */
|
||||||
|
// Total size of the queue.
|
||||||
|
uint32_t size; /* size of queue */
|
||||||
|
// Index of the queue head.
|
||||||
|
uint32_t head; /* index of queue head */
|
||||||
|
// Index of the queue tail.
|
||||||
|
uint32_t tail; /* index of queue tail */
|
||||||
|
} SLUP_QUEUE;
|
||||||
|
|
||||||
|
// Structure definition for an SLUP parser.
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
// Current state of the parser.
|
||||||
|
uint8_t state;
|
||||||
|
// Index related to the head part, stored in 4 bits.
|
||||||
|
uint8_t hindex : 4;
|
||||||
|
// Index related to the tail part, stored in 4 bits.
|
||||||
|
uint8_t tindex : 4;
|
||||||
|
// Index related to the check code part, stored in 4 bits.
|
||||||
|
uint8_t cindex : 4;
|
||||||
|
// Index related to the sequence number (sn) part, stored in 4 bits.
|
||||||
|
uint8_t sindex : 4;
|
||||||
|
// Index related to the length part, stored in 2 bits.
|
||||||
|
uint8_t lindex : 2;
|
||||||
|
// Flag indicating the frame status.
|
||||||
|
uint8_t frame;
|
||||||
|
// Length of the data being processed.
|
||||||
|
uint16_t length;
|
||||||
|
// Index within the data.
|
||||||
|
uint16_t dindex;
|
||||||
|
// Check value for the data.
|
||||||
|
uint32_t check;
|
||||||
|
// Sequence number.
|
||||||
|
uint32_t sn;
|
||||||
|
} SLUP_PARSER;
|
||||||
|
|
||||||
|
// Structure definition for an SLUP dummy data structure.
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
// Compression factor, represented as a floating-point number.
|
||||||
|
float compression;
|
||||||
|
// Rate value, represented as a floating-point number.
|
||||||
|
float rate;
|
||||||
|
// Target value, represented as a 32-bit unsigned integer.
|
||||||
|
uint32_t target;
|
||||||
|
// Base value for the gap, represented as a 32-bit unsigned integer.
|
||||||
|
uint32_t gapbase;
|
||||||
|
// Count value for the gap, represented as a 32-bit unsigned integer.
|
||||||
|
uint32_t gapcount;
|
||||||
|
// Array to store data, with a size of 64 bytes.
|
||||||
|
uint8_t data[64];
|
||||||
|
} SLUP_DUMMY;
|
||||||
|
|
||||||
|
// Structure definition for the main SLUP structure that combines multiple components.
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
// SLUP configuration structure.
|
||||||
|
SLUP_CFG cfg;
|
||||||
|
// SLUP queue structure.
|
||||||
|
SLUP_QUEUE queue;
|
||||||
|
// SLUP parser structure.
|
||||||
|
SLUP_PARSER parser;
|
||||||
|
// SLUP dummy data structure.
|
||||||
|
SLUP_DUMMY dummy;
|
||||||
|
// Buffer to store data, with a maximum size defined by SLUP_FRAME_MAX.
|
||||||
|
uint8_t buffer[SLUP_FRAME_MAX];
|
||||||
|
// Size of the buffer.
|
||||||
|
uint32_t bsize;
|
||||||
|
// Sequence number.
|
||||||
|
uint32_t sn;
|
||||||
|
// Link status flag.
|
||||||
|
uint8_t link;
|
||||||
|
// Silent status flag.
|
||||||
|
uint8_t silent;
|
||||||
|
// Period value, represented as a 16-bit unsigned integer.
|
||||||
|
uint16_t period;
|
||||||
|
// Timestamp value, represented as a 32-bit unsigned integer.
|
||||||
|
uint32_t timestamp;
|
||||||
|
// Number of bits for the up direction, represented as a 32-bit unsigned integer.
|
||||||
|
uint32_t upbits;
|
||||||
|
// Number of bits for the down direction, represented as a 32-bit unsigned integer.
|
||||||
|
uint32_t downbits;
|
||||||
|
// Rate value for the up direction, represented as a 32-bit unsigned integer.
|
||||||
|
uint32_t uprate;
|
||||||
|
// Rate value for the down direction, represented as a 32-bit unsigned integer.
|
||||||
|
uint32_t downrate;
|
||||||
|
// Function pointer of type 'slup_putc_t' for writing a character.
|
||||||
|
slup_putc_t putc;
|
||||||
|
// Function pointer of type 'slup_check_t' for checking data.
|
||||||
|
slup_check_t check;
|
||||||
|
// Function pointer of type 'slup_receive_t' for receiving data.
|
||||||
|
slup_receive_t receive;
|
||||||
|
} SLUP;
|
||||||
|
|
||||||
|
int slup_init(SLUP *slup);
|
||||||
|
int slup_send(SLUP *slup, uint8_t *data, uint16_t length);
|
||||||
|
int slup_link_status(SLUP *slup, uint8_t *link);
|
||||||
|
int slup_upload_rate(SLUP *slup, uint32_t *rate);
|
||||||
|
int slup_download_rate(SLUP *slup, uint32_t *rate);
|
||||||
|
int slup_set_dummy(SLUP *slup, uint32_t rate);
|
||||||
|
void slup_getc(SLUP *slup, char c);
|
||||||
|
void slup_task(SLUP *slup);
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -3075,7 +3075,7 @@ static char floatlIntLowChar(floatl *num)
|
|||||||
// Get the character of the lowest - order digit of the integer part
|
// Get the character of the lowest - order digit of the integer part
|
||||||
c = gChar(*num);
|
c = gChar(*num);
|
||||||
// Divide the 'floatl' number by 10 to move to the next digit
|
// Divide the 'floatl' number by 10 to move to the next digit
|
||||||
*num = floatl_div(*num, floatl_from_f(10.0));
|
*num = floatl_div(*num, floatl_from_d(10.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
return c;
|
return c;
|
||||||
@ -3101,7 +3101,7 @@ static char floatlDitHighChar(floatl *num)
|
|||||||
if (ST1(temp)) // The decimal part is not 0
|
if (ST1(temp)) // The decimal part is not 0
|
||||||
{
|
{
|
||||||
// Multiply the 'floatl' number by 10 to get the next digit
|
// Multiply the 'floatl' number by 10 to get the next digit
|
||||||
temp = floatl_mul(temp, floatl_from_f(10.0));
|
temp = floatl_mul(temp, floatl_from_d(10.0));
|
||||||
// Get the character of the highest - order digit of the new number
|
// Get the character of the highest - order digit of the new number
|
||||||
c = gChar(temp);
|
c = gChar(temp);
|
||||||
// Update the original 'floatl' number
|
// Update the original 'floatl' number
|
||||||
|
|||||||
@ -41,6 +41,8 @@ TEST_LIST += floatl
|
|||||||
TEST_LIST += flmath
|
TEST_LIST += flmath
|
||||||
TEST_LIST += ramt
|
TEST_LIST += ramt
|
||||||
TEST_LIST += romt
|
TEST_LIST += romt
|
||||||
|
TEST_LIST += cant
|
||||||
|
TEST_LIST += slup
|
||||||
# TEST_LIST += cpul
|
# TEST_LIST += cpul
|
||||||
TEST_LIST += date
|
TEST_LIST += date
|
||||||
TEST_LIST += unitt
|
TEST_LIST += unitt
|
||||||
|
|||||||
377
test/test_cant.c
Normal file
377
test/test_cant.c
Normal file
@ -0,0 +1,377 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#if defined(TEST_TARGET_cant)
|
||||||
|
#include <varch/command.h>
|
||||||
|
#include <varch/unitt.h>
|
||||||
|
#include <varch/cant.h>
|
||||||
|
#include <varch/cQueue.h>
|
||||||
|
#else
|
||||||
|
#include "init.h"
|
||||||
|
#include "command.h"
|
||||||
|
#include "unitt.h"
|
||||||
|
#include "kern.h"
|
||||||
|
#include "cant.h"
|
||||||
|
#include "cQueue.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/************************************************************************************/
|
||||||
|
/************************************* Unit Test ************************************/
|
||||||
|
/************************************************************************************/
|
||||||
|
|
||||||
|
// #define EXIT_TEST
|
||||||
|
extern uint64_t unitt_clock(void);
|
||||||
|
|
||||||
|
static int test_0(void)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 100; i++)
|
||||||
|
{
|
||||||
|
if (0)
|
||||||
|
{
|
||||||
|
|
||||||
|
#if defined (EXIT_TEST)
|
||||||
|
exit(0);
|
||||||
|
#endif
|
||||||
|
return UNITT_E_FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return UNITT_E_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void unitt_task(void)
|
||||||
|
{
|
||||||
|
static UNITT_TCASE rand_tests[] = {
|
||||||
|
UNITT_TCASE(test_0),
|
||||||
|
// UNITT_TCASE(test_1),
|
||||||
|
// UNITT_TCASE(test_2),
|
||||||
|
};
|
||||||
|
|
||||||
|
static UNITT suites[] = {
|
||||||
|
{ "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock },
|
||||||
|
};
|
||||||
|
|
||||||
|
UNITT_EXE(suites);
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************************************************/
|
||||||
|
/************************************* Base Test ************************************/
|
||||||
|
/************************************************************************************/
|
||||||
|
|
||||||
|
/////////////////////////////////
|
||||||
|
//// Simulating CAN BUS /////
|
||||||
|
/////////////////////////////////
|
||||||
|
|
||||||
|
typedef int (*canbus_receive_t)(uint32_t canid, uint8_t *data, uint16_t length);
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t devid;
|
||||||
|
canbus_receive_t receive;
|
||||||
|
} CanDevType;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t devid;
|
||||||
|
uint16_t length;
|
||||||
|
uint32_t canid;
|
||||||
|
uint8_t data[64];
|
||||||
|
} CanTpType;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
CanDevType devs[8];
|
||||||
|
uint8_t devn;
|
||||||
|
cQueue(CanTpType, 1024) tp;
|
||||||
|
} CanBusType;
|
||||||
|
|
||||||
|
static CanBusType canbus;
|
||||||
|
static uint8_t canbus_maxdev = (uint8_t)(sizeof(canbus.devs) / sizeof(canbus.devs[0]));
|
||||||
|
|
||||||
|
static int canbus_attach_dev(uint8_t devid, canbus_receive_t receive)
|
||||||
|
{
|
||||||
|
if (devid >= canbus_maxdev) return -1;
|
||||||
|
if (!receive) return -2;
|
||||||
|
|
||||||
|
canbus.devs[devid].devid = devid;
|
||||||
|
canbus.devs[devid].receive = receive;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int canbus_transer(uint8_t devid, uint32_t canid, uint8_t *data, uint16_t length)
|
||||||
|
{
|
||||||
|
CanTpType tp;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (devid >= canbus_maxdev) return -1;
|
||||||
|
if (canbus.devs[devid].devid == 0xFF) return -3;
|
||||||
|
if (!data) return -4;
|
||||||
|
if (length > 64) return -5;
|
||||||
|
|
||||||
|
tp.devid = devid;
|
||||||
|
tp.length = length;
|
||||||
|
tp.canid = canid;
|
||||||
|
for (uint16_t i = 0; i < length; i++) tp.data[i] = data[i];
|
||||||
|
|
||||||
|
ret = cQueue_push(canbus.tp, tp);
|
||||||
|
if (!ret) return -6;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void canbus_init()
|
||||||
|
{
|
||||||
|
memset(&canbus, 0, sizeof(canbus));
|
||||||
|
|
||||||
|
for (uint8_t devid = 0; devid < canbus_maxdev; devid++)
|
||||||
|
{
|
||||||
|
canbus.devs[devid].devid = 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
cQueue_init(canbus.tp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void canbus_task()
|
||||||
|
{
|
||||||
|
CanTpType tp;
|
||||||
|
|
||||||
|
while (cQueue_pop(canbus.tp, tp))
|
||||||
|
{
|
||||||
|
for (uint8_t devid = 0; devid < canbus_maxdev; devid++)
|
||||||
|
{
|
||||||
|
if (canbus.devs[devid].devid == 0xFF) continue;
|
||||||
|
if (canbus.devs[devid].devid == tp.devid) continue;
|
||||||
|
|
||||||
|
if (canbus.devs[devid].receive)
|
||||||
|
{
|
||||||
|
canbus.devs[devid].receive(tp.canid, tp.data, tp.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////
|
||||||
|
//// Simulating CAN DEV /////
|
||||||
|
/////////////////////////////////
|
||||||
|
|
||||||
|
static CANT cant0;
|
||||||
|
static CANT cant1;
|
||||||
|
|
||||||
|
static int dev0_can_transmit(uint32_t canid, uint8_t *data, uint16_t length)
|
||||||
|
{
|
||||||
|
int ret = canbus_transer(0, canid, data, length);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dev1_can_transmit(uint32_t canid, uint8_t *data, uint16_t length)
|
||||||
|
{
|
||||||
|
int ret = canbus_transer(1, canid, data, length);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dev0_can_receive(uint32_t canid, uint8_t *data, uint16_t length)
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
printf("[DEV0][%08x] ", canid);
|
||||||
|
for (int i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
printf("%02x ", data[i]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
#endif
|
||||||
|
cant_receive(&cant0, canid, data, length);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dev1_can_receive(uint32_t canid, uint8_t *data, uint16_t length)
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
printf("[DEV1][%08x] ", canid);
|
||||||
|
for (int i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
printf("%02x ", data[i]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
#endif
|
||||||
|
cant_receive(&cant1, canid, data, length);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////
|
||||||
|
///// CANT //////////
|
||||||
|
/////////////////////////////////
|
||||||
|
|
||||||
|
static CANT cant0 = {
|
||||||
|
.config = {
|
||||||
|
.channel = 0,
|
||||||
|
.baundrate = CANT_BAUDRATE_500K,
|
||||||
|
.period = 5,
|
||||||
|
.transmit = dev0_can_transmit,
|
||||||
|
.receive = NULL,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static CANT cant1 = {
|
||||||
|
.config = {
|
||||||
|
.channel = 0,
|
||||||
|
.baundrate = CANT_BAUDRATE_500K,
|
||||||
|
.period = 5,
|
||||||
|
.transmit = dev1_can_transmit,
|
||||||
|
.receive = NULL,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static void dev0_cant_task(void)
|
||||||
|
{
|
||||||
|
static uint32_t count = 0;
|
||||||
|
|
||||||
|
count += 5;
|
||||||
|
if (count >= 25200000) count = 0;
|
||||||
|
|
||||||
|
if (count % 5 == 0)
|
||||||
|
{
|
||||||
|
cant_task(&cant0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count % 1000 == 0)
|
||||||
|
{
|
||||||
|
static uint8_t data[64];
|
||||||
|
static uint32_t sc = 0;
|
||||||
|
*(uint32_t *)data = sc++;
|
||||||
|
cant_transmit(&cant0, 0x75, data, 64);
|
||||||
|
|
||||||
|
printf("road = %d\r\n", cant0.busload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dev1_cant_task(void)
|
||||||
|
{
|
||||||
|
cant_task(&cant1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_base(void)
|
||||||
|
{
|
||||||
|
printf("cant test!\r\n");
|
||||||
|
|
||||||
|
/* can bus init */
|
||||||
|
canbus_init();
|
||||||
|
task_create(5, canbus_task);
|
||||||
|
|
||||||
|
/* can dev init */
|
||||||
|
canbus_attach_dev(0, dev0_can_receive);
|
||||||
|
canbus_attach_dev(1, dev1_can_receive);
|
||||||
|
|
||||||
|
/* cant protcol init */
|
||||||
|
cant_init(&cant0);
|
||||||
|
cant_init(&cant1);
|
||||||
|
task_create(5, dev0_cant_task);
|
||||||
|
task_create(5, dev1_cant_task);
|
||||||
|
|
||||||
|
cant_set_dummy_canid(&cant0, 0x777);
|
||||||
|
cant_set_busload(&cant0, 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************************************************/
|
||||||
|
/************************************* Command ************************************/
|
||||||
|
/************************************************************************************/
|
||||||
|
|
||||||
|
static void usage(void)
|
||||||
|
{
|
||||||
|
printf(
|
||||||
|
"Usage: cant [opt] [arg] ...\n"
|
||||||
|
"\n"
|
||||||
|
"options:\n"
|
||||||
|
" -e <execute> Specifies the function to execute, the default is the <base> test\n"
|
||||||
|
" <base> Test base function\n"
|
||||||
|
" <ut> Unit test\n"
|
||||||
|
" -h Print help\n"
|
||||||
|
" -v Print version\n"
|
||||||
|
" -u [<period>] Unit test period, unit ms, the default is 1000ms\n"
|
||||||
|
"\n"
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
char *execute = NULL;
|
||||||
|
int ut_period = 1000;
|
||||||
|
int period = 1000; // ms
|
||||||
|
|
||||||
|
/* reset getopt */
|
||||||
|
command_opt_init();
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
int opt = command_getopt(argc, argv, "e:hvu::p:");
|
||||||
|
if (opt == -1) break;
|
||||||
|
|
||||||
|
switch (opt)
|
||||||
|
{
|
||||||
|
case 'p' :
|
||||||
|
period = atoi(command_optarg);
|
||||||
|
break;
|
||||||
|
case 'u' :
|
||||||
|
if (command_optarg) ut_period = atoi(command_optarg);
|
||||||
|
break;
|
||||||
|
case 'e' :
|
||||||
|
execute = command_optarg;
|
||||||
|
break;
|
||||||
|
case 'v' :
|
||||||
|
printf("cant version %d.%d.%d\r\n", CANT_V_MAJOR, CANT_V_MINOR, CANT_V_PATCH);
|
||||||
|
return 0;
|
||||||
|
case '?':
|
||||||
|
printf("Unknown option `%c`\r\n", command_optopt);
|
||||||
|
return -1;
|
||||||
|
case 'h' :
|
||||||
|
default:
|
||||||
|
usage();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (execute)
|
||||||
|
{
|
||||||
|
if (!strcmp(execute, "base"))
|
||||||
|
{
|
||||||
|
test_base();
|
||||||
|
}
|
||||||
|
else if (!strcmp(execute, "ut"))
|
||||||
|
{
|
||||||
|
#if defined(TEST_TARGET_cant)
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
unitt_task();
|
||||||
|
usleep(1000 * ut_period);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
printf("create task %d\r\n", task_create(ut_period, unitt_task));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
test_base();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************************************************/
|
||||||
|
/************************************ Test entry ************************************/
|
||||||
|
/************************************************************************************/
|
||||||
|
|
||||||
|
#if defined(TEST_TARGET_cant)
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
return test(argc, argv);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
void test_cant(void)
|
||||||
|
{
|
||||||
|
command_export("cant", test);
|
||||||
|
}
|
||||||
|
init_export_app(test_cant);
|
||||||
|
#endif
|
||||||
294
test/test_slup.c
Normal file
294
test/test_slup.c
Normal file
@ -0,0 +1,294 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#if defined(TEST_TARGET_slup)
|
||||||
|
#include <varch/command.h>
|
||||||
|
#include <varch/unitt.h>
|
||||||
|
#include <varch/slup.h>
|
||||||
|
#include <varch/cQueue.h>
|
||||||
|
#else
|
||||||
|
#include "init.h"
|
||||||
|
#include "command.h"
|
||||||
|
#include "unitt.h"
|
||||||
|
#include "kern.h"
|
||||||
|
#include "slup.h"
|
||||||
|
#include "cQueue.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/************************************************************************************/
|
||||||
|
/************************************* Unit Test ************************************/
|
||||||
|
/************************************************************************************/
|
||||||
|
|
||||||
|
// #define EXIT_TEST
|
||||||
|
extern uint64_t unitt_clock(void);
|
||||||
|
|
||||||
|
static int test_0(void)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 100; i++)
|
||||||
|
{
|
||||||
|
if (0)
|
||||||
|
{
|
||||||
|
|
||||||
|
#if defined (EXIT_TEST)
|
||||||
|
exit(0);
|
||||||
|
#endif
|
||||||
|
return UNITT_E_FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return UNITT_E_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void unitt_task(void)
|
||||||
|
{
|
||||||
|
static UNITT_TCASE rand_tests[] = {
|
||||||
|
UNITT_TCASE(test_0),
|
||||||
|
// UNITT_TCASE(test_1),
|
||||||
|
// UNITT_TCASE(test_2),
|
||||||
|
};
|
||||||
|
|
||||||
|
static UNITT suites[] = {
|
||||||
|
{ "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock },
|
||||||
|
};
|
||||||
|
|
||||||
|
UNITT_EXE(suites);
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************************************************/
|
||||||
|
/************************************* Base Test ************************************/
|
||||||
|
/************************************************************************************/
|
||||||
|
|
||||||
|
//////////// sim uart dev ////////////
|
||||||
|
typedef void (*uart_rx_t)(char c);
|
||||||
|
static cQueue(char, 1024) qdev0;
|
||||||
|
static cQueue(char, 1024) qdev1;
|
||||||
|
static uart_rx_t dev0_rx = NULL;
|
||||||
|
static uart_rx_t dev1_rx = NULL;
|
||||||
|
static int dev0_uart_send(char c) { cQueue_push(qdev0, c); }
|
||||||
|
static int dev1_uart_send(char c) { cQueue_push(qdev1, c); }
|
||||||
|
static void dev0_uart_rxhandle(void) { char c = 0; while (cQueue_pop(qdev1, c)) { if (dev0_rx) dev0_rx(c); } }
|
||||||
|
static void dev1_uart_rxhandle(void) { char c = 0; while (cQueue_pop(qdev0, c)) { if (dev1_rx) dev1_rx(c); } }
|
||||||
|
static void dev0_uart_setrx(uart_rx_t rx) { dev0_rx = rx; }
|
||||||
|
static void dev1_uart_setrx(uart_rx_t rx) { dev1_rx = rx; }
|
||||||
|
//////////////////////////////////////
|
||||||
|
|
||||||
|
static uint32_t crc8(uint8_t* data, uint16_t len);
|
||||||
|
static int dev0_slup_receive(uint8_t *data, uint16_t length);
|
||||||
|
static int dev1_slup_receive(uint8_t *data, uint16_t length);
|
||||||
|
|
||||||
|
static SLUP slup0 = {
|
||||||
|
.cfg = {
|
||||||
|
.head = {'H', 'E', 'A', 'D'},
|
||||||
|
.tail = {'T', 'A', 'I', 'L'},
|
||||||
|
.hsize = SLUP_HEAD_MAX,
|
||||||
|
.tsize = SLUP_TAIL_MAX,
|
||||||
|
.csize = sizeof(uint8_t),
|
||||||
|
.ssize = sizeof(uint32_t),
|
||||||
|
},
|
||||||
|
.period = 5,
|
||||||
|
.putc = dev0_uart_send,
|
||||||
|
.check = crc8,
|
||||||
|
.receive = dev0_slup_receive,
|
||||||
|
};
|
||||||
|
static SLUP slup1 = {
|
||||||
|
.cfg = {
|
||||||
|
.head = {'H', 'E', 'A', 'D'},
|
||||||
|
.tail = {'T', 'A', 'I', 'L'},
|
||||||
|
.hsize = SLUP_HEAD_MAX,
|
||||||
|
.tsize = SLUP_TAIL_MAX,
|
||||||
|
.csize = sizeof(uint8_t),
|
||||||
|
.ssize = sizeof(uint32_t),
|
||||||
|
},
|
||||||
|
.period = 5,
|
||||||
|
.putc = dev1_uart_send,
|
||||||
|
.check = crc8,
|
||||||
|
.receive = dev1_slup_receive,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void dev0_uart_receive(char c)
|
||||||
|
{
|
||||||
|
slup_getc(&slup0, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dev1_uart_receive(char c)
|
||||||
|
{
|
||||||
|
slup_getc(&slup1, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t crc8(uint8_t* data, uint16_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 (uint32_t)crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dev0_slup_receive(uint8_t *data, uint16_t length)
|
||||||
|
{
|
||||||
|
printf("length %d\r\n", length);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dev1_slup_receive(uint8_t *data, uint16_t length)
|
||||||
|
{
|
||||||
|
printf("length %d\r\n", length);
|
||||||
|
printf("%s\r\n", data);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dev0_slup_task(void)
|
||||||
|
{
|
||||||
|
static uint32_t count = 0;
|
||||||
|
|
||||||
|
count += 5;
|
||||||
|
if (count >= 25200000) count = 0;
|
||||||
|
|
||||||
|
if (count % 5 == 0)
|
||||||
|
{
|
||||||
|
slup_task(&slup0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count % 1000 == 0)
|
||||||
|
{
|
||||||
|
uint8_t link = 0;
|
||||||
|
slup_link_status(&slup0, &link);
|
||||||
|
printf("dev0[%02x] send...\r\n", link);
|
||||||
|
slup_send(&slup0, "0123456789", 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dev1_slup_task(void)
|
||||||
|
{
|
||||||
|
slup_task(&slup1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_base(void)
|
||||||
|
{
|
||||||
|
printf("slup test!\r\n");
|
||||||
|
|
||||||
|
/* uart dev init */
|
||||||
|
cQueue_init(qdev0);
|
||||||
|
cQueue_init(qdev1);
|
||||||
|
dev0_uart_setrx(dev0_uart_receive);
|
||||||
|
dev1_uart_setrx(dev1_uart_receive);
|
||||||
|
task_create(5, dev0_uart_rxhandle);
|
||||||
|
task_create(5, dev1_uart_rxhandle);
|
||||||
|
|
||||||
|
/* slup protcol init */
|
||||||
|
slup_init(&slup0);
|
||||||
|
slup_init(&slup1);
|
||||||
|
task_create(5, dev0_slup_task);
|
||||||
|
task_create(5, dev1_slup_task);
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************************************************/
|
||||||
|
/************************************* Command ************************************/
|
||||||
|
/************************************************************************************/
|
||||||
|
|
||||||
|
static void usage(void)
|
||||||
|
{
|
||||||
|
printf(
|
||||||
|
"Usage: slup [opt] [arg] ...\n"
|
||||||
|
"\n"
|
||||||
|
"options:\n"
|
||||||
|
" -e <execute> Specifies the function to execute, the default is the <base> test\n"
|
||||||
|
" <base> Test base function\n"
|
||||||
|
" <ut> Unit test\n"
|
||||||
|
" -h Print help\n"
|
||||||
|
" -v Print version\n"
|
||||||
|
" -u [<period>] Unit test period, unit ms, the default is 1000ms\n"
|
||||||
|
"\n"
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
char *execute = NULL;
|
||||||
|
int ut_period = 1000;
|
||||||
|
int period = 1000; // ms
|
||||||
|
|
||||||
|
/* reset getopt */
|
||||||
|
command_opt_init();
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
int opt = command_getopt(argc, argv, "e:hvu::p:");
|
||||||
|
if (opt == -1) break;
|
||||||
|
|
||||||
|
switch (opt)
|
||||||
|
{
|
||||||
|
case 'p' :
|
||||||
|
period = atoi(command_optarg);
|
||||||
|
break;
|
||||||
|
case 'u' :
|
||||||
|
if (command_optarg) ut_period = atoi(command_optarg);
|
||||||
|
break;
|
||||||
|
case 'e' :
|
||||||
|
execute = command_optarg;
|
||||||
|
break;
|
||||||
|
case 'v' :
|
||||||
|
printf("slup version %d.%d.%d\r\n", SLUP_V_MAJOR, SLUP_V_MINOR, SLUP_V_PATCH);
|
||||||
|
return 0;
|
||||||
|
case '?':
|
||||||
|
printf("Unknown option `%c`\r\n", command_optopt);
|
||||||
|
return -1;
|
||||||
|
case 'h' :
|
||||||
|
default:
|
||||||
|
usage();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (execute)
|
||||||
|
{
|
||||||
|
if (!strcmp(execute, "base"))
|
||||||
|
{
|
||||||
|
test_base();
|
||||||
|
}
|
||||||
|
else if (!strcmp(execute, "ut"))
|
||||||
|
{
|
||||||
|
#if defined(TEST_TARGET_slup)
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
unitt_task();
|
||||||
|
usleep(1000 * ut_period);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
printf("create task %d\r\n", task_create(ut_period, unitt_task));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
test_base();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************************************************************************/
|
||||||
|
/************************************ Test entry ************************************/
|
||||||
|
/************************************************************************************/
|
||||||
|
|
||||||
|
#if defined(TEST_TARGET_slup)
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
return test(argc, argv);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
void test_slup(void)
|
||||||
|
{
|
||||||
|
command_export("slup", test);
|
||||||
|
}
|
||||||
|
init_export_app(test_slup);
|
||||||
|
#endif
|
||||||
Loading…
x
Reference in New Issue
Block a user