gicking / LIN_master_Arduino

LIN master node emulation with preemptive background operation
MIT License
31 stars 3 forks source link

Wrong received Data #3

Closed tim63613 closed 2 years ago

tim63613 commented 2 years ago

Hi, when I want to receive data with "receiveSlaveResponse", the received data is wrong (but just partly!). I checked it with a LIN-Oscilloscope, for example: The oscilloscope reads:

AA BB CC DD EE FF 00 00 Checksum 12

When I use the library I get: AA BB CA DF EE FF 00 00 Checksum 12

The checksum is correct, so the library doesnt throw an error. Also the received data inside the function LIN_Master::handlerReceive(void) is correct. After this, a pointer has been used with rx_handler and from there on the data gets corrupted. Please check.

tim63613 commented 2 years ago

I'm using Arduino Mega with Serial3 and Arduino 1.8.16 IDE

gicking commented 2 years ago

hi, sorry to hear this! But before I go hunting, one more question: do you use call-back functions or plain receiveSlaveResponse()? Maybe you can provide a minimal example project, which I could adapt to the LIN slave I have at hand...? Thanks in advance!

tim63613 commented 2 years ago

Principally I'm using the same contents as in the example. Here's the code:

/**
 * Testversuch einer LIN-Kommunikation
 * 
 * 27.09.2021
 * Background operation mit TaskScheduler
 * 
*/

// include files
#include "LIN_master3.h"      // read Library for communication via Serial3
#include "Tasks.h"            // read Library TaskScheduler for LIN background operation

// task scheduler periods [ms]
#define PRINT_PERIOD  5000    // period for status output
#define LIN_PERIOD    5000      // LIN frame every N ms

/*// pin to demonstrate background operation
#define PIN_TOGGLE    30
*/

// helper routine to print slave status
void printStatus(void);

// global variables
uint8_t  Rx[8];         // received data, Rx is an array of 8 bit

void setup(void)
{
  /*// show background operation
  pinMode(PIN_TOGGLE, OUTPUT);
  */

  // for user interaction via console
  Serial.begin(9600); while(!Serial); //beginning with serial communication and waiting it for being ready

  // initialize LIN master with baudrate 19200 kbit/s, LIN_V1 protocol, background operation true
  LIN_master3.begin(19200, LIN_V1, true);

  // init task scheduler (also required for LIN master emulation!)
  Tasks_Init();
  Tasks_Add((Task) LIN_scheduler, LIN_PERIOD, 0);
  Tasks_Add((Task) printStatus, PRINT_PERIOD, PRINT_PERIOD);
  Tasks_Start();

} // setup()

void loop(void)
{
  // toggle pin to show background operation
  //digitalWrite(PIN_TOGGLE, !digitalRead(PIN_TOGGLE));

} // loop()

// actual LIN scheduler. Periodically called by task scheduler
void LIN_scheduler(void)
{
  uint8_t id = 0x21; //ID of LIN-Device
  uint8_t numData = 6; //Each frame can be between 1 and 8 Bytes long. For LIN-Device one frame has 6 bytes
  uint8_t data[8];

    // get slave status. Copy data to buffer after reception
    LIN_master3.receiveSlaveResponse(id, numData, Rx);

}

// print slave response signals. Periodically called by task scheduler
void printStatus(void)
{
  // print time
  //Serial.print(millis()); Serial.println("ms");

  // LIN ok -> print received data
  if (LIN_master3.error == LIN_SUCCESS)
  {
    for (uint8_t i=0; i<8; i++)
    {
      Serial.print(i); Serial.print("\t0x"); Serial.println(Rx[i], HEX);
    }

    //if (LIN_master3.error == LIN_SUCCESS){
    //  Serial.println("success");
  }

  // print LIN error
  else {
    Serial.println("LIN error");
  }

  // reset latched error and flag for data received
  LIN_master3.error = LIN_SUCCESS;
  LIN_master3.flagRxComplete = false;

}

The long LIN_Scheduler and printing scheduler are just for test purposes, to be able to read the values with the oscilloscope. Thanks for your help gicking :)

MicrosoftTeams-image (1) MicrosoftTeams-image (2)

gicking commented 2 years ago

hi,

sorry for the delay, but I had problems getting a LIN slave, with my lab access restricted due to Covid. But now I'm up and running, so here is the setup for below tests:

LIN Slave

LIN Master

Test 1: blocking

// include files

include "LIN_master2.h" // LIN via Serial2

// pause between LIN frames

define LIN_PAUSE 2000

void setup(void) { // for user interaction via console Serial.begin(115200); while(!Serial);

// initialize LIN master (blocking operation) LIN_master2.begin(19200, LIN_V2, false);

} // setup()

void loop(void) { static uint32_t lastCall = LIN_PAUSE; uint8_t Tx[2] = {0,0}; uint8_t Rx[8];

// halt on 's' received from console (for sync if (Serial.available()) { char c = Serial.read(); if (c == 's') { while(1); } }

// simple LIN scheduler if (millis() - lastCall > LIN_PAUSE) { lastCall = millis();

// receive slave response (blocking)
LIN_master2.receiveSlaveResponse(0x05, 8, Rx);

// print status and data
//Serial.print(millis()); Serial.println("ms");
Serial.print("status: "); Serial.println(LIN_master2.flagRxComplete);
Serial.print("error: "); Serial.println(LIN_master2.error);
if (LIN_master2.error == LIN_SUCCESS)
{
  Serial.println("data:");
  for (uint8_t i=0; i<8; i++)
  {
    Serial.print("  "); Serial.print(i); Serial.print("\t0x"); Serial.println(Rx[i], HEX);
  }
}
Serial.println();

// clear pending error and flag for received data
LIN_master2.error = LIN_SUCCESS;
LIN_master2.flagRxComplete = false;

} // scheduler

} // loop()



**Result: no deviation between oscilloscope and console output**

![grafik](https://user-images.githubusercontent.com/10261226/142755933-273824ef-1e03-444a-a69d-1594a220d07a.png)

For clarity continue in separate post...
gicking commented 2 years ago

Test 2: background

/**
  Test program for background LIN master (RB neutral protocol)
*/

// include files
#include "LIN_master2.h"      // muDuino LIN via Serial2
#include "Tasks.h"

// task scheduler periods [ms]
#define PRINT_PERIOD  1000    // period for status output
#define LIN_PERIOD    100     // LIN frame every N ms

// helper routine to print slave status
void printStatus(void);

// global variables
uint8_t  Rx[8];         // received data

void setup(void)
{
  // for user interaction via console
  Serial.begin(115200); while(!Serial);

  // initialize LIN master (background operation)
  LIN_master2.begin(19200, LIN_V2, true);

  // init task scheduler (also required for LIN master emulation!)
  Tasks_Init();
  Tasks_Add((Task) LIN_scheduler, LIN_PERIOD, 0);
  Tasks_Add((Task) printStatus, PRINT_PERIOD, PRINT_PERIOD);
  Tasks_Start();

} // setup()

void loop(void)
{
  // halt on 's' received from console (for sync)
  if (Serial.available())
  {
    char c = Serial.read();
    if (c == 's')
    {
      Tasks_Pause();
    }
  }

} // loop()

// actual LIN scheduler. Periodically called by task scheduler
void LIN_scheduler(void)
{
  uint8_t         id;
  uint8_t         numData;
  uint8_t         data[8];

  // sens slave response frame
  id      = 0x05;
  numData = 8;

  // get slave status. Copy data to buffer after reception
  LIN_master2.receiveSlaveResponse(id, numData, Rx);

} // LIN_scheduler()

// print slave response signals. Periodically called by task scheduler
void printStatus(void)
{
  // LIN ok -> print received data
  if (LIN_master2.error == LIN_SUCCESS)
  {
    for (uint8_t i=0; i<8; i++)
    {
      Serial.print(i); Serial.print("\t0x"); Serial.println(Rx[i], HEX);
    }
  }

  // print LIN error status
  else {
    Serial.print("LIN error (0x");
    Serial.print(LIN_master2.error, HEX);
    Serial.print("): ");
    if (LIN_master2.error & LIN_ERROR_STATE)
      Serial.println("statemachine");
    else if (LIN_master2.error & LIN_ERROR_ECHO)
      Serial.println("echo");
    else if (LIN_master2.error & LIN_ERROR_TIMEOUT)
      Serial.println("timeout");
    else if (LIN_master2.error & LIN_ERROR_CHK)
      Serial.println("checksum");
    else if (LIN_master2.error & LIN_ERROR_MISC)
      Serial.println("misc");
  } // error

  Serial.println();

  // reset latched error and flag for data received
  LIN_master2.error = LIN_SUCCESS;
  LIN_master2.flagRxComplete = false;

} // printStatus()

Result: no deviation between oscilloscope and console output (starting from buffer 31)

grafik

For clarity continue in separate post...

gicking commented 2 years ago

In summary I cannot (easily) reproduce the bug... Strange!

One thought: the LIN and print data functions in your sketch are called in different tasks. They are both called every 5s, but are you sure that you're printing the same LIN response that you are showing in the oscilloscope plot? Or could it be that print function is executed before the LIN function, i.e. that you are actually comparing 2 different frames...? Note that I see the same: the "Serial Decoding" window shows one frame more than the console window on the left. Specifically the console output corresponds to buffers 31 downwards, but buffer 32 is missing (not yet printed).

That would be the most convenient explanation of course. One alternative is global data inconsistency between different tasks due to race conditions. As that would be pretty hard to debug, I will let you check the above hypothesis first and wait for your response.

Have a great Sunday :-)

PS: a simple test for data inconsistency: can you please start the print task with 100ms offset (and same period)? That ensures that the 2 tasks don't access the global buffers at the same time. You can do that in your code by replacing Tasks_Add((Task) printStatus, PRINT_PERIOD, PRINT_PERIOD); with Tasks_Add((Task) printStatus, PRINT_PERIOD, 100);

gicking commented 2 years ago

After having thought about it more, I am convinced that your problem is caused by conflicting data access... Basically the data is printed when the frame is not yet finished. Specifically, in your example the LIN and print tasks are executed "simultaneously", so printing inconsistent data is likely.

Following this thought, I have replaced the latched flagTxComplete and flagRxComplete flags by an inline function to read the LIN state machine state. Using this information, the updated example LIN_background (hopefully) prints data only when the LIN frame has been completely received.

Can you please let me know if this solves your problem?

tim63613 commented 2 years ago

Hello gicking, thanks for your detailled response.

In following I took the advanced / improved version of your library. To be able to compare the results of the Checksum with oscilloscope I added following lines in the .cpp (line 635):

Serial.print("Checksumme ist ");
Serial.println(chk, HEX);

I tried it with the background operation method. In fact, the arduino console shows the LIN-Frame just after 5 seconds after the lin_scheduler was called. I solved this by setting the task printStatus to a delay of 100ms (as you said).

Now, the values of the oscilloscope and the LIN-Library both show the same values (data and also checksum): image

Its seems like this error was due to data inconsistency (as you said). The values shown on the arduino console were not meaningful as the LIN-Slave reports a temperature as 12 bits and the calculated temperature was 60°C above room temperature (measuring at room temperature).

In conclusion: Now the library works just fine for me. Thank you for helping and improving your library. I would not be able to solve this issue without you! Thanks.

Best regards, Tim