Forum: Mikrocontroller und Digitale Elektronik 2 Stk. MCP2515 an einem z.B. ATMega32


von Peter C (Gast)


Lesenswert?

Hallo zusammen,
ich möchte gerne an einen ATMega32 zwei MCP2515 per SPI anschließen. Ich 
brauche zur Lösung der Aufgabe 2 CAN-Controller ????!!!!! Auch ein 
AT90CAN128 und ein MCP2515 wären denkbar.
Es soll ein bidirektionaler Datenstrom eingelesen und gefiltert 
ausgegeben werden. Der CAN arbeitet beidseitig mit 100Kbit.

Ist das überhaupt möglich ????
welche Datenrate (Anzahl der Datenrames) ließe sich theoretisch erzielen 
???
Gibt es da schon fertige Lösungen im bezahlbaren Rahmen (100-200Eur) 
oder Layouts
Mit der Programmierung in C habe ich bis jetzt nicht viel zu tun gehabt.
Würde mich aber bei entsprechenden Beispielen gerne daran versuchen.

Gruß
Peter

von (prx) A. K. (prx)


Lesenswert?

Man könnte natürlich auf den Gedanken verfallen, einen Controller mit 2 
internen CAN-Einheiten zu verwenden. Hinsichtlich Durchsatz ist der 
MCP2515 nicht der ganz grosse Wurf, dafür ist er einfach nicht gebaut. 
Der ist für einfaches Messaging gebaut, nicht für Massendatentransfer.

von Peter C. (Gast)


Lesenswert?

Hallo,
daran hatte ich auch gedacht !!! es gibt ja welche mit 2 bis z.B. 5 (und 
mehr) internen Can Controllern nur sind die nur in SMD zubekommen und 
das könnte ich mit der Dip Varinate vom MCP2515, Mega32 und dem 
Treiberbaustein 82C250 umgehen. Mir macht die Programmierung in C schon 
deutlich zuschaffen.

Gruß
Peter C.

von H.Joachim S. (crazyhorse)


Lesenswert?

Also ich habe eine Sache laufen mit 2x500kBit-CAN.
2xMCP2515 an ATMega32, Hardware-SPI, ist ziemlich viel traffic drauf, 
(Motor-CAN) läuft problemlos.

von Peter C. (Gast)


Lesenswert?

Hallo,
ja super, genau sowas suche ich.
Wäre es unverschämt nach der Lösung (Schaltplan und Code) zufragen. Ich 
würde sogar nur 100KBit benötigen. Eine Lösung für Maske/Filter findet 
sich bestimmt auch.

Gruß
Peter C.

von Frank K. (fchk)


Lesenswert?

Peter C schrieb:
> Hallo zusammen,
> ich möchte gerne an einen ATMega32 zwei MCP2515 per SPI anschließen. Ich
> brauche zur Lösung der Aufgabe 2 CAN-Controller ????!!!!! Auch ein
> AT90CAN128 und ein MCP2515 wären denkbar.
> Es soll ein bidirektionaler Datenstrom eingelesen und gefiltert
> ausgegeben werden. Der CAN arbeitet beidseitig mit 100Kbit.
>
> Ist das überhaupt möglich ????
> welche Datenrate (Anzahl der Datenrames) ließe sich theoretisch erzielen
> ???
> Gibt es da schon fertige Lösungen im bezahlbaren Rahmen (100-200Eur)
> oder Layouts

Es gibt von Olimex das SAM7-LA2 Board:
http://www.olimex.com/dev/sam7-ea2.html
Der Prozessor dadrauf hat 4 CAN-Ports. Einer ist auf einem Stecker 
herausgeführt, die anderen sind über eine Stiftleiste verfügbar. Hier 
müßtest Du noch CAN-Transceiver anschließen, aber das ist einfach. Da 
hier alles intern ist, solltest Du keine Geschwindigkeitsprobleme haben.

Das Board kostet bei Olimex selber 90€.

von Peter C. (Gast)


Lesenswert?

Hallo,
mich würde die Schaltung von Autor: H.joachim Seifert (crazyhorse) schon 
mehr interessieren. Ich hoffe er meldet sich noch einmal.

Gruß
Peter C.

von H.Joachim S. (crazyhorse)


Angehängte Dateien:

Lesenswert?

Bitteschön...

von Peter C. (Gast)


Lesenswert?

Hallo,
klasse, sind ja garnicht soviele Bauteile. Programmierst du in C ??????, 
Ist der Schaltplan mit Eagle oder Target gezeichnet worden ???
Kann ich irgend wie an den Quellcode kommen, muß ja nicht umsonst sein 
!!??

Welche Funktion übernimmt die Schaltung zwischen Motor und Steuergerät ?

mfg.
Peter

von H.Joachim S. (crazyhorse)


Lesenswert?

Quellcode kann ich dir höchstens die Grundfunktionen geben, die 
Einzelheiten im CAN sind geheim.
Das ganze fusst auf den ziemlich genialen Routinen von 
www.kreatives-chaos.com. Bisschen aufgebohrt auf die 2 CS-Steuersignale.
Das ganze dient dazu, ein Motorsteuergerät mit einem Auto zu 
verheiraten, die eigentlich nicht zusammenpassen/zusammengehören. D.h. 
alle Botschaften vom Auto und alle vom Motorsteuergerät werden 
umgebastelt und dann im passenden Format an den jeweils anderen Bus 
gesendet. Deshalb habe ich auch mit keinerlei Filtern oder Masken 
gearbeitet, alle ankommenden Botschaften werden auch vom MC empfangen, 
einsortiert, neu zusammengestellt und im passenden Zeitraster auf der 
anderen Seite gesendet.
Und die Frage nach Eagle bzw. Target überhöre ich jetzt mal, dass 
bisschen Kram wirst du doch wohl selber schaffen. Die Hauptarbeit liegt 
eh in der Software.

von Peter C. (Gast)


Lesenswert?

Hallo,
ich dachte mir schon das mehr hinter der Schaltung steckt. Auf den Teil 
wo die Frames umgearbeitet oder geschlüsselt werden verzichte ich sehr 
gerne. Auf www.kreatives-chaos.com ist der Umgang mit dem MCP und der 
lip schon beschrieben nur als "noch " nicht C User ist es allemal schwer 
die 2. Steuerleitung irgendwie herein zubekommen und da die Sprache von 
Basic schon deutlich abweicht schwer zum Quadrat. Wenn es sich bei den 
Grundfunktionen um das bidirektionale senden und empfangen geht kann ich 
dir garnicht genug danken. Wenn ich das richtig verstehe müßte man den 
mittleren Teil der deine Anpassung Auto zu Steuergerät macht für meine 
Zwecke "nur" herausnehmen und schon sollte es laufen. Ja ich glaube ich 
fantasiere, so leicht ist es sicher nicht.

Zur Aufklärung, ich entwerfe kleine Platinen mit Target (Version 400 
Pins), vom Enwurf bis zur Fertigung meiner Schaltung habe ich bis heute 
immer geschafft. Deshalb bevorzuge ich liebend gerne die DIP/DIL 
Varianten.

Also wenn ich die Grundfunktionen nutzen düfte, wäre das ein großer 
Schritt vorwärts für mich.

Gruß
Peter

von H.Joachim S. (crazyhorse)


Lesenswert?

du musst alle Grundroutinen bearbeiten, die direkt auf die SPI 
zugreifen, mal als Beispiel die mcp2515_read_rx_status:


Original:
unsigned char mcp2515_read_rx_status(void)
{unsigned char data;
  PORTB &= ~(1<<SPI_CS);    // CS low
  spi_putc(SPI_RX_STATUS);
  data = spi_putc(0xff);
  // Die Daten werden noch einmal wiederholt gesendet,
  // man braucht also nur eins der beiden Bytes auswerten.
  spi_putc(0xff);
  PORTB |= (1<<SPI_CS);      // CS high
  return data;
}

geändert: (mit einer Variablen CAN_sw), die kannst du natürlich auch an 
die Funktion übergeben, ich benutze sie allerdings als globale Variable, 
die vor dem Aufruf geschrieben wird, das hat andere Gründe, die hier 
nichts zur Sache tun)

unsigned char mcp2515_read_rx_status(void)
{unsigned char data;
  if (!CAN_sw)  PORTB &= ~(1<<SPI_CS1);    // CS1 low
      else PORTB &= ~(1<<SPI_CS2);         //CS2 low
  spi_putc(SPI_RX_STATUS);
  data = spi_putc(0xff);
  // Die Daten werden noch einmal wiederholt gesendet,
  // man braucht also nur eins der beiden Bytes auswerten.
  spi_putc(0xff);
  PORTB |= (1<<SPI_CS1 | 1<<SPI_CS2);      // beide CS high
  return data;
}

von Peter C. (Gast)


Lesenswert?

Hallo,
das heißt alle Routinen von www.kreatives-chaos.com  des MCP2515 
Beispieles ?????
Ich lese also von 1. MCP ein, speicher ID, DLC und die Datenbytes/RTR in 
Variablen und gebe sie dann von den Variablen wieder an den SPI des 2. 
MCP

Wenn ich das so sehe ist der Syntax der Sprache C das große Problem. Ob 
ich das allerdings so hinbekomme !!!!!!!

Gruß
Peter

von H.Joachim S. (crazyhorse)


Lesenswert?

der zentrale Punkt für die ganzen Variablen ist folgender:
typedef struct
{   unsigned int  id;
    unsigned char rtr;
    unsigned char length;
    unsigned char data[8];
} CANMessage;

erzeugt eine Variablenart CANMessage.
Nun kann man beliebig viele Variablen (solange der RAM reicht:-) 
erzeugen.

CANMessage rec_can0;   //für ISR0
CANMessage rec_can1;   //für ISR1

Empfangen wird grundsätzlich im Interrupt:

// External Interrupt 0 service routine
interrupt [EXT_INT0] void ext_int0_isr(void)
{
CAN_sw=0;
can_get_message(&rec_can0);
CAN0_message_received=1;

}

// External Interrupt 1 service routine
interrupt [EXT_INT1] void ext_int1_isr(void)
{
CAN_sw=1;
can_get_message(&rec_can1);
CAN1_message_received=1;
}

Empfangene Botschaften stehen nun in den Variablen, zusätzlich werden 
die flags CAN0_message_received bzw. CAN1_message_received gesetzt. In 
der main wird nun anhand dieser flags geprüft, ob was zu tun ist. Wenn 
ja, anhand der der ID entsprechend reagiert.

Da die Interrupts die Variable CAN_sw ohne Rücksicht auf Verluste 
einfach umschaltet, musst du sicherstellen, dass nicht gerade eine 
Sendeaktion läuft. Also vor jedem Senden Interrupts sperren, danach 
wieder freigeben

von Peter C. (Gast)


Lesenswert?

Hallo,
das für mich schlimme ist, ich kann dir so in etwa folgen und weiß was 
die Controller tun sollen. Ich bekomme nur sprichwörtlich die Kraft, die 
Information nicht auf den Boden - soll heißen der C Dialekt in 
Verbindung mit altem Schulenglisch ist für mich das Problem.
So suche ich Beispiel/e, die funktionieren und an dehnen ich mich weiter 
hangeln kann um so auch mehr von C zu verstehen.

Deshalb ja meine Frage nach dem Code. Ich habe mehr als nur Verständis 
das wenn etwas funktioniert man die "Geheimsache" nicht veröffentlicht. 
Wir kennen uns nicht und so muß man immer mit Spionage, blödes Wort, 
rechnen.

Ich denke ich werde deinen Schaltplan zugrunde legen (ich habe nur kein 
Jtag) und mal versuchen die Routinen mit einenm MCP und mega8 zum Laufen 
zubekommen. Wenn das geht, gehen sollte, mit dem zweiten paralell und 
wenn das auch geht CAN0 und CAN1 mit einander zuverknüpfen. Sollte ich 
das auch noch hinbekommen müßte der letzte Schritt mit Maske und Filter 
auch zuschaffen sein.

Ich habe auch einen Seite gefunden die mehr zum Thema C zeigt.
http://www.c-programme.de/

Gruß
Peter

von H.Joachim S. (crazyhorse)


Lesenswert?

Tja, vielleicht nimmst du dir auch etwas viel vor für den Anfang...
CAN neu, C neu, naja.
Wenn du Basic kannst, kannst du ja auch versuchen, die Routinen auf 
Basic umzuschreiben. Oder es gibt was entsprechendes schon fertig, kann 
ja auch sein. Hier gibts jedenfalls kaum Basic-Leute, und das hat auch 
seinen Grund (ohne jetzt eine neue Diskussion darüber anleiern zu 
wollen)
JTAG solltest du dir auf jeden Fall zulegen, damit kann man in den MC 
hineinschauen, ohne wirds deutlich zeitaufwändiger.

von Peter C. (Gast)


Lesenswert?

Hallo,
Da geb ich dir recht, je mehr ich drüber nachdenke. Der Umgang mit dem 
CAN ist zwar nicht so neu, sitz aber nicht so tief. Mit einem At90CAN 
habe ich mit Basic schon "etwas" Erfahrung, nur erscheint mir das 
Schreiben etwas langsam - lesen geht super fix. Ich denke es sollte 
schneller gehen als nur alle 15-20ms einen Frames senden zu können und 
da fällt mir ein alter Spruch ein. Nimm C und alle Sorgen sind passe.
Wahrscheinlich nur wenn man C drauf hat. Das Schulenglsich kommt auch 
nur bedingt mit dem technischen Englisch der Datenblätter mit. Ich muß 
mal schauen. Zunächst werde ich die fehlende Bauteile bei Reichelt 
bestellen und in der Zwischenzeit kann ich mir die Routinen und den 
AT90CAn nochmal zu Gemüte führen.

Okay

Gruß
Peter

von Peter C. (Gast)


Lesenswert?

Hallo crazyhorse,
ja ich weiß der Beitrag ist jetzt 14 Monate alt und ich habe mir deinen 
Rat zu Herzen genommen und wohlt mal kurz Rückmeldung geben. Ich habe 
mich voll in die CAN Nummer und Basic gehängt.
Ich hatte mir eine Lochrasterplatine mit zwei MCP2515 nehmst MCP2551 und 
da gleicher Sockel wie ein ATMega8 einen ATMega328P aufgebaut. Der hat 
den Vorteil das er pinkompatibel ist und mehr Ram und Flash hat und 
zudem ein Freq Ausgang und im max Fall bis 20MHz betrieben werden kann.
Der ATMega8 wäre schon für die kleine Bascom Lösung zu klein  (flash und 
Ram) gewesen und so war der ATMega328 schon eine gute Wahl - fand ich. 
Das Bascom Proggi läuft auch auf beiden CAN mit 100KBit in beide 
Richtungen. Leider konnte ich für mich die Buffer/Speicheung der Frames 
nichts lösen und habe aus Geschwindiglkeitsgründen vor 4 Monaten 
tatsächlich auf C umgestellt.
Aus den ehemals böhmischen Dörfern ist mitlerweile eine Nachbargemeinde 
geworden und so hangel ich mich anhand des Beispieles von kreatives 
Chaos durch.
Nach Umstellung des Programmes auf den ATMega328P hatte ich dann wohl 
einen Fehler der es erforderte die iom328p zuerweitern. Nach der 
Anpassung der Bus Frequenz konnte ich dann auch schon was empfangen und 
am Bildschirm darstellen. Der 328P hat andere Registernamen und so war 
der Umstieg mit meinem 20 Jahre alten Schulenglsich schon eine 
Herausforderung. Da in dem Testprogramm ja nicht im Interrupt empfangen 
wird sondern nach nach Auswertung des Pins im der Main 
stelle....versuche ich gerade den Empfang in den Interrupt zuverlegen.
Nach Einstellung und Angleich an den MCP versuche ichs gerade - da so 
wie ich vermutet anhand des C-Code du Codevision verwendest sieht das 
bei AVR Studio ja etwas anders aus.

//Interrupt INT1 und INT0 einschalten
EIMSK|=(1<<INT1)|(1<<INT0); // INT1 und INT0 einschalten im General 
Interrupt Control Register

// Einstellen auf fallenden Flanke
EICRA|=(1<<ISC11)|(0<<ISC10)|(1<<ISC01)|(0<<ISC00); //fallende Flanke 
löst aus

ISR (INT0_vect)
{

tCAN message;             //erzeugt ein Variable nach dem tCan struct
mcp2515_get_message(&message);    //liest einen Frame vom MCP2515

print_can_message(&message);    // ausgabe nur für kurzen Test
PRINT("Interrupt ausgelöst\n");    // ausgabe nur für kurzen Test
}


Ob es funktioniert weiß ich noch nicht es läßt sich zumindest jetzt 
compelieren. Mein Frage ...warum ich eigentlich den Betrag aufwärme - 
sollte ich nochmal ein Frage haben ob ich dann nochmal um Rat fragen 
kann.??
Okay das wars schon.........

Gruß
Peter

von H.Joachim S. (crazyhorse)


Lesenswert?

Jaja, mach nur.
Erste Anmerkungen - ich hab die Interrupts als low-level laufen. Das 
hatte auch irgendeinen Grund - weiss ich im Moment nicht, müsste ich 
nachschauen.

Und den print-Kram solltest du schnellstmöglich aus der ISR entsorgen, 
der gehört da nicht hin. Solange du in der ISR bist, funktioniert ja 
kein UART-Interrupt.

von Peter C. (Gast)


Lesenswert?

Hallo,
gute Info, das die Uart in der ISR nicht geht wuste ich jetzt nicht.

Ich bleib am Ball

Schöne Weihanchten

von Peter D. (peda)


Lesenswert?

H.joachim Seifert schrieb:
> Erste Anmerkungen - ich hab die Interrupts als low-level laufen. Das
> hatte auch irgendeinen Grund

Hat es.

Peripherie-ICs haben meistens mehrere Interruptquellen.
Es kann dann sein, daß zufällig eine 2.Quelle getriggert wird und der 
Interruptpin bleibt low.
Du arbeitest die erste ab, verläßt den Interrupt und kriegst nie wieder 
ne Flanke, Dein IC ist tot.
Beim Pegelinterrupt kann das nicht passieren.


Peter

von Peter D. (peda)


Lesenswert?

Peter C. schrieb:
> Der ATMega8 wäre schon für die kleine Bascom Lösung zu klein  (flash und
> Ram) gewesen

Das ist aber schon heftig, ist Bascom wirklich so verschwenderisch?

Du solltest versuchen, keinen Spaghetticode zu schreiben.
Sobald man ähnliche Aufgaben zweimal oder öfter macht, sollte man eine 
Unterfunktion dafür schreiben.
Dann ist auch die Fehlersuche einfacher, man muß den Fehler nur an einer 
Stelle berichtigen.
Aber auch um verschiedene Aufgaben zu trennen, kann man der besseren 
Lesbarkeit wegen Unterfunktionen benutzen.
Z.B. Funktionen für SPI-IO, CAN-Init, CAN-Senden, CAN-Empfang.


Peter

von Lutz (Gast)


Lesenswert?

Der UART funktioniert in der ISR schon, nur mit an sicherheit grenzender 
Wahrscheinlichkeit nicht interruptgesteuert (was ja sonst aus 
Geschwindigkeits- und Effektivitätsgründen der Fall ist). Das liegt 
daran, daß in einer ISR aus der AVR-libc als erste Aktion die Interrupts 
gesperrt werden. Also kann der FIFO-Interrupt im UART zwar schön sein 
Interruptflag setzen, aber ohne global interrupt enabled passiert da 
nichts weiter (welches erst als letzte Aktion bei Verlassen der ISR 
wieder eingeschaltet wird). Erst dann wird der UART-interrupt 
abgearbeitet. Du kannst natürlich mit sogenannten nested interrupts 
arbeiten, das wird aber heftigst und sollte wegen der Interruptstruktur 
beim AVR vermieden werden. Beim Cortex M3 sieht das aber schon anders 
aus.

Peter C. schrieb:
> Da in dem Testprogramm ja nicht im Interrupt empfangen
> wird sondern nach nach Auswertung des Pins im der Main
> stelle....versuche ich gerade den Empfang in den Interrupt zuverlegen.

Das vergiß mal am besten ganz schnell wieder. Die ISR soll/muß aus o.g. 
Grund so kurz wie nur irgend möglich sein, damit keine Überschneidungen 
passieren. Sowas würde sich dann z.B. in gelegentlich fehlenden Zeichen 
usw. äußern; auf jeden Fall meist nicht reproduzierbar, da von der 
aktuellen Belastung des µC abhängig.
Selbst wenn es jetzt klappen sollte: Es gibt keine Garantie und 
spätestens bei Reduzierung der timingresourcen hast du dann irgendwann 
und irgendwie ein Problem. Und dann wieder die ganze Softwarestruktur 
ummodeln motiviert nicht gerade.
Die Softwarearchitektur muß vor dem ersten Programmfetzen schon ziemlich 
konkret und logisch feststehen. Und "ISR so kurz wie möglich" ist beim 
AVR eine unumstößliche Regel. Wie gesagt, spätestens bei Erweiterungen 
(oder auch späterer simpler Taktreduzierung wegen Stromsparen oder so) 
holt einen das ein.

von Peter C. (Gast)


Lesenswert?

Hallo,
ich denke das aufgrund der Funktionen und Interpretationen sowie 
Umsetzung in Bascom der Bascom Code schon größer ist. Es kann auch 
durchaus sein das bei mir nicht alles super kompakt programmiert ist 
(learning by dooing usw.....). Vielleicht vermittelt deshalb Bascom 
einen Hauch von Geschwindigkeitsverlust/nachteil - ohne jetzt den Bascom 
Nutzer zusehr auf die Füsse zutreten - denn bis vor 4 Monaten habe ich 
auch damit gearbeitet.

Das die ISR möglichst kurz sein soll ist ja klar..Da ich möglichst viel 
durch den ATMega und die 2 MCPS2515 durchschleusen möchte dacht ich das 
aufgrund der Darstellung von H.joachim Seifert das ein begehbarer Weg 
sein kann. Seinen Ausführungen entnahm ich das in seiner Anwendung ein 
Haufen Frames durchgehen und es super läuft und sogar noch eine 
Sortierung und weitere Bearbeitung stattfindet.
Ich habe rund 15 Frames die in einer Sekunden von CAN 0 zu CAN 1 sollen 
und zurück sind es ca. 5 Frames. Die kleinste Widerholungszeit sind 
100ms - so dachte ich das es funktionieren kann selbst wenn ich mit, 
vorher Bascom und jetzt C selbst was proboiere. Ich bin kein 
Hardwarprofi und kein Programmierer und studiert habe ich das auch 
nicht. An hand meiner Forschungen ist sicher ein 32Bit MC mit min 2 
internen Can Controllern und 40MHz die beste Wahl sogar noch mit 
deutlichen Reserven - nur sind da keine Informationen zu bekommen 
zumindest nicht für für mich. Ich möchte noch selbst Hand anlegen und 
was lernen. Selbst wenn ich auch bei der Lösung feststellen muß - es 
reicht nicht - gehts vielleicht weiter mit einem LPC2129 oder wie auch 
immer.
Erst mal der AVR und die 2  x MCP2515, händelbar als DIL Varianten und 
dann sehen wir weiter.

Gruß
Peter

PS: Ich bin für jede Hilfe dankbar.

von Peter D. (peda)


Lesenswert?

Peter C. schrieb:
> An hand meiner Forschungen ist sicher ein 32Bit MC mit min 2
> internen Can Controllern und 40MHz die beste Wahl sogar noch mit
> deutlichen Reserven

Auch Deine jetzige Konfiguration hat doch deutliche Reserven.
Es ist daher effektiver, diese nutzen zu lernen. Das Wissen nutzt einem 
dann auch auf den großen Boliden.

Der Trick beim Programmieren ist eigentlich nur, einen ordentlichen 
Programmablaufplan zu erstellen. Daran kann man dann viel besser den 
Flaschenhals erkennen und durch Umstellen beseitigen.
Was man mit Worten aufgeschrieben hat, läßt sich dann auch viel leichter 
in Code umsetzen.

Leider programmieren aber viele Anfänger einfach so drauflos, ohne Plan 
und ohne zu wissen, was sie tun.
Und das ergibt dann umständlichen, fehlerträchtigen und langsamen Code. 
Und dieser Code bleibt dann auch auf einen 64Bit 1GHz Boliden langsam.


Peter

von Peter C. (Gast)


Lesenswert?

Hallo,
ja ich weiß was die Schaltung tun soll und kann das auch beschreiben.
Nur ist halt die Umsetzung meist nicht so einfach. Ich befasse mich 
jetzt seit 4 Monaten mit C und kann nicht unbedingt sagen das ich ein 
Profi bin ich würde eher so den Status kurz nach Anfänger nehmen. Aber 
ich kann in einem gewissen Umfang den C Code deuten was da passiert oder 
passieren soll. So habe ich in meiner Schaltung auch schon Fehler in der 
Software beseitigt die vom org Atmel Studio (io328p) verursacht wurde. 
Hat zwar gedauert aber mit meinem Schulenlisch habe ichs dann doch 
gefunden.

Also rein beim Empfang und der Darstellung der Frames die ich aus dem 
Notebook über einen MHS USB CAN Adapter an den MCP2515 schicke ist schon 
enorm. Selbst wenn ich mit dem MCP2515 empfange und das Eingelesene mit 
einer um 10 erhöhten ID wieder versende ist der Zeitverzug erst ab ca 8 
Frames deutlich zusehen. Bei der Bascom Variante schon ab dem 4. Frame.

So bin ich zur Zeit guter Dinge das da noch was geht und ich zur 
Verbesserung noch einen Puffer irgendiwe dazwischen bekomme.

Aber Schritt für Schritt lernen und begreifen, mir hilft es wenn ich 
erst mal selber schaue und mir das Hirn zermatere.
Klappt nicht immer !!!!!

Gruß
Peter

von Lutz (Gast)


Lesenswert?

Du hast dabei auf jeden Fall die richtige Grundeinstellung => verstehen 
und lernen wollen, nicht blind und doof copy&paste.

Auch bei den DIL-Geschichten zu bleiben finde ich hier absolut richtig, 
da du schnell und problemlos von der Theorie zur Praxis wechseln kannst.

Auf Grund der jetzigen "Probleme" wie Treffsicherheit oder Handhabung 
das Luftgewehr AVR gegen eine Panzerfaust Cortex M3 zu tauschen, wäre 
der größte Fehler, um die Scheibe in 3 m Entfernung treffen zu wollen 
...
Die Einarbeitung und Änderung nur wegen des Chips und der Toolchain 
würde dich vollends demotivieren und du hast dort erstmal ganz andere 
Probleme, die mit deinen Jetzigen nichts zu tun haben.

Wie schon von mehreren Seiten kam: Strukturiertes Arbeiten ist die Basis 
einer jeden effektiven Tätigkeit. Das schließt das (vorherige) Planen 
natürlich mit ein.

Um eventuell noch ein Mißverständnis auszuräumen: Interrupts sind dazu 
erfunden worden, um die vorhandenen Resourcen effektiver zu nutzen; 
dienen quasi der Verteilung von Resourcen, wo sie gerade benötigt 
werden. So sollte man (spätestens) bei der kleinsten Leistungsanhebung, 
wie z.B. Nutzung einer zweiten Peripherieeinheit, von z.B. Polling auf 
Interrupts umsteigen. Innerhalb der ISR sollten aber wirklich nur die 
absolut zeitkritischen Sachen gemacht werden; die evtl. weitere (weniger 
zeitkritische) Verarbeitung muß dann an anderer geeigneter Stelle 
erfolgen. Nur so ist sicherzustellen, daß ein anderer, ebenfalls 
zeitkritischer Prozeß, seine absolut notwendigste Aufgabe ebenfalls in 
der erforderlichen Zeit abarbeiten kann.
Im übertragenen Sinne bedeutet das, daß beim Receive-Interrupt der 
Inhalt innerhalb der ISR in z.B. irgendeinen Buffer ausgelesen und der 
Receive-Buffer danach sofort wieder für den nächsten Frame freigegeben 
wird (inkl. Rücksetzen von Interrupts/Flags sowie Flags zur 
Weiterverarbeitung etc.). Ende der ISR.
Die Verarbeitung der gerade ausgelesenen Message findet dann also 
außerhalb der ISR statt; z.B. durch Auswerten des in der ISR gesetzten 
Weiterverarbeitungs-Flags in der main-loop.

von Peter C. (Gast)


Lesenswert?

Hallo Lutz,
ich danke dir für deine Meinung und ermutigenden Worte.
Einen Plan habe ich selbstverständlich:
- 2 x MCP2515 an einem ATMega328P
- bidiretionaler Datenstrom
- Richtung 0 zu 1 = alles durch
- Richtung 1 zu 0 = 2 IDs herausfiltern (ID 0x261 und ID 0x263)
- Empfangs- Sendebuffer mit einbeziehen
- bei freien MC-Resourcen soll bei einer bestimmten ID die Schaltung 
abgeschaltet werden.

Mein Plan und Vorgehensweise
- zuvor habe ich mit einem CAN USB Adapter den kompletten Datenstrom mit 
geschnitten. Ich weiß also das der CAN 100KBit hat, Frames in 
verschiedenen Längen sowie RTRs dazwischen sind. Es sind insgesamt ca 
15-20 Frames die mit unterchiedlichen Zeiten wiederholt werden. Ein paar 
werden mit 100msec, 200msec, 500msec und ein kleiner Teil mit 800msec / 
1000msec versendet. Die Ids konnte ich zu 95 Prozent identifizieren und 
weiß was für Daten die Frames enthalten. Bauteilanmeldung am CAN Ring 
usw usw.......

In der Testschaltung arbeitet auf einer Seite ein CAN USB mit dem ich 
per Notebook senden/empfangen kann und die andere Seite bildet ebenfalls 
ein AT90CAN128 der auch senden/empfangen kann.
Meine Schaltung sitzt dann dazwischen und hat eine Verbindung zu einem 
Standrechner der per RS232 angebunden ist - nur um zusehen was passiert. 
das Notbook zeigt alle CAN Daten mit Zeitstempel.
Ich habe auf einer Lochrasterplatine sowohl 2 x MCP2515 mit ATMega328 
sowie auch einen AT90CAN128 mit MCP2515 gebaut. Beides habe ich auf 
Bascom zum Laufen gebracht mit mäßigem Gechwindigkeitsgewinn.

Um C zuerlernen habe ich im Internet nach was brauchbaren erklärten 
gesucht und die Seite von Kreatives Chaos war der Treffer schlecht hin.
So habe ich zunächst das Beispiel auf dem AVR Studio 4 geladen und auf 
den ATMega328P umgestellt. (das hat schon einige Zeit gedauert da ich 
auch das Datenblatt einmal umkrempeln mußte und nach der Nadel im 
Heuhaufen gesucht habe und den Fehler in der Studio eigenen Datei 
gefunden habe)
Nach dem das Beispiel dann fehlerfrei im AVR Studio compelierbar war, 
war es auch einfacher Teile des Codes zuverstehen. So war der 1. MCP und 
der ATMega328P schon mal online und als Knoten am CAN instaliert.

Da in dem Code der Int0 nicht als Interrupt sondern als PIN in der Main 
abgefragt wurde - die Main also nur darauf wartet das der PIN gesetzt 
wird arbeite ich an der Umstellung auf den Interrupt - zur Zeit.

Weiter möchte ich dann die Initialsierung des 2. MCP2515 einbauen und 
den Empfang gleichzeit über beide Seiten darstellen.
Dann die empfangen Frames von CAN 0 und CAN 1 in einem Buffer 
zwischenspeichern und bis dahin wirds sicher noch Monate dauern, 
überkreuz aus dem Buffer versenden.
Wenn das klappt ...........wird sicher noch das Filtern vor dem Buffer 
erfolgen müssen. Macht ja keinen Sinn was zu speichern was man nicht 
braucht.

so ....das wäre mein Plan.
Anregungen oder Tipps nehme ich gerne entgegen.....

Gruß
Peter

von Lutz (Gast)


Lesenswert?

Peter C. schrieb:
> Da in dem Code der Int0 nicht als Interrupt sondern als PIN in der Main
> abgefragt wurde - die Main also nur darauf wartet das der PIN gesetzt
> wird arbeite ich an der Umstellung auf den Interrupt - zur Zeit.

Die main läuft also immer durch und schaut gerade in dem Moment, ob der 
Pin gesetzt ist. Wenn ja, wird die message weiterverarbeitet.
=> Das wäre schlecht, denn alles was nach der Pinabfrage kommt, geht 
erst weiter, wenn die vorherige messageverarbeitung zu Ende ist. In der 
Zeit kann evtl. schon eine weitere message (auch in einem anderen 
Empfangsregister) überschrieben werden/verloren gehen.

Als Lösung "einfach" wie beschrieben den INT aktivieren und in der ISR 
den buffer auslesen, ein Flag in der main setzen, den INT zurücksetzen 
und weiter geht das Programm. Insgesamt ist der Code für alles ja schon 
da; du mußt ihn nur entsprechend auf die ISR und die main aufteilen.

von Flo (Gast)


Lesenswert?

Geht es dabei zufällig um das anzeigen von eigenen Daten im FIS? ID 
0x261 und 0x263 würde da passen.

von Peter C. (Gast)


Lesenswert?

Hallo Lutz,
wenn ich den Demo-Code richtig deute läuft die Main sogar nicht mal 
durch sondern wartet in dieser Endlosschleife auf den Empfang.

  while (1) {
    // warten bis wir eine Nachricht empfangen
    if (mcp2515_check_message()) {
      PRINT("Nachricht empfangen!\n");

      // Lese die Nachricht aus dem Puffern des MCP2515
      if (mcp2515_get_message(&message)) {
        print_can_message(&message);
        PRINT("\n\n");

      //schickt direkt Message + 1 zurück
  //     message.id = message.id +1;
  //    mcp2515_send_message(&message);
      }
      else {
        PRINT("Kann die Nachricht nicht auslesen\n\n");
      }
    }
  }

  return 0;
}

Okay..........also den ISR int0 auf "low Level" damit auch der zweite 
schnell nachfolgende Interrupt mitbekommen wird und doch besser in der 
ISR den MCP-Buffer auslesen in Framebuffer speichern und mit flag in der 
Main weiterverarbeiten.

Im Moment stecke ich am Interrupt, nachdem die ISR ausgelöst wurde 
vermutet ich gehts nicht weiter denn der Interrupt im MCP wird nicht 
gelöscht. Nach EInschalten liegen am Int0 5V nach Empfang 0V un dkeine 
Änderung mehr........

//aktivieren des int0 im ATMega328P
EIMSK|=(1<<INT0);
//fallende Flanke
EICRA|=(1<<ISC01);
der muß dann wohl auf low Level gem. Datenblatt ATMega328P Seite 73 !!??
schreibt man das so EICRA|=(0<<ISC01); oder so EICRA &= ~(1<<ISC01);
In der ISR komme ich mit

ISR (INT0_vect)
{

tCAN message;                      // def struct variable
mcp2515_get_message(&message);     // hol den Frame aus dem MCP Buffer 
noch ohne weiter speichern und flag
print_can_message(&message);      //nur zum zeigen

}

da ist auch noch was falsch.

Gruß
Peter

von Lutz (Gast)


Lesenswert?

Ist leider ziemlich schwierig bzw. unmöglich, aus den Codefetzen auf das 
gewünschte Verhalten bzw. Gesamtverhalten des Programms zu schließen.

Zumindest die o.g. while-Schleife macht anscheinend schon etwas 
sinnvolles (wobei wieder uneingeschränkt Satz 1 zum Tragen kommt). Das
1
return 0; 
2
}
gehört aber schon nicht mehr zur while-Schleife und wird vermutlich das 
Ende von main sein.

Die Schleife prüft (anscheinend) ständig, ob eine Nachricht empfangen 
wurde. Wenn nicht wieder zum Schleifenanfang.
Wenn ja, wird das (anscheinend) mit PRINT ausgegeben.
Dann wird (anscheinend) versucht, die message in eine (vermutliche) 
struct zu schreiben.
Wenn das erfolgreich war, wird die message (anscheinend) ausgegeben und 
anschließend noch 2 Zeilenumbrüche angefügt.
Wenn das nicht erfolgreich war, wird das (anscheinend) mit PRINT 
ausgegeben.

Du mußt beide Chips getrennt sehen:
MCP2515:
Erstmal mußst du dem MCP2515 "beibringen", welche messages er überhaupt 
annehmen soll. Das macht er ausschließlich über die Identifier. Dazu 
gehört auch, in welchem Empfangsbuffer die message landen soll. Und du 
mußt ihm sagen, wie er eine empfangenen Nachricht nach außen 
signalisieren soll. Ich glaube, man konnte beim MCP2515 einmal zwischen 
der Methode "Sammelinterrupt" wählen. Egal was er hat hat, er 
signalisiert das nur an einem Pin nach außen. Dann müßtest du den 
MCP2515 durch auslesen von Registern (über die SPI-Schnittstelle) 
gezielt befragen, was er denn hat. Oder man konfiguriert ihn so, daß er 
pro Empfangsbuffer einen bestimmten Pin signalisiert. Damit wäre dann 
nach außen schon klar, welcher Empfangsbuffer ausgelesen werden muß. Das 
reduziert die erforderliche Kommunikation mit dem MCP2515.

ATMegaxx:
Hier schließt du die oder den o.g. Pin "Signalpin" des MCP2515 an (je 
nach dem, ob Sammelinterrupt oder nicht). Den Pin des ATMega mußt du als 
externen Interrupt einrichten. In der ISR muß dann, je nach gewählter 
Methode ("Sammelinterrupt oder nicht"), die Befragung des MCP2515 
angeschoben werden.

***
Hier kommt dann das vorher diskutierte Thema zum Tragen, was in die ISR 
kommt und was in die while-Schleife gepackt wird.
***

Zum Ende der ISR muß auf jeden Fall der Interrupt des ATMega wieder 
zurückgesetzt werden.
Wann der Interrupt des MCP2515 zurückgesetzt wird, hängt wiederum von 
der gewählten Methode "Sammelinterrupt" ab. Eventuell werden die 
Interrupts auch durch Auslesen des Empfangsbuffers oder Ausführen der 
ISR automatisch zurückgesetzt; weiß ich aber nicht mehr, Steht aber 
natürlich im Datenblatt des MCP2515 und des ATMega.

Peter C. schrieb:
> schreibt man das so EICRA|=(0<<ISC01); oder so EICRA &= ~(1<<ISC01);

Letzteres. Mal dir am besten binär mal beide Schreibweisen auf, dann 
verstehst du, was da eigentlich gemacht wird.
Im ersten Fall verODERst du die Zahl Null, welche vorher um ISC01 
Stellen nach links geschoben wurde (ISC01 wird irgendwo, wahrscheinlich 
in der atmega328io.h so stehen: #define ISC01 1). Wenn an der Stelle 
vorher eine Eins stand, bleibt sie da auch stehen. Wohl nicht das, was 
gewünscht ist.

Im zweiten Fall verUNDest eine Bitmaske, bei der das gewünschte Bit in 
der Maske Null ist. Das ergibt auf jeden Fall als Ergebnis Null, egal 
was vorher da stand. Wohl das, was gewünscht ist.
Wie gesagt, am besten mal binar aufschreiben.

Jetzt ist aber der Entenbraten fällig!

von Peter C. (Gast)


Angehängte Dateien:

Lesenswert?

Hallo Lutz,
so anbei der komplette C Code mit allem aus diesem Verzeichnis auch mit 
der geänderten io328p

Der MCP ist so eingestellt das jede empfangener Frames einen Interrupt 
an dem Sammel-PIN des MCP auslöst und damit am int0/PD.2 des Atmega 
anliegt. Im Demo Programm wir der PD.2 als PIN genutzt und in der 
mcp2515_check_message() abgefragt und dann mit 
mcp2515_get_message(&message) erst der Frame vom MCP geholt. In der 
While schleife wird bei jeden Durchlauf geprüft ob ein Frame da ist - 
und so können schon welche verloren gehen oder gegangen sein - speziell 
gesehen auf das was später mal raus werden soll - hatte ich schon 
beschrieben.

Ich habe die ISR zum int0 angelegt und in der main eingestellt. Um zu 
sehen ob die ISR tatsächlich auch auslöst mit einem Todesstrafen Print 
versehen.
Dann versucht dort ein Flag zusetzen um diese dann in der Main 
auzuwerten und das auslesen des MCP anzustoßen. geht aber so nicht,da 
sind die C  Lücken wohl doch noch zugroß.

bin für jeden Tipp dankbar

Gruß
Peter

von Lutz (Gast)


Lesenswert?

Nur ganz kurz durch die main geflogen:

Laut 7.0 Datasheet MCP2515:
When an interrupt occurs, the INT pin is driven low by the MCP2515 and 
will remain low until the interrupt is cleared by the MCU.
=> Der MCP setzt den entsprechenden Interruptpin auf low. Du mußt also 
den ATMega-Interrupt auf einen Flankenwechsel von high nach low 
einstellen. Gemäß Table 13-2 Datasheet ATMega328P: (1<<ISC01). Der ISC00 
muß Null bleiben. Da du erstmal nur mit INT0 testen 
solltest/kannst/willst, kannst du also einfach erstmal
EICRA = (1<<ISC01);
schreiben.

von Peter C. (Gast)


Lesenswert?

Hallo Lutz,
ja diese Einstellung funktioniert auch, der Interrupt wird ausgelöst. In 
der ISR schreibe ich dann einfach eine 1 in meine Flag Variable die ich 
dann in der Main per Print anzeigen lasse bzw wieder mit 0 überschreibe 
um zu sehen ob in der nächsten ISR Auslösung wieder eine 1 
hereingeschrieben wurde.
Da der Int vom MCP ja zwingend per SPI bzw. Bitmodify vom ATMega 
zurückgesetzt werden muß ist die high -> low Flanke erstmal kein Problem 
da zu den Tests die Frames nur langsam mit 800mSec wiederholt werden und 
so Zeit zur Reaktion bleibt.
Wenn später deutlich mehr Frames folgen, kann es da nicht passieren, das 
wenn der Int vom MCP durch den ATMega abgearbeitet / der Frame 
ausgelesen wird, das dann bereits ein zweiter Frame im MCP angekommen 
ist und den Int-MCP auf low hält der ATMega dies aber nicht erkennt weil 
der Int0 "nur" auf den Flankenwechsel High->low reagiert und so ein 
Frame, vom Atmega unbemerkt, im MCP bleibt. Der ATMega setzt ja auch nur 
das Register im MCP zurück welches zuvor als meldendes und ausgelesendes 
identifizerit wurde - oder ????????????
Wurde deshalb weiteroben die "Low Level" Einstellung bevorzugt ???? und 
so der ATMega immer nach einem angekommenden Frame in den 2 Buffern 
sucht (Statusabfrage) solange der Int-MCP auf Low bleibt ??????

Ich habe versucht in der main mein ISR Flag abzufragen und so das 
auslesen des MCP anzustossen - da geht aber noch was in die Hose. Aus 
Basic kenne ich die Do  -  Loop Schleife die sich immer wiederholt, 
stellt die  While () ähnliches da ???? Diese Frage traue ich mich als 
Anfänger garnicht zustellen.

Gruß
Peter

von Peter C. (Gast)


Lesenswert?

Hallo Lutz,
also so funktioniert es jetzt mit dem setzen des Flag in der ISR und 
Auswertung in der Main. Direkt nach dem Einlesen schicke ich dén Frames 
wieder mit ID+10 zurück auf den CAN (sicher nicht schön aber der test 
heiligt die Mittel - damit es keine Kollisionen und Fehler gibt).
So schafft der MCP 50 Frames einzulesen und 50 Frames zuversenden und 
das in einer Sekunde. Da die Frames mit wie schon geschrieben mit 100 
msec, 200 msec usw bis 1000 msec eingehen und auch wieder direkt 
versendet werden haben die Einzelframes nur eine Verzögerung von 1-2 
mSec.
Das übersteigt meine künsten Erwartungen - ich bin begeistert.

Dann werde ich mich mal an den Buffer wagen...........
Achja damit auch der zweite Empfangsbuffer im MCP den Rollover mitspielt 
habe ich noch das BUKT Bit gesetzt (MCP Seite 27)

// turn off filters => receive any message
mcp2515_write_register(RXB0CTRL, (1<<RXM1)|(1<<RXM0)|(1<<BUKT));
mcp2515_write_register(RXB1CTRL, (1<<RXM1)|(1<<RXM0));

volatile uint8_t flag;

ISR (INT0_vect)
{
flag = 1;
}

while (1) {

   if (flag == 1) {

   // Lese die Nachricht aus dem Puffern des MCP2515
         mcp2515_get_message(&message);
   //      PRINT(" ID : 0x%3x\n", message.id);  //nur Test

        //schickt direkt Message + 1 zurück
          message.id = message.id +10; //  ID + 10 senden auf gleichen 
CAN
          mcp2515_send_message(&message);

        flag = 0;    //  zurücksetzen des ISR flag
      }
    }


  return 0;
}

von Lutz (Gast)


Lesenswert?

Peter C. schrieb:
> Wenn später deutlich mehr Frames folgen, kann es da nicht passieren, das
> wenn der Int vom MCP durch den ATMega abgearbeitet / der Frame
> ausgelesen wird, das dann bereits ein zweiter Frame im MCP angekommen
> ist und den Int-MCP auf low hält der ATMega dies aber nicht erkennt weil
> der Int0 "nur" auf den Flankenwechsel High->low reagiert und so ein
> Frame, vom Atmega unbemerkt, im MCP bleibt. Der ATMega setzt ja auch nur
> das Register im MCP zurück welches zuvor als meldendes und ausgelesendes
> identifizerit wurde - oder ????????????
> Wurde deshalb weiteroben die "Low Level" Einstellung bevorzugt ???? und
> so der ATMega immer nach einem angekommenden Frame in den 2 Buffern
> sucht (Statusabfrage) solange der Int-MCP auf Low bleibt ??????

Na ja, der MCP2515 läßt sich ja schon den Geschwindigkeitsanforderungen 
entsprechend nutzen. Grundsätzlich erfüllt er die Spezifikation mit 1 
MBit/s, ist aber noch verhältnismäßigig simpel (jede Funktion für sich 
alleine; das Gesamtzusammenspiel des Chips ist schon deutlich doller. 
Aber bei wenig Bitrate kommt halt jeder einfach und schnell zu seinem 
Ziel).
Man kann an den Schrauben drehen:
- SPI-Takt. Geht bis 10 MHz. Da kann man schon abschätzen, wieviel Bit/s 
theoretisch gehen. Kommt dann aber auch auf den Overhead an. Wie komme 
ich schnellstmöglich an meine Informationen? Lese ich dieses oder jenes 
Register erstmal pauschal aus? Hangel ich mich Schritt für Schritt mit 
Abfragen an meine Information? Auch unterstützt da das automatische 
address-increment; man braucht nur weiter die Schnittstelle takten.
- Interrupt-Pins. Man kann einen Sammelinterrupt nehmen und o.g. 
Abfrageorgie praktizieren. Man kann aber auch jeden Empfangsbuffer 
einzeln per Pin signaliseren lassen. Dann ist schon mal klar, welchen 
Buffer man auslesen muß und man muß weniger per SPI kommunizieren.
- Man kann Rollover als letztes Mittel nutzen.
- Generell haben die meisten CAN-Chips mehr Sende- als Empfangsbuffer. 
Warum nur?
- Man kann übelst Takte zählen und schauen, ob es nicht kürzer geht, 
wenn man den Empfangsbuffer schon in der ISR ausliest. Während der 
Inhalt per SPI rüber geht, puffert im Zweifelsfalle das MAB, bis der 
nächste Frame evtl. gültig wird und dann erst in den Empfangsbuffer 
kopiert wird.

Da geht bestimmt noch mehr.

von Luigi (Gast)


Lesenswert?

Hallo, Peter

So, jetzt schreib ich auch meinen Senf dazu.. :)
Ich habe was ähnliches wie Du auch schon mit einem Mega8 gemacht, sogar 
mit bis zu 4 x 2515. Bei meinen recht nidrigen Bitraten gab's nie 
Probleme.

Wenn ich etwas daraus gelernt habe dann: Lass wenns irgendwie möglich 
ist die Finger weg von Interrupts (im AVR, die Interrupt-Leitungen von 
den Controllern machen ggf. schon Sinn.

Dein letztes Beispiel klappt genauso gut wenn Du auf den ISR (INT0_vect) 
komplett verzichtest und in main() statt if (flag == 1) einfach den 
Portpin abfragst an dem der Interrupt anliegt.
Damit vermeidest Du insbesondere die Fehlerquelle (die in Deinem Code 
noch da ist), dass ein zweiter IRQ kommt noch bevor Du flag = 0 setzt.
Wenn Du das unbedingt so machen willst, mach' es jedenfalls in der Zeile 
unmittelbar nach der Abfrage des flag.

Auf KEINEN Fall solltest Du in irgend einem Interrupt eine SPI 
Kommunikation machen. Die Probleme die Du bekommst wenn ein Interrupt in 
eine laufende Kommunikation, vielleicht sogar mit einem anderen 2515 
hineinkracht, kannst Du damit wirksam vermeiden.

Ich verzichte übrigens komplett auf die Interrupt-Leitungen und frage 
den Buffer-Status nur über SPI ab. Mit einer schnellen SPI und wenn Du 
beide Empfangsbuffer der 2515 (Rollover Buffer 0 -> Buffer 1) nutzen 
kannst funktioniert das nach meiner Erfahrung ausgezeichnet ohne das was 
verloren geht.

Ich hoffe ich konnte was konstruktives zu Deinem Projekt beitragen...
lg
Luigi

von Lutz (Gast)


Lesenswert?

Nun ja, das ist in der Tat ein freies Land und da darf natürlich ein 
Jeder sagen und denken, wie es ihm behagt.

Bei sachlichen Themen helfen Fakten da allerdings extrem weiter.

Luigi schrieb:
> Auf KEINEN Fall solltest Du in irgend einem Interrupt eine SPI
> Kommunikation machen. Die Probleme die Du bekommst wenn ein Interrupt in
> eine laufende Kommunikation, vielleicht sogar mit einem anderen 2515
> hineinkracht, kannst Du damit wirksam vermeiden.

Du kennst die zu Grunde liegende avr-libc schon, ja?

#define ISR (vector,
      attributes
      )
Introduces an interrupt handler function (interrupt service routine) 
that runs with global interrupts initially disabled by default with no 
attributes specified.

Jede ISR aus der avr-libc sichert als erstes mal das SREG, dann cli(); 
und restored als letztes das SREG beim Verlassen wieder. Zumindest war 
es mal so.
Wie kann da jetzt in einer ISR ein Interrupt dazwischenhauen und das von 
dir beschriebene Verhalten verursachen???

u.A.w.g.

P.S.: Bitte zerstöre wegen deiner (wegen Zensur habe ich den Inhalt der 
Klammer schon selber gelöscht) Erfahrungen einem ernsthaft Lernenden 
nicht den Lehrweg. Echt nicht persönlich gemeint, aber nur weil du die 
Interrupts nicht verstanden hast, müssen andere nichts falsches lernen. 
Und das zuerst gelernte bleibt nun mal am besten hängen ...

Aber sollen bitte noch andere ihre Meinung dazu sagen; es kann ja 
schließlich auch sein, daß ich mich irre.

von Peter C. (Gast)


Lesenswert?

Hallo zusammen,
ich wollte eigentlich nur mitteilen das ich die nächste Stufe in meinem 
Plan geschafft habe.(mit euren Tipps).... Im Vorfeld habe ich einwenig 
geforscht um Informationen zum Datendurchsatz und dem Händling 
zubekommen, auf viel bin ich nicht gestossen mit dem ich zum damaligen 
Zeitpunkt was hätte anfangen können und ohne Ahnung .....wonach hätte 
ich fragen sollen. Wie der Anfang des Beitrages ja zeigt bin ich dann 
vor 14 Monaten dem Tipp von  H.joachim Seifert nachgekommen und hatte es 
zuerst mit Bascom propiert. Mit Bascom habe ich einwenig Erfahrung aber 
das war gestern....

Sicher bin ich auch auf Ergebnisse gestossen die auf 16/32Bitter MCs 
basierten oder auch Kauflösungen.
Aber ich wollte selbst ran und verstehen was da so abgeht. Vor 4 Monaten 
habe ich die Bascomlösung geschmiessen und habe mit C begonnen - kein 
leichter Schritt. Ich habe viel gelesen und habe aufgrund der Dil 
Varinaten genau diese Chips gewählt. Ich kann die Platine selbst 
herstellen, ich kann eine Testschaltung selbst aufbauen und so ist auch 
der Weg ein Teil des Zieles.
Meine Elektronikerausbildung ist 25 Jahre her und da spieleten diese 
Dinge(MCs) noch garkeine Rolle ganz zu schweigen vom Schulenglisch und 
den Schwierigkeiten mit den Datenblättern und dem technischem Englisch. 
So bleiben mir sicher Dinge verborgen die andere mit einem Handstreich 
begreifen und umsetzen können.

Ich ich finde es toll, das diese Eure Meinungen hier zusammen kommen und 
viele Wege aufzeigen wie es gehen kann oder welche Erfahrung jeder 
Einzelne gemacht hat. Alles geäußerte ist es wert uberdacht und beachtet 
zu werden.

Wie schon gesagt.....geschrieben ich bin für jeden Tipp dankbar.

Gruß
Peter

von H.Joachim S. (crazyhorse)


Lesenswert?

Ja, sorry, bin noch nicht dazu gekommen, deine mir geschickten Sourcen 
durchzusehen. Jahresende ist bei mir immer etwas stressig und zu allem 
Überfluss habe ich mir einen netten PC-Virus eingefangen. Ist noch nicht 
geklärt, woher der kam. Aber 4 Rechner komplett neu aufgesetzt....:-(

Interrupt ist auf jeden Fall der richtige Weg, lass dich da nicht irre 
machen. Habe ich ja auch ganz am Anfang geschrieben: wenn du was senden 
willst, vorher die Ints sperren und anschliessend wieder freigeben, ist 
ja kein Problem, da du (bzw. dein Programm) weiss, wann es etwas senden 
willst.
Zum Zeitverhalten:
Bei mir laufen 2 x 500kbit, mit 11bit ID und i.a. 8 Datenbytes/frame. 
Das macht 130 bit pro message. 2µs/bit * 130 = 260µs/frame.
Um diesen auszulesen oder einen frame zu senden, braucht man ca. 20 
SPI-Transfers. Bei 8MHz SPI-Takt/16MHz MC-Takt dauert das gerade mal 
20µs.
Also, angenommen, beide Busse laufen mit Volldampf (kommt in der Praxis 
nicht mal annähernd vor), dann hast du alle 130µs einen frame zu 
lesen/zu senden. Rechnen wir grosszügig mit Interruptaufruf/return 30µs. 
Macht eine Auslastung von 25% der Prozessorzeit, also eigentlich 
Langeweile...
Kürzere messages verschlechtern diesen Wert etwas, aber niemals 
bedohlich (SPI-Transfers werden ja auch weniger bei kürzeren messages)
Du hast in jedem Fall Zeit genug, alles zu erledigen, was anfällt. Und 
wenn die CAN-Bus-Rate niedrieger ist, hast du sogar noch deutlich mehr 
Zeit.

von Peter C. (Gast)


Lesenswert?

Hallo crazyhorse,
okay arbeitet dann deine Schaltung von der Hand in den Mund ???? soll 
heißen der empfangene Frame wird per Int vom MCP-0 dem Mega auf dem int0 
(für Can0) mitgeteilt und der sperrt nur den Int0 (für Can0) holt den 
Frame aus dem MCP-0 noch in der ISR und überträgt den Frame in den 2. 
MCP (CAN1) und gibt dann den int0 wieder frei. In der Zeit kommen schon 
wieder weitere Frames an die dank des low Level ISR auch nicht vergessen 
werden können ???????

Arbeitest du mit einem Zwischenpuffer der die Framerichtung im Auge 
behält oder hat deine Variable CAN_sw=1; CAN_sw=0; eine direkte 
Abarbeitungsrichtung zur Folge ?????? hast du die Umschaltvariable 
direkt mit in den CAN struct mit eingebaut ???? oder separiert.

Wenn ein Zwischenspeicher voller wird und die Versendung nicht so 
schnell hinterher kommt könnte natürlich auch ein Speicherüberlauf 
vorkommen !!!!!!?????

Wie gesagt vor 14 Monaten habe ich noch nicht gecheckt was da so 
geht....heute nach 4 Monaten ("C") bin ich deutlich weiter wenn auch 
noch lange lange nicht auf dem Level.

Auf meinem CAN sind es 2 x 100KBit mit Datenframes mit unterschiedlicher 
Länge und das reicht von 8byte und einer Wiederholungsrate von 1000msec 
bis hin zu einem Datenbyte mit 100msec. Eine Wiederholungsrate die 
kürzer als 100msec ist, ist nicht dabei.

Würde ein Mitschnitt des Datentransfer für die fachliche Beurteilung 
helfen können ???

Gruß
Peter

von Luigi (Gast)


Lesenswert?

Hallo nochmal Peter!
Hallo, Lutz!

Scheinbar habe ich mich da etwas unglücklich ausgedrückt, jedenfalls 
schlecht genug um von Lutz gleich in die Spinner-Ecke gestellt zu 
werden... (:

Ich hoffe dass ich die Interrupts ganz gut verstehe, danke dennoch für 
die Belehrung.

Der Punkt auf den ich hinauswollte ist der, dass es Probleme geben kann, 
wenn Du sowohl aus einer ISR als auch aus dem main() Kontext die SPI 
Schnittstelle ansteuerst.
Nimm an Du startest eine SPI Kommunikation aus main() und diese wird von 
einer ISR unterbrochen.
Wenn Du in der ISR ebenfalls SPI machst, wird die ursprüngliche 
Kommunikation gestört, das passiert natürlich auch dann wenn 2 
unterschiedliche Geräte angesteuert werden, da ja CS von der 
Original-Kommunikation noch gesetzt ist.
Das Ergebnis ist dann leider ziemlich unvorhersehbar.
Natürlich kann man das über Flags synchronisieren, aber was macht man in 
der ISR wenn man draufkommt dass man grad kein SPI machen kann..?

Noch ein kurzes Wort zu H.joachims "Interrupt ist auf jeden Fall der 
richtige Weg":
Jetzt fange ich auch ein bisschen rechnen an...
Du hast also 260µs pro Frame. Mit einem hohen Anteil an Remote-Frames 
kommt man etwas tiefer, vielleicht 200µs.
Das Abfragen des Empfangs-Register-Status über SPI dauert ungefähr 2 µs 
also 1/100tel davon.
Bei 2 x 2515 also ca. 4 µs.

Wenn Du laufend, während nichts anderes zu tun ist, die Statusregister 
der 2515 abfragst, kannst Du also innerhalb von 4 µs auf einen 
eingegangenen Frame reagieren.
Wenn man dasselbe mit Interrupts macht, kann man das vielleicht auf 1 µs 
drücken.
Keinesfalls kann es passieren, dass deswegen ein Frame verloren geht.
Außerdem hat man eine laufende Prüfung (die ich gerne verwende) ob der 
2515 noch korrekt ansprechbar ist.

Bitte beachte dass ich nicht sagen möchte dass die Verwendung der 
Interrupts jedenfalls sinnlos ist, es geht nur i.A. auch ohne, so dass 
man sich in vielen Fällen die zusätzlichen Leitungen sowie die Port-Pins 
sparen kann.

So, jetzt will ich Euch Experten nicht länger davon abhalten, andere 
Poster zu zerreissen und wechsle wieder in die englischsprachlichen 
Foren.
Irgendwie scheint es dort eine positivere Grundstimmung gegenüber Laien 
die Interrupts nicht verstehen zu geben...

lg
Luigi

von H.Joachim S. (crazyhorse)


Lesenswert?

Moin Peter.

ich habe noch etliche weitere Buffer drin

CANMessage motor0_rec[6];
CANMessage motor0_trans[10];
CANMessage motor1_rec[10];
CANMessage motor1_trans[9];

In die Receivebuffer wird erstmal alles nur anhand der ID einsortiert 
dann, die Daten umgerechnet/umsortiert und entsprechenden Felder der 
Transmitbuffer geschrieben, welche dann im passenden Zeitraster wieder 
gesendet werden. Das brauchst du aber alles gar nicht, da du gar nicht 
kreuz und quer alles verwursten musst.

Ich weiss im Moment gar nicht mehr genau, wo dein eigentliches Problem 
liegt, der trööt ist mir zu lang geworden, nochmal alles durchzulesen...

Wie ist im Moment deine Hardware? 1 oder 2 MCP2515? Hast du eine 
garantiert funktionierende Gegenstelle (z.B. CAN/USB Adapter oder 
irgendein DevBoard, mit dem du die CAN-Daten anzeigen kannst?)

Fang an mit einem, empfange im Interrupt und sende es zurück mit 
geänderter ID.

Vorausgesetzt, dein Empfang funktioniert, ist das Programm trivial:
CANMessage rec_can0, transmit_can0;
while (1)
   {if (CAN0_message_received)
       {#asm ("cli")
        CAN0_message_received=0;
        transmit_can0=rec_can0;
        transmit_can0.id=transmit_can0.id+1;
        CAN_sw=0;
        can_send_message (&transmit_can0);
        #asm ("sei")
       }
   }

Kann man kürzer schreiben, und man sollte auch nicht blind irgendwelche 
IDs (transmit_can0.id+1) auf einen realen Bus werfen. Für nen 
Testbetrieb an deinem eigenen System geht das natürlich.

Und mit 2 MCP2515 sieht das auch nicht viel anders aus:
CANMessage rec_can0, transmit_can0, rec_can1, transmit_can1;
while (1)
   {if (CAN0_message_received)    //CAN0 empfangen?
       {#asm ("cli")
        CAN0_message_received=0;
        transmit_can1=rec_can0;
        CAN_sw=1;                 //SPI für MCP2515_1
        can_send_message (&transmit_can1);
        #asm ("sei")
       }
    if (CAN1_message_received)    //CAN1 empfangen?
       {#asm ("cli")
        CAN1_message_received=0;
        transmit_can0=rec_can1;
        CAN_sw=0;                  //SPI für MCP2515_0
        can_send_message (&transmit_can0);
        #asm ("sei")
       }
   }

Sendet also alles empfangene auf dem anderen Bus und behandelt erst mal 
nur die reinen Datenframes.

von Peter C. (Gast)


Lesenswert?

Hallo,
also mein ursprüngliches Problem vor 14 Monaten war das ich einen CAN zu 
CAN Router bauen wollte und das ohne Plan mit wenig CAN Kenntnissen und 
mehr Bascom Erfahrung...................Nach 10 Monaten also vor 4 
Monaten habe ich zwar einen CAN zu CAN Router mit Bascom und 2 x MCP2515 
und Atmega328P sowie mit AT90CAN128 und einem MCP2515 realisiert aber 
das war nicht mit dem Datendurchsatz zu vergleichen was jetzt mit C und 
dem MCP und dem ATMega328P schon geht.
4 Monate intensives lernen und probieren haben mich einen deutlichen 
Schritt an den CAN und große Schritte näher an C gebracht.

Hardware:
Notebook mit CAN USB von MHS, Lochrasterplatine (SPI Leitungen noch 
nicht optimal) mit 2 x MCP2515 und ATMega328P und noch einen AT90CAN128 
(langsames lesen/schreiben= Bascom) sowie RS232 Anzeigemöglichkeit auf 
dem Stand PC auf dem auch AVR Studio ist.
Von der Seite her kann ich 2 CAN Strecken ausbauen wo sich in der Mitte 
die Platine mit den 2 x MCP und der ATMega befindet - so kann ich den 
Durchsatz ansatzweise testen.

Um beide MCP mit dem Atmega zu nutzen muß ich dann aber noch die 
Initialisierung auf beide bringen, ich muß mir noch mal dein Beispiel 
ansehen wie ich die CS Pins in die Grundfunktionen einbringen muß - ohne 
dem gehen ja nicht beide.

Ich schau aber erstmal ob ichs selbst hinbekommen - dann bleibt es 
besser kleben

Danke.....

Gruß
Peter

von H.Joachim S. (crazyhorse)


Lesenswert?

Also Initialierung:

CAN_sw=0;
mcp2515_init(0x00,0xb8,0x05);   //CAN0 500kbit,100kBit natürlich anders 
:-)
CAN_sw=1;
mcp2515_init(0x00,0xb8,0x05);   //CAN1 500kbit



// Global enable interrupts
#asm("sei")

while (1)
   {
   .
   .
    }


und das hier:

if (!CAN_sw)  PORTB &= ~(1<<SPI_CS1);    // CS1 low
      else PORTB &= ~(1<<SPI_CS2);         //CS2 low
.
.
.
 PORTB |= (1<<SPI_CS1 | 1<<SPI_CS2);      // beide CS high

steckt in:
- mcp2515_read_register
- mcp2515_write_register
- mcp2515_write_register_f
- mcp2515_bit_modify
- mcp2515_read_rx_status
- mcp2515_init
- can_get_message
- can_send_message

also überall da, wo im Origninal vorkommt
PORTB &= ~(1<<SPI_CS);    // CS low
ersetzen durch

if (!CAN_sw)  PORTB &= ~(1<<SPI_CS1);    // CS1 low
      else PORTB &= ~(1<<SPI_CS2);         //CS2 low

und

PORTB |= (1<<SPI_CS);      // CS high

ersetzen durch
PORTB |= (1<<SPI_CS1 | 1<<SPI_CS2);      // beide CS high

das ist alles.

Blöde Frage am Rande: liegen deine /CS überhaupt beide an PortB?

von Lutz (Gast)


Lesenswert?

Luigi schrieb:
> Scheinbar habe ich mich da etwas unglücklich ausgedrückt,

hat ja jetzt aber gut geklappt.

> jedenfalls schlecht genug um von Lutz gleich in die
> Spinner-Ecke gestellt zu werden... (:

Ach was! Nich gleich Pippi in die Augen kriegen.

Luigi schrieb:
> Der Punkt auf den ich hinauswollte ist der, dass es Probleme geben kann,
> wenn Du sowohl aus einer ISR als auch aus dem main() Kontext die SPI
> Schnittstelle ansteuerst.
> Nimm an Du startest eine SPI Kommunikation aus main() und diese wird von
> einer ISR unterbrochen.

Das Grundproblem bzw. die Grundlösung ist aber die Gleiche wie beim 
vorherigen Mißverständnis: Wenn ich etwas im Programm habe, was nicht 
unterbrochen werden darf, muß ich diese Unterbrechung eben verhindern! 
In der ISR macht die avr-libc das schon von alleine (weil eine ISR hier 
niemals unterbrochen werden sollte), an anderen Stellen muß es der 
Programmierer halt selber machen (da er ja als einziger diese Stellen 
kennt). In diesem Falle z.B. in jeder SPI-Funktion als erstes SREG 
sichern, cli(), machen, was zu machen ist und als letztes wieder das 
SREG restoren. Wobei hier beim MCP2515 wohl außer der Initialisierung am 
Anfang eigentlich nur noch Interrupts als "SPI-Trigger" in Frage kommen 
dürften. Kann aber allgemein in anderen Fällen ganz anders sein; hast du 
recht.

Rumgerechnet habe ich nun nicht ...

Luigi schrieb:
> Bitte beachte dass ich nicht sagen möchte dass die Verwendung der
> Interrupts jedenfalls sinnlos ist, es geht nur i.A. auch ohne, so dass
> man sich in vielen Fällen die zusätzlichen Leitungen sowie die Port-Pins
> sparen kann.

Steht außer Frage. Zumal der Interrupt sicher nicht das Erste auf der 
Welt war.
Nur hatte ich weiter oben schon argumentiert, daß
1.) ein Anfänger als erstes lernen sollte, wie es "richtig" ist. Wenn er 
später Erfahrung hat, kann er selber Abschätzen und daraufhin 
entscheiden, wie er es im konkreten Einzelfall macht.
2.) bei Programmerweiterungen aus Performancegründen sehr schnell ein 
komplettes Redesign der Softwarestruktur erforderlich werden kann, was 
man sich dann hätte sparen können. Klar kann man auch erst Bascom
lernen ...
War jetzt fies, ich weiß. Aber bestimmt verständlich (was ich inhaltlich 
meine).

von Peter C. (Gast)


Lesenswert?

Hallo H.joachim Seifert,
die ISR mit der MCP-Abfrage drin läuft, den Teil der CAN Routinen aus 
der Demo habe ich schon zum Teil angepasst hier muß ich aber mehr tun da 
hier auch der/die CSs sowie die SPI Initialisierung drin ist.
Aber auch in der Funktion sieht es etwas anders aus
Da muß ich mal sehen ob ich da *rec_can0 über eine Variable 
einschleusen/übergeben kann so das auch *rec_can1 übergeben werden 
kann(siehe unten...), man könnte natürlich die Funktion doppelt 
schreiben und dann anpassen aber das finde ich jetzt schon 
Mist..........und ich möchte auch keinen ünötigen Flashspeicher 
verschwenden.

Achja die CS liegen beide auf PortB einmal PB1 und PB2

Die CAN_Sw macht mit noch zuschaffen da ich nochmal nach der "globalen 
Variable" schauen muß wie die angelegt wird, so meckert das AVR Studio 
noch - da will ich aber erst selbst ran. So ist auch die schreibweise 
Codevision zum AVR Studio für mich schon unterschiedlich.

Aber ich sehe Licht am Ende des Tunnels und es ist nicht die 
Eisenbahn......Spaß beiseite - ich will damit sagen das ich begeistert 
bei der Sache bin..

@Lutz,
>Klar kann man auch erst Bascom lernen ...
>War jetzt fies, ich weiß. Aber bestimmt verständlich (was ich inhaltlich
> meine).

Nee..... ist nicht fies wenn man weiß wie man da hingekommen ist.
Vor Jahr und Tag habe ich mit den C-Control Dingern von Conrad 
angefangen, die waren recht schnell am Ende den die Ansprüche stiegen. 
Dann kam die open C-Control, da ging schon etwas mehr und die waren 
günstiger. Aber auch von den Preisen wurde man ruckzuck von den Beinen 
geholt und so war der dritte Schritt Atmel AVR und als Basic 
geschädigter war Bascom das nahliegenste. Nur mit CAN anlein wäre mit 
Bascom nicht Schluß gewesen doch wo es jetzt um hardwarenahe 
Programmierung geht und daraus resultierend um Geschwindigkeit ist C für 
mich "die" Wahl. Wenn mir das einer vor Jahren gesagt hätte, hätte ich 
den noch ausgelacht. Allerdings muß ich heute sagen - gut das ich 
umgestiegen bin.


uint8_t mcp2515_get_message(CANMessage *rec_can0)
{
  // read status
  uint8_t status = mcp2515_read_status(SPI_RX_STATUS);
  uint8_t addr;

  if (bit_is_set(status,6)) {
    // message in buffer 0
    addr = SPI_READ_RX;
  }
  else if (bit_is_set(status,7)) {
    // message in buffer 1
    addr = SPI_READ_RX | 0x04;
  }
  else {
    // Error: no message available
    return 0;
  }

  RESET(MCP2515_CS_0);
  spi_putc(addr);

  // read id
  rec_can0->id  = (uint16_t) spi_putc(0xff) << 3;
  rec_can0->id |=            spi_putc(0xff) >> 5;

  spi_putc(0xff);
  spi_putc(0xff);

  // read DLC
  uint8_t length = spi_putc(0xff) & 0x0f;

  rec_can0->header.length = length;
  rec_can0->header.rtr = (bit_is_set(status, 3)) ? 1 : 0;

  // read data
  for (uint8_t i=0;i<length;i++) {
    rec_can0->data[i] = spi_putc(0xff);
  }
  SET(MCP2515_CS_0);

  // clear interrupt flag
  if (bit_is_set(status, 6)) {
    mcp2515_bit_modify(CANINTF, (1<<RX0IF), 0);
  }
  else {
    mcp2515_bit_modify(CANINTF, (1<<RX1IF), 0);
  }

  return (status & 0x07) + 1;
}

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

von Peter C. (Gast)


Lesenswert?

Hallo zusammen,
zunächst wünsche ich allen ein glückliches und gesundes neues Jahr 2012.
Direkt an dieser Stelle möchte ich mich für die kompetente 
Hilfsbereitschaft speziell bei
- H.joachim Seifert
- Lutz
- Peter Dannegger
bedanken.
Ohne die richtigen Initialzündungen eurerseits hätte ich das als "C 
Anfänger" nicht hinbekommen. Die Schaltung empfängt nun bidirektional 
die CAN-Frames zwischen der o.g. Demohardware und versendet auch 
entsprechend der eingestellten CAN_SW Variable. Damit habe ich mit eurer 
Hilfe schon einen großen Teil meines Planes erreichen können.
Die Bearbeitungsgeschwindigkeit ist schon klasse.

>Einen Plan habe ich selbstverständlich:
>- 2 x MCP2515 an einem ATMega328P
>- bidiretionaler Datenstrom
>- Richtung 0 zu 1 = alles durch
>- Richtung 1 zu 0 = 2 IDs herausfiltern (ID 0x261 und ID 0x263)
>- Empfangs- Sendebuffer mit einbeziehen
>- bei freien MC-Resourcen soll bei einer bestimmten ID die Schaltung
>abgeschaltet werden.

Die Einstellung der Empfangs Masken und Filter im MCP sollte ich auch 
noch hinbekommen obwohl mich die Datenfilterung im AVR direkt brennender 
interessiert da ich nur einzelne IDs herausfiltern will und nicht den 
Rest um die 0x261 und 0x263 herum definieren will.

DANKE !!!!!

Gruß
Peter

von H.Joachim S. (crazyhorse)


Lesenswert?

CANMessage rec_can0, transmit_can0, rec_can1, transmit_can1;
while (1)
   {if (CAN0_message_received)    //CAN0 empfangen?
       {#asm ("cli")
        CAN0_message_received=0;
        CAN_sw=1;                 //SPI für MCP2515_1
        can_send_message (&rec_can0);   //alle unverändert senden
        #asm ("sei")
       }
    if (CAN1_message_received)    //CAN1 empfangen?
       {#asm ("cli")
        CAN1_message_received=0;
        switch (rec_can1.id)
             {case 0x261: {      //tu hier was du mit 0x261 machen 
willst
                          break;
                          }
              case 0x263: {      //dto mit 0x263
                          break;
                          }
              default:    {transmit_can0=rec_can1;   //alle anderen 
senden
                           CAN_sw=0;             //SPI für MCP2515_0
                           can_send_message (&transmit_can0);
                           break;
                           }
              }
        #asm ("sei")
       }
   }

ist natürlich nur ein Grundgerüst. Funktioniert auch, wenn du keine 
remote- oder errorframes hast.

Ansonsten fand ich deine Beharrlichkeit schon beeindruckend, findet man 
nicht mehr allzuhäufig heute. Was sich nicht mit KlickiBunti 
zusammenschieben lässt, ist eh alles Mist....

von Peter C. (Gast)


Lesenswert?

Hallo H.joachim
vielen dank für die Blumen. Sicher gibt es Lösungen die man per copy und 
paste übernehmen kann, allerdings stellt sich dies meinst als Griff ins 
Klo dar sobald man einen Fehler hat der entweder beim portieren oder 
sogar an der Hardware selbst liegt - also in der Regel eigene Schuld 
ist.
Mir lag und liegt immer noch viel daran zu verstehen was da passiert und 
auch passieren soll. Nur so bin ich in der Lage auch als Anfänger Fehler 
zu finden und zu beseitigen oder wenn das nicht klappt die Fehler 
zubeschreiben und mit Tipps dem Fehler auf die Pelle rücken zukönnen.
Was ich auf jeden Fall gelernt habe das es für die Umsetzung immer 
verschieden Ansätze gibt und man auch seinen Weg finden muß.

Die Case select Lösung werde ich mal für mich beleuchten und mal sehen 
ob so für mich lösbar ist. Bei der Schaltung bzw dem Durchschleusen habe 
ich einige wenige RTRs drin...... ich muß mal sehen.
Sobald ich die Demo- in die Probehardwareplatine umgesetzt habe werde 
ich einen Probelauf in der Zielhardware starten.

Tausend Dank

Gruß
Peter

von Tobi Jay (Gast)


Lesenswert?

Hallo erstmal,

ich würde gerne ein ähnliches System aufbauen und dabei aber mehr 
MCP2515 an einen AVR anbinden, als es INT-Pins gibt. Das heißt am 
Beispiel ATmega16, gleich 3 MCP2515 oder mehr.
Gibt es da eine Möglichkeit, das zu realisieren oder bin ich nur am 
träumen. Ich hab im Tutorial etwas über ein SPI Kommando gelesen als 
Alternative zur Interruptleitung, stimmt das?

Allgemein zum MCP2515 steht im Datenblatt ein Puffer von 2 oder 3 
Nachrichten. Kann ich diesen als abrufbaren Zwischenspeicher von 
Nachrichten nutzen, die noch nich zum AVR weitergeleitet wurden?

Der Experte liest sich gleich raus, das ich meine ersten wackligen 
Schritte auf der CAN-Bus Leitung mache, und deshalb kann ich jede Hilfe 
brauchen.

Gruß Tobi

von Tobi Jay (Gast)


Lesenswert?

Hi, nochmal ich,

ist ein Interrupt Controller eine Möglichkeit? Könnte man mit dem 
Interrupt Controller die verschiedenen MCP2515 Interrupts koordinieren?

von Tom M. (tomm) Benutzerseite


Lesenswert?

Tobi Jay schrieb:
> ich würde gerne ein ähnliches System aufbauen und dabei aber mehr
> MCP2515 an einen AVR anbinden, als es INT-Pins gibt.

Auf den moderneren AVRs hast du massenhaft "int-Pins". Schau dir mal die 
8er-Familie (48/88/168/328) genauer an: Nebst den klassischen INT0 und 
INT1 Interrupts gibt es auch die PCINTx, welche zwar über einen 
gemeinsamen Interrupt feuern, aber leicht auseinander gehalten werden 
können.

Siehe Kapitel 11.3 im atmega..8 Datenblatt (alternate port functions).

Tobi Jay schrieb:
> ist ein Interrupt Controller eine Möglichkeit?

Nicht nötig, s.o.

von Roland H. (batchman)


Lesenswert?

> Auf den moderneren AVRs hast du massenhaft "int-Pins". Schau dir mal die
> 8er-Familie (48/88/168/328) genauer an: Nebst den klassischen INT0 und
> INT1 Interrupts gibt es auch die PCINTx, welche zwar über einen
> gemeinsamen Interrupt feuern, aber leicht auseinander gehalten werden
> können.

Was verstehst Du unter "PCINTs leicht auseinander halten"? Ein Flag pro 
Pin, welches man in der ISR abfragen könnte, habe ich nicht gefunden. 
Was mir auch nicht klar ist, ob die ISR tatsächlich für jeden PCINT 
extra 1x aufgerufen wird (und nicht dass z. B. bei mehreren 
gleichzeitigen PCINTs diese nur 1x angesprungen wird).

von Peter D. (peda)


Lesenswert?

Roland H. schrieb:
> Was verstehst Du unter "PCINTs leicht auseinander halten"?

Nun, der MCP läßt ja ohne eine Aktion den Interruptpin für ewig auf low.
Also einfach den Pin abfragen, ob der MCP daran einen Interrupt hat.


Peter

von Roland H. (batchman)


Lesenswert?

> Nun, der MCP läßt ja ohne eine Aktion den Interruptpin für ewig auf low.
> Also einfach den Pin abfragen, ob der MCP daran einen Interrupt hat.

Die Frage war allgemein zum PCINT gemeint.

D. h. einfach bezieht sich auf "einfach Pin abfragen".
Bleiben die Frage, ob zeitgleiche "pin changes" in einem ISR-Aufruf 
landen, oder ob er einen puffert (solange die ISR noch verarbeitet 
wird), und ob ggf. weitere verloren gehen.

von Peter D. (peda)


Lesenswert?

Der Pin-Change Interrupt hat nur ein Flag. Man muß also im Handler alle 
aktivierten Eingänge prüfen und bearbeiten.

Und man kann die Flanke nicht auswählen. Man kriegt also direkt danach 
noch einen Dummy-Interrupt, wo alle Eingänge high sind.

Man kann aber auch am Ende des Handlers das Flag löschen. Man muß dann 
aber nochmal prüfen ob gerade in diesem Moment ein Eingang wieder low 
geworden ist, denn diesen hätte man dann mit gelöscht und er ginge 
verloren.


Peter

von Frank K. (fchk)


Lesenswert?

Schau Dir das mal an:

http://www.kreatives-chaos.com/artikel/sja1000-testboard

Hier wird ein Mega162 und ein SJA1000 CAN-Controller verwendet. Du 
kannst mehrere SJA1000 anschließen und musst nur das !CS-Signal für 
jeden einzelnen SJA aus den oberen Adressleitungen ausdekodieren, z.B. 
mit einem 74HC138
1
              HC138
2
           --------------+
3
A8---------+1          16+---VCC
4
A9---------+2          15+---!CS1
5
A10--------+3          14+---!CS2
6
A15--------+4          13+---!CS3
7
A14--------+5          12+---!CS4
8
VCC--------+6          11+---!CS5
9
!CS8-------+7          10+---!CS6
10
GND--------+8           9+---!CS7
11
           +-------------+
Die anderen Leitungen zum AVR kannst Du alle parallel schalten, auch die 
IRQ-Leitung. Die Interrupt-Routine muss dann eben nur alle SJAs 
abfragen, ob irgendwo ein Interrupt ausgelöst wurde, und den dann 
bearbeiten. Da die SJA1000-Register hier aber direkt im Adressraum des 
Prozessors liegen und wie RAM-Adressen direkt auslesbar und beschreibbar 
sind, ist das kein so großer Overhead. Das machen andere auch so, und in 
früheren Zeiten (z.B. C64) war diese Methode gängige Praxis.

In dem Link wird ein Mega162 benutzt. Wenn Dir die Pins knapp werden 
(und die Chancen stehen nicht schlecht), dann kannst Du auf einen 
Mega641 oder 1281 ausweichen.

fchk

PS: Die beiden Quarze ersetzt Du am Besten durch einen 16 MHz 
Quarzoszillator, der dann Prozessor und die SJA1000 taktet.

fchk

von dionysos 3. (dionysos328)


Lesenswert?

Ich probiere mich mich auch gerade daran, eine Kombination aus 
ATmega328P und MCP2515 mit der canlib von kreatives-chaos.com zum Laufen 
zu bekommen.

Das ist mein erstes wirkliches Projekt mit einem µC und dementsprechen 
groß sind die Anlaufschwierigkeiten.

Ich wäre dankbar für einen Tipp, an welchen Stellen ich die canlib vor 
dem Kompilieren überall verändern muss, damit ich mit dem ATmega328P 
statt des ATmega8 arbeiten kann.

Dazu verwende ich einen 8MHz Quarz, dafür muss ich wohl auch die 
Bittimings ändern.
Dafür fehlt mir leider auch der Ansatz.

Vielen Dank im Voraus!

von Falk B. (falk)


Lesenswert?

Eben WEIL du eine Anfänger bist, solltest du solche Modifikationen 
vorerst lassen. Baue es 1:1 auf. Atmege8 und 16 MHz Quarz. Wenn das 
läuft sehen wir weiter.

von dionysos 3. (dionysos328)


Lesenswert?

Die Entscheidung über die zu verwendende Hardware liegt leider nicht in 
meiner Macht.
Das Projekt ist so angesetzt und muss zuende geführt werden.

Daher meine Bitte um Denkanstöße!

von Falk B. (falk)


Lesenswert?

Tja, dann muss es halt sein.

>Ich wäre dankbar für einen Tipp, an welchen Stellen ich die canlib vor
>dem Kompilieren überall verändern muss, damit ich mit dem ATmega328P
>statt des ATmega8 arbeiten kann.

Einfach Controller im Projekt umstellen und schauen was beim Compilieren 
passiert. Wenn man Glück hat, gibt es keine Fehler, wenn nicht, muss man 
Registernamen und Bitdefinitionen anpassen.

>Dazu verwende ich einen 8MHz Quarz, dafür muss ich wohl auch die
>Bittimings ändern.

Das ist der leichtere Teil, einfach neu ausrechnen. Hab ich auch mal mit 
dem gleichen Projekt vom kreativen Kaos gemacht.

defaults.h
1
// 125kbit/s @ 10 MHz XCLK
2
//#define CAN_BAUD_DIV ((1<<BRP2))
3
4
// 125kbit/s @ 16 MHz XCLK
5
#define CAN_BAUD_DIV ((1<<BRP2)|(1<<BRP1)|(1<<BRP0))

Ins Datenblatt schauen und die Rechung nachvollziehen.

MFG
Falk

von Falk B. (falk)


Lesenswert?

Hmm, jetz wo ich es sehe, hab ich damals auch den ATmega328 genutzt ;-)
Die Umstellung war nicht sonderlich schwer, obwohl ich ein paar Pins 
verschoben habe.

von Florian (Gast)


Lesenswert?

Hallo,

sorry dass ich das hier nochmal nach oben ziehe, aber aus welchen Grund 
sind denn die Ausgänge von IC6 und IC2 auf der Zeichnung von H.Joachim 
Seifert negiert?

Ist es außerdem denkbar, statt zwei MCP2551 nur einen MCP2551 und 
zusätzlich noch einen TJA1055 (Lowspeed CAN) zu verwenden? Kann dieser 
Analog zum MCP2551 angeschlossen werden, oder wird zusätzliches 
benötigt? Der TJA1055 hat ja noch einige Pins mehr...

VG!

von Rudolph (Gast)


Angehängte Dateien:

Lesenswert?

Florian schrieb:
> aber aus welchen Grund
> sind denn die Ausgänge von IC6 und IC2 auf der Zeichnung von H.Joachim
> Seifert negiert?

/INT und /CS? Das sind einfach nur low-aktive Eingänge und meiner 
Meinung nach so etwas seltsam gezeichnet.

Florian schrieb:
> Ist es außerdem denkbar, statt zwei MCP2551 nur einen MCP2551 und
> zusätzlich noch einen TJA1055 (Lowspeed CAN) zu verwenden?

Klar geht das, nur wird der TJA1055 eben anders verschaltet was Du ja 
auch schon erkannt hast.
Die zusätzlichen Pins kann man auch nicht einfach weglassen.

Etwas hässlich habe ich das gerade aus einem EAGLE Design gezogen mit 
dem TJA1054, die drei Pins 4,5 und 6 gehen dabei an I/O Pins vom 
Controller.

Der wichtigste Unterschied ist die Wakeup-Funktionalität die ich in dem 
Design aber nicht benutzt habe.

von Florian (Gast)


Lesenswert?

Vielen Dank Rudolph!

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.