C CAN Kommunikation

Janiiix3

Aktives Mitglied
28. Sep. 2013
1.333
10
38
Hannover
Sprachen
  1. ANSI C
  2. C#
Hallo Leute,

macht es Sinn, bei einem etwas größerem Befehlssatz, den der Controller auswerten muss, mit einer Art "RingPuffer" zu arbeiten?

Manchmal gehen mir einige Befehle verloren habe ich das Gefühl...

Gerade wenn ich via. CAN z.B den SPI Bus bediene...
Lasse ich eine LED blinken (mit einem SPI Chip mit D-OUT) in einem Sekundentakt, passiert es manchmal das die Sekunde nicht eingehalten wird. Vermutlich liegt es daran das er einfach die Befehle mal nicht mit bekommt...

Habt ihr einen Lösungsvorschlag?
 
Monsen,

naja, eine CAN-Botschaft wird ja in ein Struct gepackt, im Normalfall. Natürlich kannst Du ein Array von Structs anlegen und das als eine Ringpuffer nutzen, das geht schon.

Aber, was ist eigentlich Dein Problem? Ist Dein Board Sender oder Empfänger? Was bedeutet "etwas größer"? Von welcher Geschwindigkeit und Buslast reden wir gerade?

Wenn Du von CAN + SPI sprichst, verwendest Du wohl den MCP2515 richtig? Der Chip hat ja /INT-Pin, der gesetzt wird, wenn eine valide Botschaft empfangen wurde. Wie überprüfst Du das? Polling oder ExtInt?
 
Habe mich wirklich undeutlich ausgedrückt.
Also ich kommuniziere zwischen PC <-> ATMEGA128CAN...
Das ganze läuft mit einem MCP2515 aber nicht via. SPI sondern Seriell (RxD & TxD).

Das funktioniert ja auch soweit alles ganz gut.
Sende ich jetzt nur zu schnell Botschaften (CAN läuft erstmal auf 500Kb/s), sende ich jetzt aber zwei unterschiedliche Befehle in einem sehr kurzen Zeitraum (100ms), kommt es ab und zu mal vor das er den Befehl nicht ausführt...

Mit dem SPI möchte ich später einfach per PC andere SPI oder I2C Devices auslesen.
 
Ich dachte MC2515 kann man nur über SPI ansteuern?

Wie lang sind die Botschaften? Eine Botschaft kann ja bis 130 bit lang sein, mit allem drum und dran.

Und lass Dir doch nicht alles aus der Nase ziehen, mehr infos bitte, dann kann man Dir auch helfen :)
 
Das ist unterschiedlich.
Maximal verschicke ich 8 Bytes, dies aber eher sehr selten.

Hier ist mal mein "CAN Parser"



CodeBox C
uint8_t can_parser(CAN_messageType msg)
{
   switch(recMsg.id)
   {
     case CPU_CTRL_ID:
     {
       switch (recMsg.data[0])
       {
         case HEARTBEAT_ON:
         {
           HEARTBEAT_LED_ON;
           send_str_can(CPU_CTRL_ID + HEARTBEAT_ON, "ok");
         }break;
         
         case HEARTBEAT_OFF:
         {
           HEARTBEAT_LED_OFF;
           send_str_can(CPU_CTRL_ID + HEARTBEAT_OFF, "ok");
         }break;
         
         case HEARTBEAT_TOGGLE:
         {
           HEARTBEAT_LED_TOGGLE;
           send_str_can(CPU_CTRL_ID + HEARTBEAT_TOGGLE, "ok");
         }break;
         
         case TRANSMIT_ID:
         {
           send_str_can(CPU_CTRL_ID+TRANSMIT_HW_VERSION, hardware_id);
         }break;
         
         case TRANSMIT_SW_VERSION:
         {
           send_str_can(CPU_CTRL_ID+TRANSMIT_SW_VERSION, software_version);
         }break;
         
         case TRANSMIT_HW_VERSION:
         {
           send_str_can(CPU_CTRL_ID+TRANSMIT_SW_VERSION, hardware_version);
         }break;
         
         case WRITE_TIME:
         {
           RX8564_set_Time(recMsg.data[1],recMsg.data[2],recMsg.data[3]);
           send_str_can(CPU_CTRL_ID + WRITE_TIME, "ok");
         }break;
         
         case WRITE_DATE:
         {
           RX8564_set_Date(recMsg.data[1],recMsg.data[2],recMsg.data[3],recMsg.data[4]);
           send_str_can(CPU_CTRL_ID + WRITE_DATE, "ok");
         }break;
         
         case TRANSMIT_TIME:
         {
           RX8564_get_Data();
           send_bytes_can(TIME_ID,0x03,rx8564.hours,rx8564.minutes,rx8564.secounds,0x00,0x00,0x00,0x00,0x00);           
         }break;
         
         case TRANSMIT_DATE:
         {
           RX8564_get_Data();
           send_bytes_can(DATE_ID,0x04,rx8564.day,rx8564.day_name,rx8564.month,rx8564.year,0x00,0x00,0x00,0x00);           
         }break;
         
         case I2C_ACCESS:
         {
           switch(recMsg.data[1])
            {
              case I2C_INIT :
              {
                i2c_init();
                send_str_can(CPU_CTRL_ID + I2C_INIT, "ok");
              }break;
             
              case I2C_START_WRITE :
              {
               i2c_start_wait(recMsg.data[2]); // slave address
               i2c_write(recMsg.data[3]); // register address
               i2c_write(recMsg.data[4]); // register value
               i2c_stop();   
               send_str_can(CPU_CTRL_ID + I2C_START_WRITE, "ok");
              }break;
             
              case I2C_START_READ :
              {
               i2c_start_wait(recMsg.data[2]+I2C_WRITE); // slave address
               i2c_write(recMsg.data[3]); // register address
               i2c_rep_start(recMsg.data[2]+I2C_READ);
               send_u8_can(CPU_CTRL_ID,i2c_readNak());
               i2c_stop();               
              }break;
             
              case I2C_START_READ_x_BYTE :
              {
               char Buffer[recMsg.data[4]];
               uint8_t cnt = 0;
                   
               i2c_start_wait(recMsg.data[2]+I2C_WRITE); // slave address
               i2c_write(recMsg.data[3]); // register address
               i2c_rep_start(recMsg.data[2]+I2C_READ);
               for (cnt = 0 ; cnt < recMsg.data[4]; cnt++)
               {
                 Buffer[cnt] = i2c_readAck();
               }
               Buffer[cnt] = i2c_readNak();
               Buffer[cnt++] = '\0'; // end of string
               i2c_stop();
           
               send_str_can(CPU_CTRL_ID + I2C_ACCESS, Buffer);               
              }break;
            }
         }break;
         
         case SPI_ACCESS:
         {
           switch(recMsg.data[1])
           {
             case SPI_MASTER_INIT:
             {
               spi_master_init(recMsg.data[2],recMsg.data[3]);
             }break;
             
             case SPI_MASTER_WRITE:
             {
               spi_disable_chip();
               spi_enable_chip();
               spi_master_transmit(recMsg.data[2]); // address
               spi_master_transmit(recMsg.data[3]); // data byte
               send_u8_can(CPU_CTRL_ID + SPI_ACCESS, spi_receive());
               spi_disable_chip();   
             }break;
             
             case SPI_MASTER_RECEIVE:
             {
               char Buffer[8];
               uint8_t cnt = 0;
               spi_disable_chip();
               spi_enable_chip();
             
               spi_master_transmit(recMsg.data[2]);
               for (cnt = 0 ; cnt < recMsg.data[3] ; cnt++)
               {
                 spi_master_transmit(0x00); // dummy byte
                 Buffer[cnt] = spi_receive(); // store received bytes
               }
               Buffer[cnt] = '\0'; // end of string
               spi_disable_chip();
               send_str_can(CPU_CTRL_ID + SPI_ACCESS, Buffer);               
             }break;
           }
         }break;
       }
     }break;   // end switch(CPU_CTRL_ID)
     
     case CPU_CALIBRATION_MODE:
     {
       if (recMsg.data[0] == WRITE_CALIBRATION_DATE) // write calibration date
       {
         eeprom_update_byte(&calibration_day,   recMsg.data[1]); // day
         eeprom_update_byte(&calibration_month,   recMsg.data[2]); // month
         eeprom_update_byte(&calibration_year,   recMsg.data[3]); // year
       }
       if (recMsg.data[0] == TRANSMIT_CALIBRATION_DATE) // transmit calibration date
       {
         send_bytes_can(CPU_CALIBRATION_MODE + TRANSMIT_CALIBRATION_DATE,3, eeprom_read_byte(&calibration_day),eeprom_read_byte(&calibration_month),eeprom_read_byte(&calibration_year),0x00,0x00,0x00,0x00,0x00);
       }
     }break;
   }// end switch (recMsg.id)

   
   /* clear receive buffer */
   for (uint8_t x = 0 ; x < recMsg.length ; x++)
   {
     recMsg.data[x]   = 0;
     recMsg.id     = 0;
     recMsg.length   = 0;
     recMsg.msk     = 0;
   }// end for
   return 0;
}// end can_parser


Gerade was "Heartbeat" angeht. Diese werden meistens nicht wirklich Zyklisch "ein" bzw "aus" geschaltet, wenn ich den entsprechenden Befehl dafür schicke.

Mein Controller läuft mit vollen 16MHz..

Was für Infos brauchst du denn noch?
 
Hallo Janiiix,

den struct Parameter CAN_messageType msg verwendest du in deiner Funktion gar nicht, du hast die ganze CAN Sache (Kommandos/Parameter) global definiert?!

Da ja sicherlich die Übertragung eines CAN Kommandos weniger als eine Sekunde benötigt und du nach der SPI Übertragung (LED toggeln) über CAN bestätigst, dass das Kommando abgearbeitet wurde und du vorher auch kein neues Kommando an den Mikrocontroller sendest, dürfte der Fehler nicht an zu vielen / zu schnell gesendeten Kommandos liegen. (Oder sendest du zB ein HEARTBEAT_nn während ein anderes Kommanso noch abgearbeitet wird?) Ein Ringbuffer wird hier sicher nichts nutzen, da du wahrscheinlich Kommandos nicht quasiparallel abarbeitest. Vielleicht passiert etwas vor deiner can_parser Funktion oder jetzt am Fall SPI mit dem LED Toggeln bei der Übertragung, also bei der Hardware (Leitungen zu lang ö.ä., SCK und CS sind ggf. für den Slave wegen Störungen kritisch).

Am einfachsten ist dein HEARTBEAT_ON oder HEARTBEAT_OFF Kommando, da hier nur eine LED (sicherlich direkt über IO Pin) an oder ausgeschaltet wird. Hier könntest du den CAN Bereich mal testen. Zum Beispiel bei HEARTBEAT_ON die LED Toggeln. Sende mehrfach (tausende mal) das Kommando, bei gerader Anzahl hat die LED den Zustand wie am Anfang, bei ungerader Anzahl nicht. Wenn das funktioniert, könnte es noch an CAN Parametern liegen. Wenn du bei SPI weiter testest, verwende mal nach CS ein kleines Delay (us) erst dann senden, verringer testweise die SPI Rate, den richtigen SPI Mode (CPOL/CPHA) hast du eingestellt?

Hmmm, vielleicht helfen dir ja die paar Hinweise und Ideen etwas weiter :)

Dirk :ciao:
 
Hallo Janiiix,

den struct Parameter CAN_messageType msg verwendest du in deiner Funktion gar nicht, du hast die ganze CAN Sache (Kommandos/Parameter) global definiert?!

Da ja sicherlich die Übertragung eines CAN Kommandos weniger als eine Sekunde benötigt und du nach der SPI Übertragung (LED toggeln) über CAN bestätigst, dass das Kommando abgearbeitet wurde und du vorher auch kein neues Kommando an den Mikrocontroller sendest, dürfte der Fehler nicht an zu vielen / zu schnell gesendeten Kommandos liegen. (Oder sendest du zB ein HEARTBEAT_nn während ein anderes Kommanso noch abgearbeitet wird?) Ein Ringbuffer wird hier sicher nichts nutzen, da du wahrscheinlich Kommandos nicht quasiparallel abarbeitest. Vielleicht passiert etwas vor deiner can_parser Funktion oder jetzt am Fall SPI mit dem LED Toggeln bei der Übertragung, also bei der Hardware (Leitungen zu lang ö.ä., SCK und CS sind ggf. für den Slave wegen Störungen kritisch).

Am einfachsten ist dein HEARTBEAT_ON oder HEARTBEAT_OFF Kommando, da hier nur eine LED (sicherlich direkt über IO Pin) an oder ausgeschaltet wird. Hier könntest du den CAN Bereich mal testen. Zum Beispiel bei HEARTBEAT_ON die LED Toggeln. Sende mehrfach (tausende mal) das Kommando, bei gerader Anzahl hat die LED den Zustand wie am Anfang, bei ungerader Anzahl nicht. Wenn das funktioniert, könnte es noch an CAN Parametern liegen. Wenn du bei SPI weiter testest, verwende mal nach CS ein kleines Delay (us) erst dann senden, verringer testweise die SPI Rate, den richtigen SPI Mode (CPOL/CPHA) hast du eingestellt?

Hmmm, vielleicht helfen dir ja die paar Hinweise und Ideen etwas weiter :)

Dirk :ciao:


Ja, dass stimmt. Die ganzen Variablen habe ich "global" definiert. Der "CAN_PARSER" Funktion übergebe ich aktuell Parameter die ich nicht verwende. Das sollte den µC doch auch schon um einiges ausbremsen oder?

Wenn ich die Befehle zum "ein / aus" schalten der LED manuell vom PC sende, klappt das wunderbar. Je schneller desto mehr Fehler treten auf.
Die LED "Heartbeat" hängt direkt an einem I/O vom µC... Also das hat rein gar nichts mit dem SPI zu tun, dort treten manchmal auch schon die Fehler auf...
 
Ja, dass stimmt. Die ganzen Variablen habe ich "global" definiert. Der "CAN_PARSER" Funktion übergebe ich aktuell Parameter die ich nicht verwende. Das sollte den µC doch auch schon um einiges ausbremsen oder?
Das wird nichts ausmachen, der Compiler meldet ggf. dass etwas definiert ist und nicht genutzt wird. Es wird hier aber nicht das Problem sein.

Wenn ich die Befehle zum "ein / aus" schalten der LED manuell vom PC sende, klappt das wunderbar. Je schneller desto mehr Fehler treten auf.
o_O "Klappt das wunderbar" oder treten hier jetzt schon Fehler auf?

So wie ich es raus lese, treten schon bei "Heartbeat" Fehler auf, oder? Wartest du auch auf dir Rückmeldung vom Mikrocontroller, bevor du neue Kommandos sendest?
 
Leider kann ich das mit meiner aktuellen Software noch nicht abbilden.
Wahrscheinlich wird es klappen, wenn ich solange warte bis ich eine Antwort vom µC bekomme.

Hier wo ich arbeite, bauen wir auch sehr sehr viele CAN Geräte. Gerade die kommen sehr gut ohne eine Antwort klar. Das heißt hier Sende ich Zyklisch Befehle z.B ne LED Togglen und das klappt prima...
Nur leider sind das uhralte Geräte und die setzen auch nicht auf nem MEGA auf.
 

Anhänge

  • can.jpg
    can.jpg
    196,6 KB · Aufrufe: 4
Wartest du auch auf dir Rückmeldung vom Mikrocontroller, bevor du neue Kommandos sendest?

Das ist unüblich, dass man auf eine Antwort wartet. CAN ist eher so "fire and forget", im Fehlerfall gibt es eine Rückmeldung vom SG oder einen Eintrag im Fehlerspeicher.

@Janiix3: Kannst Du bitte die Funktion "send_str_can" zeigen? Glaube, da ist das Problem begraben.
 
Das ist unüblich, dass man auf eine Antwort wartet. CAN ist eher so "fire and forget", im Fehlerfall gibt es eine Rückmeldung vom SG oder einen Eintrag im Fehlerspeicher.

@Janiix3: Kannst Du bitte die Funktion "send_str_can" zeigen? Glaube, da ist das Problem begraben.

Die Funktion hat damit überhaupt nichts zu tun. Es liegt ja daran was ich vom PC empfange und nicht was ich zum PC sende.

Aber klar, hier ist die Funktion :



CodeBox C
void send_str_can(uint16_t id, char *str)
{   
   msg.id     = id; // string id
   msg.length   = strlen(str); // size of the length from the string
   for (uint8_t x = 0 ; x < msg.length ; x++)
   {
     msg.data[x] = str[x];
   }
   can_tx(msg);   
}
 
Hm Okay...
Ich habe ja jedes mal die Empfangenen Date nach jedem abarbeiten gelöscht...
Das scheint das Problem gewesen zu sein, dass das so lange gedauert hat...
Schade, dass das nicht klappt



CodeBox C
  /* clear receive buffer */
   for (uint8_t x = 0 ; x < recMsg.length ; x++)
   {
//      recMsg.data[x]   = 0;
//      recMsg.id     = 0;
//      recMsg.length   = 0;
//      recMsg.msk     = 0;
   }// end for
 
Ich habe ja jedes mal die Empfangenen Date nach jedem abarbeiten gelöscht...
Das scheint das Problem gewesen zu sein, dass das so lange gedauert hat


Hier stimmt sowieso etwas nicht.

recMsg.length verwendest du als Bedingung in der for-Schleife, änderst das aber auch in der Schleife.
Eine Schleife bräuchtest du nur um das Array .data[] zu initialisieren.

Das .length bezieht sich auf die Anzahl der Daten im Array? Wichtig ist dann auch, dass das Array .data[] ausreichend groß definiert ist.
 
Hier stimmt sowieso etwas nicht.

recMsg.length verwendest du als Bedingung in der for-Schleife, änderst das aber auch in der Schleife.
Eine Schleife bräuchtest du nur um das Array .data[] zu initialisieren.

Das .length bezieht sich auf die Anzahl der Daten im Array? Wichtig ist dann auch, dass das Array .data[] ausreichend groß definiert ist.


Die Länge (Anzahl der Bytes) bekomme ich vom PC mitgeteilt, diese Information steht dann im ".lenght"
Da ich nicht immer unnötig alle 8 Bytes löschen möchte, habe ich das als Bediung genommen.
 
-:-*

Kein wunder das es Asymetrisch war!
Habe den Buffer bei jedem Aufruf vom "CAN-PARSER" gelöscht. Dumme Idee!
Das sollte mit in die "switch - case", und zwar jedes mal wenn er einen Befehl abgearbeitet hat.

Danke für eure Bemühungen ;)
 

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)