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.

TMS320F28335: Reinitializing of eCAN hangs.

Part Number: TMS320F28335

We have a problem with re-initializing eCAN where we are waiting for the CCE bit to set after issuing a CCR request.  It hangs in an infinite loop.  We configure CAN for 125Kbaud and attempt to communicate with an old device.  If there is no response, we reconfigure for 500Kbaud and attempt to communicate with a newer device.  This configuration has worked for many years but is now failing.  The first initialization after powerup always succeeds, the re-initialization now fails.  The cause appears to be due to a race condition that with recent code changes, we are now failing.

The remote device is powered by the local device, so both power up at the same time.  The remote device performs its power up initialization and then starts trying to communicate with the local device sending packets every 1 millisecond.  Up to now, the local device was able to complete its check for the old style device and configure itself for the new device before the remote device started communicating.  But now, it appears that the local device is trying to re-initialize while the remote device is communicating and the CCE bit never sets.

The code itself is very old.  It was ported to the F28335 from an F2812 system.  It was originally developed with Code Composer 3 and uses DSP/BIOS.  Here is the initialization function:

void CAN_init(int kBaud, CAN_pIsr pISR)
{
	int mbx;
	Uns old;
	unsigned long	baud = kBaud;
//	unsigned long	baud = kBaud*2000UL;
	unsigned long	brp;
	unsigned int	bt = 15;
	unsigned long	sam=0;
	unsigned long	sjw=0;
	unsigned int	SpPercent = 80;
	unsigned long	tseg1;
	unsigned long	tseg2;

	Overwrite = ~0;
	brp = ((SYSCLKOUT * 500UL)/(baud*bt))-1;
//	brp = ((SYSCLKOUT * 1000000UL)/(baud*bt))-1;
	bt--;
	tseg1 = ((SpPercent*bt)/100);
	tseg2 = bt-tseg1;
	tseg1--;
	tseg2--;

	ISR = pISR;


	for (mbx = 0; mbx < NUM_MAILBOXES; mbx++){
		mbxISR[mbx] = NULL;
	}

	asm(" EALLOW");
	old = DisableInterrupts();
	CANMC |= SRES; // master reset
	if (kBaud && brp>=1 && brp<=255 && sjw<=3 && sam<=1 && tseg1<=15 && tseg2<=7 && tseg2<=tseg1){
		PCLKCR |= ECANENCLK;
		GpioCtrlRegs.GPAPUD.bit.GPIO18 = 0;	    // Enable pull-up for GPIO18 (CANRXA)
		GpioCtrlRegs.GPAPUD.bit.GPIO19 = 0;	    // Enable pull-up for GPIO19 (CANTXA)
		GpioCtrlRegs.GPAQSEL2.bit.GPIO18 = 3;   // Asynch qual for GPIO18 (CANRXA)
		GpioCtrlRegs.GPAMUX2.bit.GPIO18 = 3;	// Configure GPIO18 for CANRXA operation
		GpioCtrlRegs.GPAMUX2.bit.GPIO19 = 3;	// Configure GPIO19 for CANTXA operation
		RestoreInterrupts(old);

		CANTIOC |= TXFUNC;
		CANRIOC |= RXFUNC;
		CANMC |= CCR | SCB | ABO | DBO; 
		asm(" EDIS");

		while(!(CANES & CCE))
			Sleep(1);

		asm(" EALLOW");
		CANBTC = (brp<<16)+(sjw<<8)+(sam<<7)+(tseg1<<3)+tseg2;
		CANMC &= ~CCR;
		CANMIL = 0xffffffff;	// Set all mailbox interrupts to line 1, CAN_isr1()
//		CANGIM = 0x00007f03;	// Set which conditions will generate an interrupt
		CANGIM = 0x00000803;	// Set which conditions will generate an interrupt
		asm(" EDIS");

		while((CANES & CCE))
			Sleep(1);

		old = HWI_disable();
		PIEACK = M_INT9;		// Acknowledge INT9 interrupt to PIE
		PIEIER9 |= INTx5;		// Enable Peripheral Interrupt 9_5
		PIEIER9 |= INTx6;		// Enable Peripheral Interrupt 9_5
		IER |= M_INT9;			// Enable CPU Interrupt 9
		HWI_restore(old);
	}
	else{
		PCLKCR &= ~ECANENCLK;
		RestoreInterrupts(old);
	}
}

I found a bug in this code where the else clause doesn't issue an EDIS under a "disable" condition (zero baud), but this does not impact the issue.  The DisableInterrupts/RestoreInterrupts functions disable all but the highest priority interrupt.  I can try to rearrange our code to win the race again, but I would like to come up with a concrete solution that doesn't rely on luck.  Any suggestions?

  • I am unable to determine if you are using 32-bit reads/writes looking at your code. This is extremely important. For this and other debug tips, please download my Application report http://www.ti.com/lit/SPRA876. Most CAN issues can be resolved by going through this checklist. 

    Once you go through the cycle of setting CCR=0 (and ensuring CCE=0), from that point on, to be able to see CCE bit becoming a 1 (after setting CCR bit), the CANRX pin must be sensed high, since CANRX pin being sensed high equates to bus being idle. Eventually, however, CANRX pin *will* go high due to the bit-stuffing requirements, so not sure why the infinite loop occurs. One idea to try to is to use a timer and perform a soft reset of the module if this happens.

  • All access to the registers are 32 bit.  On line 33, we do a soft reset.  On line 45, we set the CCR bit along with some other bits. So you are suggesting to try a soft reset and then setting CCR bit periodically while waiting for the CCE bit?

    One thing that is not clear from the documentation does the CCR bit need to be set AFTER the goes idle?  If so, how do I detect the idle condition?

  • The first initialization after powerup always succeeds,

    At power-up, CCR bit is already 1 (i.e. the reset value of this bit is 1). So, I am not surprised you don't have issues at power-up. 

    Eventually, however, CANRX pin *will* go high due to the bit-stuffing requirements

    Thinking a bit more about this, it is the bus-idle condition that module cares about, not simply CANRX pin "going high" (CANRX pin will be high during bus-idle). IOW, changes to bit-timing are allowed only during bus-idle. More importantly, the node is allowed to join the bus only during a bus-idle. 

    So you are suggesting to try a soft reset and then setting CCR bit periodically while waiting for the CCE bit?

    Yes, although it is unclear at this point in time why you are unable to set the CCE bit., especially since you mention the code has been working for many years.

    One thing that is not clear from the documentation does the CCR bit need to be set AFTER the goes idle?

    No. You should be able to set the CCR bit asynchronous to bus activity. However, the node will wait until the bus goes idle, to set CCE and allow you to change the bit-timings. Likewise, the node will wait until the bus goes idle to clear CCE and allow the node to rejoin the bus.

    If so, how do I detect the idle condition?

    I don't think there is a need to, based on the above. I have not tried this but you may be able to poll the RM/TM bit in the CANES register.

  • With an oscilloscope, I found that on powerup the remote device is silent for about 2 seconds and then starts chattering trying to start communications.  It doesn't allow an idle condition once it starts communicating.  This explains why the CCE bit is never set.  Since I can't change the remote device, I will create some type of baud rate detection so CAN only gets initialized once.  Thank you for your input.