mBed CAN Channel 1 Error Frames on Reading

21 Jun 2010 . Edited: 21 Jun 2010

 

The following code is meant to log all CAN traffic on two separate vehicle CAN networks on the same vehicle. CAN1=125000k, CAN2=500000k. The logging part seems to work well. However, during testing this logger in my vehicle I noticed some odd behaviors of the vehicle when the mBed was connected. Every now and again the blinkers would skip a few flashes and the radio display would reset. I hooked up an independent CAN monitoring tool (Vector CANAlyzer) and noticed the following:

1) Maximum bus load was ~30%. This is good.

2) Error frames on the CAN1=125000k bus. This is not good. I think the error frames are preventing some of the network messages from getting through.

3) No error frames on CAN2=500000k

 

To isolate where the error frames are coming from I did the following:

1) Unplugged my logging tool from the OBDII connector. Error Frames went away.

2) Reconnected the logging tool to the OBDII connector and then unplugged the mBed from the MCP2551 CAN Transceivers. This means that the MCP2551 circuits were still connected the CAN bus. The problem went away. This seems to indicate that the issue is not with the MCP2551 CAN Hardware but with the mBed itself.

 

My assumption is that the mBed is passively reading the CAN buses. If this is the case I can’t see how the mBed could be generating error frames. Perhaps my assumptions are incorrect and the mBed is not passively reading the CAN Bus. Perhaps the mBed is ACK/NACK the messages incorrectly?

 

Either I’m missing something or there is a bug in the mBed CAN library? Anyone have any ideas? Is there any more data I could collect to try and isolate this issue?

Thanks.

 

<<<< CODE >>>>>

 

// Full Bus CAN Monitor and Logger Tool
// 
// Two CAN Buses are monitored and logged to PC via USB port.
// CAN messages are time-stamped and sorted before sending them out the serial port.
// Freddie Leaf
// June 20, 2010
// Version 1.0

#include "mbed.h"
#include "CAN.h"

#define TRUE 1
#define FALSE 0

DigitalOut led1(LED1);
DigitalOut led2(LED2);
DigitalOut led3(LED3);
DigitalOut led4(LED4);

// Setup CAN1 Controller, MSCAN 125K
CAN can1(p9, p10);

// Setup CAN2 Controller, HSCAN 500K
CAN can2(p30, p29);

// Setup USB Serial Port
Serial pc(USBTX, USBRX); // tx, rx

int main() 
{
    char MessageFoundCAN1;
    char MessageFoundCAN2;
    
    int TimeStampCAN1;
    int TimeStampCAN2;
    
    
    CANMessage CAN1_MsgRx;  // Message reception on CAN1
    CANMessage CAN2_MsgRx;  // Message reception on CAN2
    
    Timer FreeRunningTimer;  //Free running timer for CAN message stamp

    // USB Serail Port baud rate
    pc.baud(230400);
    
    // CAN1, MSCAN, 125kbit/s
    can1.frequency(125000);

    // CAN2, HSCAN, 500kbit/s
    can2.frequency(500000);

    
    /****** Program Starts Here *******/
    
    pc.printf("Connected to mBed...\r\n");
    
    // This is a free running timer that is used for the CAN message time stamp 
    FreeRunningTimer.start();

    // For completeness, initialize these Time Stamps to 0
    TimeStampCAN1 = 0;
    TimeStampCAN2 = 0;

    while (1)
    {     
        MessageFoundCAN1 = FALSE;  // Do this every loop so that we dont log old data
        MessageFoundCAN2 = FALSE;  // Do this every loop so that we dont log old data
         
        if ( can1.read(CAN1_MsgRx) )  // See if any CAN messages are in CAN1 Buffer- MSCAN 
        {
            TimeStampCAN1 = FreeRunningTimer.read_us();
            MessageFoundCAN1 = TRUE;
            led1 = !led1; // Toggle LED1
        }
                
        if ( can2.read(CAN2_MsgRx) )  // See if any CAN messages are in CAN2 Buffer- HSCAN
        {
            TimeStampCAN2 = FreeRunningTimer.read_us();
            MessageFoundCAN2 = TRUE;
            led2 = !led2; // Toggle LED2
        }        
        
        if( MessageFoundCAN1 || MessageFoundCAN2 )  // Message were found on CAN1 or CAN2 so print them.
        {        
            if( TimeStampCAN1 < TimeStampCAN2 ) // CAN1 Message is earlier message
            {     
                if ( MessageFoundCAN1 == TRUE )  // only print if message was found
                {
                    // Print CAN1 Message First
                    pc.printf("%d %04X 1 ", TimeStampCAN1, CAN1_MsgRx.id);
                    for (char i=0; i<CAN1_MsgRx.len; i++) 
                    {
                        pc.printf("%02X ", CAN1_MsgRx.data[i]);
                    }     
                    
                    pc.printf ("%d", FreeRunningTimer.read_us() - TimeStampCAN1 );  // debug code
                    
                    printf("\r\n");    
                    
                    led4 = !led4; // Toggle LED4 to show that somehting was sent to the PC via USB  
                }
                
                if ( MessageFoundCAN2 == TRUE )  // only print if message was found
                {
                    // Print CAN2 Message Second
                    pc.printf("%d %04X 2 ", TimeStampCAN2, CAN2_MsgRx.id);
                    for (char i=0; i<CAN2_MsgRx.len; i++) 
                    {
                        pc.printf("%02X ", CAN2_MsgRx.data[i]);
                    }
                    
                    pc.printf ("%d", FreeRunningTimer.read_us() - TimeStampCAN2 );  // debug code
                                        
                    printf("\r\n");                 
                    
                    led4 = !led4; // Toggle LED4 to show that somehting was sent to the PC via USB
                }
            }
            else  // CAN2 Message is earlier message or both messages have the same time stamp
            { 
                if ( MessageFoundCAN2 == TRUE )  // only print if message was found
                {            
                    // Print CAN2 Message First
                    pc.printf("%d %04X 2 ", TimeStampCAN2, CAN2_MsgRx.id);
                    for (char i=0; i<CAN2_MsgRx.len; i++) 
                    {
                        pc.printf("%02X ", CAN2_MsgRx.data[i]);
                    }     

                    pc.printf ("%d", FreeRunningTimer.read_us() - TimeStampCAN2 );  // debug code
                                        
                    printf("\r\n");
                    
                    led4 = !led4; // Toggle LED4 to show that somehting was sent to the PC via USB
                }
                
                if ( MessageFoundCAN1 == TRUE )  // only print if message was found
                {                
                    // Print CAN1 Message Second
                    pc.printf("%d %04X 1 ", TimeStampCAN1, CAN1_MsgRx.id);
                    for (char i=0; i<CAN1_MsgRx.len; i++) 
                    {
                        pc.printf("%02X ", CAN1_MsgRx.data[i]);
                    }
                    
                    pc.printf ("%d", FreeRunningTimer.read_us() - TimeStampCAN1 );  // debug code
                    
                    printf("\r\n");     
                    
                    led4 = !led4; // Toggle LED4 to show that somehting was sent to the PC via USB
                }
                
            }//else
            
        }//if
        
    }//while
    
}//main
21 Jun 2010 . Edited: 21 Jun 2010

Can I ask which version of the library you are using? We recently updated the library to support more CAN speeds than were previously supported, and it is possible that a bug crept in at that point.

Also, by default, the mbed is "receiving" on the CAN bus, and not passively monitoring. To make it a passive listener (which also disables CAN transmissions), you need to set bit 1 of the appropriate MOD register to 1 (CAN1 for p9 & p10, CAN2 for P30 & P29):

LPC_CAN1->MOD |= (1 << 1);
You also need to do this before you start reading the bus. If you require any changes to the CAN block after you start reading or writing the bus, you have to disable the CAN controller first:

LPC_CAN1->MOD |= 1;          // Disble CAN controller
LPC_CAN1->MOD |= (1 << 1);   // Put into listen only mode
LPC_CAN1->MOD &= ~(1);       // Re-enable CAN controller

21 Jun 2010

How do I check which version of the CAN library that I am using?   I just #include "CAN.h" and use whatever that gave me.

21 Jun 2010

In the compiler, if you click on the link to the library "mbed", which has the little cog icon, it will pull up library details, including the revision. It should say "Revision 23: up to date". If it does not, press the "Update to the latest revision!" button.

21 Jun 2010

It does say "Revision 23: up to date".

21 Jun 2010

I'll file this as a bug, and investigate. Just one quick thing - I notice your text says it is a 120K bus, but your code is setting it to 125K. Does changing the frequency to 120000 help?

21 Jun 2010

The 120K in my post was a mistake sorry for the confusion.  I'm going to edit that now. The baud rate is 125K as shown in the code.

I'm going to try running a test with the CAN buses as "passive listeners".  Please let me know if I have the correct understand of how to put the CAN buses into "passive listener" mode as shown in the code below.  I'll post the results of the experiment later today.

Also, as a general question, where are the commands that manipulate individual registers documented?  I know I can look at the NXP LPC1768 data sheet for the register definitions but how do I know which ones are supported and what the actual mBed commands to manipulate them are?  Like this command for instance, "LPC_CAN1->MOD |= 1", where is the ->MOD documents?

Thanks for your help!

 

int main() 
{
    char MessageFoundCAN1;
    char MessageFoundCAN2;
    
    int TimeStampCAN1;
    int TimeStampCAN2;
    
    
    CANMessage CAN1_MsgRx;  // Message reception on CAN1
    CANMessage CAN2_MsgRx;  // Message reception on CAN2
    
    Timer FreeRunningTimer;  //Free running timer for CAN message stamp

    // USB Serail Port baud rate
    pc.baud(230400);
    
    // CAN1, MSCAN, 125kbit/s
    can1.frequency(125000);

    // CAN2, HSCAN, 500kbit/s
    can2.frequency(500000);
    
    //LPC_CAN1->MOD |= 1;          // Disble CAN controller 1
    LPC_CAN1->MOD |= (1 << 1);   // Put into listen only mode
    //LPC_CAN1->MOD &= ~(1);       // Re-enable CAN controller

    //LPC_CAN2->MOD |= 1;          // Disble CAN controller 2
    LPC_CAN2->MOD |= (1 << 1);   // Put into listen only mode
    //LPC_CAN2->MOD &= ~(1);       // Re-enable CAN controller
        
    /****** Program Starts Here *******/
    
    pc.printf("Connected to mBed...\r\n");
    
    // This is a free running timer that is used for the CAN message time stamp 
    FreeRunningTimer.start();

21 Jun 2010

There aren't any mbed commands to modify these registers directly, except via normal C pointer accesses, which is what LPC_CAN1->MOD is doing. The CMSIS header file (which is included via the mbed.h file) provides a load of useful structures, and defines all the memory addresses you need. For the LPC1768, this file is called LPC17xx.h, and you can have a look at it by click on the link you can see when you look at the library properties (http://mbed.org/projects/libraries/svn/mbed/trunk), and then looking in the LPC1768 directory. The mbed library is to get people up and prototyping rapidly, but there is always the extra control if people need it. The CAN part of the library hadn't been looked at since it was written a few months ago, which is why it is getting a bit of a re-work at the moment - I'll investigate whether putting the mbed into monitor mode should be an option in the libary.

As for your code example, that looks OK to me. Alas, I don't have a CANCase device or similar to hand, so I cannot try out the bit timings in detail.

The other thing you could try is shifting the sample point or frequency slightly yourself. The LPC_CANx->BTR register is the one that controls the bit timing. It is of the form 0x00pq40nnn, where:

p = (TSEG2 - 1), of the range 0..7

q = (TSEG1 - 1), of the range 0..0xF

nnn = (divide - 1), after a prescale divide of 4

[The 4 gives a Sample Jump Width of 1]

The library is currently 0x000D400B, which gives a TSEG2 of 1, a TSEG1 of 14, and divide of 12. Including the sync bit, this gives us:

So TSEG2 + TSEG1 + Sync == 16 bits.

24 Mhz / (16 bits * 12) = 24e6/192 == 125e3 (125 kbit/s)

So, try reducing TSEG1 to make it a little faster (0x000C400B), incread TSEG1 to make it a bit slower (0x001D400B), or try shifting the sample point (0x001C400B) etc.

If/when you get it working, could you provide an update to say what did and didn't work?

21 Jun 2010 . Edited: 21 Jun 2010

The Error Frames are still there when I put the device in "Passive Mode" per your recommendation.  I'm going to do some more testing later.  Here is the Error Frame message from CAN Alyzer.  Maybe it means something to someone.

   0.013955 2  424             Rx   d 8 1E 49 1F BB A6 50 00 00
   0.013981 1  215             Rx   d 8 27 10 27 10 27 10 27 10
   0.014231 1  417             Rx   d 8 00 00 00 00 00 00 00 00
   0.014485 1  418             Rx   d 8 08 00 00 7F C0 00 00 00
   0.014721 1  430             Rx   d 8 C9 C9 00 02 00 10 00 4A
   0.014921 2  ErrorFrame ECC: 01111011
   0.014921 CAN 2 Status:chip status error active  - TxErr: 0 RxErr: 1
   0.015113 1  47              Rx   d 8 00 60 00 00 00 00 00 00
   0.015354 1  230             Rx   d 8 17 00 85 0B 00 00 00 00
   0.016039 2  43A             Rx   d 8 1C 24 17 06 00 00 7E FE
22 Jun 2010

I think I narrowed this down to an issue with the 125000K setup for sure.  I swapped the CAN channel baud rates as shown below and also switched the CAN physical layers chips.  The issues followed the 125000K CAN bus, not the hardware.  This tells me that it is not the mBed hardware or the CAN Physical layer hardware (BTW, I am using the Microchip MCP2551 CAN Physical layers chips).  It is something to do with the 125000k baud rate setup.

 

I wanted to mention that even when there are error frames present there is some CAN traffic coming through on the 125000k bus.  Not sure if this gives any additional clues.

 

When I get some time I'll experiment with tweaking the baud rate.

 

// Original Code:
// Setup CAN1 Controller, MSCAN 125K
CAN can1(p9, p10);

// Setup CAN2 Controller, HSCAN 500K
CAN can2(p30, p29);


// Test Code   
// Setup CAN1 Controller, HSCAN 500K
CAN can1(p30, p29);

// Setup CAN2 Controller, MSCAN 125K
CAN can2(p9, p10);

22 Jun 2010 . Edited: 22 Jun 2010

Do the errors go away completely, if you disable the CAN block?

LPC_CAN1->MOD |= 1;

Also, try disconnecting the TX pin of the mbed on that bus.

22 Jun 2010

Disconnecting the Tx pin made the frame errors go away.

I had one of the CAN experts at my work look at the CAN signals on a scope. I 'm not a CAN expert myself so I may be translating this not as acurately as he told it to me....

The issue is that the mBed is NACKING many valid messages.  The reason is that the TSEG and SWJ settings are not correct for 125K.  The settings in the mBed are sampling the bits too far toward at the end of the bits.  Some messages that happen to have bits on the shorter end of the spec are being interpreted as invalid messages.  You should set the TSEG and SWJ so that the sample point is about 60%.  There is an SAE specfication, SAEJ2283, that shows valid setting and sample points.

I hope this helps and maybe you could send me an experimental CAN library with these setting adjusted?  Or, could you show me some code that I could use with the existing library that will correct these values?

On the brighter side, the 500K bus was working flawlessly and had no error frames!

 

 

23 Jun 2010

I haven't played with CAN on mbed yet, but will do at some point - hence following this thread.

I'm curious - does the mbed NACK the messages even in listen only mode? Unless there is a hardware bug in the LPC1768, it shouldn't do that I beleive.

The manual notes "A write access to the bits MOD.1 and MOD.2 is possible only if the Reset Mode is entered previously". I wonder if that was the case in your code, and hence listen-only mode isn't actually working - ie perhaps you need to set into passive mode before setting the CAN frequency on each bus? Or perhaps you should uncomment the reset operation (to disable & reenable the CAN controllers) to see if it makes any difference?

 

23 Jun 2010 . Edited: 23 Jun 2010

As Andrew says, the device should not be NACKing in listen only mode (which is why I asked if you could unplug the Tx pin). As suggested, you need to uncomment the lines that disable and re-enable the CAN controller.

As for the sample point, try setting the BTR manually to something like 0x001C400B or 0x002B400B (which move it progressively forward) - again with the CAN controller disabled.

The calculation for the speed for CAN is still under development - it is already improved (as it just wouldn't set many frequencies at all). Some applications require different sample points to others, or the system is more sensitive to frequency differences; these are things that require human judgement, and sometimes require a tweak. If you try one of the online CAN frequency timing web pages, they give you a range of possible values. I will have another pass over the library routine to see if it can be improved.

Try getting it into listen mode, and try moving the sample point as above. Hopefully, these will get you up and running.

08 Jul 2010

I have tweaked the CAN sample point in the new version (24) of the library which has just been released, and hopefully this should work better for you. It also adds a silent monitor mode.

14 Feb 2011

So, can2.monitor(1); for example? When I remove the Tx pin, I stop receiving messages completely, so looking for an alternative. Edit: putting it in monitor mode without anything else on the canbus makes it bug. Probably because there's no acknowledgement?