This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

TMS320F28054M: After a while, CAN RX stops working but TX does work and ABO is set to 1

Part Number: TMS320F28054M

Hi,

We have encountered a problem with the CAN bus on TMS320 chip. So after a while, RX on TMS stops working and we can no longer send any messages to the TMS320 but its TX do work as we receive messages from it.

We thought that it goes to bus off state and we set ABO to 1 to automatically get out of that state but it seems it is not the issue. There might be some message errors in the CAN bus and it is at 1mbit/sec with around 60% of felt capacity.

I am attaching can.c.

What could it be? 

can.c
// **************************************************************************
// the includes
#include <limits.h>

// **************************************************************************
// drivers

#include "ecan.h"
#include "ecanGPIO.h"
//#include "ecanPIEctrl.h"

#include "sw/drivers/cpu/src/32b/f28x/f2805x/cpu.h"
#include "sw/drivers/gpio/src/32b/f28x/f2805x/gpio.h"

#include "can.h"
// **************************************************************************
// modules


// **************************************************************************
// platforms


// **************************************************************************

//###########################################################################
/* TODO:
   - Properly configure BTC.
   - Add proper locking of mailbox resources (if needed/possible).
   - Support transmit priority levels.
   - Handle RTR settings.
   - Manage acceptance filters.
   - Consider using the CAN Power-Down Mode if communication is not too frequent.
   - Consider using C++ classes for mailboxes.
   - Support standard (as opposed to extended) message IDs (if needed). */

/* For some reason, many of the CAN control and status registers only support
   32-bit access. To work around this, we must first read the 32-bit register
   into a temporary, change the needed bits, then write the 32-bit temporary
   back to the register. The manual refers to this as a "shadow register".
   Notice that this is only needed for registers within the ECanaRegs struct.
   ECanaMboxes and ECanaLAMRegs aren't affected by this. */
/* FIXME: Use templates instead for type safety. */
#define WRITE_REG_FIELD(reg_name, field, value) \
    do                                          \
    {                                           \
        volatile union reg_name##_REG tmp;      \
                                                \
        tmp.all = ECanaRegs.reg_name.all;       \
        tmp.bit.field = (value);                \
        ECanaRegs.reg_name.all = tmp.all;       \
    }                                           \
    while (0)

/* WARNING: While using e.g. ECanaRegs.CANME.all may seem safe (since the '.all'
   union member covers all 32-bits), the C2000's OR and AND operations only
   support 16-bit operands when working on memory. This means that writing
   ECanaRegs.CANME.all |= (1ul << bit) will do the wrong thing; namely, the
   assembly output will look like this:

   MOV AL,*-SP[2]
   OR @$BLOCKED(ECanaRegs),AL
   MOV AL,*-SP[1]
   OR @$BLOCKED(ECanaRegs)+1,AL

   Here we're performing two 16-bit operations on ECanaRegs[CANME]. On the other
   hand, when using the temporary variable, the assembler will (hopefully) use
   the full 32-bit ACC register and the 32-bit MOVL instruction:

   MOVL      ACC,@$BLOCKED(_ECanaRegs)
   MOVL      *-SP[2],ACC
   MOV       AL,PL
   MOV       AH,PH
   OR        *-SP[2],AL
   OR        *-SP[1],AH
   MOVL      ACC,*-SP[2]
   MOVL      @$BLOCKED(_ECanaRegs),ACC */
#define SET_REG_BIT(reg_name, bit)         \
    do                                     \
    {                                      \
        volatile union reg_name##_REG tmp; \
                                           \
        tmp.all = ECanaRegs.reg_name.all;  \
        tmp.all |= (1ul << bit);           \
        ECanaRegs.reg_name.all = tmp.all;  \
    }                                      \
    while (0)

#define CLEAR_REG_BIT(reg_name, bit)       \
    do                                     \
    {                                      \
        volatile union reg_name##_REG tmp; \
                                           \
        tmp.all = ECanaRegs.reg_name.all;  \
        tmp.all &= ~(1ul << bit);          \
        ECanaRegs.reg_name.all = tmp.all;  \
    }                                      \
    while (0)

/**
 * @brief Operation modes for the CAN module.
 * @details This corresponds to the CANMC[CCR] and CANES[CCE] bits.
 */
typedef enum
{
    NormalMode = 0,
    ConfigurationMode = 1
}
CANOperationMode;

/* Since we're clearing CANMC[DBO], we don't have to convert our data to/from
   big-endian; the module does the work for us. The names of the regs are,
   however, inverted: 'Message-Data-Low' will hold the higher-order bytes, and
   'Message-Data-High' will hold the lower-order ones. */
static const CANMsgData CANMDL_MASK = 0xFFFFFFFF00000000ull;
static const CANMsgData CANMDL_SHIFT = 32ull;
static const CANMsgData CANMDH_MASK = 0xFFFFFFFFull;

/* We ask out the top 3 bits of the MSGID registers. */
static const CANMsgID MSGID_MASK = 0x1FFFFFFFul;

/* Initialize the CAN pins. */
static void init_can_pins(void)
{
    /* Enable internal pull-up resistors for the CAN pins. According to section
       1.3.2 of the manual, all GPIO pins except for ePWM outputs have their
       pull-up resistors enabled by default. Still, can't hurt to do this. */
    GpioCtrlRegs.GPAPUD.bit.GPIO30 = GPIO_Pullup_Enable;
    GpioCtrlRegs.GPAPUD.bit.GPIO31 = GPIO_Pullup_Enable;

  /* Set the RX pin to asynchronous input mode.
     According to section 1.3.4.1 of the manual, "[asynchronous mode] is used
     for peripherals where input synchronization is not required or the
     peripheral itself performs the synchronization. Examples include
     communication ports SCI, SPI, eCAN [...]". According to section 1.3.2 of
     the manual, "By default, all of the input signals are synchronized to
     SYSCLKOUT only". Since MW's HAL_setupGpios doesn't set qualification for
     this pin for some reason, let's do it here. */
    GpioCtrlRegs.GPAQSEL2.bit.GPIO30 = GPIO_Qual_ASync;

    /* Finally, configure GPIO pins 30 and 31 as CAN RX and TX, respectively. */
    GpioCtrlRegs.GPAMUX2.bit.GPIO30 = GPIO_30_Mode_CANRXA;
    GpioCtrlRegs.GPAMUX2.bit.GPIO31 = GPIO_31_Mode_CANTXA;
}

/* Enable the TX and RX pins to actually transmit and receive data. */
static inline void enable_can_pins(void)
{
    WRITE_REG_FIELD(CANTIOC, TXFUNC, 1);
    WRITE_REG_FIELD(CANRIOC, RXFUNC, 1);
}

/* Enable fully-featured eCAN mode (as opposed to the limited SCC mode). */
static inline void enable_full_can_mode(void)
{
    WRITE_REG_FIELD(CANMC, SCB, 1);
}

static void set_operation_mode(CANOperationMode mode)
{
    volatile union CANES_REG canes_tmp;

    WRITE_REG_FIELD(CANMC, CCR, mode);

    /* Wait until the mode changes. According to the manual, the RX pin must be
       sensed high before CANES[CCE] is set to 1. This shouldn't be an issue
       since we've enabled the pull-up resistor for RX. */
    /* FIXME: Consider adding a timeout to this. */
    do
    {
        canes_tmp.all = ECanaRegs.CANES.all;
    }
    while (canes_tmp.bit.CCE != mode);
}

static void clear_canmc_bits(void)
{
    /* The manual indicates that these bits must be cleared. The C2000Ware
       doesn't do it, as it relies on they having their reset value; we clear
       them here just in case. */
    volatile union CANMC_REG canmc_tmp;

    canmc_tmp.all = ECanaRegs.CANMC.all;

    /* No need to clear CANMC[CCR] here, since configure_can_btc should've taken
       care of it. Plus, if BTC is not configured, trying to clear this is
       useless since we won't transition to Normal mode anyway. */
    set_operation_mode(NormalMode);

    /* Disable Power-Down mode. */
    canmc_tmp.bit.PDR = 0;
    canmc_tmp.bit.WUBA = 0;

    /* Send the MSB first when transmitting/receiving. */
    canmc_tmp.bit.DBO = 0;

    /* Exit bus-off mode only in Normal operation mode. */
    canmc_tmp.bit.ABO = 1;

    /* Disable self-test mode. */
    canmc_tmp.bit.STM = 0;

    /* Clear CDR-related fields. */
    canmc_tmp.bit.CDR = 0;
    canmc_tmp.bit.MBNR = 0;

    /* Don't try to clear SRES, since it has no effect. */

    ECanaRegs.CANMC.all = canmc_tmp.all;
}

/* Configure the CAN bit timing. */
/* FIXME: Use appropriate values here. According to C2000Ware:

   "Bit timing parameters must be chosen based on the network parameters such as
   the sampling point desired and the propagation delay of the network.
   The propagation delay is a function of length of the cable, delay introduced
   by the transceivers and opto/galvanic-isolators (if any).

   The parameters used in this file must be changed taking into account the
   above mentioned factors in order to arrive at the bit-timing parameters
   suitable for a network". */
static void configure_can_btc(void)
{
    /* We don't use WRITE_REG_FIELD here to avoid copying the same reg multiple
       times. */
    volatile union CANBTC_REG canbtc_tmp;

    /* Switch to Configuration Mode (i.e. tell the CPU to set CANES[CCE] to 1).
       While the manual says that the reset state of the CCE bit is 1, let's do
       this here just in case some earlier code cleared it. */
    set_operation_mode(ConfigurationMode);

    /* Configure the BTC register. Notice that we won't be able to switch to
       Normal Mode if CANBTC is cleared or left with its reset value. */
    canbtc_tmp.all = 0;

    //
    // Configure bit timing parameters for eCANA
    //
    //
    // The following block is only for 60 MHz SYSCLKOUT.
    // (30 MHz CAN module clock Bit rate = 1 Mbps)
    //
    canbtc_tmp.bit.BRPREG = 2;
    canbtc_tmp.bit.TSEG2REG = 1;
    canbtc_tmp.bit.TSEG1REG = 6;

    /* This value was set by the C2000Ware sources. According to the manual,
       when this bit is set the CAN module will determine the actual level of
       the CAN bus by taking three samples and doing a majority decision. If
       this bit is cleared, only one sample will be used.
       Notice that the manual indicates that "the three-sample mode shall be
       selected only for bit rate prescale values greater than 4 (BRP > 4)". */
    canbtc_tmp.bit.SAM = 0;

    ECanaRegs.CANBTC.all = canbtc_tmp.all;

    /* Finally, switch back to Normal Mode. */
    set_operation_mode(NormalMode);
}

/* Clear all the Message Control Registers.
   This is needed as part of the CAN initialization process, as some of the
   MSGCTRL bits are in an unknown state after reset. */
static inline void clear_msgctrl_regs(void)
{
    size_t i;
    for (i = 0; i < CAN_MBOX_COUNT; ++i)
    {
        ECanaMboxes[i].MSGCTRL.all = 0;
    }
}

/* Clear the Transmission-ACK, Received-Message-Pending, and Global Interrupt
   Flags registers.
   While the manual doesn't explicitly state that this must be done, the
   C2000Ware sources do it, and can't hurt to be safe. */
void clear_transmission_and_irq_flag_registers(void)
{
    /* These are cleared by writing all-ones to them. There's no need to use a
       temporary copy since these are all 32-bit writes. */
    ECanaRegs.CANTA.all = 0xFFFFFFFFul;
    ECanaRegs.CANRMP.all = 0xFFFFFFFFul;
    ECanaRegs.CANGIF0.all = 0xFFFFFFFFul;
    ECanaRegs.CANGIF1.all = 0xFFFFFFFFul;
}

/* Initialize the eCAN module. */
void can_init(void)
{
    /* Many of the configuration registers are EALLOW-protected. To avoid having
       to write EALLOW/EDIS pairs everywhere, let's just do it here once. */
    EALLOW;

    init_can_pins();
    enable_can_pins();
    enable_full_can_mode();
    configure_can_btc();
    clear_canmc_bits();
    clear_msgctrl_regs();
    clear_transmission_and_irq_flag_registers();

    /* Disable all the mailboxes. This is needed before writing to the contents
       of any MSGID register. */
    ECanaRegs.CANME.all = 0;

    EDIS;
}

/* Clear any pending transmission requests. If a transmission was already
   initiated, it's aborted. */
static void clear_trs(CANMboxNumber mbox_num)
{
    const uint32_t mbox_bit = (1ul << mbox_num);

    if (ECanaRegs.CANTRS.all & mbox_bit)
    {
        /* Request that the current transmission be aborted by writing 1 to the
           appropriate TRR bit. Writing zeroes has no effect. */
        ECanaRegs.CANTRR.all = mbox_bit;

        /* FIXME: Consider adding a timeout to this. */
        while (ECanaRegs.CANTRS.all & mbox_bit)
        {
            /* Wait until the TRS bit is cleared. */
        }
    }
    /* Else: TRS bit is already cleared, nothing to do. */
}

static inline bool mbox_number_is_valid(CANMboxNumber mbox_num)
{
    return mbox_num < CAN_MBOX_COUNT;
}

static inline bool dlc_is_valid(CANDLC dlc)
{
    /* TODO: Should we allow DLC == 0? */
    const CANDLC MinDLC = 1;
    const CANDLC MaxDLC = 8;

    return MinDLC <= dlc && dlc <= MaxDLC;
}

static void set_mbox_msgid(CANMboxNumber mbox_num, CANMsgID msg_id)
{
    /* Set the message ID. We currently support only extended (28-bit) IDs.
       The mask clears the IDE, AME and AAM bits. */
    ECanaMboxes[mbox_num].MSGID.all = (msg_id & MSGID_MASK);

    /* Although the IDE bit only seems to apply for 'receive' mailboxes, let's
       always set it just to make sure we won't accidentally use a standard ID. */
    ECanaMboxes[mbox_num].MSGID.bit.IDE = 1;
}

static inline void set_mbox_dlc(CANMboxNumber mbox_num, CANDLC dlc)
{
    /* Leave only the bottom 4 bits of DLC values. */
    static const CANDLC DLC_MASK = 0xF;

    ECanaMboxes[mbox_num].MSGCTRL.bit.DLC = (dlc & DLC_MASK);
}

static inline bool mbox_is_enabled(CANMboxNumber mbox_num)
{
    return ECanaRegs.CANME.all & (1ul << mbox_num);
}

static CANResultCode check_params_for_mbox_communication(CANMboxNumber mbox_num)
{
    CANResultCode result = mbox_number_is_valid(mbox_num) ?
                           Success : InvalidMboxNumber;

    if (result == Success)
    {
        result = mbox_is_enabled(mbox_num) ? Success : MboxNotEnabled;
    }

    if (can_mbox_has_pending_tx(mbox_num))
    {
        result = MboxBusy;
        clear_transmission_and_irq_flag_registers();
    }

    return result;
}

static CANResultCode check_params_for_mbox_configuration(CANMboxNumber mbox_num,
                                                         CANMsgID msg_id)
{
    CANResultCode result = mbox_number_is_valid(mbox_num) ?
                           Success : InvalidMboxNumber;

    if (result == Success)
    {
        result = (msg_id <= MSGID_MASK) ? Success : InvalidMsgID;
    }

    return result;
}

CANMboxDirection can_get_mbox_direction(CANMboxNumber mbox_num)
{
    return (ECanaRegs.CANMD.all & (1ul << mbox_num)) ? Receive : Transmit;
}

CANResultCode can_disable_mbox(CANMboxNumber mbox_num)
{
    const CANResultCode result = mbox_number_is_valid(mbox_num) ?
                                 Success : InvalidMboxNumber;

    if (result == Success && mbox_is_enabled(mbox_num))
    {
        CLEAR_REG_BIT(CANME, mbox_num);
    }

    return result;
}

CANResultCode can_configure_tx_mbox(CANMboxNumber mbox_num,
                                    CANMsgID msg_id,
                                    CANDLC dlc)
{
    CANResultCode result = check_params_for_mbox_configuration(mbox_num, msg_id);

    if (result == Success)
    {
        result = dlc_is_valid(dlc) ?
                 Success : InvalidDLC;

        if (result == Success)
        {
            clear_trs(mbox_num);

            /* At this point we know that mbox_num is valid; no need to check
               the return code. */
            (void)can_disable_mbox(mbox_num);

            set_mbox_msgid(mbox_num, msg_id);
            set_mbox_dlc(mbox_num, dlc);

            /* Set the mailbox direction to 'transmit'. */
            CLEAR_REG_BIT(CANMD, mbox_num);

            /* Finally, enable the mailbox. */
            SET_REG_BIT(CANME, mbox_num);
        }
        /* Else: Invalid DLC, just bubble up the error. */
    }
    /* Else: Invalid mailbox number, just bubble up the error.*/

    return result;
}

CANResultCode can_configure_rx_mbox(CANMboxNumber mbox_num,
                                    CANMsgID msg_id,
                                    bool enable_opc)
{
    CANResultCode result = check_params_for_mbox_configuration(mbox_num, msg_id);

    if (result == Success)
    {
        /* At this point we know that mbox_num is valid; no need to check the
           return code. */
        (void)can_disable_mbox(mbox_num);

        set_mbox_msgid(mbox_num, msg_id);

        /* Set the mailbox direction to 'receive'. */
        SET_REG_BIT(CANMD, mbox_num);

        if (enable_opc)
        {
            SET_REG_BIT(CANOPC, mbox_num);
        }

        /* Finally, enable the mailbox. */
        SET_REG_BIT(CANME, mbox_num);
    }
    /* Else: Invalid params, just bubble up the error.*/

    return result;
}

/* Calculate the count of bits that we need to shift the CAN message data for
   this mailbox, according to the mailbox's configured DLC. */
static CANMsgData get_dlc_shift(CANMboxNumber mbox_num)
{
    /* This MCU defines CHAR_BIT to 16. */
    static const CANMsgData CAN_MSG_DATA_BITS = sizeof(CANMsgData) * CHAR_BIT;

    /* Get the mailbox's DLC. */
    const CANDLC dlc = ECanaMboxes[mbox_num].MSGCTRL.bit.DLC;

    return CAN_MSG_DATA_BITS - (dlc * 8);
}

static void write_msg(CANMboxNumber mbox_num, CANMsgData msg_data)
{
    const CANMsgData dlc_shift = get_dlc_shift(mbox_num);

    /* Place the message data in the mailbox. */
    msg_data <<= dlc_shift;

    ECanaMboxes[mbox_num].MDL.all = (msg_data & CANMDL_MASK) >> CANMDL_SHIFT;
    ECanaMboxes[mbox_num].MDH.all = (msg_data & CANMDH_MASK);

    /* Request transmission by writing 1 to the appropriate bit. Writing zeroes
       has no effect. */
    ECanaRegs.CANTRS.all = (1ul << mbox_num);
}

static bool msg_data_size_is_valid(CANMboxNumber mbox_num, CANMsgData msg_data)
{
    const CANMsgData dlc_shift = get_dlc_shift(mbox_num);

    /* Calculate the max allowed value for this DLC. The extra cast to
       CANMsgData is to avoid any (unlikely) integer promotion. */
    const CANMsgData max = (CANMsgData)(~(CANMsgData)0) >> (dlc_shift);

    return msg_data <= max;
}

CANResultCode can_write(CANMboxNumber mbox_num, CANMsgData msg_data)
{
    CANResultCode result = check_params_for_mbox_communication(mbox_num);

    if (result == Success)
    {
        result = can_get_mbox_direction(mbox_num) == Transmit ?
                 Success : WrongMboxConfig;

        if (result == Success)
        {
            result = msg_data_size_is_valid(mbox_num, msg_data) ?
                     Success : InvalidMsgDataSize;

            if (result == Success)
            {
                write_msg(mbox_num, msg_data);
            }
            /* Else: Message data is too large for this mailbox, just bubble up
               the error. */
        }
        /* Else: Wrong mailbox configuration, just bubble up the error.*/
    }
    /* Else: Invalid params, just bubble up the error.*/


    return result;
}

static void read_msg(CANMboxNumber mbox_num, CANMsgData *msg_data)
{
    const CANMsgData dlc_shift = get_dlc_shift(mbox_num);

    /* Get the message data from the mailbox. */
    const CANMsgData mdl = ECanaMboxes[mbox_num].MDL.all;
    const CANMsgData mdh = ECanaMboxes[mbox_num].MDH.all;

    *msg_data = ((mdl << CANMDL_SHIFT) | mdh) >> dlc_shift;
}

CANResultCode can_read(CANMboxNumber mbox_num, CANMsgData *msg_data)
{
    CANResultCode result = check_params_for_mbox_communication(mbox_num);

    if (result == Success)
    {
        result = can_get_mbox_direction(mbox_num) == Receive ?
                 Success : WrongMboxConfig;

        if (result == Success)
        {
            read_msg(mbox_num, msg_data);
        }
        /* Else: Wrong mailbox configuration, just bubble up the error.*/
    }
    /* Else: Invalid params, just bubble up the error.*/

    return result;
}

bool can_mbox_has_pending_tx(CANMboxNumber mbox_num)
{
    return ECanaRegs.CANTRS.all & (1ul << mbox_num);
}

bool can_mbox_has_pending_rx(CANMboxNumber mbox_num)
{
    return ECanaRegs.CANRMP.all & (1ul << mbox_num);
}

bool can_mbox_has_lost_msg(CANMboxNumber mbox_num)
{
    return ECanaRegs.CANRML.all & (1ul << mbox_num);
}

uint32_t getStatusErrReg(void)
{
    return ECanaRegs.CANES.all;
}

//###########################################################################
// ffsWrapper merging
/* These are based on glibc's implementation. */

int ffsl(long int i)
{
    int result;

    /* First, check that the first set bit is actually on the upper 16 bits. */
    const unsigned long int x = i & -i;

    if (x <= 0xFFFF)
    {
        result = ffs(i);
    }
    else
    {
        /* It is; add 16 to the final result. */
        result = 16 + ffs(i >> 16);
    }

    return result;
}

int ffsll(long long int i)
{
    int result;

    /* First, check that the first set bit is actually on the upper 32 bits. */
    const unsigned long long int x = i & -i;

    if (x <= 0xFFFFFFFF)
    {
        result = ffs(i);
    }
    else
    {
        /* It is; add 32 to the final result. */
        result = 32 + ffs(i >> 32);
    }

    return result;
}
//###########################################################################

  • Abyl,

                    Debugging your code is not something we can support on the e2e forum.  However, I am happy to offer the following tips:

    RX on TMS stops working and we can no longer send any messages to the TMS320 but its TX do work as we receive messages from it.

    The above is interesting. There is an erratum for the exact opposite. Please refer to "eCAN: Unexpected Cessation of Transmit Operation" in page 11 of www.ti.com/lit/SPRZ362. This module is almost 25 years old now. Thus far, we have not heard of an instance where receive operation stops randomly. 

    1. Have you confirmed that the message you believe is not being received is actually transmitted? Reading your message, it appears once reception stops, it stops permanently. Is this correct? If so, what needs to be done to make the module start receiving again? 
    2. Is the problem isolated to a specific receive mailbox or is it reception in general? If you  have multiple receive mailboxes, do they all stop receiving? 
    3. Have you checked the MSGID of the receive mailbox(es) in question to verify whether they are configured correctly to accept the transmitted messages? 
    4. If you use mask filtering, can you disable it temporarily and use exact ID match? 

    I encourage you to look at the debug tips in www.ti.com/lit/spra876.