Introduction to CAN

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Introduction to CAN

2,152 Views
ianbenton
Senior Contributor I

I have to send some data across a piece of equipment that is electrically noisy (some 160 Amp pulse currents going on) and have three options. The first two I can handle (Good old RS485, or fibreoptic), and the third is CAN which I've not done before. My expertise extends as far as having read the Wikipedia page (so I know about the hardware and the message format). 

I have LPC1517s at both ends; and although the LPC1517 manual is comprehensive in telling me what all the CAN registers do, I'm looking for a bit more of a "cookbook" tutorial on how to initialise the CAN hardware and how to send a message on the LPC1517. Could you please suggest what I should read.

Labels (1)
Tags (2)
6 Replies

1,602 Views
jeremyzhou
NXP Employee
NXP Employee

Hi Ian Benton,

To provide the fastest possible support, I'd highly recommend you to refer to the ccan_open_rom and ccan_rom demos which are from the LPCOpen library of the LPC15xx.

Have a great day,
TIC

 

-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------

0 Kudos
Reply

1,602 Views
ianbenton
Senior Contributor I

Having read the examples, I find that they add a new layer of complexity rather than offering any explanation. Now we have a whole load of new variables which are not explained either in the accompanying text nor in the comment lines. For instance, why did we reserve some memory? Doesn't the CAN interface have its own memory? What do gCanHandle and pCanHandle do? What happens when a message is received? I would have expected an IRQ Handler starting with  "CAN0_IRQHandler" somewhere, but I didn't see it. And why does a mask of 0x700 allow all messages (surely "identifiers") between 0x400 and 0x4FF? And what is a "message number"?

1,602 Views
carstengroen
Senior Contributor II

Ian,

I don't know if the following helps in any way, but this is the code I use in all of my LPC1549 projects. Please note that I use Keil's RTOS, so some of the code does not apply to your case, but perhaps there is something that fills some gaps you can use ?

//=============================================================================
// CAN.c                                                           CHG 20150324
//
//=============================================================================
// 
//-----------------------------------------------------------------------------
#include "chip.h"
#include "system.h"
#include "rom_can_15xx.h"
#include "CAN.h"


#define BITRATE100K12MHZ           0x0000050e
#define BITRATE125K12MHZ           0x00001c05
#define BITRATE250K12MHZ           0x00001c02
#define BITRATE500K12MHZ           0x00000502     
#define BITRATE1000K12MHZ          0x00000900
static MUTEX mut;
     

static CAN_HANDLE_T pCanHandle;
#define CAN_MSGOBJ_EXT  0x20000000UL   // CAN 2.0b 29-bit ID


#define MAX_CAN_PARAM_SIZE          512
static uint32_t gCANapiMem[MAX_CAN_PARAM_SIZE];
static CAN_CFG gCANConfig;

// Callback function prototypes
void CAN_rx(uint8_t msg_obj_num);
void CAN_tx(uint8_t msg_obj_num);
void CAN_error(uint32_t error_info);

// Publish CAN Callback Functions 
CAN_CALLBACKS callbacks = {
   CAN_rx,
   CAN_tx,
   CAN_error
};

//Set up CAN_API_INIT_PARAM_T:
CAN_API_INIT_PARAM_T myCANConfig = {
     (uint32_t)&gCANapiMem[0],
     LPC_C_CAN0_BASE,
     &gCANConfig,
     (CAN_CALLBACKS *)&callbacks, // need the callback for receive messages
     (CAN_CANOPENCFG *)NULL,            // not used, but allocate memory for it
     (CANOPEN_CALLBACKS *)NULL,       // not used, but allocate memory for it
};


// Number of RX messages in queue to the callback routines
#define MAX_QUEUE 8


// Allocate the dynamic data...
static _declare_box(CANMSGList, sizeof(CAN_MSG_OBJ), MAX_QUEUE);
// Allocate the mailbox
static os_mbx_declare(mbCANMSG, MAX_QUEUE); // Queue of messages that has to be played

static U64 stkthPacket[100];  
static int init=FALSE;
static int idle=TRUE;
static int overRun=0, numFrames=0;

static CAN_MSG_OBJ msg_obj;

//-----------------------------------------------------------------------------
//     CAN receive callback
//     Function is executed by the Callback handler after a CAN message has been received
//-----------------------------------------------------------------------------
void CAN_rx(uint8_t msg_obj_num){
  void *box=NULL;
  CAN_MSG_OBJ msg_obj;

  /* Determine which CAN message has been received */
  msg_obj.msgobj = msg_obj_num;
  /* Now load up the msg_obj structure with the CAN message */
  LPC_CAND_API->hwCAN_MsgReceive(pCanHandle, &msg_obj);
   
  if (msg_obj_num == 1) {

     // Remove extended bit indicator
     msg_obj.mode_id &= ~CAN_MSGOBJ_EXT;  

          box  = _alloc_box (CANMSGList);
          if (box==NULL) {
               overRun++;
               return;
          }
          numFrames++;
          memcpy(box, &msg_obj, sizeof(msg_obj));
          isr_mbx_send (mbCANMSG, box);
     }
  return;
}

//-----------------------------------------------------------------------------
//     CAN transmit callback
//     Function is executed by the Callback handler after a CAN message has been transmitted 
//-----------------------------------------------------------------------------
void CAN_tx(uint8_t msg_obj_num){
  idle=TRUE;
  
  return;
}

//-----------------------------------------------------------------------------
//     CAN error callback 
//     Function is executed by the Callback handler after
//     an error has occured on the CAN bus
//-----------------------------------------------------------------------------
void CAN_error(uint32_t error_info){
  //gErr=error_info;
  //gCnt++;
 // if ((error_info & CAN_ERROR_BOFF)==CAN_ERROR_BOFF) {
//    ledMMI(LED_DBG_YELLOW, LED_TOGGLE);
 // ledMMI(LED_DBG_YELLOW, LED_TOGGLE);

// }    
  return;
}

//-----------------------------------------------------------------------------
//     CAN interrupt handler 
//     The CAN interrupt handler must be provided by the user application.
//     It's function is to call the isr() API located in the ROM 
//-----------------------------------------------------------------------------
void C_CAN0_IRQHandler (void){
  LPC_CAND_API->hwCAN_Isr(pCanHandle);
}

//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void sendDataCAN(unsigned int id, unsigned char data[8]) {
  CAN_MSG_OBJ msg_obj;
  int i=0;

  if (init==FALSE)
    return;

  MUTEX_ACQUIRE(mut);
  
  while (idle==FALSE && i++ < 10) OS_WAIT(10);
  idle=FALSE;
  
     msg_obj.msgobj  = 0;
     msg_obj.mode_id = CAN_MSGOBJ_EXT | id;
     msg_obj.mask    = 0x0;
     msg_obj.dlc     = 8;
  for (i=0; i<8; i++) 
    msg_obj.data[i]=data[i];
     LPC_CAND_API->hwCAN_MsgTransmit(pCanHandle, &msg_obj);
  MUTEX_RELEASE(mut);

}



//-----------------------------------------------------------------------------
// 
//-----------------------------------------------------------------------------
int getOverrunCAN(void) {
  return overRun;
}

//-----------------------------------------------------------------------------
// 
//-----------------------------------------------------------------------------
int getNumFramesCAN(void) {
  return numFrames;
}

//-----------------------------------------------------------------------------
// (weak) Handler for received packets
//-----------------------------------------------------------------------------
__attribute__ ((weak)) void rxCAN(unsigned int id, unsigned char * buffer, unsigned char length) {
     //Just show message if user doesn't overload
  // messageDebug(DBG_INFO, __MODULE__, __LINE__,"CAN: ID=0x%08X, numframes=%i, overrun=%i, data=%02X %02X %02X %02X %02X %02X %02X %02X", id, numFrames, overRun, buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7]);
}


//-----------------------------------------------------------------------------
// 
//-----------------------------------------------------------------------------
static __task void thPacket(void) {
  void *msg;
  while(1) {
  
    // See if there is a message in the queue.. 
    if (os_mbx_wait (mbCANMSG, &msg, 0xFFFF)) {
      rxCAN(((CAN_MSG_OBJ*)msg)->mode_id, ((CAN_MSG_OBJ*)msg)->data, ((CAN_MSG_OBJ*)msg)->dlc);
      if (_free_box(CANMSGList, msg)) {
        //messageDebug(__MODULE__, __LINE__,"Error freeing block !");
      }
    } 
  }
}


//-----------------------------------------------------------------------------
// 
//-----------------------------------------------------------------------------
void initCAN(void) {
  
       MUTEX_INIT(mut);

     // Enable clocking for CAN and reset the controller
     Chip_Clock_EnablePeriphClock(SYSCTL_CLOCK_CAN);
     Chip_SYSCTL_PeriphReset(RESET_CAN);

       Chip_IOCON_PinMuxSet(LPC_IOCON, 0, 27, (IOCON_MODE_INACT | IOCON_DIGMODE_EN));
     Chip_IOCON_PinMuxSet(LPC_IOCON, 0, 28, (IOCON_MODE_INACT | IOCON_DIGMODE_EN));

     // CAN signal muxing LQFP48
     Chip_SWM_MovablePortPinAssign(SWM_CAN_TD1_O , 0, 27); // P0.27 CAN TxD 
     Chip_SWM_MovablePortPinAssign(SWM_CAN_RD1_I,  0, 28); // P0.28 CAN RxD     
     
  _init_box(CANMSGList, sizeof (CANMSGList), sizeof(CAN_MSG_OBJ));
  os_mbx_init(mbCANMSG, sizeof(mbCANMSG));

     gCANConfig.clkdiv = 5;
     gCANConfig.btr = BITRATE1000K12MHZ;     
     gCANConfig.isr_ena = TRUE;
     LPC_CAND_API->hwCAN_Init(&pCanHandle, (CAN_API_INIT_PARAM_T *)&myCANConfig);

     // Enable the CAN Interrupt
     NVIC_EnableIRQ(CAN_IRQn);

  // Receive everything for now..
  msg_obj.msgobj = 1;
  msg_obj.mode_id = CAN_MSGOBJ_EXT | 0x0;
  msg_obj.mask = 0x0;
     LPC_CAND_API->hwCAN_ConfigRxmsgobj(pCanHandle, &msg_obj);     

     START_THREAD_USR(thPacket, 2, (void*)stkthPacket, sizeof(stkthPacket));
     init=TRUE;
}
‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
0 Kudos
Reply

1,602 Views
ianbenton
Senior Contributor I

Thank you - it helps a little. If you were writing to the LPC15 CAN registers, it would be easy to see what was happening, as I would just look up the function of each particular register in the manual, and I would know what it did; but because you are calling C functions, and there is no manual (as far as I am aware) to say what parameters are passed and returned, in most cases, I am none-the-wiser.

0 Kudos
Reply

1,602 Views
carstengroen
Senior Contributor II

If you look in the Users Manual, there is 15 pages of documentation of the C_CAN ROM drivers that I use ?

Its really not that complicated

pastedImage_1.png

0 Kudos
Reply

1,602 Views
ianbenton
Senior Contributor I

I have read Chapter 41, and found the terminology arcane; but it is beginning to make more sense as I read it in conjunction with your software and Chapter 27. I was particularly amused by 41.4.2 - I'm glad I wasn't the only one who was confused!

I know it's not complicated - after all it is nothing but a glorified UART, and anyone who has ever used a MC6850 doesn't need software drivers provided for him before he can use it! It's cleverer than RS232/485 in that it can do all the arbitration  and error checking stuff, but simpler in that the message format is fixed.

A couple more things that are still puzzling me:

Do the API ROM drivers run the interface in "basic" mode or in whatever the other mode is called?

Why does it need RAM to be allocated to it? It has lots of RAM built in, doesn't it?

and just for interest's sake - does it figure out the baud rate for itself, or do all the nodes on the network have to be set to the same baud rate - if so, is there a common baud rate or does everyone use something differrent?

0 Kudos
Reply