Question

I'm programming using the NXP LPC1788 microcontroller, which features a couple of CAN controllers. I've been trying to get the microcontroller to transmit a single message and then receive it back from itself (local self-test).

I think that the transmission works fine (a colleague confirmed this using a probe), and when I send the message, an interrupt is generated and the handle for it is called. However, the CAN status registers (CAN1GSR [global status register, commented out in code for now] + CAN1SR [local status register]) don't report that any message has been received (the message receive buffers are empty).

Code (sorry that it's a mess):

#include "includes.h"
#include <yfuns.h>

#define CAN1          0x1
#define CAN2          0x2
#define BAUD_RATE     115200
#define LPC_APB0_BASE 0x40000000UL
#define LPC_CAN1      (LPC_APB0_BASE + 0x44000)
#define AFRAM         0x40038000

#define STD_ID_FORMAT 0
#define EXT_ID_FORMAT 1

#define DATA_FRAME   0
#define REMOTE_FRAME 1

#define RECEIVING    0
#define TRANSMITTING 1

typedef struct 
{
  uint32_t id;
  uint8_t dataA[4];
  uint8_t dataB[4];
  uint8_t len;      
  uint8_t format;
  uint8_t type;
} CAN_MSG_Type;

CAN_MSG_Type transMessage, recMessage;

void initCAN();
void setCANBaudRate();
void testCAN();
void configCANMessage(uint32_t id, uint8_t dataA[4], uint8_t dataB[4], 
                      uint8_t len, uint8_t format, uint8_t type, uint8_t dir);
void CAN_IRQHandler(void);
uint8_t checkMessage(CAN_MSG_Type* transMessage, CAN_MSG_Type* recMessage);
void receiveCANMessage(CAN_MSG_Type* recMessage);
void sendCANMessage(CAN_MSG_Type* transMessage);

/** external data **/
#pragma section=".intvec"
/** public functions **/
/*************************************************************************
 * Function Name: InitClock
 * Parameters: void
 * Return: void
 *
  * Description: Initialize PLL0 and clocks' dividers. PLL = 96MHz,
 *               CPU - 96MHz, PCLK - 48 MHz
 *
 *************************************************************************/
void InitClock(void)
{
  /*Sys Clock Select as CPU clock
    divider 1:1*/
  CLK_SetCpuClk(CPUSEL_CLKSYS,1);
  /*Select IRC oscilator as Sys clock*/
  CLK_SetSysClk(SYSSEL_IRCOSC);
  /*Enable Main oscilator*/
  CLK_MainOscSet(CLK_ENABLE,MOSCRNG_1_20MHZ);
  /*Select Main oscilator as Sys clock*/
  CLK_SetSysClk(SYSSEL_MOSC);
  /*Enable set PLL 96Hz (M = 8; P = 1)*/
  CLK_SetMainPll(CLK_ENABLE, 8-1, 0);
  /*Set peripheral divider 1:2. Peripheral clock 48MHz*/
  CLK_SetPeriphClk(2);
  /*PLL0 out is used as the input to the CPU clock divider
  divider 1:1. CPU runs at 96MHz*/
  CLK_SetCpuClk(CPUSEL_CLKPLL,1); 
}

/*************************************************************************
 * Function Name: __low_level_init
 * Parameters: void
 * Return: 
 *
 * Description: Low level system init (clock, flash accelerator, 
 *              SDRAM and vector table address)
 *              
 *
 *************************************************************************/
int __low_level_init (void)
{
  /*if debug is in SDRAM
    clock and SDRAM initialization is made
    in mac file*/
#ifndef SDRAM_DEBUG 
  int cpuclk;
  /* Flash accelerator save value*/
  FLASHCFG = (6UL<<12) | 0x3AUL;
  /*Clock Init*/
  InitClock();
  /*Flash accelerator init*/
  cpuclk = CLK_GetClock(CLK_CPU);

  if( cpuclk < 20000000 ){
    FLASHCFG = (0x0UL<<12) | 0x3AUL;
  } else if( cpuclk < 40000000 ){
    FLASHCFG = (0x1UL<<12) | 0x3AUL;
  } else if( cpuclk< 60000000 ){
    FLASHCFG = (0x2UL<<12) | 0x3AUL;
  } else if( cpuclk < 80000000 ){
    FLASHCFG = (0x3UL<<12) | 0x3AUL;
  } else if( cpuclk < 100000000 ){
    FLASHCFG = (0x4UL<<12) | 0x3AUL;
  }
  /*SDRAM init*/
  SDRAM_Init();
#endif
  /*Set vector table location*/
  VTOR  = (unsigned int)__segment_begin(".intvec");

  return  1;
}

// LOW-LEVEL INITIALISATION - END



// Params: baudrate - the baud rate of the CAN peripheral.
// Returns: None
// Description: Sets the baud rate for the CAN
void setCANBaudRate(int baudrate)
{
  uint32_t result = 0;
  uint8_t NT, TSEG1, TSEG2;
  uint32_t CANPclk = 0;
  uint32_t BRP;

  CANPclk = CLK_GetClock(CLK_PERIPH);

  result = CANPclk / baudrate;

  // Calculate suitable nominal time value.
  // NT = TSEG1 + TSEG2 + 3
  // NTEG <= 24
  // TSEG1 >= TSEG2*3
  for(NT=24; NT > 0; NT -= 2)
  {
    if(!(result % NT))
    {
      BRP = result / NT - 1;
      NT--;
      TSEG2 = NT/3 - 1;
      TSEG1 = NT - NT/3 - 1;
      break;
    }
  }

  // Enter reset mode.
  CAN1MOD = 0x01;

  // Set bit timing.
  // Default: SAM = 0x00;
  //          SJW = 0x03;
  CAN1BTR = (TSEG2 << 20) | (TSEG1 << 16) | (3 << 14) | BRP;

  // Enter normal operating mode.
  CAN1MOD = 0;
}

// Params: None
// Returns: None
// Description: Initialises the CAN peripheral.
void initCAN()
{
  // Enable power and clock.
  PCONP_bit.PCAN1 = 1;

  // Configure pins.
  IOCON_P0_00 &= ~0x7; IOCON_P0_00 |= 0x1;
  IOCON_P0_01 &= ~0x7; IOCON_P0_01 |= 0x1;

  IOCON_P0_04 &= ~0x7; IOCON_P0_04 |= 0x2;
  IOCON_P0_05 &= ~0x7; IOCON_P0_05 |= 0x2;

  IOCON_P0_21 &= ~0x7; IOCON_P0_21 |= 0x4;
  IOCON_P0_22 &= ~0x7; IOCON_P0_22 |= 0x4;

  IOCON_P2_07 &= ~0x7; IOCON_P2_07 |= 0x1;
  IOCON_P2_08 &= ~0x7; IOCON_P2_08 |= 0x1;

  // Triggers reset mode - all (writable) registers can be written to.
  CAN1MOD = 1;
  CAN1IER = 0;
  CAN1GSR = 0;

  // Aborts transmissions, releases receive buffer and clears data overrun.
  CAN1CMR_bit.AT  = 1;
  CAN1CMR_bit.RRB = 1;
  CAN1CMR_bit.CDO = 1;

  // Read to clear register.
  uint16_t i = CAN1ICR;

  AFMR = 0x01;

  SFF_SA     = 0;
  SFF_GRP_SA = 0;
  EFF_SA     = 0;
  EFF_GRP_SA = 0;
  ENDOFTABLE = 0;

  AFMR  = 0;

  // Set baud rate.
  setCANBaudRate(BAUD_RATE);
}

// Params: None
// Returns: None
// Description: Performs a self-test on the CAN peripheral.
void testCAN()
{
  uint32_t *ptr;

  // Enter reset mode.
  CAN1MOD_bit.RM  = 1;

  // Enable bypassing.
  AFMR_bit.ACCBP = 1;

  // Clear acceptance filter RAM.
  for(uint32_t i=0; i < 512; i++)
  {
    ptr  = (uint32_t*)(AFRAM+i);
    *ptr = 0;
  }

  /*
  // Set up acceptance filter values; reserve space in SFF_sa for one
  // standard format group id.
  SFF_SA     = (0x00 << 1);
  SFF_GRP_SA = (0x00 << 1);
  EFF_SA     = (0x20 << 1);
  EFF_GRP_SA = (0x20 << 1);
  ENDOFTABLE = (0x20 << 1);

  ptr = (uint32_t*)AFRAM;
  // [    Valid ID 1   ] [    Valid ID 2   ]
  // 0010 0111 1111 1111 0010 0000 0000 0000
  // *ptr = 0x2EFF2000;

  // 0010 0000 0000 0000 0010 0111 1111 1111
  *ptr = 0x20002EFF;
  */

  // Enable self-testing mode.
  CAN1MOD_bit.STM = 1;
  CAN1CMR_bit.SRR = 1;

  // Re-enter normal operational mode.
  CAN1MOD_bit.RM  = 0;

  // Enable interrupts on transmitting and receiving messages.
  CAN1IER_bit.RIE      = 1;
  CAN1IER_bit.TIE1     = 1;
  CAN1IER_bit.IDIE     = 1;
  SETENA0_bit.SETENA25 = 1;

  // [(En)/Dis]able AF bypassing.
  AFMR_bit.ACCBP  = 1;
  //AFMR_bit.ACCOFF = 0;
  //AFMR_bit.EFCAN  = 1;


  uint8_t dataA[] = { 0x12, 0x12, 0x12, 0x12 };
  uint8_t dataB[] = { 0x34, 0x34, 0x34, 0x34 };
  uint8_t dataZ[] = { 0, 0, 0, 0};
  configCANMessage(0xEFF, dataA, dataB, 8, STD_ID_FORMAT, DATA_FRAME,
                   TRANSMITTING);

  configCANMessage(0, dataZ, dataZ, 0, 0, 0, RECEIVING);

  printf("Sending message...\n");

  sendCANMessage(&transMessage);
}

// Params: id       - the id of the message (if format = STD_ID_FORMAT, then id
//                    should be 11 bits long, else if format = EXT_ID_FORMAT,
//                    then id should be 29 bits long).
//         dataA[4] - data field A.
//         dataB[4] - data field B.
//         len      - the length of the data field in bytes
//                    (0000b - 0111b: 0-7 bytes, 1xxxb - 8 bytes).
//         format   - the format of the id
//                    (STD_ID_FORMAT - 11 bit format,
//                    EXT_ID_FORMAT - 29 bit format).
//         type     - remote transmission request
//                  - (DATA_FRAME - the number of data bytes called out by the
//                    DLC field are sent from the CANxTDA and CANxTDB
//                    registers,
//                    REMOTE FRAME - a remote frame is sent).
//         dir      - specifies if the message format is for messages being
//                    sent or received (0 - receiving, 1 - transmitting).
// Returns: None
// Description: Configures the format for transmitting and receiving messages.
void configCANMessage(uint32_t id, uint8_t dataA[4], uint8_t dataB[4],
                               uint8_t len, uint8_t format, uint8_t type,
                               uint8_t dir)
{
  if(dir)
  {
    transMessage.id     = id;

    for(int i=0; i < 4; i++)
    {
      transMessage.dataA[i]  = dataA[i];
      transMessage.dataB[i]  = dataB[i];
    }

    transMessage.len    = len;
    transMessage.format = format;
    transMessage.type   = type;
  }
  else
  {
    recMessage.id     = id;

    for(int i=0; i < 4; i++)
    {
      recMessage.dataA[i]  = dataA[i];
      recMessage.dataB[i]  = dataB[i];
    }

    recMessage.len    = len;
    recMessage.format = format;
    recMessage.type   = type;
  }
}

// Params: None
// Returns: None
// Description: Handles messages received over CAN.
void CAN_IRQHandler(void)
{  
  printf("IRQ\n");

  /*
  // Check receive buffer status.
  if(CAN1GSR_bit.RBS) 
  {
    printf("You have mail\n");
  }
  else
  {
    printf("You don't have mail\n");
  }
  */
  CAN1ICR_bit;

  if(CAN1SR_bit.RBS)
  {
    receiveCANMessage(&recMessage);

    // Validate received and transmitted information.
    if(checkMessage(&transMessage, &recMessage))
    {
      printf("Self test is successful\n");
    }
    else
    {
      printf("Self test is not successful\n");
    }
  }
}

// Params: transMessage - the message being transmitted.
//         recMessage - the message being received.
// Returns: Whether the two messages are the same (0 - false, 1 - true).
// Description: Compares the two messages to each other and determines if
//              they are identical
uint8_t checkMessage(CAN_MSG_Type* transMessage, CAN_MSG_Type* recMessage)
{
  if(transMessage->format != recMessage->format) return FALSE;
  if(transMessage->id != recMessage->id) return FALSE;
  if(transMessage->len != recMessage->len) return FALSE;
  if(transMessage->type != recMessage->type) return FALSE;

  for(int i=0; i < 4; i++)
  {
    if(transMessage->dataA[i] != recMessage->dataA[i]
       || transMessage->dataB[i] != recMessage->dataB[i]) return FALSE;
  }

  return TRUE;
}

// Params: recMessage - a message loaded with the information of the incoming
//         data.
// Returns: None
// Description: loads data received over CAN into a message format. 
void receiveCANMessage(CAN_MSG_Type* recMessage)
{
  uint32_t data;

  // Check status of receive buffer.
  if(CAN1SR_bit.RBS)
  {
    recMessage->format = CAN1RFS_bit.FF;
    recMessage->type   = CAN1RFS_bit.RTR;
    recMessage->len    = CAN1RFS_bit.DLC;
    recMessage->id     = CAN1RID;

    if(recMessage->type == DATA_FRAME)
    {
      // Read first four bytes of data.
      data = CAN1RDA;

      recMessage->dataA[0] = (data & 0x000000FF) >> 0;
      recMessage->dataA[1] = (data & 0x0000FF00) >> 8;
      recMessage->dataA[2] = (data & 0x00FF0000) >> 16;
      recMessage->dataA[3] = (data & 0xFF000000) >> 24;

      // Read second four bytes of data.
      data = CAN1RDB;

      recMessage->dataB[0] = (data & 0x000000FF) >> 0;
      recMessage->dataB[1] = (data & 0x0000FF00) >> 8;
      recMessage->dataB[2] = (data & 0x00FF0000) >> 16;
      recMessage->dataB[3] = (data & 0xFF000000) >> 24;

      // Release receive buffer.
      CAN1CMR_bit.RRB = 1;
    }
    // If the received frame is just a Remote Frame, then we do not have data
    // and we release the receive buffer.
    else
    {
      CAN1CMR_bit.RRB = 1;
    }
  }
}

// Params: transMessage - the message to be transmitted.
// Returns: None
// Description: Prepares a message to be transmitted over CAN.
void sendCANMessage(CAN_MSG_Type* transMessage)
{
  uint32_t data;

  // Check status of transmit buffer 1.
  if(CAN1SR_bit.TBS1)
  {
    CAN1TFI1_bit.DLC = transMessage->len;

    if(transMessage->type == REMOTE_FRAME)
    {
      CAN1TFI1_bit.RTR = 1;
    }
    else
    {
      CAN1TFI1_bit.RTR = 0;
    }

    if(transMessage->format == EXT_ID_FORMAT)
    {
      CAN1TFI1_bit.FF = 1;
    }
    else
    {
      CAN1TFI1_bit.FF = 0;
    }

    // Write CAN ID.
    CAN1TID1 = transMessage->id;

    data = ((transMessage->dataA[0]) << 0)
         | ((transMessage->dataA[1]) << 8)
         | ((transMessage->dataA[2]) << 16)
         | ((transMessage->dataA[3]) << 24);

    // Write first four bytes of data.
    CAN1TDA1 = data;

    data = ((transMessage->dataB[0]) << 0)
         | ((transMessage->dataB[1]) << 8)
         | ((transMessage->dataB[2]) << 16)
         | ((transMessage->dataB[3]) << 24); 

    // Write second four bytes of data.
    CAN1TDB1 = data;

    // Select transfer buffer 1.
    CAN1CMR_bit.STB1 = 1;

    // Write transmission request.
    CAN1CMR_bit.TR = 1;

    while(!CAN1SR_bit.TCS1)
    {
      printf("Waiting...\n");
    };

    //printf("Got this far\n");
  }
}

int main()
{
  // Initialise CAN.
  initCAN();

  // Enter self-test mode.
  testCAN();

  printf("In main\n");

  while(1);

  return 0;
}

Output from Terminal I/O:

Sending message...
WIRQ
aiting...
In main

The code is also listed at http://pastebin.com/8kEmbvta

EDIT: The thread's dead and it's unlikely to be viewed again, but I replaced the line

IOCON_P0_08 &= ~0x7; IOCON_P2_08 |= 0x1;

with

IOCON_P2_08 &= ~0x7; IOCON_P2_08 |= 0x1;

thanks to HonkyTonk. It didn't seem to have any effect on my code, but then again I think these pins are designated to CAN controller 2 (I'm using CAN controller 1).

Était-ce utile?

La solution

First off, you don't need to configure all those pins. I've got it working using just these pins:

IOCON_P0_00 &= ~0x7; IOCON_P0_00 |= 0x1; // RD1.
IOCON_P0_01 &= ~0x7; IOCON_P0_01 |= 0x1; // TD1.
IOCON_P0_04 &= ~0x7; IOCON_P0_04 |= 0x2; // RD2.
IOCON_P0_05 &= ~0x7; IOCON_P0_05 |= 0x2; // TD2.

In your case, probably even the last two aren't needed.

Next off, you mentioned that you've confirmed that you're transmitting, but not receiving. What you need to do to get it to receive is move

CAN1CMR_bit.SRR = 1;  

from inside TestCAN() and replace

CAN1CMR_bit.TR = 1;

with it. The CAN controller disables reception while transmitting unless you write the SSR bit instead of TR, which'd explain the problem you're having.

Thirdly, everytime you receive a message, you'll be stuck inside the handle you wrote unless you read the Receive Interrupt bit from the Interrupt and Capture register. Reading any of the first 10 bits of that register also clears them, and clearing the RI bit will stop the handle from firing more than once. Add the following line of code somewhere inside CAN_IRQHandler():

CAN1ICR_bit.RI;

Lastly, the id you're using (0xEFF) seems to be using too many bits; try replacing it with another id like 0x7FF.

-Tag

Autres conseils

Assumption: You are using Keil Uvision IDE. I think you are checking in Peripherals>CAN>Communication. It only shows transmit and not receive.(your doubt)

If you want to confirm that a loopback is also receiving check in Peripherals>System Viewer>CAN. Check if receive buffers are updated after a transmit.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top