Hallo zusammen
Ich beschäftige mich seit geraumer Zeit mit dem Thema Arduino und dem
CAN-Bus. Mein Ziel ist es, eigene Komponenten für die Märklin
Modelleisenbahn herzustellen.
Den CAN-Bus habe ich mit einem MCP2515 und einem MCP2551 zum laufen
gebracht. Ich kann Meldungen versenden und empfangen. und die gesendeten
Meldungen kommen auch auf der CS2 von Märklin an. Getestet habe ich
dies, indem ich jede Sekunde eine Meldung gesendet habe und geschaut
habe, was auf der CS2 ankommt.
Jetzt habe ich aber leider das Problem, dass wenn ich viele Meldungen
senden muss, diese nicht alle versendet werden, wenn von der CS2
ebenfalls Meldungen gesendet werden.
Kann es sein, dass es auf dem CAN-Bus Kollisionen gibt und die Meldungen
verloren gehen?
Was passiert, wenn ich empfangene Meldungen nicht vom MCP2515 lade?
Wenn die CS2 Von Märklin nicht an den CAN-Bus angeschlossen ist, werden
alle Meldungen vom Arduino versendet und ich kann diese mit einem
zweiten Arduino empfangen.
Gruss Chasperli
Roger Kreienbühl schrieb:> Kann es sein, dass es auf dem CAN-Bus Kollisionen gibt und die Meldungen> verloren gehen?
Kollisionen kann und wird es natürlich geben.
Ob die verloren gehen oder nicht, hängt von deinen unbekannten
CAN-Routinen ab bzw. wie dein Programm mit einem gemeldeten
Arbitrierungsfehler umgeht.
> Was passiert, wenn ich empfangene Meldungen nicht vom MCP2515 lade?
Was sagt das Datenblatt zum MCP2515 zu diesem Fall?
hallo,
es gibt Kollisionen. Hierbei wird die höher poriorisierte Nachricht
überleben, die niederpriorisierte Nachricht nicht. Das ist aber ein
Kennkriterium des CAN-Busses und nicht änderbar.
Wenn deine Mailbox im Can Receiver zu lange verbleibt, wird sie von der
nächsten Nachricht überschrieben. Aus diesem Grund sind die Mailboxen in
der Lage, einen Interrupt auszulösen. Die anhängende ISR sollte diese
Nachricht in eine messageInputQueue kopieren, wo sie dann ausgewertet
werden kann.
Gruss
Robert
Hallo
Ich nutze die Canduino Library von
http://www.daedalus.ei.tum.de/index.php/de/dokumentation/projekte/komm-struktur/canduino
Anhand des Beispielcodes habe ich dann mein Programm gesschrieben. Die
Library habe ich erweitert, damit auch Extended Identfier benutzt werden
können.
Zudem habe ich eine zusätzliche Funktion geschrieben, welche jenachdem
über den ersten freien Buffer versendet. Die Library habe ich in RMDuino
umbenannt.
Leider bin ich noch nicht sehr bewandert was das "interpretieren" von
Datenblättern angeht.
Hier mein Code:
1
#include<RMDuino.h>
2
#include<SPI.h>
3
4
#define BUS_SPEED 250
5
6
#define belegt 1
7
#define frei 0
8
9
intrmpin[]={3,4,5};
10
intrmvalue[]={0,0,0};
11
intrmstatus[]={frei,frei,frei};
12
13
intledpin[]={3,4,5};
14
15
byteframe_id[4];
16
17
bytelength=8;
18
19
20
intmodulno=0;
21
22
23
voidsetup(){
24
Serial.begin(9600);
25
26
pinMode(ledpin[0],OUTPUT);
27
pinMode(ledpin[1],OUTPUT);
28
pinMode(ledpin[2],OUTPUT);
29
30
digitalWrite(ledpin[0],LOW);
31
digitalWrite(ledpin[1],LOW);
32
digitalWrite(ledpin[2],LOW);
33
34
frame_id[0]=0x01;
35
frame_id[1]=0x0B;
36
frame_id[2]=0x73;
37
frame_id[3]=0x30;
38
39
CAN.begin();
40
CAN.baudConfig(BUS_SPEED);
41
CAN.setMode(NORMAL);
42
43
//Warte 5 Sekunden damit auch hochgeladen werden kann,
44
// wenn die vorherige Iteration den Serial Port zuspammt
voidCANClass::begin()//constructor for initializing can module.
8
{
9
// set the slaveSelectPin as an output
10
pinMode(SCK,OUTPUT);
11
pinMode(MISO,INPUT);
12
pinMode(MOSI,OUTPUT);
13
pinMode(SS,OUTPUT);
14
pinMode(RESET,OUTPUT);
15
16
// initialize SPI:
17
SPI.begin();
18
SPI.setDataMode(SPI_MODE0);
19
SPI.setClockDivider(SPI_CLOCK_DIV4);
20
SPI.setBitOrder(MSBFIRST);
21
22
digitalWrite(RESET,LOW);/* RESET CAN CONTROLLER*/
23
delay(10);
24
digitalWrite(RESET,HIGH);
25
delay(100);
26
}
27
28
voidCANClass::baudConfig(intbitRate)//sets bitrate for CAN node
29
{
30
byteconfig0,config1,config2;
31
32
switch(bitRate)
33
{
34
case10:
35
config0=0x31;
36
config1=0xB8;
37
config2=0x05;
38
break;
39
40
case20:
41
config0=0x18;
42
config1=0xB8;
43
config2=0x05;
44
break;
45
46
case50:
47
config0=0x09;
48
config1=0xB8;
49
config2=0x05;
50
break;
51
52
case100:
53
config0=0x04;
54
config1=0xB8;
55
config2=0x05;
56
break;
57
58
case125:
59
config0=0x03;
60
config1=0xB8;
61
config2=0x05;
62
break;
63
64
case250:
65
config0=0x01;
66
config1=0xB8;
67
config2=0x05;
68
break;
69
70
case500:
71
config0=0x00;
72
config1=0xB8;
73
config2=0x05;
74
break;
75
case1000:
76
//1 megabit mode added by Patrick Cruce(pcruce_at_igpp.ucla.edu)
77
//Faster communications enabled by shortening bit timing phases(3 Tq. PS1 & 3 Tq. PS2) Note that this may exacerbate errors due to synchronization or arbitration.
78
config0=0x80;
79
config1=0x90;
80
config2=0x02;
81
}
82
digitalWrite(SS,LOW);
83
delay(10);
84
SPI.transfer(WRITE);
85
SPI.transfer(CNF0);
86
SPI.transfer(config0);
87
delay(10);
88
digitalWrite(SS,HIGH);
89
delay(10);
90
91
digitalWrite(SS,LOW);
92
delay(10);
93
SPI.transfer(WRITE);
94
SPI.transfer(CNF1);
95
SPI.transfer(config1);
96
delay(10);
97
digitalWrite(SS,HIGH);
98
delay(10);
99
100
digitalWrite(SS,LOW);
101
delay(10);
102
SPI.transfer(WRITE);
103
SPI.transfer(CNF2);
104
SPI.transfer(config2);
105
delay(10);
106
digitalWrite(SS,HIGH);
107
delay(10);
108
}
109
110
//Method added to enable testing in loopback mode.(pcruce_at_igpp.ucla.edu)
111
voidCANClass::setMode(CANModemode){//put CAN controller in one of five modes
112
113
bytewriteVal,mask,readVal;
114
115
switch(mode){
116
caseCONFIGURATION:
117
writeVal=0x80;
118
break;
119
caseNORMAL:
120
writeVal=0x00;
121
break;
122
caseSLEEP:
123
writeVal=0x20;
124
break;
125
caseLISTEN:
126
writeVal=0x60;
127
break;
128
caseLOOPBACK:
129
writeVal=0x40;
130
break;
131
}
132
133
mask=0xE0;
134
135
digitalWrite(SS,LOW);
136
SPI.transfer(BIT_MODIFY);
137
SPI.transfer(CANCTRL);
138
SPI.transfer(mask);
139
SPI.transfer(writeVal);
140
digitalWrite(SS,HIGH);
141
142
}
143
144
145
voidCANClass::send_0()//transmits buffer 0
146
{
147
148
//delays removed from SEND command(pcruce_at_igpp.ucla.edu)
149
//In testing we found that any lost data was from PC<->Serial Delays,
150
//Not CAN Controller/AVR delays. Thus removing the delays at this level
151
//allows maximum flexibility and performance.
152
digitalWrite(SS,LOW);
153
SPI.transfer(SEND_TX_BUF_0);
154
digitalWrite(SS,HIGH);
155
}
156
157
voidCANClass::send_1()//transmits buffer 1
158
{
159
digitalWrite(SS,LOW);
160
SPI.transfer(SEND_TX_BUF_1);
161
digitalWrite(SS,HIGH);
162
}
163
164
voidCANClass::send_2()//transmits buffer 2
165
{
166
digitalWrite(SS,LOW);
167
SPI.transfer(SEND_TX_BUF_2);
168
digitalWrite(SS,HIGH);
169
}
170
171
charCANClass::readID_0()//reads ID in recieve buffer 0
172
{
173
charretVal;
174
digitalWrite(SS,LOW);
175
delay(10);
176
SPI.transfer(READ_RX_BUF_0_ID);
177
retVal=SPI.transfer(0xFF);
178
delay(10);
179
digitalWrite(SS,HIGH);
180
delay(10);
181
returnretVal;
182
}
183
184
charCANClass::readID_1()//reads ID in reciever buffer 1
185
{
186
charretVal;
187
digitalWrite(SS,LOW);
188
delay(10);
189
SPI.transfer(READ_RX_BUF_1_ID);
190
retVal=SPI.transfer(0xFF);
191
delay(10);
192
digitalWrite(SS,HIGH);
193
delay(10);
194
returnretVal;
195
}
196
197
charCANClass::readDATA_0()//reads DATA in recieve buffer 0
198
{
199
charretVal;
200
digitalWrite(SS,LOW);
201
delay(10);
202
SPI.transfer(READ_RX_BUF_0_DATA);
203
retVal=SPI.transfer(0xFF);
204
delay(10);
205
digitalWrite(SS,HIGH);
206
delay(10);
207
returnretVal;
208
}
209
210
charCANClass::readDATA_1()//reads data in recieve buffer 1
211
{
212
charretVal;
213
digitalWrite(SS,LOW);
214
delay(10);
215
SPI.transfer(READ_RX_BUF_1_DATA);
216
retVal=SPI.transfer(0xFF);
217
delay(10);
218
digitalWrite(SS,HIGH);
219
delay(10);
220
returnretVal;
221
}
222
223
//extending CAN data read to full frames(pcruce_at_igpp.ucla.edu)
224
//It is the responsibility of the user to allocate memory for output.
225
//If you don't know what length the bus frames will be, data_out should be 8-bytes
So nicht. Eine Nachricht, die verschwindet, ist weg. Es wird nichts
detektiert. Einzige Abhilfe ist die Implementation eines Rollcounters in
jedes Paket.
Gruss
Robert
Normalerweise wird ein Paket nochmals gesendet. Es sei denn, das
One-Shot Bit ist gesetzt:
CANCTRL:
bit 3:
OSM: One Shot Mode bit
1 = Enabled. Message will only attempt to transmit one time
0 = Disabled. Messages will reattempt transmission, if required
Wie ist das Bit denn gesetzt ?
R. Freitag schrieb:> So nicht. Eine Nachricht, die verschwindet, ist weg. Es wird nichts> detektiert. Einzige Abhilfe ist die Implementation eines Rollcounters in> jedes Paket.
Ohne jetzt der Spezialist für MCP2515 zu sein.
Eine andere CAN IMplementierung macht das so
1
INT8UMCP_CAN::sendMsg()
2
{
3
INT8Ures,res1,txbuf_n;
4
uint16_tuiTimeOut=0;
5
6
do{
7
res=mcp2515_getNextFreeTXBuf(&txbuf_n);/* info = addr. */
nachdem der MCP2515 angewiesen wurde, die Nachricht zu versenden, wird
offenbar ein Control Register gelesen und festgestellt, ob die Nachricht
rausging (mit einer Timeout Option).
Sehe ich mir das Datenblatt zum MCP2515 an
https://www.sparkfun.com/datasheets/DevTools/Arduino/MCP2515.pdf
dann ist auf Seite 17 ein Flowchart, in dem der Fall im Prinzip
behandelt wird. Dort wird von diesem Control Register das Bit MLOA
untersucht, welches doch eigentlich genau für diesen Fall gilt, dass
eine Message nicht korrekt raus ging, weil die Arbitrierung verloren
ging.
Wie man das dann behandeln will, ist eine andere Sache. Normalerweise
würde ich sagen: bischen warten und nochmal versuchen, denn irgendwann
muss ja auch der andere Busteilnehmer fertig sein.
Springender Punkt:
Das alles fällt unter den Punkt 'Error Handlung'. Und das vermisse ich
komplett im vom TO geposteten Code. Dieser Code ist 'Schönwettercode'.
WEnn die Sonne scheint und der Wind günstig steht, dann funktioniert das
alles. Aber wehe, wehe, wenn nicht. Robuster Code sieht anders aus. Der
muss auch eine entsprechende Fehlerbehandlung enthalten.
Hallo,
"weil die Arbitrierung verloren ging" != weil eine Buskollision
stattfand.
Im Falle einer Buskollision fehlt ei Paket. Ein Rollcounter ist ein
kleine Teil eines Paketes, welchjes einen Zählerstand darstellt, der bei
jedem Senden eines neuen Paketes ein rauf (oder runter) gezählt wird.
Der Empfänger kann die Zählerstände erkennen, fehlt ein Wert in einer
sonst vollständigen Kette, ist das Paket verloren gegangen.
Gruss
Robert
Karl Heinz hat natürlich vollkommen recht. Ein Error Handling ist
notwendig. Der obige Code reagiert (in irgendeiner Form) auf
Übertragungsfehler.
Aber Retransmission wird normalerweise automatisch gemacht. Es sei denn,
man verwendet den One-Shot Modus:
3.4 One-Shot Mode
One-shot mode ensures that a message will only attempt to transmit one
time. Normally, if a CAN message loses arbitration, or is destroyed by
an error frame, the message is retransmitted. With One-shot mode
enabled, a message will only attempt to transmit one time, regardless of
arbitration loss or error frame.
One-shot mode is required to maintain time slots in deterministic
systems, such as TTCAN.
Gerd B. schrieb:> Aber Retransmission wird normalerweise automatisch gemacht.
Wobei ich nicht schlau geworden bin.
Wird das im Falle eines Falles vom MCP 'unendlich oft' wiederholt?
Denn dann hab ich dem Autor eventuell unrecht getan.
Er hat schon ein implizites einfaches Error Handling drinn. Wenn die
Message nicht rausgeht, dann bleibt der Sendebuffer 'allokiert' und der
nächste Aufruf von
void CANClass::load_and_send(byte length,byte *identifier,byte *data)
findet keinen freien Buffer mehr vor.
Allerdings ist die Behandlung davon
1
else{
2
// all buffer used => could not send message
3
Serial.println("Fehler");
4
return;
5
}
ein bischen dürftig. Wie überhaupt der Code in dieser Beziehung ein
wenig dürftig ist. Wird load_and_send zu schnell aufgerufen, dann ist es
fast unvermeidlich, dass es trotz 3-er Sendebuffer im MCP irgendwann mal
dazu kommt, dass eine Msg aufgrund nicht verfügbarer Sendebuffer eben
nicht rausgeht. Da jetzt einfach "Fehler" hinzuschreiben und den
Aufrufer noch nicht mal darüber zu informieren, das geht gar nicht.
R. Freitag schrieb:> "weil die Arbitrierung verloren ging" != weil eine Buskollision> stattfand.
Buskollisionen gibt es beim CAN eigentlich gar nicht. Genau das soll
durch die Bitarbitrierung ja verhindert werden.
> Im Falle einer Buskollision fehlt ei Paket. Ein Rollcounter ist ein> kleine Teil eines Paketes, welchjes einen Zählerstand darstellt, der bei> jedem Senden eines neuen Paketes ein rauf (oder runter) gezählt wird.
Ich kenne das unter dem Begriff "Message Counter".
Danke Für die vielen und schnellen Antworten. Ich werd mich somit
nochmals in das Datenblatt einlesen und die MCP_CAN Library anschauen.
Gruss Chasperli