Verify lpcxpresso54s018m CAN Tx FIFO "setting transmission requests for multiple Tx buffers"

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Verify lpcxpresso54s018m CAN Tx FIFO "setting transmission requests for multiple Tx buffers"

Jump to solution
1,060 Views
mazen
Contributor II

Technical support could not answer this and sent me here. 

Seeing that the distinction between Tx FIFOs and Tx Queues (sections 36.14.7.3 and 36.14.7.4 in UM11155) seems to be suspiciously ambiguous, and that the SDK function "MCAN_TransmitAddRequests" in fact only ever adds a single request (despite the function name indicating a plurality of requests), instead of spending time on this, I thought it would be wise to see if the following has already been confirmed by anyone:

 

I would very much like to exploit the CAN Tx FIFO feature that "allows for setting transmission requests for multiple Tx buffers with one write". This feature is documented in UM11155 36.8.35. However, this does not seem to be functional.  This has forced me to settle for a suboptimal solution.

I have made 10 minor modifications of source in the provided simple SDK project "lpcxpresso54s018m_mcan_loopback" (which sets a single transmission request for a single Tx buffer) to illustrate this apparent deficiency. Now there are 2 Tx buffers for which the transmission requests are set simultaneously.  However, only one request is added.

Does this feature really work?

 

Here are the 10 changes:

1. replaced #define MSG_RAM_SIZE (TX_BUFFER0_OFS + 8 + CAN_DATASIZE)
with
#define MSG_RAM_SIZE (TX_BUFFER0_OFS + ((8 + CAN_DATASIZE)<<1))


2. replaced volatile bool rxComplete = false;
with
volatile unsigned char rxComplete = 0;


3. added PRINTF("\r\nCAN0_IRQ\n\r");
in
CAN0_IRQ0_IRQHandler


4. replaced rxComplete = true;
with
rxComplete++;


5. replaced txBuffer.dedicatedSize = 1U;
with
txBuffer.dedicatedSize = 0;


6. replaced txBuffer.fqSize = 0;
with
txBuffer.fqSize = 2U;


7. added txBuffer.mode = kMCAN_txFifo;
to
txBuffer config


8. replaced MCAN_TransferSendBlocking(EXAMPLE_MCAN, 0, &txFrame);
with
MCAN_WriteTxBuffer(EXAMPLE_MCAN, 0, &txFrame);
MCAN_WriteTxBuffer(EXAMPLE_MCAN, 1, &txFrame);


9. replaced MCAN_TransmitAddRequest(EXAMPLE_MCAN, 0);
with
EXAMPLE_MCAN->TXBAR |= ((uint32_t)3U);


10. replaced while (!rxComplete)
with
while (rxComplete!=2)

 

/*
* Copyright (c) 2016, Freescale Semiconductor, Inc.
* Copyright 2016-2021 NXP
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/

#include "fsl_debug_console.h"
#include "fsl_mcan.h"
#include "pin_mux.h"
#include "board.h"
#include "stdlib.h"

#include <stdbool.h>
/*******************************************************************************
* Definitions
******************************************************************************/
#define USE_CAN0FD (0U)
/*
* CAN_DATASIZE DLC BYTES_IN_MB
* 8 8 kMCAN_8ByteDatafield
* 12 9 kMCAN_12ByteDatafield
* 16 10 kMCAN_16ByteDatafield
* 20 11 kMCAN_20ByteDatafield
* 24 12 kMCAN_24ByteDatafield
* 32 13 kMCAN_32ByteDatafield
* 48 14 kMCAN_48ByteDatafield
* 64 15 kMCAN_64ByteDatafield
*
* CAN data size (pay load size), DLC and Bytes in Message buffer must align.
*
*/
#define DLC (15)
#define BYTES_IN_MB kMCAN_64ByteDatafield
/* If not define USE_CANFD or define it 0, CAN_DATASIZE should be 8. */
#define CAN_DATASIZE (8U)
/* If user need to auto execute the improved timming configuration. */
#define USE_IMPROVED_TIMING_CONFIG (1U)
#define EXAMPLE_MCAN_IRQHandler CAN0_IRQ0_IRQHandler
#define EXAMPLE_MCAN_IRQn CAN0_IRQ0_IRQn
#define EXAMPLE_MCAN CAN0
#define MCAN_CLK_FREQ CLOCK_GetMCanClkFreq(0U)
#define STDID_OFFSET (18U)
#define STD_FILTER_OFS 0x0
#define RX_FIFO0_OFS 0x10U
#define TX_BUFFER0_OFS 0x20U
#define MSG_RAM_SIZE (TX_BUFFER0_OFS + ((8 + CAN_DATASIZE)<<1))
/*******************************************************************************
* Prototypes
******************************************************************************/

/*******************************************************************************
* Variables
******************************************************************************/
volatile unsigned char rxComplete = 0;
mcan_rx_buffer_frame_t rxFrame;
mcan_tx_buffer_frame_t txFrame;
uint8_t tx_data[CAN_DATASIZE];
uint8_t rx_data[CAN_DATASIZE];
#ifndef MSG_RAM_BASE
/* The initial value of 1 is just to avoid variables being placed in the bss section (which will cause hole between data
* section and bss section), Message RAM must be initialized with 0 before used. */
#if defined(__CC_ARM) || defined(__ARMCC_VERSION)
__attribute__((aligned(1U << CAN_MRBA_BA_SHIFT), section(".data"))) uint8_t msgRam[MSG_RAM_SIZE] = {1U};
#else
SDK_ALIGN(uint8_t msgRam[MSG_RAM_SIZE], 1U << CAN_MRBA_BA_SHIFT) = {1U};
#endif
#else
#define msgRam MSG_RAM_BASE
#endif

/*******************************************************************************
* Code
******************************************************************************/

void CAN0_IRQ0_IRQHandler(void)
{
PRINTF("\r\nCAN0_IRQ\n\r");

MCAN_ClearStatusFlag(EXAMPLE_MCAN, CAN_IR_RF0N_MASK);
MCAN_ReadRxFifo(EXAMPLE_MCAN, 0, &rxFrame);
rxComplete++;
SDK_ISR_EXIT_BARRIER;
}

/*!
* @brief Main function
*/
int main(void)
{
mcan_config_t mcanConfig;
mcan_frame_filter_config_t rxFilter;
mcan_std_filter_element_config_t stdFilter;
mcan_rx_fifo_config_t rxFifo0;
mcan_tx_buffer_config_t txBuffer;

/* Initialize board hardware. */
/* attach 12 MHz clock to FLEXCOMM0 (debug console) */
CLOCK_AttachClk(BOARD_DEBUG_UART_CLK_ATTACH);

/* Set MCAN clock 180/6=30MHz. */
CLOCK_SetClkDiv(kCLOCK_DivCan0Clk, 6U, true);

BOARD_InitBootPins();
BOARD_BootClockPLL180M();
BOARD_InitDebugConsole();

PRINTF("\r\n==MCAN loopback functional example -- Start.==\r\n\r\n");

/* Get MCAN module default Configuration. */
/*
* mcanConfig.baudRate = 500000U;
* mcanConfig.baudRateFD = 2000000U;
* mcanConfig.enableCanfdNormal = false;
* mcanConfig.enableCanfdSwitch = false;
* mcanConfig.enableLoopBackInt = false;
* mcanConfig.enableLoopBackExt = false;
* mcanConfig.enableBusMon = false;
*/
MCAN_GetDefaultConfig(&mcanConfig);
/* Enable external loop back mode.
* Current node treats its own transmitted messages as received messages,
* Other node can receive Current node transmitted messages,
* Other node can't transmit messages or ACK signal to Current node.
*/
mcanConfig.enableLoopBackExt = true;
#if (defined(USE_CANFD) && USE_CANFD)
/* Enable Bit Rate Switch to make baudRateD make sense.*/
mcanConfig.enableCanfdSwitch = true;
#endif

#if (defined(USE_IMPROVED_TIMING_CONFIG) && USE_IMPROVED_TIMING_CONFIG)
mcan_timing_config_t timing_config;
memset(&timing_config, 0, sizeof(timing_config));
#if (defined(USE_CANFD) && USE_CANFD)
if (MCAN_FDCalculateImprovedTimingValues(mcanConfig.baudRateA, mcanConfig.baudRateD, MCAN_CLK_FREQ, &timing_config))
{
/* Update the improved timing configuration*/
memcpy(&(mcanConfig.timingConfig), &timing_config, sizeof(mcan_timing_config_t));
}
else
{
PRINTF("No found Improved Timing Configuration. use default configuration\r\n\r\n");
}
#else
if (MCAN_CalculateImprovedTimingValues(mcanConfig.baudRateA, MCAN_CLK_FREQ, &timing_config))
{
/* Update the improved timing configuration*/
memcpy(&(mcanConfig.timingConfig), &timing_config, sizeof(mcan_timing_config_t));
}
else
{
PRINTF("No found Improved Timing Configuration. use default configuration\r\n\r\n");
}
#endif
#endif

MCAN_Init(EXAMPLE_MCAN, &mcanConfig, MCAN_CLK_FREQ);

/* Set Message RAM base address and clear to avoid BEU/BEC error. */
MCAN_SetMsgRAMBase(EXAMPLE_MCAN, (uint32_t)msgRam);
memset((void *)msgRam, 0, MSG_RAM_SIZE * sizeof(uint8_t));

/* STD filter config. */
rxFilter.address = STD_FILTER_OFS;
rxFilter.idFormat = kMCAN_FrameIDStandard;
rxFilter.listSize = 1U;
rxFilter.nmFrame = kMCAN_reject0;
rxFilter.remFrame = kMCAN_rejectFrame;
MCAN_SetFilterConfig(EXAMPLE_MCAN, &rxFilter);

stdFilter.sfec = kMCAN_storeinFifo0;
/* Classic filter mode, only filter matching ID. */
stdFilter.sft = kMCAN_classic;
stdFilter.sfid1 = 0x123U;
stdFilter.sfid2 = 0x7FFU;
MCAN_SetSTDFilterElement(EXAMPLE_MCAN, &rxFilter, &stdFilter, 0);

/* RX fifo0 config. */
rxFifo0.address = RX_FIFO0_OFS;
rxFifo0.elementSize = 1U;
rxFifo0.watermark = 0;
rxFifo0.opmode = kMCAN_FifoBlocking;
rxFifo0.datafieldSize = kMCAN_8ByteDatafield;
#if (defined(USE_CANFD) && USE_CANFD)
rxFifo0.datafieldSize = BYTES_IN_MB;
#endif
MCAN_SetRxFifo0Config(EXAMPLE_MCAN, &rxFifo0);

/* TX buffer config. */
memset(&txBuffer, 0, sizeof(txBuffer));
txBuffer.address = TX_BUFFER0_OFS;
txBuffer.dedicatedSize = 0;//1U;
txBuffer.fqSize = 2U;//0;
txBuffer.datafieldSize = kMCAN_8ByteDatafield;
txBuffer.mode = kMCAN_txFifo;

#if (defined(USE_CANFD) && USE_CANFD)
txBuffer.datafieldSize = BYTES_IN_MB;
#endif
MCAN_SetTxBufferConfig(EXAMPLE_MCAN, &txBuffer);

/* Enable RX fifo0 new message interrupt using interrupt line 0. */
MCAN_EnableInterrupts(EXAMPLE_MCAN, 0, CAN_IE_RF0NE_MASK);
EnableIRQ(CAN0_IRQ0_IRQn);

/* Enter normal mode. */
MCAN_EnterNormalMode(EXAMPLE_MCAN);

/* Config TX frame data. */
uint8_t cnt = 0;
for (cnt = 0; cnt < CAN_DATASIZE; cnt++)
{
tx_data[cnt] = cnt;
}
txFrame.xtd = kMCAN_FrameIDStandard;
txFrame.rtr = kMCAN_FrameTypeData;
txFrame.fdf = 0;
txFrame.brs = 0;
txFrame.dlc = 8U;
txFrame.id = 0x123U << STDID_OFFSET;
txFrame.data = tx_data;
txFrame.size = CAN_DATASIZE;
#if (defined(USE_CANFD) && USE_CANFD)
txFrame.fdf = 1;
txFrame.brs = 1;
txFrame.dlc = DLC;
#endif
// MCAN_TransferSendBlocking(EXAMPLE_MCAN, 0, &txFrame);
MCAN_WriteTxBuffer(EXAMPLE_MCAN, 0, &txFrame);
MCAN_WriteTxBuffer(EXAMPLE_MCAN, 1, &txFrame);
//MCAN_TransmitAddRequest(EXAMPLE_MCAN, 0);
EXAMPLE_MCAN->TXBAR |= ((uint32_t)3U);

while (rxComplete!=2)
{
}

rxFrame.size = CAN_DATASIZE;
/* After call the API of rMCAN_TransferReceiveFifoNonBlocking success, we can
* only get a point (rxFrame.data) to the fifo reading entrance.
* Copy the received frame data from the FIFO by the pointer(rxFrame.data). */
memcpy(rx_data, rxFrame.data, rxFrame.size);

PRINTF("Received Frame ID: 0x%x\r\n", rxFrame.id >> STDID_OFFSET);
PRINTF("Received Frame DATA: ");
cnt = 0;
while (cnt < rxFrame.size)
{
PRINTF("0x%x ", rx_data[cnt++]);
}
PRINTF("\r\n\r\n==MCAN loopback functional example -- Finish.==\r\n");

while (1)
{
}
}

0 Kudos
Reply
1 Solution
1,049 Views
mazen
Contributor II
I had some time to look further into this. 
 
Aside from the fact that in loopback mode, multiple frames can be written to the RxFifo before the first RF0NE interrupt occurs, the real problem is that the suggested examples are not correct.
 
The examples should show the RxFifo being emptied in the interrupt (rather than just one element being read from it).  
 
However, this is still not doable because the driver functions are also not implemented correctly.  Concretely, the "MCAN_ReadRxFifo" always returns "kStatus_Success" even if the FIFO is empty.
 
I have made the following changes to fsl_mcan.c and I suggest that everyone does the same or something similar:
 
static uint8_t MCAN_IsRxFifoEmpty(CAN_Type *base)
{
if(base==CAN0)
return ((base->RXF0S & CAN_RXF0S_F0GI_MASK) >> CAN_RXF0S_F0GI_SHIFT) == ((base->RXF0S & CAN_RXF0S_F0PI_MASK) >> CAN_RXF0S_F0PI_SHIFT);
 
else
return ((base->RXF1S & CAN_RXF1S_F1GI_MASK) >> CAN_RXF1S_F1GI_SHIFT) == ((base->RXF1S & CAN_RXF1S_F1PI_MASK) >> CAN_RXF1S_F1PI_SHIFT);
}
 
status_t MCAN_ReadRxFifo(CAN_Type *base, uint8_t fifoBlock, mcan_rx_buffer_frame_t *pRxFrame)
{
    /* Assertion. */
    assert((0U == fifoBlock) || (1U == fifoBlock));
    assert(NULL != pRxFrame);
 
    if(MCAN_IsRxFifoEmpty(base))
    return kStatus_Fail;
 
    mcan_rx_buffer_frame_t *elementAddress = NULL;
    if (0U == fifoBlock)
    {
        elementAddress = (mcan_rx_buffer_frame_t *)(MCAN_GetMsgRAMBase(base) + MCAN_GetRxFifo0ElementAddress(base));
    }
    else
    {
        elementAddress = (mcan_rx_buffer_frame_t *)(MCAN_GetMsgRAMBase(base) + MCAN_GetRxFifo1ElementAddress(base));
    }
    (void)memcpy(pRxFrame, elementAddress, 8U);
    pRxFrame->data = (uint8_t *)((uint32_t)elementAddress + 8U);
    /* Acknowledge the read. */
    if (0U == fifoBlock)
    {
        base->RXF0A = (base->RXF0S & CAN_RXF0S_F0GI_MASK) >> CAN_RXF0S_F0GI_SHIFT;
    }
    else
    {
        base->RXF1A = (base->RXF1S & CAN_RXF1S_F1GI_MASK) >> CAN_RXF1S_F1GI_SHIFT;
    }
    return kStatus_Success;
}

View solution in original post

0 Kudos
Reply
1 Reply
1,050 Views
mazen
Contributor II
I had some time to look further into this. 
 
Aside from the fact that in loopback mode, multiple frames can be written to the RxFifo before the first RF0NE interrupt occurs, the real problem is that the suggested examples are not correct.
 
The examples should show the RxFifo being emptied in the interrupt (rather than just one element being read from it).  
 
However, this is still not doable because the driver functions are also not implemented correctly.  Concretely, the "MCAN_ReadRxFifo" always returns "kStatus_Success" even if the FIFO is empty.
 
I have made the following changes to fsl_mcan.c and I suggest that everyone does the same or something similar:
 
static uint8_t MCAN_IsRxFifoEmpty(CAN_Type *base)
{
if(base==CAN0)
return ((base->RXF0S & CAN_RXF0S_F0GI_MASK) >> CAN_RXF0S_F0GI_SHIFT) == ((base->RXF0S & CAN_RXF0S_F0PI_MASK) >> CAN_RXF0S_F0PI_SHIFT);
 
else
return ((base->RXF1S & CAN_RXF1S_F1GI_MASK) >> CAN_RXF1S_F1GI_SHIFT) == ((base->RXF1S & CAN_RXF1S_F1PI_MASK) >> CAN_RXF1S_F1PI_SHIFT);
}
 
status_t MCAN_ReadRxFifo(CAN_Type *base, uint8_t fifoBlock, mcan_rx_buffer_frame_t *pRxFrame)
{
    /* Assertion. */
    assert((0U == fifoBlock) || (1U == fifoBlock));
    assert(NULL != pRxFrame);
 
    if(MCAN_IsRxFifoEmpty(base))
    return kStatus_Fail;
 
    mcan_rx_buffer_frame_t *elementAddress = NULL;
    if (0U == fifoBlock)
    {
        elementAddress = (mcan_rx_buffer_frame_t *)(MCAN_GetMsgRAMBase(base) + MCAN_GetRxFifo0ElementAddress(base));
    }
    else
    {
        elementAddress = (mcan_rx_buffer_frame_t *)(MCAN_GetMsgRAMBase(base) + MCAN_GetRxFifo1ElementAddress(base));
    }
    (void)memcpy(pRxFrame, elementAddress, 8U);
    pRxFrame->data = (uint8_t *)((uint32_t)elementAddress + 8U);
    /* Acknowledge the read. */
    if (0U == fifoBlock)
    {
        base->RXF0A = (base->RXF0S & CAN_RXF0S_F0GI_MASK) >> CAN_RXF0S_F0GI_SHIFT;
    }
    else
    {
        base->RXF1A = (base->RXF1S & CAN_RXF1S_F1GI_MASK) >> CAN_RXF1S_F1GI_SHIFT;
    }
    return kStatus_Success;
}
0 Kudos
Reply