MCAN Configurations MCUXpresso IDE for LPCXpresso54628

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

MCAN Configurations MCUXpresso IDE for LPCXpresso54628

Jump to solution
3,514 Views
SimonSchnee
Contributor III

Hello!

I am writing a MCAN module which I will use for a project. I used for implementation the mcan_interrupt_transfer example.

But when I implement it in almost the same way, I can't transmit or receive any CAN message (see my implementation below). I wonder if I need to do specific settings in the peripheral or pin section of the MCUXpresso IDE.

Here are some settings I did for now:

  • Pins CAN0_RD and CAN0_TD => enabled
  • Pins CAN1_RD and CAN1_TD => enabled
  • Clocks for CAN0 and CAN1 enabled
  • CAN0 => Mode: Interrupts
  • CAN1 => Mode: Transfer

 

Another question would be, if it is possible to transmit message without having to use the interrupt transmit function MCAN_TransferSendNonBlocking. I would rather use the function MCAN_WriteTxBuffer. Is this possible or transmitting data in the "same" way?

 

Source code:

/**
 * @file    test.c
 * @brief   Application entry point.
 */
#include "board.h"
#include "peripherals.h"
#include "stdlib.h"
#include "pin_mux.h"
#include "clock_config.h"
#include "fsl_debug_console.h"
#include "LPC54628.h"
#include "fsl_mcan.h"
#include "fsl_gpio.h"
#include "fsl_pint.h"
#include "fsl_inputmux.h"

#include <stdio.h>
#include <stdbool.h>

/*******************************************************************************
 * Defines
 ******************************************************************************/
#define LED2_PORT	3U
#define LED2_PIN	3U
#define LED3_PORT	2U
#define LED3_PIN	2U
#define LED_PIN_ON	0U
#define LED_PIN_OFF	1U

#define SW5_PORT_PIN	kINPUTMUX_GpioPort1Pin1ToPintsel
#define SW5_ALT_FUNC	kPINT_PinInt0

#define CAN_INPUT		CAN0
#define CAN_OUTPUT		CAN1
#define CAN_DATASIZE	(8U)
#define MCAN_CLK_FREQ	CLOCK_GetMCanClkFreq(0U)
#define STD_FILTER_OFS	0x0
#define STDID_OFFSET	(18U)
#define RX_FIFO0_OFS	0x10U
#define TX_BUFFER_OFS   0x20U
#define MSG_RAM_SIZE   	(TX_BUFFER_OFS + 8 + CAN_DATASIZE)

#define RX_IDENTIFIER	0x123U
#define TX_IDENTIFIER	0x124U

/*******************************************************************************
 * Variables
 ******************************************************************************/
static mcan_handle_t mcanHandle_0;
static mcan_handle_t mcanHandle_1;
static mcan_config_t mcanConfig;

static mcan_std_filter_element_config_t stdFilter;
static mcan_fifo_transfer_t rxXfer;
static mcan_rx_buffer_frame_t rxFrame;
static uint8_t rx_data[CAN_DATASIZE];
static mcan_frame_filter_config_t rxFilter;
static mcan_rx_fifo_config_t rxFifo0;

static mcan_tx_buffer_frame_t txFrame;
static mcan_buffer_transfer_t txXfer;
static mcan_tx_buffer_config_t txBuffer;
static uint8_t tx_data[CAN_DATASIZE];
static uint8_t numMessage = 0;
#ifndef MSG_RAM_BASE
SDK_ALIGN(uint8_t msgRam[MSG_RAM_SIZE], 1U << CAN_MRBA_BA_SHIFT);
#else
#define msgRam MSG_RAM_BASE
#endif

static volatile bool rxComplete = false;
static volatile bool txComplete = false;



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

/*!
 * @brief CAN0 IRQ handler (Receiver)
 */
static void CAN0_IRQHandler(CAN_Type *base, mcan_handle_t *handle, status_t status, uint32_t result, void *userData)
{
	if(status == kStatus_MCAN_RxFifo0Idle)
	{
		GPIO_PortToggle(GPIO, LED2_PORT, 1u << LED2_PIN);
		PRINTF("Received CAN 0 message.\r\n");
		rxComplete = true;
	}
	else if(status == kStatus_MCAN_TxIdle)
	{
		PRINTF("Transfered CAN 0 message.\r\n");
	}
}

/*!
 * @brief CAN1 IRQ handler (Receiver)
 */
static void CAN1_IRQHandler(CAN_Type *base, mcan_handle_t *handle, status_t status, uint32_t result, void *userData)
{
	if(status == kStatus_MCAN_RxFifo0Idle)
	{
		GPIO_PortToggle(GPIO, LED3_PORT, 1u << LED3_PIN);
		PRINTF("Received CAN 1 message.\r\n");
	}
	else if(status == kStatus_MCAN_TxIdle)
	{
		txComplete = true;
		PRINTF("Transfered CAN 1 message.\r\n");
	}
}

/*!
 * @brief Initialize routine for CAN0 (receiver) and CAN1 (transeiver)
 */
static void CAN_Init(void)
{
	PRINTF("Initializing CAN ports.\r\n");

	/* Initialize board hardware. */
	/* attach 12 MHz clock to FLEXCOMM0 (debug console) */
	CLOCK_AttachClk(BOARD_DEBUG_UART_CLK_ATTACH);
	/* Set MCAN clock 220Mhz/11=20MHz. */
	CLOCK_SetClkDiv(kCLOCK_DivCan0Clk, 11U, true);
	CLOCK_SetClkDiv(kCLOCK_DivCan1Clk, 11U, true);

	/* Initialize CAN structure */
	MCAN_GetDefaultConfig(&mcanConfig);

	/* Improve timing config */
	mcan_timing_config_t timing_config;
	memset(&timing_config, 0, sizeof(timing_config));

    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. Just used default configuration\r\n\r\n");
    }

    PRINTF("MCAN Clk-Frequency: %d Hz\r\n", MCAN_CLK_FREQ);

    /* Initialize CAN with modified CAN structure */
	MCAN_Init(CAN_INPUT, &mcanConfig, MCAN_CLK_FREQ);
	MCAN_Init(CAN_OUTPUT, &mcanConfig, MCAN_CLK_FREQ);

	/* Create MCAN handle structure and set call back function. */
	MCAN_TransferCreateHandle(CAN_INPUT, &mcanHandle_0, CAN0_IRQHandler, NULL);
	MCAN_TransferCreateHandle(CAN_OUTPUT, &mcanHandle_1, CAN1_IRQHandler, NULL);

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

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

	stdFilter.sfec = kMCAN_storeinFifo0;
	/* Classic filter mode, only filter matching ID. */
	stdFilter.sft   = kMCAN_classic;
	stdFilter.sfid1 = RX_IDENTIFIER;
	stdFilter.sfid2 = 0x7FFU;
	MCAN_SetSTDFilterElement(CAN_INPUT, &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;
	MCAN_SetRxFifo0Config(CAN_INPUT, &rxFifo0);

	/* TX buffer config. */
	memset(&txBuffer, 0, sizeof(txBuffer));
	txBuffer.address       = TX_BUFFER_OFS;
	txBuffer.dedicatedSize = 1U;
	txBuffer.fqSize        = 0;
	txBuffer.datafieldSize = kMCAN_8ByteDatafield;
	MCAN_SetTxBufferConfig(CAN_OUTPUT, &txBuffer);

	/* Finish software initialization and enter normal mode, synchronizes to
	 * CAN bus, ready for communication */
	MCAN_EnterNormalMode(CAN_INPUT);
	MCAN_EnterNormalMode(CAN_OUTPUT);

	PRINTF("Available for CAN communication.\r\n");
}

/*!
 * @brief Interrupt handler for Switch 5 => transfer CAN message
 */
static void SW_Interrupt_Handler(pint_pin_int_t pintr, uint32_t pmatch_status)
{
	GPIO_PortToggle(GPIO, LED3_PORT, 1u << LED3_PIN);

	PRINTF("Press any key to trigger one-shot transmission\r\n\r\n");
	GETCHAR();

	PRINTF("Transfering CAN message...\r\n");

	/* Config TX frame data. */
	uint8_t cnt = 0;
	for (cnt = 0; cnt < CAN_DATASIZE; cnt++)
	{
		tx_data[cnt] = cnt;
	}

	tx_data[0] += numMessage++;
	txFrame.xtd  = kMCAN_FrameIDStandard;
	txFrame.rtr  = kMCAN_FrameTypeData;
	txFrame.fdf  = 0;
	txFrame.brs  = 0;
	txFrame.dlc  = 8U;
	txFrame.id   = TX_IDENTIFIER << STDID_OFFSET;
	txFrame.data = tx_data;
	txFrame.size = CAN_DATASIZE;
	txXfer.frame     = &txFrame;
	txXfer.bufferIdx = 0;

	//status_t status = MCAN_WriteTxBuffer(CAN_OUTPUT, 0, &txFrame);
	status_t status = MCAN_TransferSendNonBlocking(CAN_OUTPUT, &mcanHandle_1, &txXfer);
	if(status == kStatus_Success)
	{
		PRINTF("Transfered CAN message!\r\n");
	}
	else
	{
		PRINTF("*Error: Failed to transfered CAN message*\r\n");
	}

	txComplete = false;

}

/*!
 * @brief Initialize routine for LED2 and SW5
 */
void GPIO_Init(void)
{
	PRINTF("Initializing LED2 and SW5.\r\n");

	/* Define the init structure for the LED and SW pin*/
	gpio_pin_config_t led_config = {
		kGPIO_DigitalOutput,
		0,
	};

	/* Init output LED2 and 3 GPIO. */
	GPIO_PortInit(GPIO, LED2_PORT);
	GPIO_PinInit(GPIO, LED2_PORT, LED2_PIN, &led_config);
	GPIO_PinWrite(GPIO, LED2_PORT, LED2_PIN, LED_PIN_OFF);

	GPIO_PortInit(GPIO, LED3_PORT);
	GPIO_PinInit(GPIO, LED3_PORT, LED3_PIN, &led_config);
	GPIO_PinWrite(GPIO, LED3_PORT, LED3_PIN, LED_PIN_OFF);

	/* Init input switch 5 GPIO. */
	INPUTMUX_Init(INPUTMUX);
	INPUTMUX_AttachSignal(INPUTMUX, SW5_ALT_FUNC, SW5_PORT_PIN);
	/* Turnoff clock to inputmux to save power. Clock is only needed to make changes */
	INPUTMUX_Deinit(INPUTMUX);

	/* Initialize the PINT module */
	PINT_Init(PINT);
	/* Make PINT channel 0 react to falling edges */
	PINT_PinInterruptConfig(PINT, SW5_ALT_FUNC, kPINT_PinIntEnableFallEdge, SW_Interrupt_Handler);

	/* Enable callbacks for PINT0 by Index */
	PINT_EnableCallbackByIndex(PINT, SW5_ALT_FUNC);
}

/*
 * @brief   Application entry point.
 */
int main(void)
{
	/* Init board hardware. */
 	BOARD_InitBootPins();
	BOARD_InitBootClocks();
	BOARD_InitBootPeripherals();

	/* Init FSL debug console */
	BOARD_InitDebugConsole();

	/* Init LED and SW */
	GPIO_Init();

	/* Init CAN TX and RX */
	CAN_Init();

	uint8_t cnt = 0;

    /* Enter an infinite loop, print received CAN messages. */
    while(1)
    {
    	PRINTF("Press any key to trigger one-shot transmission\r\n\r\n");
		GETCHAR();

		PRINTF("Transfering CAN message...\r\n");

		/* Config TX frame data. */
		cnt = 0;
		for (cnt = 0; cnt < CAN_DATASIZE; cnt++)
		{
			tx_data[cnt] = cnt;
		}

		tx_data[0] += numMessage++;
		txFrame.xtd  = kMCAN_FrameIDStandard;
		txFrame.rtr  = kMCAN_FrameTypeData;
		txFrame.fdf  = 0;
		txFrame.brs  = 0;
		txFrame.dlc  = 8U;
		txFrame.id   = TX_IDENTIFIER << STDID_OFFSET;
		txFrame.data = tx_data;
		txFrame.size = CAN_DATASIZE;
		txXfer.frame     = &txFrame;
		txXfer.bufferIdx = 0;

		//status_t status = MCAN_WriteTxBuffer(CAN_OUTPUT, 0, &txFrame);
		status_t status = MCAN_TransferSendNonBlocking(CAN_OUTPUT, &mcanHandle_1, &txXfer);
		if(status == kStatus_Success)
		{
			PRINTF("Transfered CAN message!\r\n");
		}
		else
		{
			PRINTF("*Error: Failed to transfered CAN message*\r\n");
		}

		while (!txComplete)
		{
		}
		txComplete = false;

		/* Start receive data through Rx FIFO 0. */
		memset(rx_data, 0, sizeof(uint8_t) * CAN_DATASIZE);

		/* the MCAN engine can't auto to get rx payload size, we need set it. */
		rxFrame.size = CAN_DATASIZE;
		rxXfer.frame = &rxFrame;
		MCAN_TransferReceiveFifoNonBlocking(CAN_INPUT, 0, &mcanHandle_0, &rxXfer);

		while (!rxComplete)
		{
		}
		rxComplete = false;

		/* 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");
    }

    return 0 ;
}

 

Best regards.

Labels (2)
Tags (1)
0 Kudos
1 Solution
3,463 Views
SimonSchnee
Contributor III

I do not know what changed now, but both transmitting and receiving is now working.

 

Nevertheless thank you for your input.

 

Current implementation:

/*
 * Copyright 2016-2022 NXP
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * o Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * o Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * o Neither the name of NXP Semiconductor, Inc. nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * @file    test.c
 * @brief   Application entry point.
 */
#include "board.h"
#include "peripherals.h"
#include "stdlib.h"
#include "pin_mux.h"
#include "clock_config.h"
#include "fsl_debug_console.h"
#include "fsl_mcan.h"
#include "fsl_gpio.h"
#include "fsl_pint.h"
#include "fsl_inputmux.h"

#include <stdio.h>
#include <stdbool.h>

/*******************************************************************************
 * Defines
 ******************************************************************************/
#define LED2_PORT	3U
#define LED2_PIN	3U
#define LED3_PORT	2U
#define LED3_PIN	2U
#define LED_PIN_ON	0U
#define LED_PIN_OFF	1U

#define SW5_PORT_PIN	kINPUTMUX_GpioPort1Pin1ToPintsel
#define SW5_ALT_FUNC	kPINT_PinInt0

#define CAN_INPUT		CAN0
#define CAN_OUTPUT		CAN0
#define CAN_DATASIZE	(8U)
#define CAN_BITRATE		(500000U)
#define MCAN_CLK_FREQ	CLOCK_GetMCanClkFreq(0U)
#define STD_FILTER_OFS	0x0
#define STDID_OFFSET	(18U)
#define RX_FIFO0_OFS	0x10U
#define TX_BUFFER_OFS   0x20U
#define MSG_RAM_SIZE   	(TX_BUFFER_OFS + 8 + CAN_DATASIZE)

#define RX_IDENTIFIER	0x123U
#define TX_IDENTIFIER	0x124U

/*******************************************************************************
 * Variables
 ******************************************************************************/
static mcan_config_t mcanConfig;
static mcan_std_filter_element_config_t stdFilter;
//static mcan_fifo_transfer_t rxXfer;
static mcan_rx_buffer_frame_t rxFrame;
static uint8_t rx_data[CAN_DATASIZE];
static mcan_frame_filter_config_t rxFilter;
static mcan_rx_fifo_config_t rxFifo0;

static mcan_tx_buffer_frame_t txFrame;
static mcan_buffer_transfer_t txXfer;
static mcan_tx_buffer_config_t txBuffer;
static uint8_t tx_data[CAN_DATASIZE];
static uint8_t numMessage = 0;
#ifndef MSG_RAM_BASE
SDK_ALIGN(uint8_t msgRam[MSG_RAM_SIZE], 1U << CAN_MRBA_BA_SHIFT);
#else
#define msgRam MSG_RAM_BASE
#endif

static volatile bool rxComplete = false;
static volatile bool txComplete = false;



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

/*!
 * @brief CAN0 IRQ handler (Receiver)
 */
void CAN0_IRQ0_IRQHandler(void)
{
    MCAN_ClearStatusFlag(CAN_INPUT, CAN_IR_RF0N_MASK);
    MCAN_ReadRxFifo(CAN_INPUT, 0, &rxFrame);
    rxComplete = true;
    SDK_ISR_EXIT_BARRIER;
}


/*!
 * @brief Initialize routine for CAN0 (receiver) and CAN1 (transeiver)
 */
static void CAN_Init(void)
{
	PRINTF("Initializing CAN ports.\r\n");

	/* Initialize board hardware. */
	/* attach 12 MHz clock to FLEXCOMM0 (debug console) */
	CLOCK_AttachClk(BOARD_DEBUG_UART_CLK_ATTACH);
	/* Set MCAN clock 220Mhz/11=20MHz. */
	CLOCK_SetClkDiv(kCLOCK_DivCan0Clk, 11U, true);
	CLOCK_SetClkDiv(kCLOCK_DivCan1Clk, 11U, true);

	/* Initialize CAN structure */
	MCAN_GetDefaultConfig(&mcanConfig);
	mcanConfig.baudRateA = CAN_BITRATE;
	mcanConfig.enableCanfdNormal = false;

	/* Improve timing config */
	mcan_timing_config_t timing_config;
	memset(&timing_config, 0, sizeof(timing_config));

    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. Just used default configuration\r\n\r\n");
    }

    PRINTF("MCAN Clk-Frequency: %d Hz\r\n", MCAN_CLK_FREQ);
    PRINTF("Bitrate: %d Hz\r\n", mcanConfig.baudRateA);

    /* Initialize CAN with modified CAN structure */
	MCAN_Init(CAN_INPUT, &mcanConfig, MCAN_CLK_FREQ);
	MCAN_Init(CAN_OUTPUT, &mcanConfig, MCAN_CLK_FREQ);

	/* Create MCAN handle structure and set call back function. */
//	MCAN_TransferCreateHandle(CAN_INPUT, &mcanHandle_0, CAN0_IRQHandler, NULL);
//	MCAN_TransferCreateHandle(CAN_OUTPUT, &mcanHandle_1, CAN1_IRQHandler, NULL);

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

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

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

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

	/* RX fifo0 config. */
	rxFifo0.address       = RX_FIFO0_OFS;
	rxFifo0.elementSize   = 1U;
	rxFifo0.watermark     = 0;
	rxFifo0.opmode        = kMCAN_FifoBlocking;
	rxFifo0.datafieldSize = kMCAN_8ByteDatafield;
	MCAN_SetRxFifo0Config(CAN_INPUT, &rxFifo0);

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

	/* TX buffer config. */
	memset(&txBuffer, 0, sizeof(txBuffer));
	txBuffer.address       = TX_BUFFER_OFS;
	txBuffer.dedicatedSize = 1U;
	txBuffer.fqSize        = 0;
	txBuffer.datafieldSize = kMCAN_8ByteDatafield;
	MCAN_SetTxBufferConfig(CAN_OUTPUT, &txBuffer);

	/* Finish software initialization and enter normal mode, synchronizes to
	 * CAN bus, ready for communication */
	MCAN_EnterNormalMode(CAN_INPUT);
	MCAN_EnterNormalMode(CAN_OUTPUT);

	PRINTF("Available for CAN communication.\r\n");
}

/*!
 * @brief Interrupt handler for Switch 5 => transfer CAN message
 */
static void SW_Interrupt_Handler(pint_pin_int_t pintr, uint32_t pmatch_status)
{
	GPIO_PortToggle(GPIO, LED3_PORT, 1u << LED3_PIN);

	PRINTF("Press any key to trigger one-shot transmission\r\n\r\n");
	GETCHAR();

	PRINTF("Transmitting CAN message...\r\n");

	/* Config TX frame data. */
	uint8_t cnt = 0;
	for (cnt = 0; cnt < CAN_DATASIZE; cnt++)
	{
		tx_data[cnt] = cnt;
	}

	tx_data[0] += numMessage++;
	txFrame.xtd  = kMCAN_FrameIDStandard;
	txFrame.rtr  = kMCAN_FrameTypeData;
	txFrame.fdf  = 0;
	txFrame.brs  = 0;
	txFrame.dlc  = 8U;
	txFrame.id   = TX_IDENTIFIER << STDID_OFFSET;
	txFrame.data = tx_data;
	txFrame.size = CAN_DATASIZE;
	txXfer.frame     = &txFrame;
	txXfer.bufferIdx = 0;

	status_t status = MCAN_WriteTxBuffer(CAN_OUTPUT, 0, &txFrame);
	MCAN_TransmitAddRequest(CAN_OUTPUT, 0);
	//status_t status = MCAN_TransferSendNonBlocking(CAN_OUTPUT, &mcanHandle_1, &txXfer);
	//status_t status = MCAN_TransferSendBlocking(CAN_OUTPUT, 0, &txFrame);
	if(status == kStatus_Success)
	{
		PRINTF("Transmited CAN message!\r\n");
	}
	else
	{
		PRINTF("*Error: Failed to transmit CAN message*\r\n");
	}

	txComplete = false;

}

/*!
 * @brief Initialize routine for LED2 and SW5
 */
void GPIO_Init(void)
{
	PRINTF("Initializing LED2 and SW5.\r\n");

	/* Define the init structure for the LED and SW pin*/
	gpio_pin_config_t led_config = {
		kGPIO_DigitalOutput,
		0,
	};

	/* Init output LED2 and 3 GPIO. */
	GPIO_PortInit(GPIO, LED2_PORT);
	GPIO_PinInit(GPIO, LED2_PORT, LED2_PIN, &led_config);
	GPIO_PinWrite(GPIO, LED2_PORT, LED2_PIN, LED_PIN_OFF);

	GPIO_PortInit(GPIO, LED3_PORT);
	GPIO_PinInit(GPIO, LED3_PORT, LED3_PIN, &led_config);
	GPIO_PinWrite(GPIO, LED3_PORT, LED3_PIN, LED_PIN_OFF);

	/* Init input switch 5 GPIO. */
	INPUTMUX_Init(INPUTMUX);
	INPUTMUX_AttachSignal(INPUTMUX, SW5_ALT_FUNC, SW5_PORT_PIN);
	/* Turnoff clock to inputmux to save power. Clock is only needed to make changes */
	INPUTMUX_Deinit(INPUTMUX);

	/* Initialize the PINT module */
	PINT_Init(PINT);
	/* Make PINT channel 0 react to falling edges */
	PINT_PinInterruptConfig(PINT, SW5_ALT_FUNC, kPINT_PinIntEnableFallEdge, SW_Interrupt_Handler);

	/* Enable callbacks for PINT0 by Index */
	PINT_EnableCallbackByIndex(PINT, SW5_ALT_FUNC);
}

/*
 * @brief   Application entry point.
 */
int main(void)
{
	/* Init board hardware. */
 	BOARD_InitBootPins();
	BOARD_InitBootClocks();
	BOARD_InitBootPeripherals();

	/* Init FSL debug console */
	BOARD_InitDebugConsole();

	/* Init LED and SW */
	GPIO_Init();

	/* Init CAN TX and RX */
	CAN_Init();

	uint8_t cnt = 0;
	rxFrame.size = CAN_DATASIZE;

    /* Enter an infinite loop, print received CAN messages. */
    while(1)
    {
		if(rxComplete)
		{
			/* 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");
			rxComplete = false;
		}
    }

    return 0;
}

View solution in original post

0 Kudos
16 Replies
3,491 Views
frank_m
Senior Contributor III

I have not worked with the CAN peripheral of the LPC546xx yet, but with both this MCU and with CAN (on other MCUs).

And I am not sure why you picked this example. CAN is multi-user bus, which supports a multitude of nodes on one bus.

The example you picked is basically a CAN gateway, which translates messages from one bus to another. My company uses such gateway functionality in one of it's devices, and it is everything but trouble-free.I am not sure if you need this complexity for your application.

 

0 Kudos
3,487 Views
SimonSchnee
Contributor III

There are two examples for the CAN module. One for testing CAN internally in a loopback and the other one uses interrupts to detect receiving and transmitting of CAN messages.

I just wanna use the CAN module for receiving and transmitting of CAN messages. Receiving should use interrupts to correspond fast to a incoming message. Thats why I used this example.

Do you have a better example for my purpose or any sugestion how to get it to work?

 

Best regards.

0 Kudos
3,482 Views
frank_m
Senior Contributor III

As said, I have not used the LPC546xx CAN peripheral yet, and thus did not check the related SDK examples.

But AFAIK, the loopback is based on an internal loopback mode of the peripheral, and you would have to change the configuration. Other MCUs do not have such a mode, and one needs to connect Tx and Rx manually.

> ... and the other one uses interrupts to detect receiving and transmitting of CAN messages.

If this is not the mentioned gateway example, I would rather choose that.

By the way, you usually can't transmit CAN messages from an interrupt directly. CAN is a message-arbitrated bus, and another node might block you. Therefore most CAN peripherals have "message boxes" with either private memory or a chunk reserved from GPRAM. Upon a TxE interrupt, one only needs to refill such a message buffer (with the message usually taken from a SW queue) and mark it for transmission.

Your mileage might vary. I had been working similar issues/implementations on Fujitsu/Cypress MCUs lately, whith different CAN peripheral implementations.

0 Kudos
3,478 Views
SimonSchnee
Contributor III

Thanks for the input.

I got receiving of CAN messages working now, with the loopback example (I just did not enable loopback, therefore I can receive messages from connected CAN simulation).
But I got the problem, that when I want to transmit something over CAN it just does not send a message on the CAN bus. For example it is stuck in the  MCAN_TransferSendBlocking() function at the point of MCAN_IsTransmitOccurred() => that means it is waiting until the transmission is done, which is not the case ...

I also wanted to only use the MCAN_WriteTxBuffer() function to write the CAN data into the transmit buffer. Inserting data into the tansmit buffer works, but it also does not send the data on the CAN bus.

Am I doing something wrong here?

 

Best regards.

0 Kudos
3,472 Views
frank_m
Senior Contributor III

> But I got the problem, that when I want to transmit something over CAN it just does not send a message on the CAN bus. For example it is stuck in the  MCAN_TransferSendBlocking() function at the point of MCAN_IsTransmitOccurred() => that means it is waiting until the transmission is done, which is not the case ...

I am not sure about your level of experience with CAN, but it seems you are more of a beginner in this regard.

For proper transmission, you need at least one second node (CAN bus device). Otherwise, a transmit will not succeed. You can connect a scope to the bus lines, and compare it to the specs.

To forgo the physical bus configuration (with transceivers, cabling and termination resistor), you can  connect two boards directly (i.e. cross the Rx and Tx on digital level) with not too long lines.

Another essential tool is a CAN adapter for the PC, and some CAN monitoring software. My company uses the PEAK PCAN-USB, and alternatively the (more expensive) Vektor adapters and software.

Alternatively, you might manage with a scope that includes a CAN protocol interpretation package (scope firmware).

0 Kudos
3,468 Views
SimonSchnee
Contributor III

Yes you are right.

But I am already using a setup with LPCXpresso54628 with CAN-Dual extension Board and a PEAK-USB connected to both CAN ports, as well as a CAN monitoring program PCAN-Explorer. As I already mentioned, sending from the PCAN-explorer to the development board is functioning, so I can read incoming CAN messages. But if I want to transmit a message it won't send it from the board to the PCAN-explorer and is halted in the function MCAN_TransferSendBlocking (more specific at function MCAN_IsTransmitOccurred).
But when I transmit a message using the example program "mcan_interrupt_transfer", I see incoming messages in the PCAN-explorer.

That's why I dont understand why transmission won't function with my own program.

Best regards.

 

Here, my test code:

/*
 * Copyright 2016-2022 NXP
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * o Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * o Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * o Neither the name of NXP Semiconductor, Inc. nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * @file    test.c
 * @brief   Application entry point.
 */
#include "board.h"
#include "peripherals.h"
#include "stdlib.h"
#include "pin_mux.h"
#include "clock_config.h"
#include "fsl_debug_console.h"
#include "fsl_mcan.h"
#include "fsl_gpio.h"
#include "fsl_pint.h"
#include "fsl_inputmux.h"

#include <stdio.h>
#include <stdbool.h>

/*******************************************************************************
 * Defines
 ******************************************************************************/
#define LED2_PORT	3U
#define LED2_PIN	3U
#define LED3_PORT	2U
#define LED3_PIN	2U
#define LED_PIN_ON	0U
#define LED_PIN_OFF	1U

#define SW5_PORT_PIN	kINPUTMUX_GpioPort1Pin1ToPintsel
#define SW5_ALT_FUNC	kPINT_PinInt0

#define CAN_INPUT		CAN0
#define CAN_OUTPUT		CAN1
#define CAN_DATASIZE	(8U)
#define CAN_BITRATE		(500000U)
#define MCAN_CLK_FREQ	CLOCK_GetMCanClkFreq(0U)
#define STD_FILTER_OFS	0x0
#define STDID_OFFSET	(18U)
#define RX_FIFO0_OFS	0x10U
#define TX_BUFFER_OFS   0x20U
#define MSG_RAM_SIZE   	(TX_BUFFER_OFS + 8 + CAN_DATASIZE)

#define RX_IDENTIFIER	0x123U
#define TX_IDENTIFIER	0x124U

/*******************************************************************************
 * Variables
 ******************************************************************************/
static mcan_config_t mcanConfig;
static mcan_std_filter_element_config_t stdFilter;
//static mcan_fifo_transfer_t rxXfer;
static mcan_rx_buffer_frame_t rxFrame;
static uint8_t rx_data[CAN_DATASIZE];
static mcan_frame_filter_config_t rxFilter;
static mcan_rx_fifo_config_t rxFifo0;

static mcan_tx_buffer_frame_t txFrame;
static mcan_buffer_transfer_t txXfer;
static mcan_tx_buffer_config_t txBuffer;
static uint8_t tx_data[CAN_DATASIZE];
static uint8_t numMessage = 0;
#ifndef MSG_RAM_BASE
SDK_ALIGN(uint8_t msgRam[MSG_RAM_SIZE], 1U << CAN_MRBA_BA_SHIFT);
#else
#define msgRam MSG_RAM_BASE
#endif

static volatile bool rxComplete = false;
static volatile bool txComplete = false;



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

/*!
 * @brief CAN0 IRQ handler (Receiver)
 */
void CAN0_IRQ0_IRQHandler(void)
{
    MCAN_ClearStatusFlag(CAN_INPUT, CAN_IR_RF0N_MASK);
    MCAN_ReadRxFifo(CAN_INPUT, 0, &rxFrame);
    rxComplete = true;
    SDK_ISR_EXIT_BARRIER;
}


/*!
 * @brief Initialize routine for CAN0 (receiver) and CAN1 (transeiver)
 */
static void CAN_Init(void)
{
	PRINTF("Initializing CAN ports.\r\n");

	/* Initialize board hardware. */
	/* attach 12 MHz clock to FLEXCOMM0 (debug console) */
	CLOCK_AttachClk(BOARD_DEBUG_UART_CLK_ATTACH);
	/* Set MCAN clock 220Mhz/11=20MHz. */
	CLOCK_SetClkDiv(kCLOCK_DivCan0Clk, 11U, true);
	CLOCK_SetClkDiv(kCLOCK_DivCan1Clk, 11U, true);

	/* Initialize CAN structure */
	MCAN_GetDefaultConfig(&mcanConfig);
	mcanConfig.baudRateA = CAN_BITRATE;
	mcanConfig.enableCanfdNormal = false;

	/* Improve timing config */
	mcan_timing_config_t timing_config;
	memset(&timing_config, 0, sizeof(timing_config));

    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. Just used default configuration\r\n\r\n");
    }

    PRINTF("MCAN Clk-Frequency: %d Hz\r\n", MCAN_CLK_FREQ);
    PRINTF("Bitrate: %d Hz\r\n", mcanConfig.baudRateA);

    /* Initialize CAN with modified CAN structure */
	MCAN_Init(CAN_INPUT, &mcanConfig, MCAN_CLK_FREQ);
	MCAN_Init(CAN_OUTPUT, &mcanConfig, MCAN_CLK_FREQ);

	/* Create MCAN handle structure and set call back function. */
//	MCAN_TransferCreateHandle(CAN_INPUT, &mcanHandle_0, CAN0_IRQHandler, NULL);
//	MCAN_TransferCreateHandle(CAN_OUTPUT, &mcanHandle_1, CAN1_IRQHandler, NULL);

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

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

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

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

	/* RX fifo0 config. */
	rxFifo0.address       = RX_FIFO0_OFS;
	rxFifo0.elementSize   = 1U;
	rxFifo0.watermark     = 0;
	rxFifo0.opmode        = kMCAN_FifoBlocking;
	rxFifo0.datafieldSize = kMCAN_8ByteDatafield;
	MCAN_SetRxFifo0Config(CAN_INPUT, &rxFifo0);

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

	/* TX buffer config. */
	memset(&txBuffer, 0, sizeof(txBuffer));
	txBuffer.address       = TX_BUFFER_OFS;
	txBuffer.dedicatedSize = 1U;
	txBuffer.fqSize        = 0;
	txBuffer.datafieldSize = kMCAN_8ByteDatafield;
	MCAN_SetTxBufferConfig(CAN_OUTPUT, &txBuffer);

	/* Finish software initialization and enter normal mode, synchronizes to
	 * CAN bus, ready for communication */
	MCAN_EnterNormalMode(CAN_INPUT);
	MCAN_EnterNormalMode(CAN_OUTPUT);

	PRINTF("Available for CAN communication.\r\n");
}

/*!
 * @brief Interrupt handler for Switch 5 => transfer CAN message
 */
static void SW_Interrupt_Handler(pint_pin_int_t pintr, uint32_t pmatch_status)
{
	GPIO_PortToggle(GPIO, LED3_PORT, 1u << LED3_PIN);

	PRINTF("Press any key to trigger one-shot transmission\r\n\r\n");
	GETCHAR();

	PRINTF("Transmitting CAN message...\r\n");

	/* Config TX frame data. */
	uint8_t cnt = 0;
	for (cnt = 0; cnt < CAN_DATASIZE; cnt++)
	{
		tx_data[cnt] = cnt;
	}

	tx_data[0] += numMessage++;
	txFrame.xtd  = kMCAN_FrameIDStandard;
	txFrame.rtr  = kMCAN_FrameTypeData;
	txFrame.fdf  = 0;
	txFrame.brs  = 0;
	txFrame.dlc  = 8U;
	txFrame.id   = TX_IDENTIFIER << STDID_OFFSET;
	txFrame.data = tx_data;
	txFrame.size = CAN_DATASIZE;
	txXfer.frame     = &txFrame;
	txXfer.bufferIdx = 0;

	//status_t status = MCAN_WriteTxBuffer(CAN_OUTPUT, 0, &txFrame);
	//status_t status = MCAN_TransferSendNonBlocking(CAN_OUTPUT, &mcanHandle_1, &txXfer);
	status_t status = MCAN_TransferSendBlocking(CAN_OUTPUT, 0, &txFrame);
	if(status == kStatus_Success)
	{
		PRINTF("Transmited CAN message!\r\n");
	}
	else
	{
		PRINTF("*Error: Failed to transmit CAN message*\r\n");
	}

	txComplete = false;

}

/*!
 * @brief Initialize routine for LED2 and SW5
 */
void GPIO_Init(void)
{
	PRINTF("Initializing LED2 and SW5.\r\n");

	/* Define the init structure for the LED and SW pin*/
	gpio_pin_config_t led_config = {
		kGPIO_DigitalOutput,
		0,
	};

	/* Init output LED2 and 3 GPIO. */
	GPIO_PortInit(GPIO, LED2_PORT);
	GPIO_PinInit(GPIO, LED2_PORT, LED2_PIN, &led_config);
	GPIO_PinWrite(GPIO, LED2_PORT, LED2_PIN, LED_PIN_OFF);

	GPIO_PortInit(GPIO, LED3_PORT);
	GPIO_PinInit(GPIO, LED3_PORT, LED3_PIN, &led_config);
	GPIO_PinWrite(GPIO, LED3_PORT, LED3_PIN, LED_PIN_OFF);

	/* Init input switch 5 GPIO. */
	INPUTMUX_Init(INPUTMUX);
	INPUTMUX_AttachSignal(INPUTMUX, SW5_ALT_FUNC, SW5_PORT_PIN);
	/* Turnoff clock to inputmux to save power. Clock is only needed to make changes */
	INPUTMUX_Deinit(INPUTMUX);

	/* Initialize the PINT module */
	PINT_Init(PINT);
	/* Make PINT channel 0 react to falling edges */
	PINT_PinInterruptConfig(PINT, SW5_ALT_FUNC, kPINT_PinIntEnableFallEdge, SW_Interrupt_Handler);

	/* Enable callbacks for PINT0 by Index */
	PINT_EnableCallbackByIndex(PINT, SW5_ALT_FUNC);
}

/*
 * @brief   Application entry point.
 */
int main(void)
{
	/* Init board hardware. */
 	BOARD_InitBootPins();
	BOARD_InitBootClocks();
	BOARD_InitBootPeripherals();

	/* Init FSL debug console */
	BOARD_InitDebugConsole();

	/* Init LED and SW */
	GPIO_Init();

	/* Init CAN TX and RX */
	CAN_Init();

	uint8_t cnt = 0;
	rxFrame.size = CAN_DATASIZE;

    /* Enter an infinite loop, print received CAN messages. */
    while(1)
    {
		if(rxComplete)
		{
			/* 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");
			rxComplete = false;
		}
    }

    return 0;
}
0 Kudos
3,464 Views
SimonSchnee
Contributor III

I do not know what changed now, but both transmitting and receiving is now working.

 

Nevertheless thank you for your input.

 

Current implementation:

/*
 * Copyright 2016-2022 NXP
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * o Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * o Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * o Neither the name of NXP Semiconductor, Inc. nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * @file    test.c
 * @brief   Application entry point.
 */
#include "board.h"
#include "peripherals.h"
#include "stdlib.h"
#include "pin_mux.h"
#include "clock_config.h"
#include "fsl_debug_console.h"
#include "fsl_mcan.h"
#include "fsl_gpio.h"
#include "fsl_pint.h"
#include "fsl_inputmux.h"

#include <stdio.h>
#include <stdbool.h>

/*******************************************************************************
 * Defines
 ******************************************************************************/
#define LED2_PORT	3U
#define LED2_PIN	3U
#define LED3_PORT	2U
#define LED3_PIN	2U
#define LED_PIN_ON	0U
#define LED_PIN_OFF	1U

#define SW5_PORT_PIN	kINPUTMUX_GpioPort1Pin1ToPintsel
#define SW5_ALT_FUNC	kPINT_PinInt0

#define CAN_INPUT		CAN0
#define CAN_OUTPUT		CAN0
#define CAN_DATASIZE	(8U)
#define CAN_BITRATE		(500000U)
#define MCAN_CLK_FREQ	CLOCK_GetMCanClkFreq(0U)
#define STD_FILTER_OFS	0x0
#define STDID_OFFSET	(18U)
#define RX_FIFO0_OFS	0x10U
#define TX_BUFFER_OFS   0x20U
#define MSG_RAM_SIZE   	(TX_BUFFER_OFS + 8 + CAN_DATASIZE)

#define RX_IDENTIFIER	0x123U
#define TX_IDENTIFIER	0x124U

/*******************************************************************************
 * Variables
 ******************************************************************************/
static mcan_config_t mcanConfig;
static mcan_std_filter_element_config_t stdFilter;
//static mcan_fifo_transfer_t rxXfer;
static mcan_rx_buffer_frame_t rxFrame;
static uint8_t rx_data[CAN_DATASIZE];
static mcan_frame_filter_config_t rxFilter;
static mcan_rx_fifo_config_t rxFifo0;

static mcan_tx_buffer_frame_t txFrame;
static mcan_buffer_transfer_t txXfer;
static mcan_tx_buffer_config_t txBuffer;
static uint8_t tx_data[CAN_DATASIZE];
static uint8_t numMessage = 0;
#ifndef MSG_RAM_BASE
SDK_ALIGN(uint8_t msgRam[MSG_RAM_SIZE], 1U << CAN_MRBA_BA_SHIFT);
#else
#define msgRam MSG_RAM_BASE
#endif

static volatile bool rxComplete = false;
static volatile bool txComplete = false;



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

/*!
 * @brief CAN0 IRQ handler (Receiver)
 */
void CAN0_IRQ0_IRQHandler(void)
{
    MCAN_ClearStatusFlag(CAN_INPUT, CAN_IR_RF0N_MASK);
    MCAN_ReadRxFifo(CAN_INPUT, 0, &rxFrame);
    rxComplete = true;
    SDK_ISR_EXIT_BARRIER;
}


/*!
 * @brief Initialize routine for CAN0 (receiver) and CAN1 (transeiver)
 */
static void CAN_Init(void)
{
	PRINTF("Initializing CAN ports.\r\n");

	/* Initialize board hardware. */
	/* attach 12 MHz clock to FLEXCOMM0 (debug console) */
	CLOCK_AttachClk(BOARD_DEBUG_UART_CLK_ATTACH);
	/* Set MCAN clock 220Mhz/11=20MHz. */
	CLOCK_SetClkDiv(kCLOCK_DivCan0Clk, 11U, true);
	CLOCK_SetClkDiv(kCLOCK_DivCan1Clk, 11U, true);

	/* Initialize CAN structure */
	MCAN_GetDefaultConfig(&mcanConfig);
	mcanConfig.baudRateA = CAN_BITRATE;
	mcanConfig.enableCanfdNormal = false;

	/* Improve timing config */
	mcan_timing_config_t timing_config;
	memset(&timing_config, 0, sizeof(timing_config));

    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. Just used default configuration\r\n\r\n");
    }

    PRINTF("MCAN Clk-Frequency: %d Hz\r\n", MCAN_CLK_FREQ);
    PRINTF("Bitrate: %d Hz\r\n", mcanConfig.baudRateA);

    /* Initialize CAN with modified CAN structure */
	MCAN_Init(CAN_INPUT, &mcanConfig, MCAN_CLK_FREQ);
	MCAN_Init(CAN_OUTPUT, &mcanConfig, MCAN_CLK_FREQ);

	/* Create MCAN handle structure and set call back function. */
//	MCAN_TransferCreateHandle(CAN_INPUT, &mcanHandle_0, CAN0_IRQHandler, NULL);
//	MCAN_TransferCreateHandle(CAN_OUTPUT, &mcanHandle_1, CAN1_IRQHandler, NULL);

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

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

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

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

	/* RX fifo0 config. */
	rxFifo0.address       = RX_FIFO0_OFS;
	rxFifo0.elementSize   = 1U;
	rxFifo0.watermark     = 0;
	rxFifo0.opmode        = kMCAN_FifoBlocking;
	rxFifo0.datafieldSize = kMCAN_8ByteDatafield;
	MCAN_SetRxFifo0Config(CAN_INPUT, &rxFifo0);

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

	/* TX buffer config. */
	memset(&txBuffer, 0, sizeof(txBuffer));
	txBuffer.address       = TX_BUFFER_OFS;
	txBuffer.dedicatedSize = 1U;
	txBuffer.fqSize        = 0;
	txBuffer.datafieldSize = kMCAN_8ByteDatafield;
	MCAN_SetTxBufferConfig(CAN_OUTPUT, &txBuffer);

	/* Finish software initialization and enter normal mode, synchronizes to
	 * CAN bus, ready for communication */
	MCAN_EnterNormalMode(CAN_INPUT);
	MCAN_EnterNormalMode(CAN_OUTPUT);

	PRINTF("Available for CAN communication.\r\n");
}

/*!
 * @brief Interrupt handler for Switch 5 => transfer CAN message
 */
static void SW_Interrupt_Handler(pint_pin_int_t pintr, uint32_t pmatch_status)
{
	GPIO_PortToggle(GPIO, LED3_PORT, 1u << LED3_PIN);

	PRINTF("Press any key to trigger one-shot transmission\r\n\r\n");
	GETCHAR();

	PRINTF("Transmitting CAN message...\r\n");

	/* Config TX frame data. */
	uint8_t cnt = 0;
	for (cnt = 0; cnt < CAN_DATASIZE; cnt++)
	{
		tx_data[cnt] = cnt;
	}

	tx_data[0] += numMessage++;
	txFrame.xtd  = kMCAN_FrameIDStandard;
	txFrame.rtr  = kMCAN_FrameTypeData;
	txFrame.fdf  = 0;
	txFrame.brs  = 0;
	txFrame.dlc  = 8U;
	txFrame.id   = TX_IDENTIFIER << STDID_OFFSET;
	txFrame.data = tx_data;
	txFrame.size = CAN_DATASIZE;
	txXfer.frame     = &txFrame;
	txXfer.bufferIdx = 0;

	status_t status = MCAN_WriteTxBuffer(CAN_OUTPUT, 0, &txFrame);
	MCAN_TransmitAddRequest(CAN_OUTPUT, 0);
	//status_t status = MCAN_TransferSendNonBlocking(CAN_OUTPUT, &mcanHandle_1, &txXfer);
	//status_t status = MCAN_TransferSendBlocking(CAN_OUTPUT, 0, &txFrame);
	if(status == kStatus_Success)
	{
		PRINTF("Transmited CAN message!\r\n");
	}
	else
	{
		PRINTF("*Error: Failed to transmit CAN message*\r\n");
	}

	txComplete = false;

}

/*!
 * @brief Initialize routine for LED2 and SW5
 */
void GPIO_Init(void)
{
	PRINTF("Initializing LED2 and SW5.\r\n");

	/* Define the init structure for the LED and SW pin*/
	gpio_pin_config_t led_config = {
		kGPIO_DigitalOutput,
		0,
	};

	/* Init output LED2 and 3 GPIO. */
	GPIO_PortInit(GPIO, LED2_PORT);
	GPIO_PinInit(GPIO, LED2_PORT, LED2_PIN, &led_config);
	GPIO_PinWrite(GPIO, LED2_PORT, LED2_PIN, LED_PIN_OFF);

	GPIO_PortInit(GPIO, LED3_PORT);
	GPIO_PinInit(GPIO, LED3_PORT, LED3_PIN, &led_config);
	GPIO_PinWrite(GPIO, LED3_PORT, LED3_PIN, LED_PIN_OFF);

	/* Init input switch 5 GPIO. */
	INPUTMUX_Init(INPUTMUX);
	INPUTMUX_AttachSignal(INPUTMUX, SW5_ALT_FUNC, SW5_PORT_PIN);
	/* Turnoff clock to inputmux to save power. Clock is only needed to make changes */
	INPUTMUX_Deinit(INPUTMUX);

	/* Initialize the PINT module */
	PINT_Init(PINT);
	/* Make PINT channel 0 react to falling edges */
	PINT_PinInterruptConfig(PINT, SW5_ALT_FUNC, kPINT_PinIntEnableFallEdge, SW_Interrupt_Handler);

	/* Enable callbacks for PINT0 by Index */
	PINT_EnableCallbackByIndex(PINT, SW5_ALT_FUNC);
}

/*
 * @brief   Application entry point.
 */
int main(void)
{
	/* Init board hardware. */
 	BOARD_InitBootPins();
	BOARD_InitBootClocks();
	BOARD_InitBootPeripherals();

	/* Init FSL debug console */
	BOARD_InitDebugConsole();

	/* Init LED and SW */
	GPIO_Init();

	/* Init CAN TX and RX */
	CAN_Init();

	uint8_t cnt = 0;
	rxFrame.size = CAN_DATASIZE;

    /* Enter an infinite loop, print received CAN messages. */
    while(1)
    {
		if(rxComplete)
		{
			/* 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");
			rxComplete = false;
		}
    }

    return 0;
}
0 Kudos
3,457 Views
frank_m
Senior Contributor III

>I do not know what changed now, but both transmitting and receiving is now working.

I am using the PCAN Explorer as well.

For environments like yours, check that the setting "Listen Only" is enabled. Otherwise, the adapter might interfere with the CAN bus in strange ways, I had similar problems in the past.

0 Kudos
3,453 Views
SimonSchnee
Contributor III

Ok, I will try that out.

Interestingly when I want to use CAN0 as receiver and CAN1 as transmitter, receiving works just fine, but the transmission won't work on CAN1.

Do you have an idea if a setting is missing?

 

Best regards.

0 Kudos
3,447 Views
frank_m
Senior Contributor III

I am not sure about the example, and your setup. Are CAN0 and CAN1 working as two nodes on the same bus ? That would be fine as a standalone demonstration, but I can't see a use case for an actual device.

If that is true, the reason transmissions doesn't work there is most probably hidden in the configuration of CAN1.

But as said, (albeit having an OM13098 with the same MCU) I have never looked at the SDK CAN examples.

Adapting such examples for your applications usually involved intensive study of the CAN documentation in the MCU user manual.

0 Kudos
3,444 Views
SimonSchnee
Contributor III

I am using the CAN0 as receiving node and CAN1 as transmitting node, both are seperated, so read and write on different CAN lines/busses.

For example CAN0 of the board is connected to PCAN-USB Pro CAN1 and CAN1 of the board is connected to PCAN-USB Pro on CAN2.

I just would like to seperate does two CAN ports (transmitting and receiving).

0 Kudos
3,410 Views
SimonSchnee
Contributor III

Does someone know, why CAN0 is fully functioning on LPCXpresso54628 with CAN extension Board and CAN1 with same settings won't work?

I used the same setting for both CAN ports, but only CAN0 is functioning for transmitting and receiving. 
(for reference see included implementation in previous post)

0 Kudos
3,374 Views
frank_m
Senior Contributor III

Besides of the MCAN configuration, the GPIO configuration must be approriate as well. Which means, both pins must be enabled and set up as CAN "alternate function".

With NXP's SDK this is usually done in pinmux.c / pinmux.h.

Perhaps the CAN1 Tx is not initialized/enabled ?

0 Kudos
3,366 Views
SimonSchnee
Contributor III

I initialized the CAN1 in the same way as CAN0.
Also selected the CAN1 pins in the pin configurator.

Everything should therefore be enabled

0 Kudos
3,362 Views
frank_m
Senior Contributor III

> Also selected the CAN1 pins in the pin configurator.

If I get that right - have you checked the generated source code ?

And have you manually checked the configuration and initialisation code for the respective GPIO pin(s) ?

Just speaking from experience with other projects.

0 Kudos
3,215 Views
SimonSchnee
Contributor III

I checked the configurations for both CAN clocks. But first I thought if I activate them manually in the main function it should work. But I realized that you need to enable them in the Clock configurations and set the dividers accordingly.

Thank you for your help

0 Kudos