AT90CAN 128 - CAN interface init/read problems

Go To Last Post
9 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello,

CPU - AT90CAN128
Compiler - Imagecraft ICCAVR 7.15

I am trying to recieve information from PEAK FMS simulator. The connection is ok, I can see the signals by oscilloscope.
The problem is on a firmware level. I use external 8 MHz crystal Osc. I am pretty sure it is not a hardware problem, and also that it is not a problem in functions or part of the code, which is not given in the following code blocks.

Here are the ways I try to read / init:

//in header file
/***********************************************/
//             CAN mask and define
#define MSK_CANGCON_ENA  	0x02
#define MSK_CANGCON_GRES 	0x01
#define DLC_MAX            8
#define CH_DISABLE         0x00
#define CH_RxENA           0x80
#define CH_TxENA           0x40
#define MSK_CANGIE_ENRX    0x20
#define MSK_CANGIE_ENTX    0x10
#define MSK_CANSTMOB_RxOk  0x20
/***********************************************/
void CAN_Init(void)
{
    CANGCON = (1 << SWRES);
    while(ENFG==1) ;    
    CANGIE = 0x20 ;     // enable RX
   
    CANIE2 = 0xFF ;     //  enable MOb0 to MOb7
    CANIE1 = 0x00 ;     //  disalbe MOb8 to MOb14

    CANBT1  = 0x02; //set baud rate 250 kHz
    CANBT2  = 0x0C;
    CANBT3  = 0x37;
   
    CANPAGE = 0<<4 ;    // select MOb0
    CANCDMOB = 0x00 ;   // disable all transmission and reception

    CANIDM1 = 0x00 ;   // clear Mask, let all IDs pass   
    CANIDM2 = 0x00 ;
    CANIDM3 = 0x00 ;
    CANIDM4 = 0x00 ;

    CANGCON = 0x02 ;   // enable CAN
   
    while(ENFG==0) ;   // waite until CAN bus being enabled 
}

void can_rx(void)
{
CANPAGE = 0<<4 ;                    // select MOb0
CANCDMOB = 0x80 ;                   // enable reception
}


void can_rx_inter(void)  
{
  int i;
  
  if((CANSTMOB & 0x20)==0x20)      // check if RXOK flag has been set
  {
     CANSTMOB &= 0xDF ;            // clear RXOK flag
     for(i=0;i<8;i++)
     {
        TxUART(CurrentOutRS,CANMSG); //I use this for diagnostics
     }
     TOGGLEBIT(LED_PORT,LED_PIN); //I use this for diagnostics
  }
} 

In main() I call those two functions:
can_rx();
can_rx_inter();

I also try working with interrupt:

void CAN_Init(void)
{
  char tmpC, tmpD;
  
  CANGCON |= MSK_CANGCON_GRES; // reset CAN 
  // reset all mailboxes 
  for (tmpC = 0; tmpC < 15; tmpC++) //tmpC is num of channel
  {
    CANPAGE  = tmpC << 4;
    CANCDMOB = CH_DISABLE;
    CANSTMOB  = 0;
    CANIDT1  = 0;
    CANIDT2  = 0;
    CANIDT3  = 0;
    CANIDT4  = 0;
    CANIDM1  = 0;
    CANIDM2  = 0;
    CANIDM3  = 0;
    CANIDM4  = 0;
    for (tmpD = 0; tmpD < 8; tmpD++) CANMSG = 0; //tmpD is num_data
  }
  // setup bit timing at 250k with 8 MHz crystal
  CANBT1  = 0x02;
  CANBT2  = 0x0C;
  CANBT3  = 0x37;

  // interrupt configuration 
CANIE2|=0x01;   // IECH0=1
  CANGIE = ((1<<ENRX)|(1<<ENIT));   // Can_Rx & IT enable
  CANGCON |=  MSK_CANGCON_ENA;
  
  while (!(CANGSTA & (1<<ENFG))); //wait until module ready
}

#pragma interrupt_handler int_CAN_isr: iv_CAN_TRANSFER
void int_CAN_isr(void)
{
  //unsigned int id;
    
  TxUART(CurrentOutRS, 'A'); //diagnostic
  // echo receive data on channel 0 reception
  CANPAGE = (0 << 4);    // CHNB=0x00; select channel 0
  if((CANSTMOB & MSK_CANSTMOB_RxOk) == MSK_CANSTMOB_RxOk)
  {
    TxUART(CurrentOutRS, CANMSG); //diagnostic
    TOGGLEBIT(LED_PORT,LED_PIN);
  }
 
 CANPAGE = (0 << 4);
 CANSTMOB=0x00;         // reset channel 0 status
 CANEN2 |= (1 << 0);    // channel 0 enable
 CANCDMOB = DLC_MAX;    // receive 8 bytes
 CANCDMOB |= CH_RxENA;  // reception enable
 CANGIT = CANGIT;       // reset all flags
}

For writing that code I have used examples from different places, mostly from this forum. Still, I cannot receive a single byte via CAN interface.

Thank you for your time :)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

additional info :
Regarding the second source. With interrupts

  CANPAGE=(0<<MOBNB0);                     // select MOb0
  CANCDMOB=((1<<CONMOB1)|(1<<DLC3)); //alex effect chance

Two ways to implement the upper code:
Variant A: Implementing it after init stage causes entering in the interrupt (success!) -> I receive about 150 sequent symbols 'A' (my diagnostic)
(TxUART(CurrentOutRS, 'A'); //diagnostic )
but it seems the interrupt is not caused by RxOK

  if((CANSTMOB & MSK_CANSTMOB_RxOk) == MSK_CANSTMOB_RxOk)
  {
    TxUART(CurrentOutRS, CANMSG); //diagnostic
    TOGGLEBIT(LED_PORT,LED_PIN);
  }

Variant B: If implemented in the init_CAN stage causes total program crash.

Probably the problem is not in the general CAN init, but in my poor understanding of channels and MOB manipulation. If you have any ideas, help is still appreciated. If I find something more, I will write again.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm not familiar with your IAR compiler and I don't see any macro or definition for ENFG. I don't think this will work:
while(ENFG==1);
because ENFG represents bit number 2 of the CANGSTA register, so ENFG usually has a value of 2. From your first CAN_Init example, it looks like a do nothing line of code that resolves this:
while(2==1);
The same goes for this one:
while(ENFG==0);
which resolves to:
while(2==0);

A good C compiler optimizer would remove both of the above whiles.

In your first CAN_Init example you failed to initialize all of the critical CANCDMOB and CANSTMOB registers for each and every MOb (you only initialized CANCDMOB for MOb 0). When the CAN is first turned on (CANGCON register ENASTB bit = 1) all of these MOb registers must already be initialized or the CAN controller will get confused by the uninitialized garbage and or your code will respond to false CANSTMOB polling/interrupt flags.

Typically these:
TxUART(CurrentOutRS, CANMSG);
TOGGLEBIT(LED_PORT,LED_PIN);
called inside an interrupt will usually cause the compiler to push/pop all the AVR registers (not very efficient). If TxUART() actually waits for characters to be sent, then it will have a huge impact on the interrupt response execution. In this case set a flag/count inside the interrupt and let main() code detect the flag and do the TxUART(). If TxUART() uses interrupts itself, keep in mind its being called from inside interrupt code where global interrupts are disabled. This might hang your AVR waiting for an interrupt response that can't happen or if TxUART() does an SEI (enabled global interrupts) it will allow interrupts to nest and most likely crash your program.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Here is a commented example. This has not been compiled (i.e. checked) and should be treated like pseudo code since its not for any particular C compiler.

void int can_init (void) {

    unsigned char mob;			// temporary mob value (points to selected MOb)

        // ensure global interrupts are disabled first, before enabling any CAN interrupts
    cli();

        // the entire CANGCON register (including ENASTB) is cleared after SWRES
        // The SWRES is abrupt and will destroy all/any CAN activity in progress on this CAN node
    CANGCON = (1 << SWRES);	// initialize the CAN state machine and only the general CAN registers

    // CANGSTA is cleared by a reset

        // None of the MOb registers automatically initialize upon a reset, so initialize the two critical MOb registers
        //   using CANPAGE as a pointer to each MOb
    for (mob = 0; mob < 15; mob++) {
        CANPAGE = (mob << 4);		// set the MOb page number index for each MOb 0 to 14
        CANCDMOB = 0;			// set each MOb to configuration to disabled *critical
        CANSTMOB = 0;			// clear all the MOb interrupt flags *critical
    }

    // all the remaining MOb values are setup when a MOb is initialized, before the MOb is enabled (CANCDMOB)

    // the CANGIT General interrupt flag register is cleared by the reset

        // Set the CAN baud rate
        //    do not forget about the CLKPR register and CKDIV8 fuse settings
    CANBT1 = 0x??;			// Timing - Baud Rate Prescaler
    CANBT2 = 0x??;			// Timing - Re-Synchronization Jump Width and Propagation
    CANBT3 = 0x??;			// Timing - Phase Segment 1, Phase Segment 2 and Sample Point

        // Select the CAN polling/interrupts to be enabled (ENBX and ENOVRT are not enabled in this example)
    CANGIE = (1 << ENIT)      | (1 << ENBOFF) | (1 << ENRX)    | (1 << ENTX) |
                      (1 << ENERR) | (0 << ENBX)      | (1 << ENERG) | (0 << ENOVRT);

    // if ENIT is cleared, the CANGIT register read only CANIT bit may be polled instead of using interrupts
    // if ENBX is set, special rules must be followed before the BXOK bit is able to be cleared
    // ENOVRT is for the OVT IT interrupt, all the other CANGIE bits are for the CAN IT interrupt

        // Select the MOb(s) to be enabled for polling or interrupts (all 15 are enabled in this example)
    CANIE1 = (1 << IEMOB14) | (1 << IEMOB13) | (1 << IEMOB12) |
                     (1 << IEMOB11) | (1 << IEMOB10) | (1 << IEMOB9)   | (1 << IEMOB8);
    CANIE2 = (1 << IEMOB7) | (1 << IEMOB6) | (www.avrfreaks.net(1 << IEMOB4) |
                     (1 << IEMOB3) | (1 << IEMOB2) | (1 << IEMOB1) | (1 << IEMOB0);

        // Enable the CAN unit
    CANGCON = (1 << ENASTB);

        // This sei may be deferred if the rest of the AVR is not ready for global interrupts to be enabled
        // if ENIT is cleared, the CAN IT interrupt will not be active after the SEI
    sei();
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sorry, there is a FRUSTRATING text corruption bug that hits any post I submit over a certain size. The correction to the #&$*%^ corruption is:

CANIE2 = (1 << IEMOB7) | (1 << IEMOB6) | (1 << IEMOB5) | (1 << IEMOB4) |

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you Mike, your support helped me and I really appreciate it. I wanted to thank you earlier, but also wanted to give complete feedback here.

I want to share the solution here, so that other colleagues will not lose that much time as me.

First, ofcourse

while(ENFG==1);

was wrong syntax.
The initialize basics of the example, given by Mike are good. Here is my initialization and it works

void CAN_Init(void)
{
  char tmpC, mob;
  
  CANDRV_ENABLE(); //turns on the CAN driver chip
  CANGCON |= MSK_CANGCON_GRES; // initialize the CAN state machine and only the general CAN registers 
  while (TESTBIT(CANGSTA,ENFG)==1) ;   // waite until CAN bus being enabled

  for (mob = 0; mob < 14; mob++) // reset all mailboxes 
  {
    CANPAGE  = mob << 4;
    CANCDMOB = 0;
    CANSTMOB  = 0;
    CANIDT1  = 0;
    CANIDT2  = 0;
    CANIDT3  = 0;
    CANIDT4  = 0;
    CANIDM1  = 0;
    CANIDM2  = 0;
    CANIDM3  = 0;
    CANIDM4  = 0;
    for (tmpC = 0; tmpC < 8; tmpC++) CANMSG = 0; //tmpD is num_data
  }
  
  // setup bit timing at 250k with 8 MHz crystal
  CANBT1  = 0x02;
  CANBT2  = 0x0C;
  CANBT3  = 0x37;
  
  CANGCON |=  MSK_CANGCON_ENA;
  while (TESTBIT(CANGSTA,ENFG)==0) ;   // waite until CAN bus being enabled
  
  /* Channel 0 init */
 CANPAGE = (0 << 4);               /* CHNB=0x00; select channel 0 */
 CANSTMOB  = 0x00;                 /* reset channel status */
 CANCDMOB = 0;            /* reset control and dlc register */

 CANCDMOB |= DLC_MAX;                         /* Reception 8 bytes.*/
 CANCDMOB |= CH_RxENA;                        /* Reception enabled without buffer.*/

 /* interrupt configuration */
 //CANIE1 = 0x7f; //Enable Mobs 14-8
 //CANIE2 = 0xff; //Enable Mobs 7-0 
 CANIE2 = 0x01; //Enamble only MOB 0
 CANIE1 = 0x00;
 
 //CANIE2|=0x01;                                           /* IECH0=1 */
 CANGIE = ((1<<ENRX)|(1<<ENIT));              /* Can_Rx & IT enable */
 //CANGIE = (1<<ENRX);                       //no interrupts scheme
      
}

Depending on whether you want to use interrupts or not, you can change the CANGIE.
If you want to use more MOBs, CANIE1 and CANIE2 should be changed, also each one should be initialized (e.g. /* Channel 0 init */ part).

I also use only reading. For transmitting CANGIE should also have |(1<<ENTX).

Now, what caused me SO MUCH trouble is the following:

Quote:

Source: AT90CAN128 AUTOMATIVE.pdf
33.2 Errata Description

1. CAN transmission after 3-bit intermission
If a Transmit Message Object (MOb) is enabled while the CAN bus is busy with an on going
message, the transmitter will wait for the 3-bit intermission before starting its transmission.
This is in full agreement with the CAN recommendation.
If the transmitter lost arbitration against another node, two conditions can occur:
...
etc etc
...

Workaround implementation
The workaround is to have the last MOb (MOb14) as "spy" enabled all the time; it is the MOb
of lowest priority. If a MOb other than MOb14 is programmed in receive mode and its acceptance
filter matches with the incoming message ID, this MOb will take the message. MOb14
will only take messages than no other MObs will have accepted. MOb14 will need to be reenabled
fast enough to manage back to back frames. The deadline to do this is the beginning
of DLC slot of incoming frames as explained above.
Minimum code to insert in CAN interrupt routine:

__interrupt void can_int_handler(void)
{
if ((CANSIT1 & 0x40) == 0x40 ) /* MOb14 interrupt (SIT14=1) */
{
CANPAGE = (0x0E << 4); /* select MOb14 */
CANSTMOB = 0x00; /* reset MOb14 status */
CANCDMOB = 0x88; /* reception enable */
}
........
........
}

After understanding and implementing this, everything comes to its place...

Good luck :)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

while (TESTBIT(CANGSTA,ENFG)==1) ; // waite until CAN bus being enabled

I don't have access to your TESTBIT source, but the data sheet definition of ENFG is 0 = CAN controller disable and 1 = CAN controller enable. If you enter a while loop when the CAN is enabled that never exits if ENFG = 1 (CAN controller enabled) you would just hang forever rather than wait. Since it takes time after the SWRES before ENFG is set, its most probable that ENFG = 0 every time the above while is executed, which means the while never waits. Unless I am making a totally wrong assumption about TESTBIT, wouldn't this be correct instead:

while (TESTBIT(CANGSTA,ENFG)==0) ; // waite until CAN bus being enabled

If this is the correct while wait code (i.e. ==0), it demonstrates that waiting for the ENFG enable isn't necessary since the old while never worked. In this case you could delete this entire while wait.

If you do use a wait loop for ENFG to enable, consider what might happen if your CAN bus malfunctioned. Your AVR would hang in a loop forever because the CAN bus wiring had a problem and ENFG is never set. If you really want to wait for ENFG I recommend an additional timeout (like a counter timer) with a timeout value calculation based on the CAN baud rate, required CAN bus initialization bits with extra padding time of course. The timeout would be designed to free your code from an infinite while (ENFG) loop, possibly with some type of error indication.

If the CAN bus has problems and ENFG never enables even though you never waited for ENFG, you may still setup any MOb, except there will never be any CANSTMOB or CANGIT activity. If your AVR has any other tasks besides CAN (like controlling other hardware), not being able to establish CAN communication shouldn't disable the entire AVR processor. This type of problem may arise even if you do wait on ENFG because a disconnected CAN node will usually always Rx recessive to the AVR CAN. The difference is if the CAN node enables with a disconnected CAN bus, any CAN Tx will generate endless CANSTMOB register AERR responses because there is nothing to respond with the required CAN frame ACK and the automatic Tx retries just keep going and going.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Correct.
What actually happens is that that code part is useless actually, because it works without it. Otherwise, yes, it is wrong and yes, it should be protected with timer break; (without forgetting to SEI() first)

Offtopic:
This is my test bit definition
#define TESTBIT(A,B) (A&(1<<B))

Thanks again

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Anybody from this topic still active and could help with some AT90CAN128 issues?