CAN problem

Hi,

I’m trying to make a couple of Panda II boards talk to one another over CAN bus (4.1.6.0 firmware).
I had barebone shields etched based on the EMX_DevSys_sch.pdf schematic.
Since my project isn’t working at all, I’m back tracking to eliminate errors.
Even using the below simplified code (copied largely from the documentation) and only one shield with only a 120Ohm resistor, I’m getting no results (no messages are received).
Even when I remove the shield alltogether and bridge the CAN receive and transmit pins, I don’t receive a single CAN message.

The output of the underneath program is

+++ Very simple CAN test +++

of posted messages : 1

messages sent :False

of posted messages : 1

messages sent :False

transmit errors :0

messages received :0

What am I overlooking ?
Should the Panda board not receive its own messages when RX an TX are bridged ?
I have searched the Wiki and the Forum but don’t get any solution from there.


using System;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using GHIElectronics.NETMF.FEZ;
using GHIElectronics.NETMF.Hardware;

namespace Simple_CAN_test
{
    public class Program
    {
        const int BRP = 24;
        const int T1 = 15;
        const int T2 = 8; 
        static OutputPort LED = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.LED, true);
        static CAN.Message[] msgList;
        static CAN.Message[] rcvList;

        public static void Main()
        {
            Debug.Print("+++ Very simple CAN test +++");
            msgList = new CAN.Message[10];
            rcvList = new CAN.Message[10];
            CAN can = new CAN(CAN.Channel.Channel_1, ((T2 - 1) << 20) | ((T1 - 1) << 16) | ((BRP - 1) << 0));
            can.DataReceivedEvent += new CANDataReceivedEventHandler(can_DataReceivedEvent);
            can.ErrorReceivedEvent += new CANErrorReceivedEventHandler(can_ErrorReceivedEvent);
            can.Reset();

            msgList[0] = new CAN.Message();
            msgList[0].ArbID = 0x55;
            msgList[0].Data[0] = 1;
            msgList[0].Data[1] = 2;
            msgList[0].Data[2] = 3;
            msgList[0].Data[3] = 4;
            msgList[0].Data[4] = 5;
            msgList[0].Data[5] = 6;
            msgList[0].Data[6] = 7;
            msgList[0].Data[7] = 8;
            // Send the 8 bytes for example
            msgList[0].DLC = 8;
            msgList[0].IsEID = false;
            msgList[0].IsRTR = false;

            while (true)
            {
                int numberOfMessagesPosted = can.PostMessages(msgList, 0, 1);
                Debug.Print("# of posted messages : " + numberOfMessagesPosted);
                Debug.Print("# messages sent :" + can.PostedMessagesSent);
                Thread.Sleep(100);
                Debug.Print("# of posted messages : " + numberOfMessagesPosted);
                Debug.Print("# messages sent :" + can.PostedMessagesSent);
                Debug.Print("# transmit errors :" + can.TransmitErrorCount);
                Debug.Print("# messages received :" + can.ReceivedMessagesCount);
                //can.Reset();
                Thread.Sleep(1000);
            }
        }

        static void can_DataReceivedEvent(CAN sender, CANDataReceivedEventArgs args)
        {
            Debug.Print(">>> can_DataReceivedEvent <<<");

            // read as many messages as possible
            int count = sender.GetMessages(rcvList, 0, rcvList.Length);
            for (int i = 0; i < count; i++)
            {
                Debug.Print("MSG: ID = " + msgList[i].ArbID + " at time = " + msgList[i].TimeStamp);
            }
        }

        static void can_ErrorReceivedEvent(CAN sender, CANErrorReceivedEventArgs args)
        {
            Debug.Print(">>> can_ErrorReceivedEvent <<<");

            switch (args.Error)
            {
                case CAN.Error.Overrun:
                    Debug.Print("Overrun error. Message lost");
                    break;

                case CAN.Error.RXOver:
                    Debug.Print("RXOver error. Internal buffer is full. Message lost");
                    break;

                case CAN.Error.BusOff:
                    Debug.Print("BusOff error. Reset CAN controller.");
                    sender.Reset();
                    break;
            }
        }
    }
}

This is normal and will never ever work :slight_smile: This is CAN not serial port!

You need 2 nodes and both running at exact same bit timing. You also need a phy with 120ohm resistor on wires…and a ground between the 2 boards

In CAN there is no such thing as a rx and tx pin. Also be sure you have resistors on the bus that set a bias level, in addition to the terminating resistors.

You might want to read some info about the CAN bus first.

Regards.

Gus,

I do have phys (MCP2551), 120Ohm resistors and ground connection. Cfr attached picture
Thing is, if all that doesn’t work, where do you go from there ?
Why should looping RX and TX never work ? Seems to me that this is exactly the same as if the node would be alone on the wire ? The example on the Wiki also assumes that all sent messages are picked up by the sender.
Are there other examples out there than the one in the wiki (that keeps coming back everywhere) ?

Wouter,

I DID read up on the CAN bus before attempting this and I have been experimenting for days now. Thanks for not assuming total and utter laziness on my behalf.
The CAN wires are labeled “RD” and “TD” (Transmit Data and Receive Data ?) on the Panda board and “input” and “output” in the documentation. Which names does the CAN wizard community prefer to use ?

Stefaan

Eagle schematics file(of the home-made CAN shield) attached for completeness

Stefaan

The two wire in CAN are called CANHI and CANLO refering to their level.

Oops, you were talking about the pins that come from the microcontroller. I thought you swapped the CAN bus wires :slight_smile:

Bridging the RX/TX on the same board will not work. As in a CAN network, there must be at least one remote node that ACK’s the CAN message. A single node will not ACK it’s own messages unless you tell the hardware to enable loopback mode if available (but on some hardware, the data doesn’t even come out of the device in that case).

Have you added bias resistors? I cannot see them on your picture.

I’ve posted a schematic yesterday in http://www.tinyclr.com/forum/2/4126/#/2/msg39130
It also contains a CAN transceiver and bias resistors. (You only need one pair of bias resistors on a bus).

Thanks Wouter,

I overlooked the ACK field. Now I understand why at least two devices are needed.

Your schematic is the same as mine except for the two resistors between CANH, CANL and GND.
I don’t see those resistors in any of the other example designs (I looked at the EMX schematics and the CAN shield of Sparkfun).
What is the function of those resistors ?

Stefaan

When a transceivers are in a ressesive state, there must be some bias network to define bus levels at 2.5 volts.

But the good news seems to be that the MCP2551 contains those internally. See http://ww1.microchip.com/downloads/en/AppNotes/00228a.pdf

Don’t know why I added them. I think I wanted to play safe.
There seems to be a better way for external biasing then what I’ve used: see figure 13 of
http://www.nxp.com/documents/application_note/AN00020.pdf

But again, with the transceiver that you use it should idd work without external biasing.

Fyi: here they add the 1k resistors too on their products:
http://www.modtronix.com/product_info.php?products_id=415
http://www.modtronix.com/products/mxd1can/mxd1can_sch.pdf

selectable with jumpers. And they use the same transceivers.

I must have picked it up somewhere from a Microchip schematic, but can’t find it back…

Wiki is updated http://wiki.tinyclr.com/index.php?title=CAN

Hi folks,

I managed to solve my hardware problems. I used the 3.3V power supply on the MCP2551 where apparently it will only work on 5V. I misinterpreted one of the example schematics.

I now have one board sending a CAN message to another board. No more BusOff errors and the DataReceivedEvent is thrown on the receiving device.

However, I now run into a strange software problem :
Using the underneath code, when sending a CAN message, I only get these messages reported :

can_DataReceivedEvent <<<

messages received : 1

Nothing else.
When I put a breakpoint at

int count = sender.GetMessages(buffer, 0, sender.ReceivedMessagesCount); 

, and step forward using F10, the yellow arrow simply disappears (indicating that no further instruction is executed ?) and the debugger stalls. I can only wake the debugger up by disconnecting the board. Also, the LED stops blinking which indicates that the program somehow crashed (rather severely).
All this seems to point to some problem in the GetMessages instruction.

I’d like to use the 4.1.3.0 Firmware which has the previous CAN driver to see if that one works better, but I can’t find it. Can someone point me to the download place of the older firmwares ?

Thanks,
Stefaan


using System;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using GHIElectronics.NETMF.FEZ;
using GHIElectronics.NETMF.Hardware;

namespace Simple_CAN_test
{
    public class Program
    {
        const int BRP = 24;
        const int T1 = 15;
        const int T2 = 8; 
        static OutputPort LED = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.LED, true);

        public static void Main()
        {
            Debug.Print("+++ Very simple CAN receiver +++");
            LED.Write(false);
            CAN can = new CAN(CAN.Channel.Channel_1, ((T2 - 1) << 20) | ((T1 - 1) << 16) | ((BRP - 1) << 0));
            can.DataReceivedEvent += new CANDataReceivedEventHandler(can_DataReceivedEvent);
            can.ErrorReceivedEvent += new CANErrorReceivedEventHandler(can_ErrorReceivedEvent);
            can.Reset();


            while (true)
            {
                LED.Write(true);
                Thread.Sleep(50);
                LED.Write(false);
                Thread.Sleep(200);
            }
        }

        static void can_DataReceivedEvent(CAN sender, CANDataReceivedEventArgs args)
        {
            Debug.Print(">>> can_DataReceivedEvent <<<");
            Debug.Print("# messages received : " + sender.ReceivedMessagesCount);
            var buffer = new CAN.Message[100];

            // read as many messages as possible
            int count = sender.GetMessages(buffer, 0, sender.ReceivedMessagesCount);
            Debug.Print("Getting here !");  // Execution never reached this point
            
            for (int i = 0; i < count; i++)
            {
                Debug.Print("MSG: ID = " + buffer[i].ArbID + " at time = " + buffer[i].TimeStamp);
            }
            LED.Write(false);
        }

        static void can_ErrorReceivedEvent(CAN sender, CANErrorReceivedEventArgs args)
        {
            Debug.Print(">>> can_ErrorReceivedEvent <<<");
        }
    }
}

Your problem might be related to this one: http://www.tinyclr.com/forum/10/4030/

That thread also contains this link to 4.1.3:

[url]http://www.ghielectronics.com/downloads/NETMF/GHI%20NETMF%20v4.1%20SDK(4.1.3.0).zip[/url]
(Seems like you’ll have to manually add the ).zip extension)

The old CAN driver works fine. This means there is a bug in the new CAN driver !

This is a workaround : If all of the CAN.Messages in the array that is passed to the GetMessages function are instantiated, then the new firmware works fine.


            for (int i = 0; i < msgs.Length; i++) msgs[i] = new CAN.Message();

The old firmware has the same problem, but that one throws an exception when the messages are not instantiated. The new firware stalls without throwing an exception.

What is the typical timeframe for solving a bug like this ?

mvg,
Stefaan

Nice finding!

You could maintain a global static array of initiated messages that you use in the receive event and not create the array inside the receive event for GC’s sake.

Wouter,

That’s what I do right now.
However, I should say that this kind of programming style hurts my object-oriented hart. I hope GHI can solve this problem.

Stefaan

Well, I don’t see a big problem in the fact that the receive method needs an initialized array… But it should be mentioned in the documentation.

In fact, it is better that they leave the initialization up to the user. As then you can use a static array to prevent the GC from kicking in on each received message.

Wouter,

I understand there is a performance penalty associated with creating and cleaning up objects, but I thought .NET MF was an object-oriented environment. Having to pass a fully initialized static array of message objects to a method is realy bad odor in terms of object-orientation. In the end you’ll be writing functional programs using objects from GHI and Microsoft. In that case, what is the point of having .NET MF anyway ?

I guess CAN.Message would better be a struct instead of an object. It doesn’t have any behaviour anyway.

Stefaan

I agree that it should be a struct. And I’m also totally into OO…

But we should not forget that our NETMF code runs on a very limited controller where the GC can eat up lot’s of processing power.

Anyway, +1 for converting CAN.Message to a struct. Which would be a better solution to the original problem.

Good catch. It should throw null reference exception.

It is done like this on purpose. CAN interrupt or GPIO interrupt should be processed as fast as possible. Can you imagine every time you receive a CAN message, we started allocating dynamic memory… This will slow your system by folds. CAN messages are time critical usually, delaying this is very bad. On smaller systems you have to think of compromises to make things run better.