Using the CAN bus

Discussion to talk about software related topics only.
User avatar
lgitlitz
Posts: 331
Joined: Wed Apr 23, 2008 11:43 am
Location: San Diego, CA
Contact:

Re: Using the CAN bus

Post by lgitlitz »

I am not sure what version of the NNDK you are using but RTR functions were added around release 2.4 rc1. Look at the header file:
C:\CVSroot\nburn\MOD5234\include\canif.h
This header has the RTR descriptions and a RTR example in the comments.

There are three functions related to serving RTR messages:
int SetRTRMessage( DWORD id, BYTE *data, BYTE len, int channel = -1 );
int ReplaceRTRMessage( int channel, BYTE *data, BYTE len );
int StopRTRMessage( int channel );

The CanRxMessage constructor now has two new versions that are used for receiving messages from an RTR address. Calling this constructor will actually send out an RTR request, automatically switch to an RX buffer and then receive the data response from the RTR server.
CanRxMessage( DWORD id, WORD timeout );
CanRxMessage( DWORD id, WORD timeout, BYTE length );
oleg_osov
Posts: 20
Joined: Tue Jan 04, 2011 1:58 pm
Location: Montreal, Canada
Contact:

Re: Using the CAN bus

Post by oleg_osov »

Hi Dave & Larry!

First of all - thank you for a reply. I'm using NNDK-MOD5234-KIT, Carrier board V1.12, NBEclipse 2.4 RC2

I have to create a CANopen master node on a NetBurner hardware. I almost ported CANfestival to NetBurner. I made CANfestival timers owing to Larry's StopWatch timer code. It helped me a lot, thank you. If you don't mind I will share my code (based on your timers) with CANfestival community. It'll help to save a time for future developers.

CANfestival uses a main loop to receive all messages from the CAN bus and then to dispatch them according to protocol. That is why I want to receive and process all messages in one place. I need to send RTR and ordinary messages from different places, but process all messages in one place. I want to send RTR message, but not to wait at all.

Code: Select all

uchar canSend_driver(CAN_HANDLE fd0, Message const *m)
{
  if ( can_channel > 0 ) {
     if(m->rtr == NOT_A_REQUEST) {
       if ( SendMessage( NormToNbId( m->cob_id ),
                        (PBYTE) m->data,
                        m->len,
                        DONT_WAIT ) != CAN_OK )
        return 1;
     } else {
       CanRxMessage rtr_msg( NormToNbId( m->cob_id ), DONT_WAIT );
     }
     printf("==> ");
     print_message(m);
  }

  return 0;
}
And I didn't find in the manual how to receive ALL messages.

I learned CANSERIAL example and I can't understand it. It could connect two NetBurner boards, application is the same on both boards.

It registers queue to receive CAN messages with id==0x123

Code: Select all

      //REgister to listen for CAN data 
      //We will listed for frames with a normal id of 123
      int chan1 = RegisterCanRxFifo( NormToNbId( 0x123 ), &fifo );
And then it sends a message with id=101 (0x65).

Code: Select all

               if ( SendMessage( NormToNbId( 101 ),
                                 ( PBYTE ) buffer,
                                 strlen( buffer ),
                                 50 ) == CAN_OK )
I don't understand it =(
I learned canif.cpp sources, and I found no explanation how the ID filtration is working and which ID should I use to receive all messages.
Best regards,
Oleg Osovitskiy
User avatar
lgitlitz
Posts: 331
Joined: Wed Apr 23, 2008 11:43 am
Location: San Diego, CA
Contact:

Re: Using the CAN bus

Post by lgitlitz »

The RTR functionality is built into the hardware of the CAN peripheral. Since you want to serve RTR messages you should use the function:
int SetRTRMessage( DWORD id, BYTE *data, BYTE len, int channel = -1 );
This will set up an RTR channel. It will receive RTR requests and automatically reply with the RTR message, no CPU time required. You can then also change the message or stop serving with:
int ReplaceRTRMessage( int channel, BYTE *data, BYTE len );
int StopRTRMessage( int channel );

To receive all messages for the normal channels you need to correctly set the global mask. This is done either when you call CanInit or you can call ChangeGlobalMask after CAN is initialized. In the mask a '0' is a don't care address bit and '1' will check that address bit. So a mask of 0x0 will allow all addresses to be received and a mask of 0xFFFFFFFF will only allow the assigned address to be received. You can also use the RegisterCanSpecialRxFifo function for two RX channels that have their own mask instead of the global mask.
oleg_osov
Posts: 20
Joined: Tue Jan 04, 2011 1:58 pm
Location: Montreal, Canada
Contact:

Re: Using the CAN bus

Post by oleg_osov »

lgitlitz wrote:The RTR functionality is built into the hardware of the CAN peripheral. Since you want to serve RTR messages you should use the function:
int SetRTRMessage( DWORD id, BYTE *data, BYTE len, int channel = -1 );
This will set up an RTR channel. It will receive RTR requests and automatically reply with the RTR message, no CPU time required. You can then also change the message or stop serving with:
int ReplaceRTRMessage( int channel, BYTE *data, BYTE len );
int StopRTRMessage( int channel );
Thank you for the answer, Larry. May be my english is rather bad, but I meant completely different thing. I don't need to serve RTR messages. I have to build CANopen master, that is why I need to send RTR messages. But the main problem is I want to have a single message processing queue. So I need to sent RTR message and do not want to wait a response.

I plan to use such code:

Code: Select all

CanRxMessage rtr_msg( NormToNbId( m->cob_id ), DONT_WAIT );
lgitlitz wrote:To receive all messages for the normal channels you need to correctly set the global mask. This is done either when you call CanInit or you can call ChangeGlobalMask after CAN is initialized. In the mask a '0' is a don't care address bit and '1' will check that address bit. So a mask of 0x0 will allow all addresses to be received and a mask of 0xFFFFFFFF will only allow the assigned address to be received. You can also use the RegisterCanSpecialRxFifo function for two RX channels that have their own mask instead of the global mask.
I'm puzzled again. I thought that all CAN IDs is masked with a global mask. And what is the meaning of id paremeter, when we call RegisterCanRxFifo ?

Code: Select all

//REgister to listen for CAN data
//We will listed for frames with a normal id of 123
int chan1 = RegisterCanRxFifo( NormToNbId( 0x123 ), &fifo );
Comment to this small part of code (from the CANSERIAL example) says that we listen for frames with a normal id == 0x123.

Right now I can't test my thoughts because my slave device doesn't respond at all. I can't get even BootUp message from my device. When I send a message "node#2 - go to operation mode" "id:00 rtr:0 len:2 data:01 02" my device goes to error passive state. I suppose that I need to check wiring.

I'm going to read about CAN wiring, line termination, resistors and so on....
Best regards,
Oleg Osovitskiy
User avatar
lgitlitz
Posts: 331
Joined: Wed Apr 23, 2008 11:43 am
Location: San Diego, CA
Contact:

Re: Using the CAN bus

Post by lgitlitz »

Hi,
I am not too familiar with CANopen. That RTR function should work correctly for what you are describing. There is no need to wait for a response, it is all handled by the driver.
I'm puzzled again. I thought that all CAN IDs is masked with a global mask. And what is the meaning of id paremeter, when we call RegisterCanRxFifo ?
The ID parameter of RegisterCanRxFifo is the CAN ID of that receiving channel. Since there are 16 channels, you can have that many unique receiving addresses. The global mask defines what ID bits are checked on incoming messages. With the global mask you can allow a single addressed channel to receive a whole range of addresses. For example, say you set up a RX channel with the ID of 0x210. If the global mask is set to 0xFFFFFFFF then you will receive CAN data on this channel ONLY if it is sent to address 0x210. Now if we change the mask to 0xFFFFFFFE, the RX channel will receive on addresses 0x210 and 0x211 since bit 0 is not a "do not care" bit. If you set the mask to be 0xFFFFFFF0 you will receive on 16 addresses, 0x210-0x21F, since the lower 4 bits are no longer checked in addressing. And if you set the mask to 0x0 then none of the bits are checked for addressing and you will receive all addresses on the receive channel.
oleg_osov
Posts: 20
Joined: Tue Jan 04, 2011 1:58 pm
Location: Montreal, Canada
Contact:

Re: Using the CAN bus

Post by oleg_osov »

lgitlitz wrote:The ID parameter of RegisterCanRxFifo is the CAN ID of that receiving channel. Since there are 16 channels, you can have that many unique receiving addresses.
It seens that your state is wrong.

Code: Select all

3.5.6. RegisterCanRxFifo
Synopsis:
int RegisterCanRxFifo( DWORD id, OS_FIFO * pFifo, int channel=-1 );
Description:
This function tells the CAN system to start listening for a specific CAN ID. Any incoming CAN frames that
match the ID as set by the appropriate mask will be placed into the FIFO.
According to the manual, a receiving channel is the 3rd parameter to the function, and could be omitted for an automatic choice of the channel. In my previous message the id was transformed with NormToNbId( id ), I think only CAN ids could be transformed, but not channel ids.

One more CAN related question - I have a slave board which has CANL, CANH and CAN_SHLD connectors, and my NetBurner has CANL, CANH and GND connectors. Should I connect SHLD and GND together? Is it mandatory to use twisted pair? If my wires quite short (30 sm) could it be strait wires? Does NetBurner require an additional power for the CAN interface?
Best regards,
Oleg Osovitskiy
v8dave
Posts: 333
Joined: Thu Dec 31, 2009 8:31 pm

Re: Using the CAN bus

Post by v8dave »

oleg_osov wrote: One more CAN related question - I have a slave board which has CANL, CANH and CAN_SHLD connectors, and my NetBurner has CANL, CANH and GND connectors. Should I connect SHLD and GND together? Is it mandatory to use twisted pair? If my wires quite short (30 sm) could it be strait wires? Does NetBurner require an additional power for the CAN interface?
If only for testing, you can use just about any wire you like. For a proper installation I would recommend twisted pair cable but it all depends on the environment you are putting this in. I have run 4 core standard alarm cable around my house before for a weather station and heating control system and run power on 2 wires and CAN on the remaining 2. The bus was running at 125Kbps with no issues and no errors.

Ideally you should connect the SHIELD and GND to avoid EMC issues but for testing you can run without. You may, in some installations only connect the 1 end to SHIELD if you find that the installation is subjected to ground loop issues. It all depends on the wiring of the rest of the plant.

Dave...
oleg_osov
Posts: 20
Joined: Tue Jan 04, 2011 1:58 pm
Location: Montreal, Canada
Contact:

Re: Using the CAN bus

Post by oleg_osov »

Hello.

I have to implement a master, which has a single message processing queue, but it has to request messages with RTR. A slave board doesn't give me a response on a RTR message. Could someone give me a clue where am I wrong? A want to receive ALL messages from the bus.

Initialisation of the CAN subsystem:

Code: Select all

int canInit_driver(int32 bitRate)
{
  /* Enable CAN function */

  J2[39].function( PINJ2_39_CAN0RX );
  J2[42].function( PINJ2_42_CAN0TX );

  /* Check if bit rate is correct ... */
  if((bitRate != 125000) &&
     (bitRate != 250000) &&
     (bitRate != 500000) &&
     (bitRate != 1000000)) bitRate = 125000; /* Default value */

  /* Start the CAN subsystem, set the speed, mask and IRQ level */
  if( (CanInit(bitRate, 0 /* Mask value of 0 receives everything */ )) != CAN_OK)
     return -1;

  /* Init the fifo before use */
  OSFifoInit(&can_fifo);

  /* We will listen for all CAN frames */
  can_channel  = RegisterCanRxFifo( NormToNbId( 0x0 ), &can_fifo ); [b]/* id=0x0 - is it correct? */[/b]

  return 0;
}
Shutdown of the CAN:

Code: Select all

int canStop_driver(void)
{
  /* Unregister the fifo which is used to communicate with the CAN subsystem */
  UnRegisterCanFifo(can_channel);

  /* Mark the channel as invalid */
  can_channel = -1;

  /* Shutdown the CAN subsystem */
  CanShutDown();

  return 0;
}
Read data from fifo, no dellays are allowed:

Code: Select all

uchar canReceive_driver(CAN_HANDLE fd0, Message *m)
{
    if ( can_channel >= 0 )
    {
      /* Receive a message from FIFO, don't wait for a new message */
      CanRxMessage can_msg( &can_fifo, 1 );
      if(can_msg.IsValid()) {
        uint16 cob_id = NbToNormId(can_msg.GetId());
        m->len    = can_msg.GetData( m->data, 8 );
        m->cob_id = UINT16_LE(cob_id); /* <== A workaround for CANfestival on LE platforms */
        printf(" <== ");
        print_message(m);
      } else {
        /* The message is invalid or timeout (the fifo is empty) */
        return 1;
      }
    }
    return 0;
}
Write data to the CAN bus:

Code: Select all

uchar canSend_driver(CAN_HANDLE fd0, Message const *m)
{
  if ( can_channel >= 0 ) {
     uint16 cob_id = UINT16_LE(m->cob_id); /* <== CANfestival's workaround */
     if(m->rtr == NOT_A_REQUEST) { // data message
       if ( SendMessage( NormToNbId( cob_id ),
                        (PBYTE) m->data,
                        m->len,
                        DONT_WAIT ) != CAN_OK )
        return 1;
     } else { // RTR message
       CanRxMessage rtr_msg( NormToNbId( cob_id ), DONT_WAIT, m->len);
       /*
       if(rtr_msg.IsValid()) {
         Message pdo;
         uint16 cob_id = NbToNormId(rtr_msg.GetId());
         dsysMemSet( &pdo, 0, sizeof(pdo));
         pdo.len    = rtr_msg.GetData( pdo.data, 8 );
         pdo.cob_id = (uint16)UINT16_LE(cob_id); //!!!
         printf(" <==(RTR) ");
         print_message(&pdo);
       }
       */
     }
     printf("==> ");
     print_message(m);
  } else {
    iprintf("can_channel = %i\n", can_channel);
  }

  return 0;
}
My slave boards accept my commands, they change their modes (Pre-Operational ==> Operational), they response on SYNC messages, they give me boot-up messages and so on. But if I request data with RTR messages my boards give me nothing. It seems that they ignore my message. Or my message isn't transmitted to the CAN bus.

1. Is my configuration for receiving all messages from the CAN bus correct ?
2. Is my attempt to send RTR message and do not wait an answer correct?
3. May be I have to use multiple FIFOs to receive messages with multiple IDs ? It would very inconvenient for me.

Thanks in advance for any help!

P.S. canif.h has many typos and errors in examples. May be they have to be corrected, because they could stray someone from the right way.
Best regards,
Oleg Osovitskiy
User avatar
lgitlitz
Posts: 331
Joined: Wed Apr 23, 2008 11:43 am
Location: San Diego, CA
Contact:

Re: Using the CAN bus

Post by lgitlitz »

I think the problem is how you handle the RTR responses. So the first step is to send a message as an RTR which you are doing correctly with CanRxMessage. After the peripheral sends this message it will automatically change the TX buffer into an RX buffer and wait for the response. If you use a timeout parameter the CanRxMessage function then this will block and let other tasks run until you receive a response or there is a timeout. Since you use DONT_WAIT for this timeout, the function will never block and return instantly. The very next line in your code you check if the message received is valid. Chances are the CAN peripheral did not even have enough time to send out the RTR request and you are already checking the response. You either need to use a timeout parameter or check if the message was valid after enough time has passed to actually receive a response. I would definitely do your initial set up with some timeout value and then remove it later once your get the RTR messages functional.
oleg_osov
Posts: 20
Joined: Tue Jan 04, 2011 1:58 pm
Location: Montreal, Canada
Contact:

Re: Using the CAN bus

Post by oleg_osov »

lgitlitz wrote:I think the problem is how you handle the RTR responses. So the first step is to send a message as an RTR which you are doing correctly with CanRxMessage.
Thank you, Larry, for your answer.

The code you worry about is commented, and actually it is never ever been uncommented. It's just an idea how to debug a problem.

I solved my issue with RTR messages, it was an error related to BE arch and how CANfestival copes with it.

But now I faced another race condition. In my program I write messages on timer function (based on your StopWatch timer code). Timer fires a certain function quite often, which sends ordinary and RTR messages to the CAN bus, and simultaneously another function reads messages from the FIFO. And soon or late my code which reads FIFO became blocked, while writing is continue works without problems. I tried to debug this problems and found that read code blocks, when timer function sends RTR messages. What do you suggest me to do? Use critical section to avoid simultaneous reading and writing of a CAN bus? Or may be a better solution would be to read messages in the timer functions, when all messages are already gone? It called from ISR function, so I'm scared to do a heavy and long work in this function.
Best regards,
Oleg Osovitskiy
Post Reply