Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to clear TX buffer #60

Open
NeliusNDL opened this issue Feb 11, 2021 · 9 comments
Open

How to clear TX buffer #60

NeliusNDL opened this issue Feb 11, 2021 · 9 comments

Comments

@NeliusNDL
Copy link

Hello, it seems that these lines

// wait for TX buffer to free
while ((readRegister(REG_SR) & 0x04) != 0x04) {
yield();
}

and

// wait for TX complete
while ((readRegister(REG_SR) & 0x08) != 0x08) {
if (readRegister(REG_ECC) == 0xd9) {
modifyRegister(REG_CMR, 0x1f, 0x02); // error, abort
return 0;
}
yield();
}

can make the device hang, I think that the TX buffer is full when there is nothing connected to the bus to RX and ack the data. Then the buffer gets full and the device hangs.

How can the TX buffer be cleared ? or there should be a timeout for these while loops and then return an error code

@Petros144
Copy link

This error happens when you don´t have a Connection to the Network. If you have It terminated right it won´t hang.

I know it its a bad habit to have this kind of scenario in the code, but i haven´t had any issues with this behavior.

I found some page on the web that had a janky workarround for this.

@Petros144
Copy link

@NeliusNDL

EDIT:
ESP32SJA1000Class::endPacket()

int ESP32SJA1000Class::endPacket()
{
if (!CANControllerClass::endPacket()) {
return 0;
}
const uint8_t TIMEOUTVALUE = 50;
uint8_t uiTimeOut = 0;

// wait for TX buffer to free
while (((readRegister(REG_SR) & 0x04) != 0x04) && (uiTimeOut < TIMEOUTVALUE)) {
yield();
uiTimeOut++;
}
if(uiTimeOut == TIMEOUTVALUE) return -1; /* get tx buff time out */
uiTimeOut = 0;

int dataReg;

if (_txExtended) {
writeRegister(REG_EFF, 0x80 | (_txRtr ? 0x40 : 0x00) | (0x0f & _txLength));
writeRegister(REG_EFF + 1, _txId >> 21);
writeRegister(REG_EFF + 2, _txId >> 13);
writeRegister(REG_EFF + 3, _txId >> 5);
writeRegister(REG_EFF + 4, _txId << 3);

dataReg = REG_EFF + 5;

} else {
writeRegister(REG_SFF, (_txRtr ? 0x40 : 0x00) | (0x0f & _txLength));
writeRegister(REG_SFF + 1, _txId >> 3);
writeRegister(REG_SFF + 2, _txId << 5);

dataReg = REG_SFF + 3;

}

for (int i = 0; i < _txLength; i++) {
writeRegister(dataReg + i, _txData[i]);
}

if ( _loopback) {
// self reception request
modifyRegister(REG_CMR, 0x1f, 0x10);
} else {
// transmit request
modifyRegister(REG_CMR, 0x1f, 0x01);
}

// wait for TX complete
while (((readRegister(REG_SR) & 0x08) != 0x08) && (uiTimeOut < TIMEOUTVALUE)) {
if (readRegister(REG_ECC) == 0xd9) {
modifyRegister(REG_CMR, 0x1f, 0x02); // error, abort
return 0;
}
yield();
uiTimeOut++;
}
if(uiTimeOut == TIMEOUTVALUE) return -2; /* send msg timeout */

return 1;
}

//-------------------------------------------------------------

IN PROGRAM:

CAN.beginPacket(id);
byte anzahl = CAN.write(sdata, 8);
Serial.print(anzahl);
Serial.print(" Byte\t");
int ERROR_CODE = CAN.endPacket(); // must be a int
Serial.print(ERROR_CODE); Serial.print('\t');
switch (ERROR_CODE) {
case 1: Serial.print("Buf sent!\r\n");
break;
case -1: Serial.print("Get tx buff time out!\r\n");
break;
case -2: Serial.print("Send msg timeout!\r\n");
break;
default: Serial.print("Buf send failed!\r\n");
}

//-------------------------------------------------------------

Console:

Message to send: 00 01 09 03 04 05 06 07 8 Byte 1 Buf sent!
Message to send: 00 01 0A 03 04 05 06 07 8 Byte 1 Buf sent!
Message to send: 00 01 0B 03 04 05 06 07 8 Byte -2 Send msg timeout!
Message to send: 00 01 0C 03 04 05 06 07 8 Byte -1 Get tx buff time out!
Message to send: 00 01 0D 03 04 05 06 07 8 Byte -1 Get tx buff time out!
Message to send: 00 01 0E 03 04 05 06 07 8 Byte -1 Get tx buff time out!
Message to send: 00 01 0F 03 04 05 06 07 8 Byte -1 Get tx buff time out!
Message to send: 00 01 10 03 04 05 06 07 8 Byte -1 Get tx buff time out!
Message to send: 00 01 11 03 04 05 06 07 8 Byte -1 Get tx buff time out!
Message to send: 00 01 12 03 04 05 06 07 8 Byte -1 Get tx buff time out!
Message to send: 00 01 13 03 04 05 06 07 8 Byte 1 Buf sent!
Message to send: 00 01 15 03 04 05 06 07 8 Byte 1 Buf sent!
Message to send: 00 01 14 03 04 05 06 07 8 Byte 1 Buf sent!

@NeliusNDL
Copy link
Author

@Petros144 thank you, you are most helpful !!!!
I have implemented this and it is working as expected.
If there is a device plugged in to listen and RX, then all is well. If I leave the CAN bus unconnected (but still properly terminated) while I continually try to send keep alive ID's I get send errors as expected. When I then reconnect a device everything works well.

But, if I leave the CAN bus disconnected for at least an hour, it seems the TX buffer gets backed up. When I then reconnect a device to the CAN bus I get the " Get tx buff time out!" error.
This also confirms now that before, when the task was stuck in the while loop is was because of this error "tx buff time out".

I need a way to flush the CAN bus when this happens, not even ESP.restart() can do the trick, only a power reset (or pressing reset button) can resolve the problem.

@Petros144
Copy link

@NeliusNDL i just had a look at the Datasheet, and I found this:

/* address and bit definitions for the Command Register /
#define CommandReg XBYTE[1]
#define TR_Bit 0x01 /
transmission request bit /
#define AT_Bit 0x02 /
abort transmission bit / <<<<<<< this seems interresting
#define RRB_Bit 0x04 /
release receive buffer bit /
#define CDO_Bit 0x08 /
clear data overrun bit /
#if defined (PeliCANMode)
#define SRR_Bit 0x10 /
self reception request bit */

//--------------------------

also see here:
https://www.nxp.com/docs/en/application-note/AN97076.pdf
(Page 29)

Example for the PeliCAN mode:
Definitions for the different constants and variables, etc., are given in the Appendix. Variables may be interpreted
different in BasicCAN and PeliCAN mode, e.g., “InterruptEnReg” points to the Control Register in BasicCAN
mode but to the Interrupt Enable Register in PeliCAN mode. The language “C” is used for programming.
After having initialized the CAN controller according to the example given in chapter 4.2.1, normal
communication can be started.
.
.
/* wait until the Transmit Buffer is released /
do
{
/
start a polling timer and run some tasks while waiting
break the loop and signal an error if time too long /
} while((StatusReg & TBS_Bit ) != TBS_Bit );
/
Transmit Buffer is released, a message may be written into the buffer /
/
in this example a Standard Frame message shall be transmitted /
TxFrameInfo = 0x08; /
SFF (data), DLC=8 /
TxBuffer1 = 0xA5; /
ID1 = A5, (1010 0101) /
TxBuffer2 = 0x20; /
ID2 = 20, (0010 0000) /
TxBuffer3 = 0x51; /
data1 = 51 /
.
.
TxBuffer10 = 0x58; /
data8 = 58 /
/
Start the transmission /
CommandReg = TR_Bit ; /
Set Transmission Request bit */
.
.
The TS and RS flags in the Status Register can be used for detecting, that the CAN controller has reached the
idle-state. The TBS- and TCS-flags can be checked for a successful transmission.

Im not a professional in CAN or c++ mabe you can see some helpfull stuff here.

@NeliusNDL
Copy link
Author

NeliusNDL commented Apr 14, 2021

@Petros144 I have added a the following line:
vTaskDelay(5/portTICK_PERIOD_MS); // wait in 5ms chunks
inside the 2 x while loops just before the yield(); command.
I think that the yield(); is not very predictable and maybe sometimes there is nothing to yield(); to and that this time will just be too fast for the CAN TX buffer to become ready and maybe I get a false return of error code of -1

ie
// wait for TX buffer to free
while (((readRegister(REG_SR) & 0x04) != 0x04) && (uiTimeOut < TIMEOUTVALUE)) {
vTaskDelay(5/portTICK_PERIOD_MS); // wait in 5ms chunks
yield();
uiTimeOut++;
}

note that I have also made
const uint8_t TIMEOUTVALUE = 10; // x 5ms = 50ms note there is an additional delay regulated by the yield();
I know that this is perhaps a bit long

@pooyasa
Copy link

pooyasa commented May 17, 2022

Thanks to @Petros144 I was able to fix the hanging problem, however, from that point forward, I only got Send msg timeout. So I put a

  case -2: 
    Serial.print("Send msg timeout!\r\n");
    CAN.sleep();
    delay(50);
    CAN.wakeup();
    break;

and the problem was solved.

@kouuta
Copy link
Contributor

kouuta commented Mar 20, 2023

I encountered a similar situation.
When I sent packets while the bus was in an unstable state, the ESP32 froze.
(For example, when there was no termination resistance on the bus)
I made changes to the library and created a sample code based on the code above.
Please refer to pull request #118 for details.

@aghorp27
Copy link

aghorp27 commented May 18, 2023

Hi all,
I tried implementing all the above solutions but my problem still persists.
I have connected ESP 32 with MCP 2551.
Initially, the code was stuck at CAN.endPacket(); after implementing the above solution I get "tx buff time out!" continuously.
The issue is only during transmission, as it receives data properly from CAN analyzer
Please help.

@ojuniour
Copy link

ojuniour commented Nov 8, 2024

I'm surprised no fix for the MCP2515 module as this issue exists in that version also.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants