在LPC1788微控制器上通过CAN接收消息时遇到问题

时间:2012-09-04 14:57:43

标签: c microcontroller lpc

我正在使用恩智浦LPC1788微控制器进行编程,该微控制器具有几个CAN控制器。我一直试图让微控制器发送一条消息,然后从自身接收回来(本地自检)。

我认为传输工作正常(同事用探针确认了这一点),当我发送消息时,会产生一个中断并调用它的句柄。但是,CAN状态寄存器(CAN1GSR [全局状态寄存器,现在在代码中注释] + CAN1SR [本地状态寄存器])不报告已收到任何消息(消息接收缓冲区为空)。

代码(抱歉这是一团糟):

#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;
}

终端I / O的输出:

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

该代码也列在http://pastebin.com/8kEmbvta

编辑:线程死了,不太可能再被查看,但我更换了线

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

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

感谢HonkyTonk。它似乎对我的代码没有任何影响,但我认为这些引脚被指定为CAN控制器2(我使用的是CAN控制器1)。

2 个答案:

答案 0 :(得分:0)

首先,您不需要配置所有这些引脚。我只使用这些引脚就可以了:

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.

在你的情况下,甚至可能不需要最后两个。

接下来,你提到你已经确认你正在传输,但没有收到。你要接受的是移动

CAN1CMR_bit.SRR = 1;  

从TestCAN()内部替换

CAN1CMR_bit.TR = 1;

用它。 CAN控制器在发送时禁用接收,除非您写入SSR位而不是TR,这可以解释您遇到的问题。

第三,每次收到消息时,除非您从中断和捕获寄存器读取接收中断位,否则您将陷入写入的句柄中。读取该寄存器的前10位中的任何一位也会清除它们,清除RI位将使句柄停止多次触发。在CAN_IRQHandler()中的某处添加以下代码行:

CAN1ICR_bit.RI;

最后,您正在使用的ID(0xEFF)似乎使用了太多位;尝试用另一个id替换它,如0x7FF。

-Tag

答案 1 :(得分:0)

假设:您使用的是Keil Uvision IDE。 我想你正在检查外围设备&gt; CAN&gt;通信。 它只显示传输而不是接收。(您的怀疑)

如果您想确认环回还在接收外围设备&gt;系统查看器&gt; CAN中检查。 检查发送后是否更新接收缓冲区。