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.

Need help on using DCAN in interrupt mode on 570LS03

I am trying to enhance an existing CAN receive driver for our 570LS032 (soon to be 570LS042). We have msg obj 1 set for transmit and msg obj 2 set for receive. I have successfully experimented with the message acceptance feature, and now want to divide up our CAN receive traffic into 2 messages, which would be msg obj 2 and msg obj 3. I want to differentiate where the 2 types of messages are stored, so I need to see which received message invoked the interrupt. I may add further filtering in the future, so am looking for a way that doesn't add too much overhead.

- Experimentally, the interrupt register (DCAN INT) always reads zero whenever I get an interrupt. In addition, the latest manual (spnu517b) shows that this register only allows for 32 msg objects (1h-20h), even though there are provisions for up to 64 msg objects. The actual CAN data I read in is accurate.

- I am confused by the manual on how to set up DCAN INTMUX12 to have msg objs 2&3 drive DCAN1INT. Existing code for msg obj 2 only sets it to 0x0100. The manual implies a correlation of  bit number set for msg obj, but why does it allow for 128 bits then if only 64 msg objs allowed? In theory, I should set 0x1100 for msg objs 2&3?

- Existing code sets up the automatic update DCAN IF3UPD12 register for msg obj 2 as 0x0010. This does not appear to be the same mapping as for the intmux register (unless the driver has an error). The manual is no help.

I have intentions of trying to further optimize this by experimenting with DMA if that is even possible, to eliminate as much CPU overhead as possible, but I need to get the interrupt working first. Thanks for any help.

  • I implement CAN interrupt driven API for Hercules & STM32. I split all mailboxes to 32 for recv & 32 for transmit organized as FIFO. Message are placed to queue.

    #include <stdio.h>
    #include "can_driver.h"
    #include "can_utils.h"
    #include "rx_uart.h"
    #include "rx_convert.h"
    #include "log.h"
    #include "freertos_utils.h"
    #include "can_tms570_ex.h"
    
    #define MAILBOX_COUNT_TX 32 //mailbixes 1-32 used for TX, mailboxes 33-64 for RX
    #define MAILBOX_COUNT_TOTAL 64 //mailbixes 1-32 used for TX, mailboxes 33-64 for RX
    
    #if defined(CAN1_CTL_CONFIGVALUE)
    uint8_t can1_data [CAN_TX_QUEUE_SIZE * sizeof(can_message_t)];
    can_device_t can1 = {
        canREG1,
        1,
        500,
            BIP_BUFFER_INIT(sizeof(can1_data),can1_data),
        0,
        0,
        0,
        0,
        0,
        0,
        false };
    #endif
    
    #if defined(CAN2_CTL_CONFIGVALUE)
    uint8_t can2_data [CAN_TX_QUEUE_SIZE * sizeof(can_message_t)];
    can_device_t can2 = {
        canREG2,
        2,
        500,
            BIP_BUFFER_INIT(sizeof(can2_data),can2_data),
        0,
        0,
        0,
        0,
        0,
        0,
        false };
    #endif
    
    #if defined(CAN3_CTL_CONFIGVALUE)
    uint8_t can3_data [CAN_TX_QUEUE_SIZE * sizeof(can_message_t)];
    can_device_t can3 = {
        canREG3,
        3,
        500,
        BIP_BUFFER_INIT(sizeof(can3_data),can3_data),
        0,
        0,
        0,
        0,
        0,
        0,
        false};
    #endif
    
    static uint32_t get_free_tx_mailbox(canBASE_t *node) {
        uint32_t mailbox;
        for (mailbox = 1; mailbox <= MAILBOX_COUNT_TX; mailbox++) {
            if (!canIsTxMessagePending (node, mailbox)) {
                return mailbox;
            }
        }
        return 0;
    }
    
    #if ((__little_endian__ == 1) || (__LITTLE_ENDIAN__ == 1))
    #else
    static const uint32 s_canByteOrder [8U] = {
        3U,
        2U,
        1U,
        0U,
        7U,
        6U,
        5U,
        4U };
    #endif
    
    static void move_to_mailbox(can_device_t *hcan, uint32_t mailbox, const can_message_t *msg) {
        uint32_t i;
    
        can_cmd_iface_t* ifx = CAN_GET_IF1(hcan->Instance);
    
        while ((ifx->IFxSTAT & IFxSTAT_BUSY) == IFxSTAT_BUSY) {
        };
    
        ifx->IFxCMD = IFxCMD_WRITE | IFxCMD_ARB | IFxCMD_CLR_INT_PND/*???*/
        | IFxCMD_CONTROL | IFxCMD_TX_RQST_NEWDAT | IFxCMD_DATA_A | IFxCMD_DATA_B;
        ifx->IFxARB &= IFxARB_MsgVal;
        ifx->IFxARB |= IFxARB_Dir_Tx;
        if (msg->is_extended) {
            ifx->IFxARB |= (IFxARB_Xtd | msg->id_ext);
        } else {
            ifx->IFxARB |= ((uint32_t) msg->id_std) << 18U;
        }
        ifx->IFxMCTL = IFxMCTL_EoB | IFxMCTL_TxIE | msg->dlc;
        for (i = 0U; i < msg->dlc; i++) {
    #if ((__little_endian__ == 1) || (__LITTLE_ENDIAN__ == 1))
            ifx->IFxDATx[i] = msg->data[i];
    #else
            ifx->IFxDATx [s_canByteOrder [i]] = msg->data [i];
    #endif
        }
        ifx->IFxMSK = 0;
        ifx->IFxNO = mailbox;
    }
    
    bool can_transmit(can_device_t *hcan, const can_message_t* msg) {
        bool res = true;
        enter_critical ();
        uint32_t mailbox = get_free_tx_mailbox (hcan->Instance);
        if (mailbox) {
            move_to_mailbox (hcan, mailbox, msg);
        } else {
            res = bip_buffer_put (&hcan->tx_queue, msg, sizeof(can_message_t));
        }
        exit_critical ();
        return res;
    }
    
    bool can_transmit_to_can(const can_device_t* hcan) {
        if (bip_buffer_get_largest_free_size (&hcan->tx_queue) < (int) sizeof(can_message_t)) {
            return false;
        }
        return true;
    }
    
    #define CAN_BTR_500     ((uint32)((uint32)0U << 16U) \
                                     | (uint32)((uint32)(2U - 1U) << 12U) \
                                     | (uint32)((uint32)((3U + 2U) - 1U) << 8U) \
                                     | (uint32)((uint32)(2U - 1U) << 6U) | (uint32)19U)
    
    #define CAN_BTR_250      ((uint32)((uint32)0U << 16U) \
                                     | (uint32)((uint32)(6U - 1U) << 12U) \
                                     | (uint32)((uint32)((3U + 6U) - 1U) << 8U) \
                                     | (uint32)((uint32)(4U - 1U) << 6U) | (uint32)19U)
    
    #define CAN_BTR_125      ((uint32)((uint32)1U << 16U) \
                                     | (uint32)((uint32)(3U - 1U) << 12U) \
                                     | (uint32)((uint32)((1U + 3U) - 1U) << 8U) \
                                     | (uint32)((uint32)(3U - 1U) << 6U) | (uint32)15U)
    
    #define CAN_BTR_INVALID 0xFFFFFFFF
    
    static uint32_t get_BTR(int speed) {
        switch (speed) {
            case 125:
                return CAN_BTR_125;
            case 250:
                return CAN_BTR_250;
            case 500:
                return CAN_BTR_500;
            default:
                return CAN_BTR_INVALID;
        }
    }
    bool can_set_speed(can_device_t* hcan, int new_speed, bool loopback) {
        uint32_t btr = get_BTR (new_speed);
        if (btr == CAN_BTR_INVALID) {
            return false;
        }
        hcan->Instance->CTL |= (DCAN_CTL_INIT | DCAN_CTL_CCE);
        while ((hcan->Instance->CTL & DCAN_CTL_INIT) != DCAN_CTL_INIT) {
    
        }
        hcan->Instance->BTR = btr;
        hcan->Instance->CTL &= ~(uint32_t) (DCAN_CTL_INIT | DCAN_CTL_CCE);
    //    hcan->Instance->CTL |= DCAN_CTL_DAR; //disable auto resend
        hcan->Instance->CTL |= DCAN_CTL_ABO; //Enable Auto Bus On
        hcan->Instance->ABOTR = 80000; //set auto bus on timer
    
        while ((hcan->Instance->CTL & DCAN_CTL_INIT) != 0) {
        }
        if (hcan->Instance->BTR != btr) {
            return false;
        }
        if (loopback) {
            canEnableloopback (hcan->Instance, Internal_Lbk);
        } else {
            canDisableloopback (hcan->Instance);
        }
        LOG_INFO(CAN, "Set CAN%d speed to %d, loopback mode %s", hcan->number, new_speed, loopback ? "on" : "off");
        hcan->speed = new_speed;
        return true;
    }
    
    static bool get_msg_from_mailbox(canBASE_t *node, uint32 messageBox, can_message_t* msg, bool* last) {
        uint8_t i;
        uint32 regIndex = (messageBox - 1U) >> 5U;
        uint32 bitIndex = 1U << ((messageBox - 1U) & 0x1FU);
    
        if ((node->NWDATx [regIndex] & bitIndex) == 0U) {
            return false;
        }
        can_cmd_iface_t* ifx = CAN_GET_IF1(node);
        while ((ifx->IFxSTAT & IFxSTAT_BUSY) == IFxSTAT_BUSY) {
        };
        ifx->IFxCMD = IFxCMD_CONTROL | IFxCMD_TX_RQST_NEWDAT | IFxCMD_ARB | IFxCMD_DATA_A | IFxCMD_DATA_B;
        ifx->IFxNO = messageBox;
        while ((ifx->IFxSTAT & IFxSTAT_BUSY) == IFxSTAT_BUSY) {
        };
        msg->is_rtr = false;
        msg->dlc = ifx->IFxMCTL & IFxMCTL_DLC_MASK;
        if (msg->dlc > 8) {
            msg->dlc = 8;
        }
        for (i = 0U; i < msg->dlc; i++) {
    #if ((__little_endian__ == 1) || (__LITTLE_ENDIAN__ == 1))
            msg->data[i] = ifx->IFxDATx[i];
    #else
            msg->data [i] = ifx->IFxDATx [s_canByteOrder [i]];
    #endif
        }
        msg->is_extended = ((ifx->IFxARB & IFxARB_Xtd) == IFxARB_Xtd);
        if (msg->is_extended) {
            msg->id_ext = ifx->IFxARB & IFxARB_ID_MASK;
        } else {
            msg->id_std = (ifx->IFxARB & IFxARB_ID_MASK) >> 18;
        }
        *last = ((ifx->IFxMCTL & IFxMCTL_EoB) == IFxMCTL_EoB);
        return true;
    }
    
    //static void canStatusChangeCallback(can_device_t *hcan, can_es_register_t es) {
    //    LOG_INFO(CAN, "CAN%d canStatusChangeNotification pda=%d, TxOk=%d RXOk=%d WakeUpPnd=%d", hcan->number, es.PDA, es.TxOk, es.RxOk, es.WakeUpPnd);
    //}
    
    static void canErrorCallback(can_device_t* hcan, can_es_register_t es) {
        LOG_ERROR(
            CAN,
            "CAN%d Error Boff=%d PERR=%d EWarn=%d EPass=%d pda=%d, TxOk=%d RXOk=%d WakeUpPnd=%d lec=%s 0x%08lX",
            hcan->number,
            es.BOff,
            es.PER,
            es.EWarn,
            es.EPass,
            es.PDA,
            es.TxOk,
            es.RxOk,
            es.WakeUpPnd,
            lec2str ((can_lec_t )es.lec),
            es.value);
        hcan->error_count++;
    }
    
    void canMessageTxCallback(can_device_t* hcan, uint32 messageBox) {
        hcan->tx_count++;
        buffer_size_t size;
        const can_message_t* msg = (const can_message_t*) bip_buffer_get_contiguous_block (&hcan->tx_queue, &size);
        if (size >= sizeof(can_message_t)) {
            move_to_mailbox (hcan, messageBox, msg);
            bip_buffer_free (&hcan->tx_queue, sizeof(can_message_t));
            size -= sizeof(can_message_t);
        }
    //    while (size > 0 && (messageBox = get_free_tx_mailbox (hcan->Instance))) {
    //        msg++;
    //        move_to_mailbox (hcan, messageBox, msg);
    //        bip_buffer_free (&hcan->tx_queue, sizeof(can_message_t));
    //        size -= sizeof(can_message_t);
    //    }
    }
    
    void canMessageRxCallback(can_device_t* hcan, uint32 messageBox) {
        can_message_t msg;
        bool last;
        while (get_msg_from_mailbox (hcan->Instance, MAILBOX_COUNT_TX + 1, &msg, &last)) {
            msg.can_number = hcan->number;
            if (!bip_buffer_put (&can_rx_queue, &msg, sizeof(msg))) {
                hcan->rx_queue_full_count++;
            } else {
                hcan->rx_count++;
            }
            if (last) {
                break;
            }
        }
    }
    
    static inline void can_interrupt(can_device_t* hcan) {
        canBASE_t* canReg = hcan->Instance;
        uint32 value = canReg->INT;
        if (value == 0) {
            return;
        }
        if (value == 0x8000U) {
            can_es_register_t es;
            es.value = canReg->ES;
            if (es.BOff || es.EPass || es.EWarn || es.PER) {
                canErrorCallback (hcan, es);
            }
        } else {
            can_cmd_iface_t* ifx = CAN_GET_IF1(canReg);
            while ((ifx->IFxSTAT & IFxSTAT_BUSY) == IFxSTAT_BUSY) {
            };
            ifx->IFxCMD = IFxCMD_CLR_INT_PND;
            ifx->IFxNO = (uint8) value;
            while ((ifx->IFxSTAT & IFxSTAT_BUSY) == IFxSTAT_BUSY) {
            };
            if (value <= MAILBOX_COUNT_TX) {
                canMessageTxCallback (hcan, value);
            } else {
                canMessageRxCallback (hcan, value);
            }
        }
    }
    
    #ifdef CAN1_CTL_CONFIGVALUE
    #pragma CODE_STATE(can1HighLevelInterrupt, 32)
    #pragma INTERRUPT(can1HighLevelInterrupt, IRQ)
    static void can1HighLevelInterrupt(void) {
        can_interrupt (&can1);
    }
    #endif
    
    #ifdef CAN2_CTL_CONFIGVALUE
    #pragma CODE_STATE(can2HighLevelInterrupt, 32)
    #pragma INTERRUPT(can2HighLevelInterrupt, IRQ)
    static void can2HighLevelInterrupt(void) {
        can_interrupt (&can2);
    }
    #endif
    
    #ifdef CAN3_CTL_CONFIGVALUE
    #pragma CODE_STATE(can3HighLevelInterrupt, 32)
    #pragma INTERRUPT(can3HighLevelInterrupt, IRQ)
    static void can3HighLevelInterrupt(void) {
        can_interrupt (&can3);
    }
    #endif
    
    static void init_can_device(can_device_t* hcan) {
        uint8_t mailbox;
        can_cmd_iface_t* ifx = CAN_GET_IF1(hcan->Instance);
        for (mailbox = 1; mailbox <= MAILBOX_COUNT_TX; mailbox++) {
            while ((ifx->IFxSTAT & IFxSTAT_BUSY) == IFxSTAT_BUSY) {
            };
            ifx->IFxMSK = IFxMSK_MDir;
            ifx->IFxARB = IFxARB_MsgVal | IFxARB_Dir_Tx;
            ifx->IFxMCTL = IFxMCTL_UMask | IFxMCTL_EoB | IFxMCTL_TxIE | 8;
            ifx->IFxCMD = IFxCMD_WRITE | IFxCMD_MASK | IFxCMD_ARB | IFxCMD_CONTROL | IFxCMD_CLR_INT_PND;
            ifx->IFxNO = mailbox;
        }
        for (mailbox = MAILBOX_COUNT_TX + 1; mailbox <= MAILBOX_COUNT_TOTAL; mailbox++) {
            while ((ifx->IFxSTAT & IFxSTAT_BUSY) == IFxSTAT_BUSY) {
            };
            ifx->IFxCMD = IFxCMD_WRITE | IFxCMD_MASK | IFxCMD_ARB | IFxCMD_CLR_INT_PND/*???*/| IFxCMD_CONTROL;
            ifx->IFxMSK = IFxMSK_MDir;
            ifx->IFxARB = IFxARB_MsgVal;
            ifx->IFxMCTL = IFxMCTL_UMask | IFxMCTL_RxIE;
            if (mailbox == MAILBOX_COUNT_TOTAL) { //last buffer in FIFO
                ifx->IFxMCTL |= IFxMCTL_EoB;
            }
            ifx->IFxNO = mailbox;
        }
        canEnableStatusChangeNotification (hcan->Instance);
        canEnableErrorNotification (hcan->Instance);
    }
    
    void RX_CAN_Init(void) {
        canInit ();
        _enable_interrupt_ ();
    #ifdef CAN1_CTL_CONFIGVALUE
        init_can_device (&can1);
        can_set_speed (&can1, can1.speed, false);
        vimChannelMap (16, 16, can1HighLevelInterrupt);
        vimEnableInterrupt (16, SYS_IRQ);
    #endif
    #ifdef CAN2_CTL_CONFIGVALUE
        init_can_device (&can2);
        can_set_speed (&can2, can2.speed, false);
        vimChannelMap (35, 35, can2HighLevelInterrupt);
        vimEnableInterrupt (35, SYS_IRQ);
    #endif
    #ifdef CAN3_CTL_CONFIGVALUE
        init_can_device (&can3);
        can_set_speed (&can3, can3.speed, false);
        vimChannelMap (45, 45, can3HighLevelInterrupt);
        vimEnableInterrupt (45, SYS_IRQ);
    #endif
    }
    
    

  • The TMS570LS032 has 32 mailboxes on DCAN1 and 16 on DCAN1. The DCAN module is defined to handle more mailboxes than are implemented on this part so there are extra register bits that are not used on this part.

    If you want both mailbox2 and mailbox3 to send interrupt requests on the DCAN1INT line, then the DCAN INTMUX12 register should be set to 0x00000006 (0110b). (I think this is what you meant above, although in the post your numbers looked like hex instead of binary.)

    Yes the bit to mailbox mapping of the IF3UPD12 register is different from the INTMUX12 register. Here bit 0 is for mailbox1.
  • Thanks for posting data, its been helpful. I have a better understanding of how DCAN works. The thing that threw me off the most was the fact that when you set up and use the automatic update feature (via IF3UPDxx regs), when the Rx interrupt comes along, the message object number is no longer in the Interrupt reg. If I disable this feature, then I can see which message obj caused the interrupt. I further need to read from IF1 not IF3 in this mode.

    My ultimate goal is to see if I could use both acceptance filtering and DMA to store messages in different queues without CPU intervention, at least until the DMA is finished.