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.

MSPM0G3507: MCAN wakeup interrupt not being generated

Part Number: MSPM0G3507

Tool/software:

I'm testing a CAN bus node implemented via a TCAN1044AV-Q1 transceiver attached to the MCAN peripheral of a MSPM0G3507 MCU.  The node is intended to operate as a receiver only.  When configured in active mode, CAN packets are properly received.  It is desired that during periods of inactivity:

  • the MCAN peripheral be placed in power-down mode and the transceiver in standby mode by MCU firmware
  • CAN Rx activity will automatically wake up the MCAN peripheral, and the corresponding wakeup interrupt will cause MCU firmware to put MCAN back into active mode and the transceiver into normal mode

Accordingly, MCU firmware was developed to implement this behavior per Section 20.4.10 of the MSPM0 G-Series Technical Reference Manual.  I have verified that when in standby mode, the transceiver drives its RXD line low upon reception of a CAN packet.  However, the MCU's MCAN (in power-down mode) is not raising a wakeup interrupt in this scenario; i.e., an XDS110 breakpoint set at the start of the MCAN interrupt handler is not being hit.

Relevant code snippets:

bool can_wakeup_event = false;

int main(void)
{
    SYSCFG_DL_init();
    
    DL_MCAN_enableClockStopWakeupInterrupt(MCAN0_INST);

    NVIC_EnableIRQ(MCAN0_INST_INT_IRQN);

    while (1) {

        __WFI();
    
        if (system_in_low_power) {
            if (can_wakeup_event) {
                can_wakeup_event = false;
                low_power_exit();
                system_in_low_power = false;
            }
        }
        else if (inactivity_timer_expired) {
            inactivity_timer_expired = false;
            low_power_enter();
            system_in_low_power = true;
        }
    }
}

void low_power_enter()
{
    /* Put CAN transceiver in standby */
    DL_GPIO_setPins(CAN_STB_PMIC_PORT, CAN_STB_PMIC_PIN);

    /* Ensure that CAN bus is idle, then put MCAN module into power down mode */
    DL_MCAN_ProtocolStatus protStatus;
    do
        DL_MCAN_getProtocolStatus(MCAN0_INST, &protStatus);
    while (protStatus.act != DL_MCAN_COM_STATE_IDLE);
    DL_MCAN_addClockStopRequest(MCAN0_INST, true);
    while (!DL_MCAN_getClockStopAck(MCAN0_INST) && !can_wakeup_event) ;
    if (DL_MCAN_getClockStopAck(MCAN0_INST))
        DL_MCAN_disableModuleClock(MCAN0_INST);
}

void low_power_exit()
{
    /* Take CAN transceiver out of standby */
    DL_GPIO_clearPins(CAN_STB_PMIC_PORT, CAN_STB_PMIC_PIN);

    /* Take MCAN module out of power down mode if applicable */
    if (!DL_MCAN_isModuleClockEnabled(MCAN0_INST)) {
        DL_MCAN_enableModuleClock(MCAN0_INST);
        DL_MCAN_addClockStopRequest(MCAN0_INST, false);
        while (DL_MCAN_getClockStopAck(MCAN0_INST)) ;
        DL_MCAN_setOpMode(MCAN0_INST, DL_MCAN_OPERATION_MODE_NORMAL);
    }
}

void MCAN0_INST_IRQHandler(void)
{
    switch (DL_MCAN_getPendingInterrupt(MCAN0_INST)) {
        case DL_MCAN_IIDX_WAKEUP:
            can_wakeup_event = true;
            break;
        case DL_MCAN_IIDX_LINE1:
            gInterruptLine1Status |= DL_MCAN_getIntrStatus(MCAN0_INST);
            DL_MCAN_clearIntrStatus(MCAN0_INST, gInterruptLine1Status,
                DL_MCAN_INTR_SRC_MCAN_LINE_1);
            break;
        default:
            break;
    }
}

Relevant MCU register values (while in active/normal mode):

  • CANFD0_MCANSS_CTRL  0x00000038
  • CANFD0_MCANSS_CLKCTL  0x00000010
  • CANFD0_IMASK  0x00000022

Note that the MCU's CANCLK is provided via 40MHz crystal (HFXT).

I was interested in using the MCAN_TEST register to read the actual value of the CAN receive pin when in power-down mode and CAN packets are received, but register access is not possible when MCAN clocks are gated.  Consequently, I modified the MCU firmware to not disable the MCAN clocks when entering power-down mode.  Then I caused the CAN node to enter power-down/standby mode and sent it a barrage of CAN packets.  I used the XDS110 debugger on the MCU to manually suspend and resume firmware execution, and observed that the RX bit in MCAN_TEST was toggling between 1 and 0.  So MCAN was seeing "dominants" but was not raising a wakeup interrupt.

Any ideas?  Am I missing something?

Thanks, Dave L.

  • Hi, 

    In the code of [SYSCFG_DL_init();], which level Low power mode do you entered?

    STANDBY0 or STOP0?

    I saw you call __WFI(); at line 13.

    Need to confirm this with Datasheet: Table 8-1. Supported Functionality by Operating Mode

    https://www.ti.com/lit/ds/slasex6b/slasex6b.pdf

    Because, in the STOPx and STANDBYx, MCAN module is power off and no configuration is retained.

    Also, [WAKEUP_INT_EN] is not working in STOPx or STANDBYx.

    Regards,

    Helic

  • Hi Helic,

    Thanks for your response.  I actually had been using STOP0 as the low power policy, so I was optimistic that this might be the problem.  However, I changed the low power policy to SLEEP0 and am still not seeing MCAN wakeup interrupts.  I verified that SLEEP0 was indeed being configured by verifying the value of the Cortex-M0+ System Control Register (SLEEPDEEP=0).  Do you see anything else that might be problematic?

    Regards, Dave L.

  • Hi, 

    Default is sleep0 when call __WFI();

    Or you can use DL API below to switch to SLEEP0:

    DL_SYSCTL_setPowerPolicyRUN0SLEEP0();

    In STOP or STANDBY mode, we usually re-config CAN-RX to GPIO and use a GPIO external interrupt to wake-up MCU and re-configure CAN peripherals to enable CAN communication.

    Do you see anything else that might be problematic?

    I never test this feature before.

    1. Make sure CAN interrupt is enable both in NVIC and in CAN peripherals.

    2. From TRM: 20.6 Interrupt and Event Support, this interrupt need to be enabled in syscfg or from your code.

    You can refer to TRM MCAN's 20.5 MCAN Integration for a more detailed MCAN interrupt routine.

    And TRM 20.4.10 Clock Stop Mode is a detailed description of CAN Clock Stop Handling.

    Regards,

    Helic

  • Hi Helic,

    I definitely would like to use STOP0 as the low-power policy (in order to consume as little power as possible in low-power mode), so re-configuring the MCAN RX pin as a GPIO will be necessary.

    According to TRM Section 8.2.1, the first step in re-assigning a peripheral function on a digital IO is to disable the currently connected peripheral function.  How should that be done for MCAN?  Driverlib provides a DL_MCAN_init() function, but no corresponding deinit function.  Should I perhaps use DL_MCAN_reset() or DL_MCAN_disablePower()?

    Regards, Dave L.

  • Hi, 

    You can just init the digital input function.

    For the detailed, please try to add a input interrupt GPIO in syscfg, and you can refer the code.

    And another important thing, when wake up from STOP, you need re-config MCAN.

    The first thing you need to do is Reset CAN and Enable CAN power.

    Please add a delay after Enable CAN power.

    Make sure MCAN has enough time to setup the power.

    have a 1600 CPU cycles delay is better.

    Regards,

    Helic

  • Hi Helic,

    I apologize for the delayed response; I was distracted by other work tasks during the past couple of weeks.

    I followed your suggestions and was successful in modifying my firmware to use a GPIO interrupt rather than a MCAN clock stop wakeup interrupt.  Now,  reception of a CAN packet while the MCU is in low-power mode (i.e., STOP0) will properly be detected and cause low-power mode to be exited.  For completeness, I've included the modified code snippets below:

    void low_power_enter()
    {
        /* Put CAN transceiver in standby */
        DL_GPIO_setPins(CAN_STB_PMIC_PORT, CAN_STB_PMIC_PIN);
        
        /* MCAN peripheral is disabled in STOP0, but CAN wakeup is required */
        /* Disable CAN and unselect function on CAN_RX pin */
        DL_MCAN_disablePower(MCAN0_INST);
        IOMUX->SECCFG.PINCM[GPIO_MCAN0_IOMUX_CAN_RX] = 0;
    
        /* Reconfigure CAN_RX pin as GPIO input and enable corresponding falling-edge interrupt */
        DL_GPIO_initDigitalInput(GPIO_MCAN0_IOMUX_CAN_RX);
        DL_GPIO_enableFastWakePins(GPIO_MCAN0_CAN_RX_PORT, GPIO_MCAN0_CAN_RX_PIN);
        DL_GPIO_setLowerPinsPolarity(GPIO_MCAN0_CAN_RX_PORT, DL_GPIO_PIN_13_EDGE_FALL);
        DL_GPIO_clearInterruptStatus(GPIO_MCAN0_CAN_RX_PORT, GPIO_MCAN0_CAN_RX_PIN);
        DL_GPIO_enableInterrupt(GPIO_MCAN0_CAN_RX_PORT, GPIO_MCAN0_CAN_RX_PIN);
    }
    
    void low_power_exit()
    {
        /* Take CAN transceiver out of standby */
        DL_GPIO_clearPins(CAN_STB_PMIC_PORT, CAN_STB_PMIC_PIN);
        
        /* Disable GPIO interrupt and unselect GPIO function on CAN_RX pin */
        DL_GPIO_disableInterrupt(GPIO_MCAN0_CAN_RX_PORT, GPIO_MCAN0_CAN_RX_PIN);
        DL_GPIO_setLowerPinsPolarity(GPIO_MCAN0_CAN_RX_PORT, DL_GPIO_PIN_13_EDGE_DISABLE);
        DL_GPIO_disableFastWakePins(GPIO_MCAN0_CAN_RX_PORT, GPIO_MCAN0_CAN_RX_PIN);
        IOMUX->SECCFG.PINCM[GPIO_MCAN0_IOMUX_CAN_RX] = 0;
    
        /* Reinitialize MCAN peripheral and reconfigure CAN_RX pin */
        DL_MCAN_reset(MCAN0_INST);
        DL_MCAN_enablePower(MCAN0_INST);
        delay_cycles(POWER_STARTUP_DELAY*100);
        DL_GPIO_initPeripheralInputFunction(GPIO_MCAN0_IOMUX_CAN_RX, GPIO_MCAN0_IOMUX_CAN_RX_FUNC);
        SYSCFG_DL_MCAN0_init();
    }
    
    void GROUP1_IRQHandler(void)
    {
        switch (DL_Interrupt_getPendingGroup(DL_INTERRUPT_GROUP_1)) {
            case DL_INTERRUPT_GROUP1_IIDX_GPIOA:
                /* CAN_RX pin reconfigured as GPIO input */
                can_wakeup_event = true;
            default:
                break;
        }
    }
    

    Many thanks for sharing your expertise!

    Regards, Dave L.