Note: This post seems to have been posted twice and much of the detail in the original post has been lost. I will try to recall those details.
I am using the KSDK fsl_flexcan_driver to implement basic transmitting and receive functionality in my application. The transmitting works consistently, however I have found that receive either only works when code execution is paused and I step through the code, or it causes a hardfault when the code is run at full speed and an attempt is made to receive a message.
I am using the FRDM-K64F with a custom shield using a Freescale 33901 CAN transceiver, however the hardware is 100% confirmed to be working correctly, since TX works and the RX messages do make it to the RX message buffer, the failure lies at some stage after that.
I have boiled the problem down to a very simple program. I will post the relevant bits here and I have attached the complete project. The hardfault is entered immediately after returning from the FLEXCAN_DRV_IRQHandler in the KSDK. I am copying it here:
/*FUNCTION********************************************************************** * * Function Name : FLEXCAN_DRV_IRQHandler * Description : Interrupt handler for FLEXCAN. * This handler read data from MB or FIFO, and then clear the interrupt flags. * This is not a public API as it is called whenever an interrupt occurs. * *END**************************************************************************/ void FLEXCAN_DRV_IRQHandler(uint8_t instance) { volatile uint32_t flag_reg; uint32_t temp; CAN_Type * base = g_flexcanBase[instance]; flexcan_state_t * state = g_flexcanStatePtr[instance]; /* Get the interrupts that are enabled and ready */ flag_reg = ((FLEXCAN_HAL_GetAllMsgBuffIntStatusFlag(base)) & CAN_IMASK1_BUFLM_MASK) & CAN_RD_IMASK1(base); /* Check Tx/Rx interrupt flag and clear the interrupt */ if(flag_reg) { if ((flag_reg & 0x20) && CAN_BRD_MCR_RFEN(base)) { if (state->fifo_message != NULL) { /* Get RX FIFO field values */ FLEXCAN_HAL_ReadRxFifo(base, state->fifo_message); /* Complete receive data */ FLEXCAN_DRV_CompleteRxMessageFifoData(instance); FLEXCAN_HAL_ClearMsgBuffIntStatusFlag(base, flag_reg); } } else { /* Check mailbox completed reception*/ temp = (1 << state->rx_mb_idx); if (temp & flag_reg) { /* Lock RX message buffer and RX FIFO*/ FLEXCAN_HAL_LockRxMsgBuff(base, state->rx_mb_idx); /* Get RX MB field values*/ FLEXCAN_HAL_GetMsgBuff(base, state->rx_mb_idx, state->mb_message); /* Unlock RX message buffer and RX FIFO*/ FLEXCAN_HAL_UnlockRxMsgBuff(base); /* Complete receive data */ FLEXCAN_DRV_CompleteRxMessageBufferData(instance); // Commented out by Angus, 8/13/2015 FLEXCAN_HAL_ClearMsgBuffIntStatusFlag(base, temp & flag_reg); } /* WHY DO THIS CHECK HERE IF WE DO IT AGAIN REGARDLESS OF "ELSE" CONDITION // Check mailbox completed transmission temp = (1 << state->tx_mb_idx); if (temp & flag_reg) { // Complete transmit data FLEXCAN_DRV_CompleteSendData(instance); // Commented out by Angus, 8/13/2015 FLEXCAN_HAL_ClearMsgBuffIntStatusFlag(base, temp & flag_reg); }*/ } /* Check mailbox completed transmission*/ temp = (1 << state->tx_mb_idx); if (flag_reg & temp) { /* Complete transmit data */ FLEXCAN_DRV_CompleteSendData(instance); // Commented out by Angus, 8/13/2015 FLEXCAN_HAL_ClearMsgBuffIntStatusFlag(base, temp & flag_reg); } } /* Clear all other interrupts in ERRSTAT register (Error, Busoff, Wakeup) */ FLEXCAN_HAL_ClearErrIntStatusFlag(base); return; }
In MQX 4.2, the IMASK flag is never changed in an ISR, but the IMASK is reset to 0 here in "FLEXCAN_DRV_CompleteRxMessageBufferData(instance)".
Also, it shouldn't matter, but it appears there is some redundant code in the KSDK IRQ Handler for checking mailbox transmission.
In FLEXCAN_HAL_GetMsgBuff(), I am wondering why for this loop:
/* Copy MB data field into user's buffer*/
for (i = 0 ; i < kFlexCanMessageSize ; i++)
They did not use the actual DLC of the received message, rather than kFlexCanMessageSize which is defined as a constant 8.
Below are the relevant pieces of custom code, main and my RX function.
main.c
#include "flexcan.h" // *************************************************************************** // Definitions // *************************************************************************** //#define DO_PRINT // Un-comment this to print out debug info void hardware_init(); /*! defined in main.c */ void init_flexcan(void); /*! defined in flexcan.c */ void hardware_init() { /* enable clock for PORTs */ CLOCK_SYS_EnablePortClock(PORTA_IDX); /*! */ CLOCK_SYS_EnablePortClock(PORTB_IDX); /*! The CAN pins are on port B */ CLOCK_SYS_EnablePortClock(PORTC_IDX); /*! */ CLOCK_SYS_EnablePortClock(PORTD_IDX); /*! The SPI pins are on port D */ CLOCK_SYS_EnablePortClock(PORTE_IDX); /*! The I2C pins are on port E */ /* Init board clock */ BOARD_ClockInit(); dbg_uart_init(); configure_can_pins(0); // Configure CAN pins OSA_Init(); //FXOS_Init seems to depend on this, so does OSA_TimeDelay(ms) init_flexcan(); // For vscp events STATUS_LED_EN; /* LED1_EN */ CLOCK_PIN_EN; /* scope this pin to test the 1 ms clock pulse width */ INIT_BTN_EN; /* init sw2 as input */ // Enable a gpio for taking the STB pin on the CAN PHY low CAN0_STB_EN; CAN0_STB_LO; } // *************************************************************************** // Main() - Main Routine // *************************************************************************** int main(void) { int i; FLEXCAN_RX_MSG_FLAGS flags; /* For transmit */ uint32_t id = 0x1eadbeef; //29-bit can ID flexcan_code_t ret; uint8_t pdata[8] = {11,22,33,44,55,66,77,88}; // data /* For receive */ uint32_t can_pid; uint32_t can_pdlc; uint32_t rxData[8]; // Init mcu and peripherals hardware_init(); while(1) { ret = FLEXCANSendMessage(id, 8, pdata, FLEXCAN_TX_XTD_FRAME); OSA_TimeDelay(500); ret = FLEXCANReceiveMessage(&can_pid, &can_pdlc, rxData, &flags); if(ret){ if(can_pdlc > 8) can_pdlc = 8; printf("ID: 0x%lx, DLC=%lu \r\n",can_pid, can_pdlc); printf("RX MB data: 0x"); for (i = 0; i < can_pdlc; i++){ printf("%02lx ", rxData[i]); } printf("\r\n"); } } return 0; }
init_flexcan() sets up 16 message buffers, the bottom 8 as RX (Inactive) and the top half as TX (Inactive). Only MB0 is used as an active RX buffer, while MB8 is used as the TX MB.
flexcan.c - FLEXCANReceiveMessage()
flexcan_code_t FLEXCANReceiveMessage(uint32_t *pid, uint32_t *pdlc, uint32_t *pdata, FLEXCAN_RX_MSG_FLAGS *msgFlags) { uint32_t result = 0; flexcan_msgbuff_t rxMb; /* Configure RX MB fields * * global rxMailboxNum = 0; * global rxIdentifier = 900; * global rxInfo * * */ if(FLEXCAN_DRV_GetReceiveStatus(BOARD_CAN_INSTANCE) == kStatus_FLEXCAN_Success) { /* Attempt to receive a message into rxMb */ result = FLEXCAN_DRV_RxMessageBufferBlocking(BOARD_CAN_INSTANCE, rxMailboxNum, &rxMb, 1000); //result = FLEXCAN_DRV_RxMessageBufferBlocking(BOARD_CAN_INSTANCE, rxMailboxNum, &rxMb, OSA_WAIT_FOREVER); //result = FLEXCAN_DRV_RxMessageBuffer(BOARD_CAN_INSTANCE, rxMailboxNum, &rxMb); if(!result) { /* Successfully received something! Begin extracting information from message * buffer which is defined on pg. 1400 of the K64 Sub-Family Ref. Manual */ *pid = rxMb.msgId; // The message ID occupies bits 0-28 (EXT) or 18-28 (STD) *pdlc = ((rxMb.cs) >> 16) & 0xF; // The data length field (DLC) occupies bits 16 - 19 *msgFlags = ((rxMb.cs) >> 21) & 0x1; // The ID Extended Bit field (IDE) is bit 21 for (result = 0; result < *pdlc; result++){ pdata[result] = rxMb.data[result]; } return FLEXCAN_SUCCESS; } } return FLEXCAN_FAIL; }
I am not attempting to do any sort of loopback, the goal is to be able to send and receive any message at any time with send and receive functions. A global mask is used to only receive extended messages ending in 0x900.
I am attaching screenshots of the CAN0 register contents before and after the hardfault:
After returning from the IRQ handler, we see that the IMASK AND IFLAG for MB0 have been disabled. There no errors in any of the CAN registers. 900 and 0x01, 0x02 are the correct data in WORD0.
Original Attachment has been moved to: basic_can_frdmk64f.zip
The adjustment of the CODE field in the KSDK from full, overrun, etc to empty appears to be handled in a much less cautious way than in FLEXCAN_Rx_Message from MQX 4.2. In fact, it appears the intended or only way of resetting a message buffer through the KSDK API is to call FLEXCAN_DRV_ConfigRxMb() to re-configure the message buffer from scratch. This really should be a 1 time initialization, as it is in MQX.
When FLEXCAN_DRV_ConfigRxMb() is added to the beginning of my receive function AND the code is paused before I send a message from my CAN analyzer, it can receive the message, print it out, and continue exection un-affected. If the code is not paused, then it hard-faults just as before.
I will experiment more with properly resetting the message buffer and commenting out the code that disables the IMASK in the KSDK IRQ.
Hi Angus,
I am checking with the design team for your issue, and for the question regarding FLEXCAN_HAL_GetMsgBuff(), I think the purpose is letting the ISR does things as simple as possible so that it just handles the format changing from BE to LET and let user check the length in application layer.
I think it is the same purpose for checking CODE field in application lay to handle the cases of full, overrun and etc.
I will let you know when I have any feedback from the KSDK design team. Thanks for your patience!
Have a great day,
Kan
Freescale Technical Support
-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------