LPC18xx CAN gateway (CAN basic mode)

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

LPC18xx CAN gateway (CAN basic mode)

1,983 Views
Dim724
Contributor I

Hello! We're working on bi-directional CAN gateway (basically a pass-through CAN0 <-> CAN1). Obviously we don't want to use any message filtering nor any acceptance masks. Any incoming message on CAN0 must be transmitted to CAN1 and vice versa. We're using Keil MBC1800 dev board with LPC1857. Unfortunately there is no example how to setup this chip in CAN basic mode. Reading this board I found an example how to setup CAN0_IRQHandler to accept any message. So the reception works now. But once we try to transmit the chip goes into infinite loop somewhere in the CAN stack. My understanding there should be something related to Tx states in CAN0_IRQHandler.

Here is our CAN0_IRQHandler. CAN1_IRQHandler is similar but using LPC_C_CAN1 instead of LPC_C_CAN0.

void CAN0_IRQHandler(void)
{
CCAN_MSG_OBJ_T can_msg;
     
uint32_t can_int, can_stat;
  while ( (can_int = Chip_CCAN_GetIntID(LPC_C_CAN0)) != 0 ) {
if ((CCAN_INT_STATUS == can_int) && (CCAN_STAT_RXOK & LPC_C_CAN0->STAT))
{
can_msg.id = (uint32_t) (LPC_C_CAN0->IF[1].ARB1 | LPC_C_CAN0->IF[1].ARB2 << 16);
 
if (can_msg.id & (0x1 << 30)){
can_msg.id &= CCAN_MSG_ID_EXT_MASK;
}
else {
can_msg.id >>= 18;
can_msg.id &= CCAN_MSG_ID_STD_MASK;
}
 
can_msg.dlc = (uint8_t) (LPC_C_CAN0->IF[1].MCTRL & CCAN_IF_MCTRL_DLC_MSK);
 
*((uint16_t *)&can_msg.data[0]) = (uint16_t) (LPC_C_CAN0->IF[1].DA1 & 0x0000FFFF);
*((uint16_t *)&can_msg.data[2]) = (uint16_t) (LPC_C_CAN0->IF[1].DA2 & 0x0000FFFF);
*((uint16_t *)&can_msg.data[4]) = (uint16_t) (LPC_C_CAN0->IF[1].DB1 & 0x0000FFFF);
*((uint16_t *)&can_msg.data[6]) = (uint16_t) (LPC_C_CAN0->IF[1].DB2 & 0x0000FFFF);
 
 
CAN_RxRdy[0] = 1;
memcpy(&CAN_RxMsg[0],&can_msg, sizeof(CCAN_MSG_OBJ_T));  
 
LPC_C_CAN0->STAT = LPC_C_CAN0->STAT & (~CCAN_STAT_RXOK);
return;
}
}
}
 
Here is our CAN init function:
void CAN_init(void) 
{
//Pinmux LPC_C_CAN0
Chip_SCU_PinMuxSet(0x3, 1, (SCU_MODE_INACT | SCU_MODE_INBUFF_EN | SCU_MODE_FUNC2)); /* CAN RD */
Chip_SCU_PinMuxSet(0x3, 2, (SCU_MODE_INACT | SCU_MODE_FUNC2)); /* CAN TD */
 
//Pinmux LPC_C_CAN1
Chip_SCU_PinMuxSet(0x1, 18, (SCU_MODE_INACT | SCU_MODE_INBUFF_EN | SCU_MODE_FUNC5)); /* CAN RD */
Chip_SCU_PinMuxSet(0x1, 17, (SCU_MODE_INACT | SCU_MODE_FUNC5)); /* CAN TD */
 
Chip_Clock_SetBaseClock(CLK_BASE_APB3, CLKIN_IDIVC, true, false);
 
Chip_CCAN_Init(LPC_C_CAN0);
Chip_CCAN_EnableTestMode(LPC_C_CAN0);
Chip_CCAN_SetBitRate(LPC_C_CAN0, CAN_BAUDRATE);
Chip_CCAN_EnableInt(LPC_C_CAN0, (CCAN_CTRL_IE | CCAN_CTRL_SIE | CCAN_CTRL_EIE));
Chip_CCAN_ConfigTestMode(LPC_C_CAN0, ( CCAN_TEST_BASIC_MODE));
NVIC_EnableIRQ(C_CAN0_IRQn);
 
Chip_CCAN_Init(LPC_C_CAN1);
Chip_CCAN_EnableTestMode(LPC_C_CAN1);
Chip_CCAN_SetBitRate(LPC_C_CAN1, CAN_BAUDRATE);
Chip_CCAN_EnableInt(LPC_C_CAN1, (CCAN_CTRL_IE | CCAN_CTRL_SIE | CCAN_CTRL_EIE));
Chip_CCAN_ConfigTestMode(LPC_C_CAN1, ( CCAN_TEST_BASIC_MODE));
NVIC_EnableIRQ(C_CAN1_IRQn);
}
 
And here is the transmit function:
void CAN_wrMsg(uint8_t ch, CAN_MSG_OBJ_T * msg)
{
if(ch == 0)
{
Chip_CCAN_Send(LPC_C_CAN0, CCAN_MSG_IF1, false, (CCAN_MSG_OBJ_T*)&msg);
Chip_CCAN_ClearStatus(LPC_C_CAN0, CCAN_STAT_TXOK);
}
else
{
Chip_CCAN_Send(LPC_C_CAN1, CCAN_MSG_IF1, false, (CCAN_MSG_OBJ_T*)&msg);
Chip_CCAN_ClearStatus(LPC_C_CAN1, CCAN_STAT_TXOK);
}
}
 
We would definitely appreciate some help and still open to any suggestions. The thing is, due to components shortage we're moving our working software (CMSIS based) from LPC1756 to LPC18xx (LPCOpen) so our timeframe is very short. 
Best regards!
0 Kudos
Reply
11 Replies

1,940 Views
Alice_Yang
NXP TechSupport
NXP TechSupport

Hello ,

This  CAN issue on Errata effects lpc185x/3x/2x/1x.

What about your basic CAN mode meaning? 

The demo under LPCopen is a simple project that can send and receive.

From your code, do you want to use the CAN0 and CAN1 in LPC1857 to communicate?

 

 

BR

Alice

 

0 Kudos
Reply

1,935 Views
Dim724
Contributor I

Alice,

On this MCU there is a CAN mode which, for some reasons, NXP called "Basic". UM10430 user's manual specifies: "The CAN Core can be set in Basic mode by programming the Test Register bit BASIC to one. In this mode the CAN controller runs without the Message RAM." We already figured out how it works, we can transmit and receive simultaneously from both channels. Btw, there is no example for this mode and I'm pretty sure very few people use it. Despite all transmit/receive functions being interrupt based, the performance of this mode is quite disappointing. In fact, if compared to LPC17xx series, NXP replaced the triple CAN Tx buffer of LPC1756, as example, by a single Tx buffer in LPC18xx. As a result, there is some lost of frames in both directions while operating in this mode, even under medium network load. So we cannot build a CAN gateway using this mode. Now the last hope will be to use the normal mode with Message RAM and somehow program the acceptance mask to accept any CAN id without specifying them. Do you have any idea how to do this? Thank you!

0 Kudos
Reply

1,917 Views
Alice_Yang
NXP TechSupport
NXP TechSupport

Hello,

Set  the CAN message interface command mask registers to 0:

Alice_Yang_0-1618995866102.png

 

 

0 Kudos
Reply

1,906 Views
Dim724
Contributor I

Alice,

Good advise, thank you! So we have initialized MSK1 and MSK2 with 0 values and we have our single Rx ID set to 0x1FFFFFF for both CAN0 and CAN1 controllers. In theory both controllers now must accept any frames within 0x0000001 to 0x1FFFFFF range. It works, so at this point we're getting close to our final goal. Now we have another issue while transferring data between CAN0 and CAN1. As we're working on a gateway, we're simple trying to receive a message from CAN0 channel and transmit it from CAN1 channel like this (inside the CAN0 IRQ

Chip_CCAN_GetMsgObject(LPC_C_CAN0, CCAN_MSG_IF1, can_int, &msg_buf);

Chip_CCAN_Send(LPC_C_CAN1, CCAN_MSG_IF1, false, &msg_buf);

So we inject a single message with canalizer connected to the CAN0 and we expect seeing it transmitted from CAN1. 

It works BUT once the function Chip_CCAN_Send is called, it generates CAN1 IRQ (which is probably normal since we're expecting TX completed event). But somehow, in this IRQ, the previously transmitted message is detected as new RX message. And since we're using the same 

Chip_CCAN_GetMsgObject(LPC_C_CAN1, CCAN_MSG_IF1, can_int, &msg_buf);

Chip_CCAN_Send(LPC_C_CAN0, CCAN_MSG_IF1, false, &msg_buf);

this will re-transmit the same message, this time through the CAN0 controller. Well, as you can imagine at the end we're going into loop where both controllers keep themselves busy by playing ping-pong with the same message (please see attached picture). As you can see, after the injection message at the line 1 the same message moves between CAN0 and CAN1.

We tried to use the Chip_CCAN_SetValidMsg(LPC_C_CAN0, CCAN_MSG_IF1, can_int, false) function to clear the message buffer but it doesn't work.

Any idea how can we prevent this behavior? Thanks again!

 

0 Kudos
Reply

1,895 Views
Alice_Yang
NXP TechSupport
NXP TechSupport

Hello Dim724,

Please allow me repeat your demands.

1) CAN0 received any data from other CAN nodes, then send it to CAN1, then CAN1 send to other CAN nodes.

2) CAN1 received any data from other CAN nodes, then send it to CAN0, then CAN0 send to other CAN nodes.

If I'm right,  why do you need this function ? How about just use one CAN node as "gateway"?

If you must do like your that, how about set a judgment, compare the received data with last time send, if the dame, just clear, if not the same, send.

 

BR

Alice

0 Kudos
Reply

1,874 Views
Dim724
Contributor I

Alice,

We're working on isolating gateway between 2 different CAN networks. The network A is connected to CAN0 and the network B to CAN1. The data exchange between both network is managed by LPC18xx MCU. There are some CAN ID from network A which are not allowed in network B and vice versa. It cannot be done using a single CAN channel. We're continuing to investigate but in our opinion there is untested bug inside LPCOpen CAN stack while both CAN controllers are working simultaneously. It appears that after each successful transmission the CAN(0/1) interrupt register (INT) continues to report the number of message object which caused interrupt even if there is no incoming messages. We're getting the Tx interrupt from successful transmission but there is no associate Rx interrupt (as it should be for each new Rx frame), so in reality there is no message pending. And there is no associated message data, cleaning buffers before calling Chip_CCAN_GetMsgObject() results in some random trash values. I'm pretty sure this case was never tested by people who used to work on LPCOpen CAN driver. Normally if you just send or receive frames from single (CAN0 or CAN1) controller all works fine. If you just receive frames on CAN0 and send them through CAN1 it works fine too. But if you do this simultaneously on both CAN0 and CAN1, something happens. It happens only if both controllers have possibility to send data to each other simultaneously. Ironically all this worked perfectly on LPC1756 with old CMSIS CAN drivers, no message-objects, no acceptance masks...we used these MCU for 10 years and never had any issues. 

0 Kudos
Reply

1,836 Views
Alice_Yang
NXP TechSupport
NXP TechSupport

Hello Dim724,

I searched and talking with internal, yes, there isn't the case.

And you said it can work well on CMSIS CAN driver, so how about port it to LPC1857? 

0 Kudos
Reply

1,971 Views
Alice_Yang
NXP TechSupport
NXP TechSupport

Hello Dim724,

What about your part number?

There is CAN demo under LPCopen for LPC18xx:

https://www.nxp.com/design/microcontrollers-developer-resources/lpcopen-libraries-and-examples/lpcop... 

0 Kudos
Reply

1,954 Views
Dim724
Contributor I

Following up, as we expected from Errata, the LPC1857 has some interactions with other peripherals on the same bus bridge... Disabling I2C0 communications brings back the CAN1_IRQHandler interrupts and (we're hoping) restores the normal CAN1 behavior...

But we still don't know how to transmit in basic CAN mode. Please advise!

0 Kudos
Reply

1,956 Views
Dim724
Contributor I

A little update on CAN reception, in fact only 1 channel works at a time. We have both channels connected to the CAN network (quite low load) and it appears there are some issues with CAN1. Often it just does not generate the interrupt. It's not happens every time but occurs randomly after reset, very confusing.  Most strange, if CAN1 gets the interrupt then CAN0 stops going into interrupt routine. And I don't see how possible both channels can interact. Also, we've noticed in the LPC18xx Errata some hardware issues with CAN1 on LPC185x, here it is:

Problem:

On the LPC185x flash-based devices, there is an issue with the C_CAN controller AHB bus address decoding that applies to both C_CAN controllers. It affects the C_CAN controllers when peripherals on the same bus are used. Writes to the ADC, DAC, I2C, and I2S peripherals can update registers in the C_CAN controller. Specifically, writes to I2C0, MCPWM, and I2S can affect C_CAN1. Writes to I2C1, DAC, ADC0, and ADC1 can affect C_CAN0. The spurious C_CAN controller writes will occur at the address offset written to the other peripherals on the same bus. For example, a write to ADC0 CR register which is at offset 0 in the ADC, will result in the same value being written to the C_CAN0 CNTL register which is at offset 0 in the C_CAN controller. Writes to the C_CAN controller will not affect other peripherals.

And as you can imagine, our dev board has the LPC1857...also this project uses I2C0 and according to UM10430 user's manual :

Remark: Use of C_CAN controller excludes operation of all other peripherals connected\to the same bus bridge. See the LPC18xx errata.

Could you please confirm if this issue is not present on lower series - LPC183x and LPC182x?

Here is again our updated CAN init function (I realized there are 2 different clock domains for CAN0 and CAN1 so there is an extra Chip_Clock_SetBaseClock(CLK_BASE_APB1, CLKIN_IDIVC, true, false) call compared to example.

void CAN_init(void) 
{
//Pinmux LPC_C_CAN0
Chip_SCU_PinMuxSet(0x3, 1, (SCU_MODE_INACT | SCU_MODE_INBUFF_EN | SCU_MODE_FUNC2)); /* CAN RD */
Chip_SCU_PinMuxSet(0x3, 2, (SCU_MODE_INACT | SCU_MODE_FUNC2)); /* CAN TD */
 
//Pinmux LPC_C_CAN1
Chip_SCU_PinMuxSet(0x1, 18, (SCU_MODE_INACT | SCU_MODE_INBUFF_EN | SCU_MODE_FUNC5)); /* CAN RD */
Chip_SCU_PinMuxSet(0x1, 17, (SCU_MODE_INACT | SCU_MODE_FUNC5)); /* CAN TD */
 
Chip_Clock_SetBaseClock(CLK_BASE_APB1, CLKIN_IDIVC, true, false);
Chip_Clock_SetBaseClock(CLK_BASE_APB3, CLKIN_IDIVC, true, false);
 
Chip_CCAN_Init(LPC_C_CAN0);
Chip_CCAN_SetBitRate(LPC_C_CAN0, CAN_BAUDRATE);
Chip_CCAN_EnableTestMode(LPC_C_CAN0);
Chip_CCAN_ConfigTestMode(LPC_C_CAN0, CCAN_TEST_BASIC_MODE);
Chip_CCAN_EnableInt(LPC_C_CAN0, (CCAN_CTRL_IE | CCAN_CTRL_SIE | CCAN_CTRL_EIE));
 
Chip_CCAN_Init(LPC_C_CAN1);
Chip_CCAN_SetBitRate(LPC_C_CAN1, CAN_BAUDRATE);
Chip_CCAN_EnableTestMode(LPC_C_CAN1);
Chip_CCAN_ConfigTestMode(LPC_C_CAN1, CCAN_TEST_BASIC_MODE);
Chip_CCAN_EnableInt(LPC_C_CAN1, (CCAN_CTRL_IE | CCAN_CTRL_SIE | CCAN_CTRL_EIE));
 
NVIC_EnableIRQ(C_CAN0_IRQn);
NVIC_EnableIRQ(C_CAN1_IRQn);
}

 

0 Kudos
Reply

1,963 Views
Dim724
Contributor I

Hello Alice,

The part number we will use for mass production will be LPC1837JET100, actually we do our evaluation tests on Keil MCB1800 equipped with LPC1857. The example provided with LPCOpen won’t work for our application (not 100% sure but unless you can setup the RX message objects for 100+ different CAN ID I don’t see how it could work). We eventually have tried this example, the transmission works fine but we got nothing on reception since we cannot specify all RX IDs. With CAN basic mode, the reception works fine but we cannot transmit. In my understanding, the basic mode requires completely different transmission setup (like using BUSY bit to execute transmission command) but there is no example how to do this and there is nothing in provided LPCOpen CCAN driver about this basic mode. It should be way simpler than using message objects and acceptance filters but even LPC18xx user’s manual barely mentions this basic mode so we definitely need a little help there.

0 Kudos
Reply