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?
:
Bearbeitet durch User
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 | 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
So nicht. Eine Nachricht, die verschwindet, ist weg. Es wird nichts detektiert. Einzige Abhilfe ist die Implementation eines Rollcounters in jedes Paket. Gruss Robert
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
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 | 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.
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.
:
Bearbeitet durch User
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
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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.