I have a KW36A development board and want to set it up to send and receive data at the typical automotive CAN2.0A with standard and extended IDs at 500kbps. No matter what I do, I can't get the correct speed or data to be sent from the bus. Using the frdmkw36_driver_examples_flexcan_interrupt_transfer and connecting it to a logic analyzer yields an output of junk data, given the default configuration of 100,000 kbps and that being set in the decoder, the output is inconsistent junk as seen in the attached images. Using the guide from the FlexCAN Bit Timing Parameters Setup post on the knowledge base did not help; junk is still being output. In the attached images, I added my clock configuration, what I tried with the peripheral configuration tool (Same result), and any other information.
Attempted troubleshooting:
Oscilloscope validates that data IS being sent out, and what speed? Nobody knows.
I wrote a quick program to see what the bus speed is by getting configuration information from the chip registers and dynamically calculating both the settings to use, and the bus speed, which did yield a valid configuration that attains 500kbps in theory, but in practice only outputs junk again. Below is the code for the two or so functions
/*!
* @brief Calculate CAN timing parameters dynamically
*/
void calculate_can_timing(uint32_t clockFreq, uint32_t desiredBaudRate, uint32_t *bestPrescaler, uint32_t *bestTQ, uint32_t *bestPropSeg, uint32_t *bestPhaseSeg1, uint32_t *bestPhaseSeg2, uint32_t *bestSjw) {
uint32_t prescaler, propSeg, phaseSeg1, phaseSeg2, sjw, totalTQ;
float samplePoint = SAMPLE_POINT_PERCENT / 100.0f;
uint32_t bestBaud = 0;
float bestError = MAX_ERROR_PERCENT;
for (totalTQ = MIN_TQ; totalTQ <= MAX_TQ; totalTQ++) {
prescaler = (clockFreq / (desiredBaudRate * totalTQ)) - 1;
if (prescaler > 255 || prescaler < 0) {
continue;
}
phaseSeg1 = (uint32_t)(totalTQ * samplePoint) - 1;
phaseSeg2 = totalTQ - phaseSeg1 - 1;
propSeg = phaseSeg1 - 1;
sjw = (phaseSeg2 < 4) ? phaseSeg2 : 4;
if (propSeg < 0 || phaseSeg1 < 0 || phaseSeg2 < 1 || sjw < 1 || sjw > 4) {
continue;
}
uint32_t actualBaud = clockFreq / ((prescaler + 1) * totalTQ);
float errorPercent = ((float)(actualBaud - desiredBaudRate) / desiredBaudRate) * 100;
if (errorPercent < bestError) {
*bestPrescaler = prescaler;
*bestTQ = totalTQ;
*bestPropSeg = propSeg;
*bestPhaseSeg1 = phaseSeg1;
*bestPhaseSeg2 = phaseSeg2;
*bestSjw = sjw;
bestBaud = actualBaud;
bestError = errorPercent;
}
}
PRINTF("Best Configuration: Prescaler=%u, PropSeg=%u, PhaseSeg1=%u, PhaseSeg2=%u, SJW=%u\n", *bestPrescaler, *bestPropSeg, *bestPhaseSeg1, *bestPhaseSeg2, *bestSjw);
}
/*!
* @brief Validate CAN timing parameters
*/
void validate_can_timing(uint32_t clockFreq, uint32_t desiredBaudRate, uint32_t prescaler, uint32_t totalTQ) {
uint32_t actualBaudRate = clockFreq / ((prescaler + 1) * totalTQ);
PRINTF("Validating configuration...\n");
PRINTF("Expected baud rate: %u kbps\n", desiredBaudRate / 1000);
PRINTF("Actual baud rate: %u kbps\n", actualBaudRate / 1000);
if (actualBaudRate == desiredBaudRate) {
PRINTF("Baud rate matches the expected value.\n");
} else {
float errorPercent = ((float)(actualBaudRate - desiredBaudRate) / desiredBaudRate) * 100;
PRINTF("Baud rate mismatch! Error: %.2f%%\n", errorPercent);
}
}
/*!
* @brief CAN initialization function
*/
void CAN_Init(void) {
uint32_t bestPrescaler = 0, bestTQ = 0, bestPropSeg = 0, bestPhaseSeg1 = 0, bestPhaseSeg2 = 0, bestSjw = 0;
calculate_can_timing(EXAMPLE_CAN_CLK_FREQ, EXPECTED_BAUD_RATE, &bestPrescaler, &bestTQ, &bestPropSeg, &bestPhaseSeg1, &bestPhaseSeg2, &bestSjw);
flexcan_config_t canConfig = {
.clksrc=kFLEXCAN_ClkSrcPeri,
.baudRate = EXPECTED_BAUD_RATE,
.maxMbNum = 16U,
.enableLoopBack = false,
.enableSelfWakeup = false,
.enableIndividMask = false,
.enableDoze = false,
.timingConfig = {
.preDivider = bestPrescaler,
.propSeg = bestPropSeg,
.phaseSeg1 = bestPhaseSeg1,
.phaseSeg2 = bestPhaseSeg2,
.rJumpwidth = bestSjw
}
};
FLEXCAN_Init(EXAMPLE_CAN, &canConfig, EXAMPLE_CAN_CLK_FREQ);
flexcan_rx_mb_config_t CAN0_rx_mb_config_0 = { .id = FLEXCAN_ID_STD(0x7DF), .format = kFLEXCAN_FrameFormatStandard, .type = kFLEXCAN_FrameTypeData };
FLEXCAN_SetRxMbConfig(EXAMPLE_CAN, RX_MESSAGE_BUFFER_NUM, &CAN0_rx_mb_config_0, true);
FLEXCAN_SetTxMbConfig(EXAMPLE_CAN, TX_MESSAGE_BUFFER_NUM, true);
PRINTF("CAN initialized with dynamically calculated timing.\n");
validate_can_timing(EXAMPLE_CAN_CLK_FREQ, EXPECTED_BAUD_RATE, bestPrescaler, bestTQ);
}
Below is my "custom" code, that I reused from the example which again yields the same result
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <assert.h>
#include "fsl_flexcan.h"
#include "fsl_debug_console.h"
#include "board.h"
#include "pin_mux.h"
#include "clock_config.h"
#include "peripherals.h"
/*******************************************************************************
* Definitions
******************************************************************************/
#define EXAMPLE_CAN CAN0
#define EXAMPLE_CAN_CLK_FREQ (CLOCK_GetFreq(kCLOCK_BusClk)) // Bus clock as example
#define RX_MESSAGE_BUFFER_NUM (0)
#define TX_MESSAGE_BUFFER_NUM (1)
#define INTERRUPT_PRIORITY 5
#define EXPECTED_BAUD_RATE 500000U
#define SAMPLE_POINT_PERCENT 87.5
#define MAX_TQ 25
#define MIN_TQ 8
#define MAX_ERROR_PERCENT 1 // Allowable error percentage
/*******************************************************************************
* Variables
******************************************************************************/
flexcan_handle_t flexcanHandle;
volatile bool txComplete = false;
volatile bool rxComplete = false;
flexcan_mb_transfer_t txXfer, rxXfer;
flexcan_frame_t frame;
uint32_t txIdentifier = 0x7DF; // Standard 11-bit ID
/*******************************************************************************
* Prototypes
******************************************************************************/
void delay_ms(uint32_t ms);
void print_actual_baud_rate(void);
void calculate_can_timing(uint32_t clockFreq, uint32_t desiredBaudRate, uint32_t *bestPrescaler, uint32_t *bestTQ, uint32_t *bestPropSeg, uint32_t *bestPhaseSeg1, uint32_t *bestPhaseSeg2, uint32_t *bestSjw);
void validate_can_timing(uint32_t clockFreq, uint32_t desiredBaudRate, uint32_t prescaler, uint32_t totalTQ);
/*******************************************************************************
* Code
******************************************************************************/
/*!
* @brief Delay function using busy wait
*/
void delay_ms(uint32_t ms) {
uint32_t cycles = (CLOCK_GetFreq(kCLOCK_CoreSysClk) / 1000) * ms;
for (uint32_t i = 0; i < cycles; ++i) {
__NOP(); // No operation to waste time
}
}
/*!
* @brief FlexCAN Call Back function
*/
static void flexcan_callback(CAN_Type *base, flexcan_handle_t *handle, status_t status, uint32_t result, void *userData) {
PRINTF("\n[Callback] Entering FlexCAN callback function.\n");
// Check for unhandled status and log error
if (status != kStatus_FLEXCAN_TxIdle && status != kStatus_FLEXCAN_RxIdle) {
PRINTF("[Callback] Unhandled status: 0x%08X. Possible error occurred.\n", status);
// Read the ESR1 register to diagnose the error
uint32_t esr1 = FLEXCAN_GetStatusFlags(base);
PRINTF("[Callback] ESR1 register: 0x%08X\n", esr1);
// Handle Bus-Off and other errors
if (esr1 & CAN_ESR1_BOFFINT_MASK) {
PRINTF("[Callback] Bus-Off detected. Attempting to recover...\n");
FLEXCAN_Enable(base, false);
FLEXCAN_Enable(base, true);
FLEXCAN_ClearStatusFlags(base, CAN_ESR1_BOFFINT_MASK);
PRINTF("[Callback] Bus-Off recovery initiated.\n");
}
FLEXCAN_ClearStatusFlags(base, esr1);
}
// Handle successful transmission
if (status == kStatus_FLEXCAN_TxIdle) {
PRINTF("[Callback] Transmission completed for message buffer: %d.\n", result);
txComplete = true;
}
// Handle successful reception
if (status == kStatus_FLEXCAN_RxIdle) {
PRINTF("[Callback] Reception completed for message buffer: %d.\n", result);
rxComplete = true;
}
PRINTF("[Callback] Exiting FlexCAN callback function.\n\n");
}
/*!
* @brief Calculate CAN timing parameters dynamically
*/
void calculate_can_timing(uint32_t clockFreq, uint32_t desiredBaudRate, uint32_t *bestPrescaler, uint32_t *bestTQ, uint32_t *bestPropSeg, uint32_t *bestPhaseSeg1, uint32_t *bestPhaseSeg2, uint32_t *bestSjw) {
uint32_t prescaler, propSeg, phaseSeg1, phaseSeg2, sjw, totalTQ;
float samplePoint = SAMPLE_POINT_PERCENT / 100.0f;
uint32_t bestBaud = 0;
float bestError = MAX_ERROR_PERCENT;
for (totalTQ = MIN_TQ; totalTQ <= MAX_TQ; totalTQ++) {
prescaler = (clockFreq / (desiredBaudRate * totalTQ)) - 1;
if (prescaler > 255 || prescaler < 0) {
continue;
}
phaseSeg1 = (uint32_t)(totalTQ * samplePoint) - 1;
phaseSeg2 = totalTQ - phaseSeg1 - 1;
propSeg = phaseSeg1 - 1;
sjw = (phaseSeg2 < 4) ? phaseSeg2 : 4;
if (propSeg < 0 || phaseSeg1 < 0 || phaseSeg2 < 1 || sjw < 1 || sjw > 4) {
continue;
}
uint32_t actualBaud = clockFreq / ((prescaler + 1) * totalTQ);
float errorPercent = ((float)(actualBaud - desiredBaudRate) / desiredBaudRate) * 100;
if (errorPercent < bestError) {
*bestPrescaler = prescaler;
*bestTQ = totalTQ;
*bestPropSeg = propSeg;
*bestPhaseSeg1 = phaseSeg1;
*bestPhaseSeg2 = phaseSeg2;
*bestSjw = sjw;
bestBaud = actualBaud;
bestError = errorPercent;
}
}
PRINTF("Best Configuration: Prescaler=%u, PropSeg=%u, PhaseSeg1=%u, PhaseSeg2=%u, SJW=%u\n", *bestPrescaler, *bestPropSeg, *bestPhaseSeg1, *bestPhaseSeg2, *bestSjw);
}
/*!
* @brief Validate CAN timing parameters
*/
void validate_can_timing(uint32_t clockFreq, uint32_t desiredBaudRate, uint32_t prescaler, uint32_t totalTQ) {
uint32_t actualBaudRate = clockFreq / ((prescaler + 1) * totalTQ);
PRINTF("Validating configuration...\n");
PRINTF("Expected baud rate: %u kbps\n", desiredBaudRate / 1000);
PRINTF("Actual baud rate: %u kbps\n", actualBaudRate / 1000);
if (actualBaudRate == desiredBaudRate) {
PRINTF("Baud rate matches the expected value.\n");
} else {
float errorPercent = ((float)(actualBaudRate - desiredBaudRate) / desiredBaudRate) * 100;
PRINTF("Baud rate mismatch! Error: %.2f%%\n", errorPercent);
}
}
/*!
* @brief CAN initialization function
*/
void CAN_Init(void) {
uint32_t bestPrescaler = 0, bestTQ = 0, bestPropSeg = 0, bestPhaseSeg1 = 0, bestPhaseSeg2 = 0, bestSjw = 0;
calculate_can_timing(EXAMPLE_CAN_CLK_FREQ, EXPECTED_BAUD_RATE, &bestPrescaler, &bestTQ, &bestPropSeg, &bestPhaseSeg1, &bestPhaseSeg2, &bestSjw);
flexcan_config_t canConfig = {
.clksrc=kFLEXCAN_ClkSrcPeri,
.baudRate = EXPECTED_BAUD_RATE,
.maxMbNum = 16U,
.enableLoopBack = false,
.enableSelfWakeup = false,
.enableIndividMask = false,
.enableDoze = false,
.timingConfig = {
.preDivider = bestPrescaler,
.propSeg = bestPropSeg,
.phaseSeg1 = bestPhaseSeg1,
.phaseSeg2 = bestPhaseSeg2,
.rJumpwidth = bestSjw
}
};
FLEXCAN_Init(EXAMPLE_CAN, &canConfig, EXAMPLE_CAN_CLK_FREQ);
flexcan_rx_mb_config_t CAN0_rx_mb_config_0 = { .id = FLEXCAN_ID_STD(0x7DF), .format = kFLEXCAN_FrameFormatStandard, .type = kFLEXCAN_FrameTypeData };
FLEXCAN_SetRxMbConfig(EXAMPLE_CAN, RX_MESSAGE_BUFFER_NUM, &CAN0_rx_mb_config_0, true);
FLEXCAN_SetTxMbConfig(EXAMPLE_CAN, TX_MESSAGE_BUFFER_NUM, true);
PRINTF("CAN initialized with dynamically calculated timing.\n");
validate_can_timing(EXAMPLE_CAN_CLK_FREQ, EXPECTED_BAUD_RATE, bestPrescaler, bestTQ);
}
/*!
* @brief Main function
*/
int main(void) {
BOARD_InitBootPins();
BOARD_InitBootClocks();
BOARD_InitDebugConsole();
PRINTF("[Main] Initializing FlexCAN...\n");
EnableIRQ(CAN0_IRQn);
NVIC_SetPriority(CAN0_IRQn, INTERRUPT_PRIORITY);
CAN_Init();
FLEXCAN_TransferCreateHandle(EXAMPLE_CAN, &flexcanHandle, flexcan_callback, NULL);
FLEXCAN_SetRxMbGlobalMask(EXAMPLE_CAN, FLEXCAN_RX_MB_STD_MASK(txIdentifier, 0, 0));
FLEXCAN_ClearMbStatusFlags(EXAMPLE_CAN, 1U << RX_MESSAGE_BUFFER_NUM);
frame.id = 0x7DF;
frame.format = kFLEXCAN_FrameFormatStandard;
frame.type = kFLEXCAN_FrameTypeData;
frame.length = 8;
frame.dataWord0 = 0x02010000;
frame.dataWord1 = 0x00000000;
txXfer.mbIdx = TX_MESSAGE_BUFFER_NUM;
txXfer.frame = &frame;
PRINTF("[Main] Sending CAN frame...\n");
FLEXCAN_TransferSendNonBlocking(EXAMPLE_CAN, &flexcanHandle, &txXfer);
while (!txComplete) { delay_ms(1); }
txComplete = false;
rxXfer.mbIdx = RX_MESSAGE_BUFFER_NUM;
rxXfer.frame = &frame;
FLEXCAN_TransferReceiveNonBlocking(EXAMPLE_CAN, &flexcanHandle, &rxXfer);
while (!rxComplete) { delay_ms(1); }
rxComplete = false;
if (frame.id == FLEXCAN_ID_STD(txIdentifier) && frame.dataWord0 == 0xF35B39BD && frame.dataWord1 == 0xD5865D3C) {
PRINTF("CAN frame received successfully!\n");
} else {
PRINTF("CAN frame mismatch! Transmission or reception failed.\n");
assert(false && "CAN frame mismatch");
}
delay_ms(500);
return 0;
}
What I want to achieve is to properly be able to read and write to standard CAN 2.0A used in most vehicles. Any help would be extremely appreciated as I have been stuck; perhaps even my CAN transceiver is broken. Attached is also my PulseView session in case anybody would like to point out an issue in my setup.