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.

TMS320F280049C: CAN peripheral hanging triggers strange behavior

Part Number: TMS320F280049C


Tool/software:

Hi,

Here is a strange behavior we observed and would like to get some help to understand what is happening and why it is happening.

We are working on setting up CAN peripheral on one of our products. We setup the CAN and ISR as below:

void setup_can(void)
{
    SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_CANA);

    // Initialize the CAN controller
    CAN_initModule(CANA_BASE);

    //
    // setup Auto Bus On and delay time (in clock cycles - default is 0h)
    //
    CAN_setAutoBusOnTime(CANA_BASE, 0UL);
    CAN_enableAutoBusOn(CANA_BASE);

    // Set up the CAN bus bit rate depending on CAN_SPEED_UC digital input; 0 = 250kBits/s / 1 = 500kBits/s
    if (0u == GpioDataRegs.GPADAT.bit.GPIO11)
    {
        CAN_setBitRate(CANA_BASE, DEVICE_SYSCLK_FREQ, 250000, 16);
    }
    else
    {
        CAN_setBitRate(CANA_BASE, DEVICE_SYSCLK_FREQ, 500000, 25);
    }

    // Enable interrupts on the CAN peripheral.
    CAN_enableInterrupt(CANA_BASE, CAN_INT_IE0 | CAN_INT_ERROR);

    CAN_enableRetry(CANA_BASE);

    // Start CAN module operations
    CAN_startModule(CANA_BASE);
}

__interrupt void can_isr(void)
{
#define CAN_ERROR_COUNT                 25

    uint32_t status_ui32, status2_ui32;
    uint16_t can_msg_frame_type[1];

    //Allow ADC1/SCIB_TX/Timer0/Timer1/SCIB_Rx to pre-empt ISR
    IER |= (M_INT1|M_INT9|M_INT13);
    IER &= (M_INT1|M_INT9|M_INT13);

    // Enable PIE interrupt group 1 - Interrupt 1 (ADCA1)
    // Enable PIE interrupt group 1 - Interrupt 3 (ADCC1)
    // Enable PIE interrupt group 1 - Interrupt 7 (TIMER0)
    // Enable PIE interrupt group 9 - Interrupt 4 (SCIB_TX)
    // Enable PIE interrupt group 9 - Interrupt 3 (SCIB_RX)
    // Enable PIE interrupt group 9 - Interrupt 4 (SCIB_TX)
    PieCtrlRegs.PIEIER1.bit.INTx1 = 1;
    PieCtrlRegs.PIEIER1.bit.INTx3 = 1;
    PieCtrlRegs.PIEIER1.bit.INTx7 = 1;
    PieCtrlRegs.PIEIER9.bit.INTx3 = 1;
    PieCtrlRegs.PIEIER9.bit.INTx4 = 1;

    // Clear ACK to allow group 1 INT
    // Clear ACK to allow group 9 INT
    PieCtrlRegs.PIEACK.bit.ACK1 = 1;
    PieCtrlRegs.PIEACK.bit.ACK9 = 1;

    // Wait one cycle as per errata
    asm("       NOP");
    EINT;

    // Read the CAN interrupt status to find the cause of the interrupt
    status_ui32 = CAN_getInterruptCause(can_base_ui16);

    // If the cause is a controller status interrupt, then get the status

    if (status_ui32 < RX_MSG_OBJ_ID_END)
    {
        can_msg_count_ui32++;

        // TX interrupt occurred on message object and the message TX is complete
        if(status_ui32 <= TX_MSG_OBJ_ID_END)
        {
            //Clear the message object interrupt
            CAN_clearInterruptStatus(can_base_ui16, status_ui32);
        }
        // RX interrupt occurred on message object and the message RX is complete
        else
        {
            // Get the received message
            CAN_readMessageWithID(can_base_ui16, status_ui32, can_msg_frame_type, (uint32_t *)can_rx_msg_identifer29_ui32, can_rx_msg_data_buffer_ui16);

            // CAN bus message receive data parser
            can_rx_msg_parser(status_ui32);

            //Clear the message object interrupt
            CAN_clearInterruptStatus(can_base_ui16, status_ui32);
        }
    }
    else
    {
        // Spurious interrupt handling can go here.
    }

    // Clear the global interrupt flag for the CAN interrupt line
    CAN_clearGlobalInterruptStatus(can_base_ui16, CAN_GLOBAL_INT_CANINT0);

    // Acknowledge the PIE interrupt
    PieCtrlRegs.PIEACK.bit.ACK9 = 1;

    DINT;
}

What we observed is during startup of the customer system due to initial noise all the nodes on the CAN bus except our node resulting in no ack to all our tx messages. The expectation is that node enters passive error state until system node acks the messages, but what we noticed is that the micro gets reset due to watchdog reset, we believe that this is cause of CAN trying to resend while we are trying to send more messages as per tx scheduler. Is this what is expected?

Also this behavior resulted in cross conduction of our complementary PWM signals resulting in unit failure.

 I have captured some plots here:

  

Green signal = Testpin set at the start of application

Red and Blue signals = Complementary PWM signals

You can clearly see the cross conduction of PWM and unknown toggling of the testpin just before the watchdog reset. 

Our question is why did the PWM and testpin get affected due to the CAN hanging?

We were able to solve the reset issue by reading error and status register in the ISR using CAN_getStatus(can_base_ui16); this is just for your information but we still want to know why the strange behavior was observed and will it be of any concern with other type of errors.

Regards,

Harsha

  • Hi Harsha,

    Channel 3 and 4 don't look like complementary PWM. What is the signal for channel 1? Which channel is the reset signal? Where and when do you serve the watchdog?

  • Hi QJ Wang,

    The previous 2 waveforms indicates the cross conduction thats why it does not look like complementary, here is how is should look like normally;

    In this snap shot channel 1 and 2 are the complementary PWM. This is how it has been setup:

    void setup_pwm_llc_bridge(void)
    {
        SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_EPWM1);
        SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_EPWM2);
    
        SysCtl_disablePeripheral(SYSCTL_PERIPH_CLK_TBCLKSYNC);
    
        //Generates the sync out pulse from EPWM1
        EPWM_setSyncOutPulseMode(EPWM1_BASE, EPWM_SYNC_OUT_PULSE_ON_COUNTER_ZERO);
    
        // Set-up TBCLK
        // Set up period of 25Khz, 4000 x 10ns
        // Phase shift 0
        // Reset Counter
        // Counter count up
        // Dont load phase shift from shadow
        EPWM_setTimeBasePeriod(EPWM1_BASE, PWM1_PERIOD_INITIAL);
        EPWM_setPhaseShift(EPWM1_BASE, PWM1_PHASESHIFT_INITIAL);
        EPWM_setTimeBaseCounter(EPWM1_BASE, 0U);
        EPWM_setTimeBaseCounterMode(EPWM1_BASE, EPWM_COUNTER_MODE_UP);
        EPWM_disablePhaseShiftLoad(EPWM1_BASE);
    
        // Set up start-of-conversion event on ePWM1A using timer event C
        EPWM_disableADCTrigger(EPWM1_BASE, EPWM_SOC_A);
        EPWM_setADCTriggerSource(EPWM1_BASE, EPWM_SOC_A, EPWM_SOC_TBCTR_U_CMPC);
        EPWM_setADCTriggerEventPrescale(EPWM1_BASE, EPWM_SOC_A, 1);
    
        // Set ePWM clock pre-scaler PWM CLK = SYS CLK
        EPWM_setClockPrescaler(EPWM1_BASE, EPWM_CLOCK_DIVIDER_1, EPWM_HSCLOCK_DIVIDER_1);
    
        // Set up shadowing, compare A value will be loaded on start of period
        EPWM_setCounterCompareShadowLoadMode(EPWM1_BASE, EPWM_COUNTER_COMPARE_A, EPWM_COMP_LOAD_ON_CNTR_ZERO);
        EPWM_setCounterCompareShadowLoadMode(EPWM1_BASE, EPWM_COUNTER_COMPARE_C, EPWM_COMP_LOAD_ON_CNTR_ZERO);
    
        // Set-up compare value
        EPWM_setCounterCompareValue(EPWM1_BASE, EPWM_COUNTER_COMPARE_A, PWM1_DUTY_INITIAL);
        // Set - up compare value for trigger of SOC at 50 duty
        EPWM_setCounterCompareValue(EPWM1_BASE, EPWM_COUNTER_COMPARE_C, (PWM1_DUTY_INITIAL >> 1));
    
    //    EPwm1Regs.ETPS.bit.INTPRD = 1;
    //    EPwm1Regs.ETPS.bit.SOCAPRD = 1;
    
        // Set actions, PWM_A high on 0 and low when compare match
        EPWM_setActionQualifierAction(EPWM1_BASE, EPWM_AQ_OUTPUT_A,EPWM_AQ_OUTPUT_LOW, EPWM_AQ_OUTPUT_ON_TIMEBASE_UP_CMPA);
        EPWM_setActionQualifierAction(EPWM1_BASE, EPWM_AQ_OUTPUT_A, EPWM_AQ_OUTPUT_HIGH, EPWM_AQ_OUTPUT_ON_TIMEBASE_ZERO);
    
    //    EPWM_setADCTriggerSource(EPWM1_BASE, EPWM_SOC_A, EPWM_SOC_TBCTR_U_CMPC);
        EPWM_enableADCTrigger(EPWM1_BASE, EPWM_SOC_A);
    
        // Deadband 10 x 10ns = 100ns
        EPWM_setRisingEdgeDelayCount(EPWM1_BASE,PWM1_DEADTIME_INITIAL);
        EPWM_setFallingEdgeDelayCount(EPWM1_BASE,PWM1_DEADTIME_INITIAL);
        // Deadband applied on rising and falling edge for the complimentary signals
        EPWM_setDeadBandDelayMode(EPWM1_BASE, EPWM_DB_RED, true);
        EPWM_setDeadBandDelayMode(EPWM1_BASE, EPWM_DB_FED, true);
    
        EPWM_setDeadBandDelayPolarity(EPWM1_BASE, EPWM_DB_RED, EPWM_DB_POLARITY_ACTIVE_HIGH);
        // EPWM_B generated as complimentary using Deadband module
        EPWM_setDeadBandDelayPolarity(EPWM1_BASE, EPWM_DB_FED, EPWM_DB_POLARITY_ACTIVE_LOW);
    
        //Enable PWM on GPIO
        GPIO_setPadConfig(0, GPIO_PIN_TYPE_STD);
        GPIO_setPinConfig(GPIO_0_EPWM1A);
    
        GPIO_setPadConfig(1, GPIO_PIN_TYPE_STD);
        GPIO_setPinConfig(GPIO_1_EPWM1B);

    When the watchdog resets the control goes to bootloader, I have a test pin toggling in the bootloader which is indicated by channel 1 of the waveforms I shared in the previous message. So in the waveforms I shared earlier when channel 1 is toggling this means that the control is in bootloader and when channel 1 stops toggling and channel 2 is set high this means that its in the application with the PWM. So this is how I tracked when and where the watchdog is triggering a reset. I tried setting up the watchdog in interrupt mode and then call SysCtl_resetDevice(); to reset but for some reason the reset function did not work.

    Also each test pins are only set in respective mode i.e. the bootloader test pin is not set in application and the application test pin is not set in the bootloader.

    Let me know if you need other information.

  • Hi,

    I noticed that the interrupts are enabled in CAN ISR: Why do you enable those interrupts (SCI, Timer, ADC) in CAN ISR?

    ==============================================

    //Allow ADC1/SCIB_TX/Timer0/Timer1/SCIB_Rx to pre-empt ISR
    IER |= (M_INT1|M_INT9|M_INT13);
    IER &= (M_INT1|M_INT9|M_INT13);

    // Enable PIE interrupt group 1 - Interrupt 1 (ADCA1)
    // Enable PIE interrupt group 1 - Interrupt 3 (ADCC1)
    // Enable PIE interrupt group 1 - Interrupt 7 (TIMER0)
    // Enable PIE interrupt group 9 - Interrupt 4 (SCIB_TX)
    // Enable PIE interrupt group 9 - Interrupt 3 (SCIB_RX)
    // Enable PIE interrupt group 9 - Interrupt 4 (SCIB_TX)
    PieCtrlRegs.PIEIER1.bit.INTx1 = 1;
    PieCtrlRegs.PIEIER1.bit.INTx3 = 1;
    PieCtrlRegs.PIEIER1.bit.INTx7 = 1;
    PieCtrlRegs.PIEIER9.bit.INTx3 = 1;
    PieCtrlRegs.PIEIER9.bit.INTx4 = 1;

    // Clear ACK to allow group 1 INT
    // Clear ACK to allow group 9 INT
    PieCtrlRegs.PIEACK.bit.ACK1 = 1;
    PieCtrlRegs.PIEACK.bit.ACK9 = 1;

    // Wait one cycle as per errata
    asm(" NOP");
    EINT;

    ============================================

    The CAN transmission should not affect the ePWM output. 

  • Hi,

    The reason we have this piece of code is to allow for interrupt preemption, basically CAN interrupt being low priority. We tried to find the root cause of why the ePWM behaved this way (if you noticed the waveform even the test pins toggled when they should not have), the only way we could replicate this failure is by forcing an ack error on CAN, that is why we believe that the CAN peripheral's misbehavior resulted in ePWM output misbehaving.

    Regards,

    Harsha 

  • Hi Harsha,

    Can you check if you will get the same disrupted ePWM signal waveforms if you disable the nested interrupts?