Forum: Mikrocontroller und Digitale Elektronik Arduino CAN-Bus Problem


von Roger K. (Firma: privat) (chasperli)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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?

: Bearbeitet durch User
von asd (Gast)


Lesenswert?

Gibt's keine Funktion:

detect_collision()

?

von R. F. (rfr)


Lesenswert?

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

von Roger K. (Firma: privat) (chasperli)


Lesenswert?

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
int rmpin[] = {3, 4, 5};
10
int rmvalue[] = {0, 0, 0};
11
int rmstatus[] = {frei, frei, frei};
12
13
int ledpin[] = {3, 4, 5};
14
15
byte frame_id[4];
16
17
byte length = 8;
18
19
20
int modulno = 0;
21
22
23
void setup() {
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
45
 
46
  delay(5000);
47
48
}
49
50
void loop() {
51
  
52
  for (int c=0; c<3; c++) {
53
    
54
    rmvalue[c] = analogRead(rmpin[c]);
55
  }
56
  
57
  for (int c=0; c<3; c++) {
58
    if (rmvalue[c] < 100 && rmstatus[c] == frei) {
59
         digitalWrite(ledpin[c], HIGH);
60
      
61
        sendStatusChange(modulno, c, 1);
62
63
        rmstatus[c] = belegt;
64
      }
65
    
66
      else if (rmvalue[c] >= 900 && rmstatus[c] == belegt) {
67
        digitalWrite(ledpin[c], LOW);
68
      
69
        sendStatusChange(modulno, c, 0);
70
71
        rmstatus[c] = frei;
72
      }
73
    
74
  }
75
76
}
77
78
79
void sendStatusChange(int modul, int pin, bool newstate) {
80
81
  int rmnumber = ((modul * 16) + pin +1);
82
  
83
  byte low = (byte)(rmnumber & 0xff);
84
  byte high = (byte)((rmnumber >> 8) & 0xff);
85
  
86
  byte frame_data[8];
87
  
88
  frame_data[0] = 0x00;
89
  frame_data[1] = 0x00;
90
  frame_data[2] = high;
91
  frame_data[3] = low;
92
  
93
  switch(newstate) {
94
    case frei:
95
      frame_data[4] = 0x01;
96
      frame_data[5] = 0x00;
97
      break;
98
    
99
    case belegt:
100
      frame_data[4] = 0x00;
101
      frame_data[5] = 0x01;
102
      break;
103
  }
104
  
105
  frame_data[6] = 0x00;
106
  frame_data[7] = 0x00;
107
108
  CAN.load_and_send(length,frame_id,frame_data);
109
  
110
  
111
}

RMDuino.h:
1
#include <SPI.h>
2
3
/*
4
Created by: Kyle Crockett
5
For canduino with 16MHz oscillator.
6
CNFx register values.
7
use preprocessor command "_XXkbps" 
8
"XX" is the baud rate.
9
10
10 kbps
11
CNF1/BRGCON1    b'00110001'     0x31
12
CNF2/BRGCON2    b'10111000'     0xB8
13
CNF3/BRGCON3    b'00000101'     0x05
14
15
20 kbps
16
CNF1/BRGCON1    b'00011000'     0x18
17
CNF2/BRGCON2    b'10111000'     0xB8
18
CNF3/BRGCON3    b'00000101'     0x05
19
20
50 kbps
21
CNF1/BRGCON1    b'00001001'     0x09
22
CNF2/BRGCON2    b'10111000'     0xB8
23
CNF3/BRGCON3    b'00000101'     0x05
24
25
100 kbps
26
CNF1/BRGCON1    b'00000100'     0x04
27
CNF2/BRGCON2    b'10111000'     0xB8
28
CNF3/BRGCON3    b'00000101'     0x05
29
30
125 kbps
31
CNF1/BRGCON1    b'00000011'     0x03
32
CNF2/BRGCON2    b'10111000'     0xB8
33
CNF3/BRGCON3    b'00000101'     0x05
34
35
250 kbps
36
CNF1/BRGCON1    b'00000001'     0x01
37
CNF2/BRGCON2    b'10111000'     0xB8
38
CNF3/BRGCON3    b'00000101'     0x05
39
40
500 kbps
41
CNF1/BRGCON1    b'00000000'     0x00
42
CNF2/BRGCON2    b'10111000'     0xB8
43
CNF3/BRGCON3    b'00000101'     0x05
44
45
800 kbps
46
Not yet supported
47
48
1000 kbps
49
Settings added by Patrick Cruce(pcruce_at_igpp.ucla.edu)
50
CNF1=b'10000000'=0x80 = SJW = 3 Tq. & BRP = 0
51
CNF2=b'10010000'=0x90 = BLTMode = 1 & SAM = 0 & PS1 = 3 & PR = 1
52
CNF3=b'00000010'=0x02 = SOF = 0  & WAKFIL = 0 & PS2 = 3
53
54
*/
55
#ifndef can_h
56
#define can_h
57
58
#define SCK 13 //spi
59
#define MISO 12
60
#define MOSI 11
61
#define SS 10
62
#define RESET 2//reset pin
63
64
#define RESET_REG 0xc0  
65
#define READ 0x03 
66
#define WRITE 0x02 //read and write comands for SPI
67
68
#define READ_RX_BUF_0_ID 0x90
69
#define READ_RX_BUF_0_DATA 0x92
70
#define READ_RX_BUF_1_ID 0x94
71
#define READ_RX_BUF_1_DATA 0x96 //SPI commands for reading CAN RX buffers
72
73
#define LOAD_TX_BUF_0_ID 0x40
74
#define LOAD_TX_BUF_0_DATA 0x41
75
#define LOAD_TX_BUF_1_ID 0x42
76
#define LOAD_TX_BUF_1_DATA 0x43
77
#define LOAD_TX_BUF_2_ID 0x44
78
#define LOAD_TX_BUF_2_DATA 0x45 //SPI commands for loading CAN TX buffers
79
80
#define SEND_TX_BUF_0 0x81
81
#define SEND_TX_BUF_1 0x82
82
#define SEND_TX_BUF_2 0x83 //SPI commands for transmitting CAN TX buffers
83
84
#define READ_STATUS 0xA0
85
#define RX_STATUS 0xB0
86
#define BIT_MODIFY 0x05 //Other commands
87
88
89
//Registers
90
#define CNF0 0x2A
91
#define CNF1 0x29
92
#define CNF2 0x28
93
#define TXB0CTRL 0x30 
94
#define TXB1CTRL 0x40
95
#define TXB2CTRL 0x50 //TRANSMIT BUFFER CONTROL REGISTER
96
#define TXB0DLC 0x35 //Data length code registers
97
#define TXB1DLC 0x45
98
#define TXB2DLC 0x55
99
#define CANCTRL 0x0F //Mode control register
100
#define CANSTAT 0x0E //Mode status register
101
102
#include "Arduino.h"
103
104
enum CANMode {CONFIGURATION,NORMAL,SLEEP,LISTEN,LOOPBACK};
105
106
class CANClass
107
{
108
private:
109
110
public:
111
        static void begin();//sets up MCP2515
112
        static void baudConfig(int bitRate);//sets up baud
113
114
        //Method added to enable testing in loopback mode.(pcruce_at_igpp.ucla.edu)
115
        static void setMode(CANMode mode) ;//put CAN controller in one of five modes
116
117
        static void send_0();//request to transmit buffer X
118
        static void send_1();
119
        static void send_2();
120
121
        static char readID_0();//read ID/DATA of recieve buffer X
122
        static char readID_1();
123
124
        static char readDATA_0();
125
        static char readDATA_1();
126
127
        //extending CAN data read to full frames(pcruce_at_igpp.ucla.edu)
128
        //data_out should be array of 8-bytes or frame length.
129
        static void readDATA_ff_0(byte* length_out,byte *data_out,byte *id_out);
130
        static void readDATA_ff_1(byte* length_out,byte *data_out,byte *id_out);
131
132
        //Adding can to read status register(pcruce_at_igpp.ucla.edu)
133
        //can be used to determine whether a frame was received.
134
        //(readStatus() & 0x80) == 0x80 means frame in buffer 0
135
        //(readStatus() & 0x40) == 0x40 means frame in buffer 1
136
        static byte readStatus();
137
138
        static void load_0(byte identifier, byte data);//load transmit buffer X
139
        static void load_1(byte identifier, byte data);
140
        static void load_2(byte identifier, byte data);
141
142
        //extending CAN write to full frame(pcruce_at_igpp.ucla.edu)
143
        //Identifier should be a value between 0 and 2^11-1, longer identifiers will be truncated(ie does not support extended frames)
144
        static void load_ff_0(byte length,byte *identifier,byte *data);
145
        static void load_ff_1(byte length,byte *identifier,byte *data);
146
        static void load_ff_2(byte length,byte *identifier,byte *data);
147
    
148
        static void load_and_send(byte length,byte *identifier,byte *data);
149
150
};
151
extern CANClass CAN;
152
#endif

RMDuino.cpp:
1
#include <SPI.h>
2
#include "Arduino.h"
3
#include "RMDuino.h"
4
5
CANClass CAN;//create can object
6
7
void CANClass::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
void CANClass::baudConfig(int bitRate)//sets bitrate for CAN node
29
{
30
        byte config0, config1, config2;
31
32
        switch (bitRate)
33
        {
34
case 10:
35
                config0 = 0x31;
36
                config1 = 0xB8;
37
                config2 = 0x05;
38
                break;
39
40
case 20:
41
                config0 = 0x18;
42
                config1 = 0xB8;
43
                config2 = 0x05;
44
                break;
45
46
case 50:
47
                config0 = 0x09;
48
                config1 = 0xB8;
49
                config2 = 0x05;
50
                break;
51
52
case 100:
53
                config0 = 0x04;
54
                config1 = 0xB8;
55
                config2 = 0x05;
56
                break;
57
58
case 125:
59
                config0 = 0x03;
60
                config1 = 0xB8;
61
                config2 = 0x05;
62
                break;
63
64
case 250:
65
                config0 = 0x01;
66
                config1 = 0xB8;
67
                config2 = 0x05;
68
                break;
69
70
case 500:
71
                config0 = 0x00;
72
                config1 = 0xB8;
73
                config2 = 0x05;
74
                break;
75
case 1000:
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
void CANClass::setMode(CANMode mode) { //put CAN controller in one of five modes
112
113
        byte writeVal,mask,readVal;
114
115
        switch(mode) {
116
        case CONFIGURATION:
117
                        writeVal = 0x80;
118
                        break;
119
        case NORMAL:
120
                  writeVal = 0x00;
121
                        break;
122
        case SLEEP:
123
                        writeVal = 0x20;
124
                break;
125
    case LISTEN:
126
                        writeVal = 0x60;
127
                break;
128
        case LOOPBACK:
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
void CANClass::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
void CANClass::send_1()//transmits buffer 1
158
{
159
        digitalWrite(SS, LOW);
160
        SPI.transfer(SEND_TX_BUF_1);
161
        digitalWrite(SS, HIGH);
162
}
163
164
void CANClass::send_2()//transmits buffer 2
165
{
166
        digitalWrite(SS, LOW);
167
        SPI.transfer(SEND_TX_BUF_2);
168
        digitalWrite(SS, HIGH);
169
}
170
171
char CANClass::readID_0()//reads ID in recieve buffer 0
172
{
173
        char retVal;
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
        return retVal;
182
}
183
184
char CANClass::readID_1()//reads ID in reciever buffer 1
185
{
186
        char retVal;
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
        return retVal;
195
}
196
197
char CANClass::readDATA_0()//reads DATA in recieve buffer 0
198
{
199
        char retVal;
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
        return retVal;
208
}
209
210
char CANClass::readDATA_1()//reads data in recieve buffer 1
211
{
212
        char retVal;
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
        return retVal;
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
226
void CANClass::readDATA_ff_0(byte* length_out,byte *data_out,byte *id_out){
227
228
        byte len,i;
229
        unsigned short id_h,id_l;
230
231
        digitalWrite(SS, LOW);
232
        SPI.transfer(READ_RX_BUF_0_ID);
233
        id_out[0] = SPI.transfer(0xFF); //id high
234
        id_out[1] = SPI.transfer(0xFF); //id low
235
        id_out[2] = SPI.transfer(0xFF); //extended id high(unused)
236
        id_out[3] = SPI.transfer(0xFF); //extended id low(unused)
237
        len = (SPI.transfer(0xFF) & 0x0F); //data length code
238
        for (i = 0;i<len;i++) {
239
                data_out[i] = SPI.transfer(0xFF);
240
        }
241
        digitalWrite(SS, HIGH);
242
        (*length_out) = len;
243
        //(*id_out) = ((id_h << 3) + ((id_l & 0xE0) >> 5)); //repack identifier
244
        
245
}
246
247
void CANClass::readDATA_ff_1(byte* length_out,byte *data_out,byte *id_out){
248
249
        byte id_h,id_l,len,i;
250
251
        digitalWrite(SS, LOW);
252
        SPI.transfer(READ_RX_BUF_1_ID);
253
        id_out[0] = SPI.transfer(0xFF); //id high
254
        id_out[1] = SPI.transfer(0xFF); //id low
255
        id_out[2] = SPI.transfer(0xFF); //extended id high(unused)
256
        id_out[3] = SPI.transfer(0xFF); //extended id low(unused)
257
        len = (SPI.transfer(0xFF) & 0x0F); //data length code
258
        for (i = 0;i<len;i++) {
259
                data_out[i] = SPI.transfer(0xFF);
260
        }
261
        digitalWrite(SS, HIGH);
262
263
        (*length_out) = len;
264
        //(*id_out) = ((((unsigned short) id_h) << 3) + ((id_l & 0xE0) >> 5)); //repack identifier
265
}
266
267
        //Adding method to read status register
268
        //can be used to determine whether a frame was received.
269
        //(readStatus() & 0x80) == 0x80 means frame in buffer 0
270
        //(readStatus() & 0x40) == 0x40 means frame in buffer 1
271
byte CANClass::readStatus() 
272
{
273
        byte retVal;
274
        digitalWrite(SS, LOW);
275
        SPI.transfer(READ_STATUS);
276
        retVal = SPI.transfer(0xFF);
277
        digitalWrite(SS, HIGH);
278
        return retVal;
279
280
}
281
282
void CANClass::load_0(byte identifier, byte data)//loads ID and DATA into transmit buffer 0
283
{
284
        digitalWrite(SS, LOW);
285
        delay(10);
286
        SPI.transfer(LOAD_TX_BUF_0_ID);
287
        SPI.transfer(identifier);
288
        delay(10);
289
        digitalWrite(SS, HIGH);
290
        delay(10);
291
292
        digitalWrite(SS, LOW);
293
        delay(10);
294
        SPI.transfer(LOAD_TX_BUF_0_DATA);
295
        SPI.transfer(data);
296
        delay(10);
297
        digitalWrite(SS, HIGH);
298
        delay(10);
299
}
300
301
void CANClass::load_1(byte identifier, byte data)//loads ID and DATA into transmit buffer 1
302
{
303
        digitalWrite(SS, LOW);
304
        delay(10);
305
        SPI.transfer(LOAD_TX_BUF_1_ID);
306
        SPI.transfer(identifier);
307
        delay(10);
308
        digitalWrite(SS, HIGH);
309
        delay(10);
310
311
        digitalWrite(SS, LOW);
312
        delay(10);
313
        SPI.transfer(LOAD_TX_BUF_1_DATA);
314
        SPI.transfer(data);
315
        delay(10);
316
        digitalWrite(SS, HIGH);
317
        delay(10);
318
}
319
320
void CANClass::load_2(byte identifier, byte data)//loads ID and DATA into transmit buffer 2
321
{
322
        digitalWrite(SS, LOW);
323
        delay(10);
324
        SPI.transfer(LOAD_TX_BUF_2_ID);
325
        SPI.transfer(identifier);
326
        delay(10);
327
        digitalWrite(SS, HIGH);
328
        delay(10);
329
330
        digitalWrite(SS, LOW);
331
        delay(10);
332
        SPI.transfer(LOAD_TX_BUF_2_DATA);
333
        SPI.transfer(data);
334
        delay(10);
335
        digitalWrite(SS, HIGH);
336
        delay(10);
337
}
338
339
void CANClass::load_ff_0(byte length,byte *identifier,byte *data)
340
{
341
        
342
        byte i,id_high,id_low;
343
344
        //generate id bytes before SPI write
345
        // id_high = (byte) (identifier >> 3);
346
        // id_low = (byte) ((identifier << 5) & 0x00E0);
347
348
        digitalWrite(SS, LOW);
349
        SPI.transfer(LOAD_TX_BUF_0_ID);
350
        SPI.transfer(identifier[0]); //identifier high bits
351
        SPI.transfer(identifier[1]); //identifier low bits
352
        SPI.transfer(identifier[2]); //extended identifier registers(unused)
353
        SPI.transfer(identifier[3]);
354
        SPI.transfer(length);
355
        for (i=0;i<length;i++) { //load data buffer
356
                SPI.transfer(data[i]);
357
        }
358
359
        digitalWrite(SS, HIGH);
360
361
}
362
363
void CANClass::load_ff_1(byte length,byte *identifier,byte *data)
364
{
365
        
366
        byte i,id_high,id_low;
367
368
        //generate id bytes before SPI write
369
        // id_high = (byte) (identifier >> 3);
370
        // id_low = (byte) ((identifier << 5) & 0x00E0);
371
372
        digitalWrite(SS, LOW);
373
        SPI.transfer(LOAD_TX_BUF_1_ID);
374
        SPI.transfer(identifier[0]); //identifier high bits
375
        SPI.transfer(identifier[1]); //identifier low bits
376
        SPI.transfer(identifier[2]); //extended identifier registers(unused)
377
        SPI.transfer(identifier[3]);
378
        SPI.transfer(length);
379
        for (i=0;i<length;i++) { //load data buffer
380
                SPI.transfer(data[i]);
381
        }
382
383
        digitalWrite(SS, HIGH);
384
385
386
}
387
388
void CANClass::load_ff_2(byte length,byte *identifier,byte *data)
389
{
390
        
391
        byte i,id_high,id_low;
392
393
        //generate id bytes before SPI write
394
        // id_high = (byte) (identifier >> 3);
395
        // id_low = (byte) ((identifier << 5) & 0x00E0);
396
397
        digitalWrite(SS, LOW);
398
399
        SPI.transfer(LOAD_TX_BUF_2_ID);
400
        SPI.transfer(identifier[0]); //identifier high bits
401
        SPI.transfer(identifier[1]); //identifier low bits
402
        SPI.transfer(identifier[2]); //extended identifier registers(unused)
403
        SPI.transfer(identifier[3]);
404
        SPI.transfer(length); //data length code
405
        for (i=0;i<length;i++) { //load data buffer
406
                SPI.transfer(data[i]);
407
        }
408
409
        digitalWrite(SS, HIGH);
410
411
}
412
413
void CANClass::load_and_send(byte length,byte *identifier,byte *data)
414
{
415
    
416
    byte i,id_high,id_low, status, address, send_buf;
417
    
418
    status = CAN.readStatus();
419
    
420
    if ((status & 0b10) == 0) {
421
    address = LOAD_TX_BUF_0_ID;
422
        send_buf = SEND_TX_BUF_0;
423
  }
424
  else if ((status & 0b1000) == 0) {
425
    address = LOAD_TX_BUF_1_ID;
426
        send_buf = SEND_TX_BUF_1;
427
  }
428
  else if ((status & 0b100000) == 0) {
429
    address = LOAD_TX_BUF_2_ID;
430
        send_buf = SEND_TX_BUF_2;
431
  }
432
  else {
433
    // all buffer used => could not send message
434
        Serial.println("Fehler");
435
    return;
436
  }
437
    
438
    //generate id bytes before SPI write
439
    // id_high = (byte) (identifier >> 3);
440
    // id_low = (byte) ((identifier << 5) & 0x00E0);
441
    
442
    digitalWrite(SS, LOW);
443
    
444
    SPI.transfer(address);
445
    SPI.transfer(identifier[0]); //identifier high bits
446
    SPI.transfer(identifier[1]); //identifier low bits
447
    SPI.transfer(identifier[2]); //extended identifier registers(unused)
448
    SPI.transfer(identifier[3]);
449
    SPI.transfer(length); //data length code
450
    for (i=0;i<length;i++) { //load data buffer
451
        SPI.transfer(data[i]);
452
    }
453
    
454
    digitalWrite(SS, HIGH);
455
    delay(50);
456
    digitalWrite(SS, LOW);
457
    SPI.transfer(send_buf);
458
    digitalWrite(SS, HIGH);
459
    
460
}

Wie kann ich eine Kollision detektieren und die Meldung allenfalls 
erneut senden?

: Bearbeitet durch User
von R. F. (rfr)


Lesenswert?

So nicht. Eine Nachricht, die verschwindet, ist weg. Es wird nichts 
detektiert. Einzige Abhilfe ist die Implementation eines Rollcounters in 
jedes Paket.

Gruss

Robert

von Roger K. (Firma: privat) (chasperli)


Lesenswert?

Was meinst du mit Rollcounter? (Tut mir leid falls dies eine blöde Frage 
ist, ich befasse mich noch nicht sehr lange mit dem Thema).

Gruss Roger

von Gerd B. (bertr2d2) Benutzerseite


Lesenswert?

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 ?

von Karl H. (kbuchegg)


Lesenswert?

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
INT8U MCP_CAN::sendMsg()
2
{
3
    INT8U res, res1, txbuf_n;
4
    uint16_t uiTimeOut = 0;
5
6
    do {
7
        res = mcp2515_getNextFreeTXBuf(&txbuf_n);                       /* info = addr.                 */
8
        uiTimeOut++;
9
    } while (res == MCP_ALLTXBUSY && (uiTimeOut < TIMEOUTVALUE));
10
11
    if(uiTimeOut == TIMEOUTVALUE) return CAN_GETTXBFTIMEOUT;            /* get tx buff time out         */
12
    uiTimeOut = 0;
13
    mcp2515_write_canMsg( txbuf_n);
14
    mcp2515_start_transmit( txbuf_n );
15
    do
16
    {
17
        uiTimeOut++;        
18
        res1= CAN.mcp2515_readRegister(txbuf_n);        /* read send buff ctrl reg   */
19
        res1 = res1 & 0x08;                                   
20
    }while(res1 && (uiTimeOut < TIMEOUTVALUE));   
21
    if(uiTimeOut == TIMEOUTVALUE) return CAN_SENDMSGTIMEOUT;            /* send msg timeout             */  
22
    return CAN_OK;
23
24
}

Interessant ist der letzte Teil
1
    mcp2515_start_transmit( txbuf_n );
2
    do
3
    {
4
        uiTimeOut++;        
5
        res1= CAN.mcp2515_readRegister(txbuf_n);        /* read send buff ctrl reg   */
6
        res1 = res1 & 0x08;                                   
7
    }while(res1 && (uiTimeOut < TIMEOUTVALUE));

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.

von R. F. (rfr)


Lesenswert?

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

von Gerd B. (bertr2d2) Benutzerseite


Lesenswert?

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.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

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.

: Bearbeitet durch User
von Rolf Magnus (Gast)


Lesenswert?

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".

von Roger K. (Firma: privat) (chasperli)


Lesenswert?

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

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.