Transformer message Bus vers analogique (LIN BUS)

Bonjour à tous,

Je voudrais utiliser un joystick communiquant en LIN BUS pour contrôler une électrovanne en PWM.

Le joystick consiste en 2 contrôleurs lin esclave.

Ci-joint, le signal lin bus, en dessous, le signal en direct sur le contrôleur et au dessous, le signal après transformation en UART. (j'utilise pour ce faire un MCP2004A).

Les trames LIN commencent toujours pas 0x55
Je voudrais récupérer les trames des ID 0xA8 et 0xE9
Il y a 8 bytes de data
La trame se termine par un checksumm (voir calcul).

Ci-dessous, les 2 trames que je voudrais récupérer.

Voici mon code arduino actuel pour voir ce que je peux récupérer

//----------------LCD---------

#include <LiquidCrystal.h>
#define RS 12    //Register Select
#define E 11     //Enable
#define D4 5     //Ligne de données 4
#define D5 4     //Ligne de données 5
#define D6 3     //Ligne de données 6
#define D7 2     //Ligne de données 7
#define COLS 16  //Nombre de colonnes
#define ROWS 2   //Nombre de lignes
LiquidCrystal lcd(RS, E, D4, D5, D6, D7);            //Instanciation de l'objet

//-------------Variable joystick-----------------

int x_axis; //Variable valeur axe X
int y_axis; // Variable Valeur axe Y
int z_axis; //Variable valeur axe Z

//-----------Variable LIN BUS------------------
const byte sync_byte = 0x55;
const byte id_joystick = 0xA8;
//const byte id_poignée = 0xE9;

//-----------Variable pour code--------------
byte inByte[9];

void setup() {
  Serial.begin(19200);
  lcd.begin(COLS, ROWS);
}

void loop() {


  //---------------Interrogation du bus série et afficahge du résultat------------------------
  byte data[10];
  byte rec[10];
  if (Serial.read() != -1) {
    if (Serial.read() == 0x55){
    Serial.readBytes(rec, 10);
      for (int j = 0; j < 10; j++) {
        data[j] = rec[j];
      }
    }
  }

  //-----------------------Affichage sur moniteur série--------------------------------------
  Serial.println("Traffic detected");
  Serial.print("Ident Byte: ");
  Serial.println(data[0], HEX);
  Serial.print("Data Byte1: ");
  Serial.println(data[1], HEX);
  Serial.print("Data Byte2: ");
  Serial.println(data[2], HEX);
  Serial.print("Data Byte3: ");
  Serial.println(data[3], HEX);
  Serial.print("Data Byte4: ");
  Serial.println(data[4], HEX);
  Serial.print("Data Byte5: ");
  Serial.println(data[5], HEX);
  Serial.print("Data Byte6: ");
  Serial.println(data[6], HEX);
  Serial.print("Data Byte7: ");
  Serial.println(data[7], HEX);
  Serial.print("Data Byte8: ");
  Serial.println(data[8], HEX);
  Serial.print("Check Byte: ");
  Serial.println(data[9], HEX);

  Serial.print("\n");

}

Avec ce code, je récupère bien les trames, mais je vois bien qu'il manque des valeurs. (les trames ne sont pas complètes) et que toutes les valeurs se mélangent

A quoi cela pourrait être dû?

J'utilise le même port série pour lire les valeurs du lin et envoyer au moniteur série, cela pourrait il poser un problème?

Mon autre question est liée au calcul du checksum.

Je parviens à le calculer, mais ne comprend pas comment l'implémenter dans le code.

J'ai trouver ce morceau de code, mais je ne le comprends pas

/* Lin defines its checksum as an inverted 8 bit sum with carry */
uint8_t Lin::dataChecksum(const uint8_t* message, char nBytes,uint16_t sum)
{
    while (nBytes-- > 0) sum += *(message++);
    // Add the carry
    while(sum>>8)  // In case adding the carry causes another carry
      sum = (sum&255)+(sum>>8); 
    return (~sum);
}

J'éspère avoir été assé clair dans mes explications.

D'avance, merci

Bonjour

  sum += *(message++);

içi est réalisée l'addition d'un terme à la somme

while(sum>>8)

permet de récupérer le 9e bit éventuel de la somme en cours et de le faire passer en poids faible (8 décalages à droite)

sum = (sum&255)+(sum>>8);

Le 9e bit (retenue de l'addition précédente) est ajouté aux 8 bits de poids faible (masque) de la somme temporaire

Il faudrait penser à lire ce que retourne Serial.readBytes(rec, 10); car il se peut que tu sortes sur timeout.

Tel qu'il est structuré, ton programme passe dans la routine d'affichage du tableau même s'il n'y a pas eu réception de données.

Ton programme n'est pas robuste aux désynchronisations. Si 0x55 se trouve dans les données rien ne t'empêche de mal synchroniser la réception des données.

fdufnews:
Il faudrait penser à lire ce que retourne Serial.readBytes(rec, 10); car il se peut que tu sortes sur timeout.

Tel qu'il est structuré, ton programme passe dans la routine d'affichage du tableau même s'il n'y a pas eu réception de données.

Ton programme n'est pas robuste aux désynchronisations. Si 0x55 se trouve dans les données rien ne t'empêche de mal synchroniser la réception des données.

Merci pour ta réponse.
Oui, je m'étais rendu compte du problème. Dailleur les données récupéréers ne sont pas cohérentes.

Il y a d'autres trames de données qui passent sur le bus mais qui n'ont pas la même longueur.

  ___________ __________ _______ ____________ _________ 
   |           |          |       |            |         |
   |Synch Break|Synch Byte|ID byte| Data Bytes |Checksum |
   |___________|__________|_______|____________|_________|
   
   Every byte have start bit and stop bit and it is send LSB first.
   Synch Break - 13 bits of dominant state ("0"), followed by 1 bit recesive state ("1")
   Synch Byte - Byte for Bound rate syncronization, always 0x55
   ID Byte - consist of parity, length and address; parity is determined by LIN standard and depends from address and message length
   Data Bytes - user defined; depend on devices on LIN bus
   Checksum - inverted 256 checksum; data bytes are sumed up and then inverted

Le début d'une trames de donnée équivaut à un état bas sur le bus d'une durée équivalente à 13 bits.

Est il possible de détecter cela sur le port série?

L'USART n'a pas de détection de BREAK sur la ligne série.
Par contre tu pourrais peut-être exploiter le temps entre 2 trames s'il est assez long en configurant le hors-temps de la liaison série.

Je n'arrive pas a expliquer pourquoi j'ai tant de pertes de données avec l'arduino alors qu'avec mon analyseur logique, toute les trames semblent complètes.

J'utilise maintenant un MEGA 2560 car plusieurs port série sont disponible

Avec le code suivant:

void loop() {
  while (Serial1.available()) {
    byte c = Serial1.read();
    Serial.print(c, HEX);    
    Serial.print(" "); 
    }

avec les 2 ports séries sur 19200 bps, je devrais récupérer toutes les données passant par le bus (les même données que via l'analyseur logique).

Hors, la plupart des trames sont incomplètes

Quelle serait l'explication ?

Le code de la méthode dataChecksum devrait focntionner.
Tu dois passer un paramètre sum égal à zéro pour un résultat correct.

Si ton joystick travaille en LIN2.0, tu dois inclure l'ID dans le calcul du checksum.
Cela semble être le cas car :
A8 7d 7d 7d 7d 08 00 00 bc donne bien un checksum de 9C
Donc l'ID A8 ou autre doit être inclus dans la vérification.

Le Serial1 (LIN) est configuré en 19200, mais qu'en est-il du Serial (terminal) ?

Avec les ports Serial 1 sur 19200 et serial sur 19200 également j'avais des pertes de données

Maintenant j'ai refait l'essai en mettant le port Serial (moniteur série) sur 38400 et le résultat est bien meilleur.

Je vais maintenant essayer d'extraire les valeurs dont j'ai besoin.

Pourquoi 38400 ?
115200 pour le moniteur série ne pose pas aucun problème.

@+

Oui, cela fonctionne aussi très bien.

J'ai une durée de 3ms entre les trames, ne serait il pas possible d'utiliser Serial.setTimeout() pour détecter ce temps sans réception de donnée?

Je ne comprend pas comment fonctionne le timeout sur le port série.

Pour info, l'arduino DUE dispose d'un USART qui peut se mettre en mode LIN, ce qui simplifierait sans doute considérablement l'interfaçage avec les contrôleurs LIN esclaves.

Les fonctionnalités exploitables dans ce mode sont (extrait du Sam3x datasheet, page 770):

LIN Mode (USART0 only)
̶ Compliant with LIN 1.3 and LIN 2.0 specifications
̶ Master or Slave
̶ Processing of frames with up to 256 data bytes
̶ Response Data length can be configurable or defined automatically by the Identifier
̶ Self synchronization in Slave node configuration
̶ Automatic processing and verification of the “Synch Break” and the “Synch Field”
̶ The “Synch Break” is detected even if it is partially superimposed with a data byte
̶ Automatic Identifier parity calculation/sending and verification
̶ Parity sending and verification can be disabled
̶ Automatic Checksum calculation/sending and verification
̶ Checksum sending and verification can be disabled
̶ Support both “Classic” and “Enhanced” checksum types
̶ Full LIN error checking and reporting
̶ Frame Slot Mode: the Master allocates slots to the scheduled frames automatically.
̶ Generation of the Wakeup signal

Oui, mais dans mon cas, je ne veux pas interagir avec le LIN BUS, je veux seulement faire du "sniffing" et en fonction des paquets reçu, les exploiter.

Pour ce qui est de mon code:

void getlin() {

  if (Serial1.available() > 0) {
    curByteMicros = micros();
    if (curByteMicros - prevByteMicros >= linbusInterval) {
   Serial.print("\n");
    }
    prevByteMicros = curByteMicros;
    byte inByte = Serial1.read();
    Serial.print(inByte, HEX);
    Serial.print(" ");

avec linbusInterval = 3000

Serial1 = LIN
Serial= Moniteur série

je parviens à séparer les paquet en détectant l'interval de temps entre ceux-ci.

Par contre je ne vois pas comment les stocker dans un tableau
data[12]

Pour ensuite pouvoir les interpréter.

Bonjour,

Voici finalement mon code qui semble bien fonctionner:

//-------------Variable joystick-----------------

int x_axis; //Variable valeur axe X
int y_axis; // Variable Valeur axe Y
int z_axis; //Variable valeur axe Z

//-----------Variable LIN BUS------------------
const byte sync_byte = 0x55;
const byte id_joystick = 0xA8;
const byte id_poignee = 0xE9;

//-------------Configuration pin ------------------
int pin_x_axis = 3;



//-----------Variable pour code--------------
byte data[12];
unsigned long linbusInterval = 3000; //Durée qui sépare 2 trames
unsigned long prevByteMicros = 0; //Moment réception message précédent
unsigned long curByteMicros = 0; //Moment actuel
boolean newData = false;
const byte numBytes = 15;
byte receivedBytes[numBytes];
byte numReceived = 0;



//--------------Config Arduino------------------
void setup() {
  Serial1.begin(19200);
  Serial.begin(115200);
  
  pinMode(pin_x_axis, OUTPUT);
}


//----------------Calcul du Checksumm-------------------------------------------
byte checksum(const uint8_t* message, char nBytes, uint16_t sum) {
  while (nBytes-- > 0) sum += *(message++); // Fait la somme des message

  while (sum >> 8) // renvoi le premier bit éventuel vers le bit de poids faible
    sum = (sum & 255) + (sum >> 8); //Inversion et ajout du 9e bit éventuel
  return (~sum);
}

//-------------------Lecture valeurs-----------------------------------------------
void parse_frame() {
  if (newData == true) {
     
  if (receivedBytes[2] == id_joystick) {
    x_axis = receivedBytes[3];
    y_axis = receivedBytes[5];
  }
else if (receivedBytes[2] == id_poignee) {
    z_axis = receivedBytes[4];
  }
           
  newData = false;
  }
}


//-----------------Réception trames lin ------------------------------------------------

void getlin() {


  static boolean recvInProgress = false;
  static byte ndx = 0;
  int startmarker = 0x0;
  byte rb;

  while (Serial1.available() > 0 && newData == false) {
    curByteMicros = micros();
    if (curByteMicros - prevByteMicros >= linbusInterval) {
      recvInProgress = false;
      receivedBytes[ndx] = '\0';
      numReceived = ndx;
      ndx = 0;
      newData = true;
      //Serial.print (recvInProgress);
      //Serial.println();
    }
    prevByteMicros = curByteMicros;
    rb = Serial1.read();
    recvInProgress = true;

    if (recvInProgress == true) {
      receivedBytes[ndx] = rb;
      ndx++;
      if (ndx >= numBytes) {
        ndx = numBytes - 1;
      }
    }
   
}
}


//--------------------Affichage trame sur moniteur série--------------------------------------

void showNewData() {
  if (newData == true) {
    for (byte n = 0; n<numReceived; n++) {
      Serial.print (receivedBytes[n], HEX);
      Serial.print (" ");
    }
    Serial.println();
   
    newData = false;
    //Serial.print(newData);
  }
}


//------------------------------Génération signal PWM-----------------------------------------------
void generatePWM() {
  analogWrite(pin_x_axis, x_axis);
}


//---------------Fonction loop------------------------
void loop() {
  getlin();
  parse_frame();
  generatePWM();




}

A ce point, je n’effectue pas le contrôle du checksumm, j'utilise le temps mort entre 2 trames pour détecter la fin / le début d'une autre trame.

Le code semble bien fonctionner, je mesure bien un signal PWM correspondant à la position des différents boutons.

Encore merci du coup de main.

Vos remarques sont à nouveau les bienvenues.

J'ai un problème similaire et ce fil m'a bien aidé,

Désolé de déterrer ce post vieux de 2 ans mais il manque une partie de votre code, je me demandais si par chance vous aviez le code complet?

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.