多年来,我一直使用PIC24EP512GP806在CAN应用中。上周我注意到,当消息计数大约为2500 MSG/SEC时,它有时没有接收到每秒广播一次的标准CAN消息。经过一周的调试,我发现问题在于对DMA传输的FIFO处理和C1RXFLU1的设置。当使用DMA和没有接收消息时,C1RXFuln寄存器中的16位是0,FNRB和FBP指向相同的FIFO缓冲器。当接收到消息时,硬件将CAN消息复制到指向FBP的缓冲器中,它设置相应的C1RxFuln位,增加FBP,并产生中断(或类似的)。问题是,当接收到消息和F时,C1RXFuln位不总是被设置。BP增加了。我用低消息率700 MSGs/s测试了3300次,每一次C1RXFuln位都没有被设置。我用标准的和扩展的CAN消息进行测试,问题也发生了。我用DMA缓冲区大小测试了24和32,这个问题出现在两个大小上。URS,FBP随着消息的接收而继续增加,但是FNBR不能移动到下一个消息,因为C1RXFuln不能被清除,因为它已经被清除了,因为它从来没有被设置过。我试着在软件中设置C1RXFuln位,然后清除它,但是它是读/清的,所以没有用。为了检查这个问题,下面可以在读取CAN消息时执行:如果FBP!= FNRB有一个要接收的消息,但是如果C1RxFuln中的FNRB位位置是明确的,则不可能清除导致FNRB增加的位。因此,即使消息被读出,下一次从FNRB位置读取消息时,我们也会得到相同的消息。PIC确实从中恢复,但并不总是以相同的方式恢复。有时,当接收到32个消息来填充缓冲器时,FBP将环绕,一旦它捕获FNRB,它将设置FIFOIF,然后重写先前的消息,并正确地开始设置C1RxFuln位一段时间。其他时间,似乎C1rxFuln位是在一段时间之后设置的,因为事情开始工作,但是没有FIFOIF产生。因为我在事实之后写了这个,我不再有很多例子,但是这里有一个例子:DMA缓冲器长度:32 DMA缓冲器0:TXDMA Buffer 1-31:RXFNRB:23 FBP:27 C1R。XFul1:0x0900C1RxFul2:0x0700 C1RxFuln:0B 0000 0111,0000 0000 0000 0000 0000 0000 000 FNRB指向0,这意味着它不认为有消息要读出,但有FBP!= FNRB。FBP也指向了0,这表明将有第二次C1RXFuln位没有设置,并且我可以确认,这是我看到的情况下,FNRB和FBP之间有几个零点。另一种选择是,当C1RXFuln位被清除时,FNRB没有增加,但是如果是这样的话,FBP不会指向具有27的位置0。我检查了勘误表,并且没有任何与这个问题有关的信息。附加信息:CAN消息正在被读取。通过轮询,DMA缓冲器的T。在每次设置RBIF时,在CAN1中断中递增计数器,并且得到由另一个设备发送的确切消息数,指示PIC正在正确接收所有消息。加油!= FNRB和相应的C1RXFuln位没有设置,偶尔我会得到相同的FNRB和相同消息的突发消息,即使我正在发送带有顺序数据的消息。工作:不要使用DMA。将所有消息放入单个接收缓冲器(Buff 1)。如果消息RX顺序要保持顺序,则不能使用多个缓冲区,除非它能够保证在C1中断中拔出消息时,没有消息不能被放入更高的缓冲器,然后是更低的,例如Buff 5和0。在C1中断中,检查C1RXFul1BIT.RXFLU1标志。如果设置,将消息读入缓冲区,增加软件缓冲区指针,然后清除标志。在主循环中,处理来自缓冲区的消息。在3800±MSgs/s的测试中,缓冲区最多包含1条消息,有时在写入Flash的情况下,有时会高达5或9。一行,几次错过了一个消息,RBOVIF和/ORC1RXOVF1标志被触发。在我的应用程序中大约有2500 MSGs/s,并且我很少写Flash或禁用中断,所以这不是问题。有问题,例如当我们使用DMA将TX数据传输到RS232时,内核在另一个应用程序中周期性地在NXP I.MX6上运行嵌入式Linux的方式崩溃。解决办法是不要使用DMA。希望有人看到这个会注意到我做错了什么。我无法想象在这个处理器上有一个与CAN和DMA相关的硬件问题,过去没有人注意到它。但是这里的解决方案似乎是不使用DMA。谢谢,卢克。
以上来自于百度翻译
以下为原文
I have been using the PIC24EP512GP806 in a CAN applica
tion for years. Last week I noticed that it was sometimes not receiving a standard CAN message that was being broadcasted once per second when the message count was around 2500msgs/sec.
After a week of debugging, I found that the problem is in the FIFO handling of the DMA transfer and the setting of the C1RXFUL1 and C1RXFUL2 bits.
When using DMA and there are no messages to receive, the 16 bits in the C1RXFULn registers are 0, and the FNRB and FBP point to the same FIFO buffer. When a message is received, the hardware copies the CAN message into the buffer pointed to be FBP, it sets the corresponding C1RXFULn bit, increases the FBP, and generates interrupts (or something like that).
The problem is that the C1RXFULn bit is not always set when a message is received and the FBP is increased. I tested this with low message rates 700msgs/s up to 3300 and every once in a while the C1RXFULn bit does not get set.
I tested with standard and extended CAN messages and the problem occurs with either.
I tested with a DMA buffer size of 24 and 32 and the problem occurs with both sizes.
When this occurs, the FBP continues to increase as messages are received, but the FNBR cannot move to the next message because the C1RXFULn cannot be cleared as it is already cleared, as it was never set. I tried setting the C1RXFULn bit in software and then clearing it but it is read/clear only, so that didn't work.
To check for this problem, the following can be performed when reading a CAN message:
If FBP != FNRB there is a message to receive, but if the FNRB bit location in C1RXFULn is clear, it is impossible to clear the bit which would cause the FNRB to increase. Therefore, even though the message is read out, the next time a message is read out from the FNRB location, we get the same message.
The PIC does recover from this, but not always in the same way. Sometimes as the 32 messages are received to fill the buffer, the FBP will wrap around and once it catches FNRB it will set the FIFOIF and then overwrite the previous messages and correctly start setting the C1RXFULn bits for a while. Other times, it seems that the C1RXFULn bit is set after a period of time because things start working but no FIFOIF is generated.
Since I am writing this after the fact, I no longer have many examples, but here is one example:
DMA Buffer length: 32
DMA Buffer 0: TX
DMA Buffer 1-31: RX
FNRB:23
FBP: 27
C1RXFUL1: 0x0000
C1RXFUL2: 0x0700
C1RXFULn: 0b 0000
0111
0000 0000 0000 0000 0000 0000
FNRB points to a 0 meaning that it doesn't think there is a message to read out, but there is since FBP!=FNRB. FBP also points to a 0 which indicates that there is going to be a second time where the C1RXFULn bit isn't set, and I can confirm that this is the case as I have seen it where there are a couple zeros between FNRB and FBP. The other option is that when the C1RXFULn bit is cleared the FNRB isn't increased, but if that was the case, the FBP wouldn't be pointing to position 27 which has a 0.
I checked the ERRATA, and there is nothing in there to do with this issue.
Additional information:
The CAN messages are being read out of the DMA buffers via polling.
I incremented a counter in the CAN1 interrupt each time RBIF was set and I got the exact number of messages that were being sent (by another device), indicating that the PIC was receiving all messages correctly.
I re-transmitted messages out of the receive routine when FBP!=FNRB and the corresponding C1RXFULn bit was not set and every once in a while I would get a burst of messages with the same FNRB and the same message even though I was sending messages with sequential data.
lib:
#define ECAN_MSG_BUF_LENGTH 32
typedef unsigned int ECANMSGBUF [ECAN_MSG_BUF_LENGTH][8];
extern __eds__ ECANMSGBUF ecan1msgBuf __attribute__((eds,space(dma)));
c:
__eds__ ECANMSGBUF ecan1msgBuf __attribute__((eds,space(dma),aligned(ECAN_MSG_BUF_LENGTH*16)));
__eds__ ECANMSGBUF ecan2msgBuf __attribute__((eds,space(dma),aligned(ECAN_MSG_BUF_LENGTH*16)));
/*********************************************************************
* Function Name : dma0init
* Description : Initialize the DMA for ECAN1 transmission
* Parameters : none
* Return Value : none
*********************************************************************/
void dma0init(void)
{
DMAPWC =0;
DMARQC =0;
DMA0CON=0x2020;
DMA0PAD=(volatile unsigned int)&C1TXD; /* ECAN 1 (C1TXD) */
DMA0CNT=0x0007;
DMA0REQ=0x0046; /* ECAN 1 Transmit */
DMA0STAL = __builtin_dmaoffset(ecan1msgBuf);
DMA0STAH = __builtin_dmapage(ecan1msgBuf);
DMA0CONbits.CHEN=1;
}
/*********************************************************************
* Function Name : dma2init
* Description : Initialize the DMA for ECAN1 reception
* Parameters : none
* Return Value : none
*********************************************************************/
void dma2init(void)
{
DMAPWC =0;
DMARQC =0;
DMA2CON=0x0020;
DMA2PAD=(volatile unsigned int)&C1RXD; /* ECAN 1 (C1RXD) */
DMA2CNT=0x0007;
DMA2REQ=0x0022; /* ECAN 1 Receive */
DMA2STAL = __builtin_dmaoffset(ecan1msgBuf);
DMA2STAH = __builtin_dmapage(ecan1msgBuf);
DMA2CONbits.CHEN=1;
}
void ecan1_switch_modes(unsigned int mode)
{
unsigned long i;
C1CTRL1bits.REQOP=mode;
for(i=0;i<1000000;i++) // From testing on the PIC24EP512GP806 at 40Mhz, this can be up to 6s.
{
if(C1CTRL1bits.OPMODE==mode)
break; // Nop();
ClrWdt();
}
}
/*********************************************************************
* Function Name : ecan1ClkInit
* Description : Init the CAN module - hardcoded for now
* Parameters : none
* Return Value : none
*********************************************************************/
void ecan1ClkInit(unsigned int speedBit)
{
C1CFG2bits.WAKFIL = 1; //This filter protects the module from wake-up due to short glitches on the CAN bus.
/* FCAN is selected to be FCY */
/* FCAN = FCY = 40MHz */
if(speedBit == 1) C1CTRL1bits.CANCKS = 0x0; //for double the baud rate to make it 500K
else C1CTRL1bits.CANCKS = 0x1; //250k. ERRATA: Operation of CANCKS is reverse of the data sheet
/* Synchronization Jump Width set to 1 TQ */
C1CFG1bits.SJW = 0x00;
/* Baud Rate Prescaler */
C1CFG1bits.BRP = 0x07;
/* Phase Segment 1 time is 5 TQ */
C1CFG2bits.SEG1PH=0x4;
/* Phase Segment 2 time is set to be programmable */
C1CFG2bits.SEG2PHTS = 0x1;
/* Phase Segment 2 time is 2 TQ */
C1CFG2bits.SEG2PH = 0x1;
/* Propagation Segment time is 2 TQ */
C1CFG2bits.PRSEG = 0x1;
/* Bus line is sampled one time at the sample point */
C1CFG2bits.SAM = 0x0;
}
/*********************************************************************
* Function Name : ecan1Init
* Description : Initialize CAN 1 and sets up filter 1 to
put everything that is received into the RX FIFO buffer
* Parameters : filterSel: picks which filter to use for CAN module
* Return Value : none
*********************************************************************/
void ecan1Init(unsigned int filterSel, unsigned int speedSel)
{
ecan1_switch_modes(4); // Request Configuration Mode
ecan1ClkInit(CAN_500KBAUD);
C1FCTRLbits.FSA=1; //0b00001; // FIFO Starts at Message Buffer 1 since the transmit is using buffer 0 only
C1FCTRLbits.DMABS=6; //0b110; // 32 CAN Message Buffers in DMA RAM - The receive FIFO can use 1-31
ecan1WriteRxAcptMask(0,0x00000000,1,1); // Pass Everything
ecan1WriteRxAcptFilter(0,0,1,15,0); // Put all messages into FIFO
// ECAN transmit/receive message control */
C1RXFUL1=C1RXFUL2=C1RXOVF1=C1RXOVF2=0x0000;
C1TR01CONbits.TXEN0=1; // ECAN1, Buffer 0 is a Transmit Buffer - Therefore, FIFO buffers 1-31 can be used as received buffers ERRATA says to only use Buffer 0 as a transmit buffer
C1TR01CONbits.TX0PRI=3; //0b11; // Message Buffer 0 Priority Level */
C1TR01CONbits.TXEN1 = 0; //ECAN1 make all other buffers receive - probably un-necessary for FIFO DMA
C1TR23CONbits.TXEN2 = 0;
C1TR23CONbits.TXEN3 = 0;
C1TR45CONbits.TXEN4 = 0;
C1TR45CONbits.TXEN5 = 0;
C1TR67CONbits.TXEN6 = 0;
C1TR67CONbits.TXEN7 = 0;
// Enter Normal Mode
ecan1_switch_modes(0);
}
/******************************************************************************
* Function : CAN1_ReceiveMessage
* Description: moves the message from the DMA memory to RAM
* Parameters : CAN_MSG *msgPntr: a pointer to the message structure in RAM
* that will store the message.
******************************************************************************/
inline unsigned int CAN1_ReceiveMessage(CAN_MSG *msgPntr)
{
unsigned int temp4, temp5, temp6;
unsigned int fnrb, fbp,rxfulBitVal;
CAN_MSG msgOut;
INTCON2bits.GIE = 0; // Disable global interrupts - weren't doing this previously, but see if it helps (does not)
// Check if there is a message in the buffer
fnrb = C1FIFObits.FNRB;
fbp = C1FIFObits.FBP;
if(fnrb == fbp)
{
INTCON2bits.GIE = 1; // Enable global interrupts
return FALSE;
}
//WORD[4] -- Extended ID(bits 0 to 15) <0:15>
temp4 = (((ecan1msgBuf[fnrb][2]>>10)&0x003F) | ((ecan1msgBuf[fnrb][1]<<6)&0xFFC0));
//WORD[5] -- Extended ID(bits 16 to 17) <0:1> & Standard ID <2:12>
temp5 = (((ecan1msgBuf[fnrb][1]>>10)&0x0003) | (ecan1msgBuf[fnrb][0]&0x1FFC));
//WORD[6] -- ????Buff Priority<0:1>??Set to 0?? & Data Len<2:5> & EID Enabled<6> & RTR Enabled<7>
temp6 = (((ecan1msgBuf[fnrb][2]<<2)&0x003C) | ((ecan1msgBuf[fnrb][0]<<6)&0x0040) | ((ecan1msgBuf[fnrb][2]>>2)&0x0080));
//Data
(*msgPntr).WORDS.WORD[0] = ecan1msgBuf[fnrb][3];
(*msgPntr).WORDS.WORD[1] = ecan1msgBuf[fnrb][4];
(*msgPntr).WORDS.WORD[2] = ecan1msgBuf[fnrb][5];
(*msgPntr).WORDS.WORD[3] = ecan1msgBuf[fnrb][6];
(*msgPntr).WORDS.WORD[4] = temp4;
(*msgPntr).WORDS.WORD[5] = temp5;
(*msgPntr).WORDS.WORD[6] = temp6;
if (fnrb<16) rxfulBitVal = bit_test(C1RXFUL1,fnrb);
else rxfulBitVal = bit_test(C1RXFUL2,fnrb-16);
if(rxfulBitVal == 0) // This should never happen - if it does, that's bad as there's no way to increment FNRB
{
msgOut.MEM.IDS.EXT_FRAME.BUS_PRIORITY = 0;
msgOut.MEM.IDS.PGN_ID.PGN = 0; // Torque/Speed Control
msgOut.MEM.IDS.EXT_FRAME.SOURCE_ADDRESS = 1;
msgOut.MEM.DATA.D_INT8[0] = C1RXFUL1>>8;
msgOut.MEM.DATA.D_INT8[1] = C1RXFUL1;
msgOut.MEM.DATA.D_INT8[2] = C1RXFUL2>>8;
msgOut.MEM.DATA.D_INT8[3] = C1RXFUL2;
msgOut.MEM.DATA.D_INT8[4] = fnrb;
msgOut.MEM.DATA.D_INT8[5] = fbp;
msgOut.MEM.DATA.D_INT8[6] = 0xFF;
msgOut.MEM.DATA.D_INT8[7] = 0xFF;
CAN1_EnqueueMessage(&msgOut);
}
// Clear the buffer full and overflow flags
if (fnrb<16)
{
C1RXFUL1&=~(1<
C1RXOVF1&=~(1<
}
else
{
C1RXFUL2&=~(1<<(fnrb-16));
C1RXOVF2&=~(1<<(fnrb-16));
}
INTCON2bits.GIE = 1; // Enable global interrupts
return TRUE;
}
Work around:
Do not use DMA.
Put all messages into a single receive buffer (buff 1). Cannot use multiple buffers if the message RX order is to be kept in sequence, unless it can be guaranteed that when pulling out messages in the C1Interrupt that there is no way that a message wouldn't be put into a higher buffer and then a lower, like buff 5 and then 0, for example. More testing would have to be done on this.
In the C1interrupt, check the C1RXFUL1bits.RXFUL1 flag. If set, read out the message into a buffer, increment the software buffer pointer and then clear the flag. Keep this as quick as possible.
In the main loop, process the messages from the buffer.
In testing at 3800+msgs/sec, the buffer contains 1 message most of the time, and sometimes up to 5 or 9 when doing things like writing flash where we disable interrupts briefly.
When writing flash multiple times in a row, a few times a message was missed and the RBOVIF and/or C1RXOVF1 flag triggered.
In my application there are around 2500msgs/s, and I am writing flash or disabling interrupts very infrequently, so this is not a problem.
I am not surprised to find this problem with the DMA as DMA often seems to have issues, such as the way our kernel crashed periodically in another application running embedded linux on an NXP i.MX6 when TX data was transferred to RS232 using DMA. The solution there was to not use DMA.
Hopefully someone looking at this would notice something that I am doing wrong. I cannot imagine that there is a hardware issue to do with CAN and DMA on this processor and nobody has noticed it in the past. But the solution here seems to be to not use DMA.
Thanks,
Luke
0