LIN_slave_portable_Arduino 1.4
Arduino library for Local Interconnect Network slave node emulation
Loading...
Searching...
No Matches
LIN_slave_Base.cpp
Go to the documentation of this file.
1
10// include files
11#include <LIN_slave_Base.h>
12
13// warn if debug is active (any debug level)
14#if defined(LIN_SLAVE_DEBUG_SERIAL)
15 #warning Debug interface is active, see file 'LIN_slave_Base.h'
16#endif
17
18
19
20/**************************
21 * PROTECTED METHODS
22**************************/
23
31{
32 uint8_t pid_tmp; // protected ID (pid already used as class variable)
33 uint8_t tmp; // temporary variable for calculating parity bits
34
35 // protect ID with parity bits
36 pid_tmp = (uint8_t) (ID & 0x3F); // clear upper bits 6 & 7
37 tmp = (uint8_t) ((pid_tmp ^ (pid_tmp>>1) ^ (pid_tmp>>2) ^ (pid_tmp>>4)) & 0x01); // pid[6] = PI0 = ID0^ID1^ID2^ID4
38 pid_tmp |= (uint8_t) (tmp << 6);
39 tmp = (uint8_t) (~((pid_tmp>>1) ^ (pid_tmp>>3) ^ (pid_tmp>>4) ^ (pid_tmp>>5)) & 0x01); // pid[7] = PI1 = ~(ID1^ID3^ID4^ID5)
40 pid_tmp |= (uint8_t) (tmp << 7);
41
42 // print debug message
43 DEBUG_PRINT(3, "ID=0x%02X / PID=0x%02X", (ID & 0x3F), pid_tmp);
44
45 // return protected ID
46 return pid_tmp;
47
48} // LIN_Slave_Base::_calculatePID()
49
50
51
59uint8_t LIN_Slave_Base::_calculateChecksum(uint8_t NumData, uint8_t Data[])
60{
61 uint16_t chk = 0x00; // frame checksum
62
63 // LIN2.x uses extended checksum which includes protected ID, i.e. including parity bits
64 // LIN1.x uses classical checksum only over data bytes
65 // Diagnostic frames with ID=0x3C/PID=0x3C and ID=0x3D/PID=0x7D always use classical checksum (see LIN spec "2.3.1.5 Checkum")
66 if (!((this->version == LIN_V1) || (this->id == 0x3C) || (this->id == 0x3D)))
67 chk = (uint16_t) this->_calculatePID(this->id);
68
69 // loop over data bytes
70 for (uint8_t i = 0; i < NumData; i++)
71 {
72 chk += (uint16_t) (Data[i]);
73 if (chk>255)
74 chk -= 255;
75 }
76 chk = (uint8_t)(0xFF - ((uint8_t) chk)); // bitwise invert and strip upper byte
77
78 // print debug message
79 DEBUG_PRINT(3, "CHK=0x%02X", chk);
80
81 // return frame checksum
82 return (uint8_t) chk;
83
84} // LIN_Slave_Base::_calculateChecksum()
85
86
87
94{
95 // dummy for base class
96 return false;
97
98} // LIN_Slave_Base::_getBreakFlag()
99
100
101
107{
108 // dummy for base class
109
110} // LIN_Slave_Base::_resetBreakFlag()
111
112
113
114/**************************
115 * PUBLIC METHODS
116**************************/
117
127LIN_Slave_Base::LIN_Slave_Base(LIN_Slave_Base::version_t Version, const char NameLIN[], uint32_t TimeoutRx, const int8_t PinTxEN)
128{
129 // Debug serial initialized in begin() -> no debug output here
130
131 // store parameters in class variables
132 this->version = Version; // LIN protocol version (required for checksum)
133 memcpy(this->nameLIN, NameLIN, LIN_SLAVE_BUFLEN_NAME); // node name e.g. for debug
134 this->timeoutRx = TimeoutRx; // timeout [us] for bytes in frame
135 this->pinTxEN = PinTxEN; // optional Tx enable pin for RS485
136
137 // initialize slave node properties
138 this->state = LIN_Slave_Base::STATE_WAIT_FOR_BREAK; // status of LIN state machine
139 this->error = LIN_Slave_Base::NO_ERROR; // last LIN error. Is latched
140 for (uint8_t i = 0; i < 64; i++)
141 {
142 this->callback[i].type_numData = 0x00; // frame type (high nibble) and number of data bytes (low nibble)
143 this->callback[i].fct = nullptr; // user callback functions (IDs 0x00 - 0x3F)
144 }
145
146 // initialize frame properties
147 this->id = 0x00; // received frame identifier
148 this->numData = 0; // number of data bytes in frame
149 for (uint8_t i = 0; i < 9; i++)
150 this->bufData[i] = 0x00; // init data bytes (max 8B) + chk
151 this->idxData = 0; // current index in bufData
152 this->timeLastRx = 0; // time [ms] of last received byte in frame
153
154} // LIN_Slave_Base::LIN_Slave_Base()
155
156
157
163void LIN_Slave_Base::begin(uint16_t Baudrate)
164{
165 // initialize debug interface with optional timeout
166 #if defined(LIN_SLAVE_DEBUG_SERIAL)
167 LIN_SLAVE_DEBUG_SERIAL.begin(115200);
168 #if defined(LIN_SLAVE_DEBUG_PORT_TIMEOUT) && (LIN_SLAVE_DEBUG_PORT_TIMEOUT > 0)
169 uint32_t startMillis = millis();
170 while ((!LIN_SLAVE_DEBUG_SERIAL) && (millis() - startMillis < LIN_SLAVE_DEBUG_PORT_TIMEOUT));
171 #else
172 while (!LIN_SLAVE_DEBUG_SERIAL);
173 #endif
174 #endif
175
176 // store parameters in class variables
177 this->baudrate = Baudrate; // communication baudrate [Baud]
178
179 // initialize slave node properties
180 this->error = LIN_Slave_Base::NO_ERROR; // last LIN error. Is latched
181 this->state = LIN_Slave_Base::STATE_WAIT_FOR_BREAK; // status of LIN state machine
182
183 // initialize optional TxEN pin to low (=transmitter off)
184 if (this->pinTxEN >= 0)
185 {
186 digitalWrite(this->pinTxEN, LOW);
187 pinMode(this->pinTxEN, OUTPUT);
188 }
189
190 // print debug message
191 DEBUG_PRINT(2, "BR=%d", (int) Baudrate);
192
193} // LIN_Slave_Base::begin()
194
195
196
202{
203 // set slave node properties
204 this->error = LIN_Slave_Base::NO_ERROR; // last LIN error. Is latched
205 this->state = LIN_Slave_Base::STATE_OFF; // status of LIN state machine
206
207 // optionally disable RS485 transmitter
209
210 // print debug message
211 DEBUG_PRINT(2, " ");
212
213} // LIN_Slave_Base::end()
214
215
216
225{
226 // drop parity bits -> non-protected ID=0..63
227 ID &= 0x3F;
228
229 // register user callback function for master request frame
230 this->callback[ID].type_numData = LIN_Slave_Base::MASTER_REQUEST | (NumData & 0x0F);
231 this->callback[ID].fct = Fct;
232
233 // print debug message
234 DEBUG_PRINT(2, "registered ID=0x%02X / PID=0x%02X", ID, this->_calculatePID(ID));
235
236} // LIN_Slave_Base::registerMasterRequestHandler
237
238
239
248{
249 // drop parity bits -> non-protected ID=0..63
250 ID &= 0x3F;
251
252 // register user callback function for slave response frame
253 this->callback[ID].type_numData = LIN_Slave_Base::SLAVE_RESPONSE | (NumData & 0x0F);
254 this->callback[ID].fct = Fct;
255
256 // print debug message
257 DEBUG_PRINT(2, "registered ID=0x%02X / PID=0x%02X", ID, this->_calculatePID(ID));
258
259} // LIN_Slave_Base::registerSlaveResponseHandler
260
261
262
268{
269 uint8_t chk_calc;
270
271 // print debug message
272 //DEBUG_PRINT(3, "state=%d", (int) this->state);
273
274 // on receive timeout [us] within frame reset state machine
276 ((micros() - this->timeLastRx) > this->timeoutRx))
277 {
278 // set error and abort frame
281
282 // flush receive buffer
283 while (this->available())
284 this->_serialRead();
285
286 // optionally disable RS485 transmitter
288
289 // print debug message
290 DEBUG_PRINT(1, "frame timeout after %ldus", (long) (micros() - this->timeLastRx));
291
292 // return immediately
293 return;
294
295 } // if frame receive timeout
296
297
298 // detected LIN BREAK (=0x00 with framing error or inter-frame pause detected)
299 // Note: received BREAK byte is consumed by child class to support also sync on SYNC byte.
300 if (this->_getBreakFlag() == true)
301 {
302 // clear BREAK flag again
303 this->_resetBreakFlag();
304
305 // start frame reception. Note: 0x00 already checked by derived class
307
308 // optionally disable RS485 transmitter
310
311 // print debug message
312 DEBUG_PRINT(3, "BREAK detected");
313
314 } // if BREAK detected
315
316
317 // A byte was received -> handle it
318 if (this->available())
319 {
320 // read received byte and reset timeout timer
321 uint8_t byteReceived = this->_serialRead();
322 this->timeLastRx = micros();
323
324 // print debug message
325 //DEBUG_PRINT(3, "Rx=0x%02X", byteReceived);
326
327 // handle byte
328 switch (this->state)
329 {
330 // LIN interface disabled, do nothing
332 break;
333
334 // just to avoid compiler warning, do nothing. Break state is handled above
336 break;
337
338 // master request frame is finished, do nothing
340 break;
341
342 // break has been received, waiting for sync field
344
345 // valid SYNC (=0x55) -> wait for ID
346 if (byteReceived == 0x55)
347 {
348 // print debug message
349 DEBUG_PRINT(3, "SYNC detected");
350
351 this->idxData = 0;
353 }
354
355 // invalid SYNC (!=0x55) -> error
356 else
357 {
358 // set error and abort frame
359 this->error = (LIN_Slave_Base::error_t) ((int) this->error | (int) LIN_Slave_Base::ERROR_SYNC);
361
362 // flush receive buffer
363 while (this->available())
364 this->_serialRead();
365
366 // optionally disable RS485 transmitter
368
369 // print debug message
370 DEBUG_PRINT(1, "SYNC error, received 0x%02X", byteReceived);
371
372 // return immediately
373 return;
374
375 } // invalid SYNC
376
377 break; // STATE_WAIT_FOR_SYNC
378
379
380 // sync field has been received, waiting for protected ID
382
383 // received (protected) ID
384 this->pid = byteReceived;
385
386 // strip parity bits from PID -> non-protected ID=0..63
387 this->id = this->pid & 0x3F;
388
389 // check PID parity bits 7+8
390 if (this->pid != this->_calculatePID(this->id))
391 {
392 // set error and abort frame
393 this->error = (LIN_Slave_Base::error_t) ((int) this->error | (int) LIN_Slave_Base::ERROR_PID);
395
396 // flush receive buffer
397 while (this->available())
398 this->_serialRead();
399
400 // optionally disable RS485 transmitter
402
403 // print debug message
404 DEBUG_PRINT(1, "PID parity error, received 0x%02X, calculated 0x%02X", this->pid, this->_calculatePID(this->id));
405
406 // return immediately
407 return;
408
409 } // PID error
410
411
412 // if slave response ID is registered, call callback function and send response
413 if ((this->callback[this->id].fct != nullptr) && (this->callback[this->id].type_numData & LIN_Slave_Base::SLAVE_RESPONSE))
414 {
415 // get type (high nibble) and number of response bytes (low nibble) from callback array
416 this->type = (LIN_Slave_Base::frame_t) (this->callback[this->id].type_numData & 0xF0);
417 this->numData = this->callback[this->id].type_numData & 0x0F;
418
419 // print debug message
420 DEBUG_PRINT(2, "handle slave response PID 0x%02X", this->pid);
421
422 // call the user-defined callback function for this ID
423 this->callback[this->id].fct(this->numData, this->bufData);
424
425 // attach frame checksum
426 this->bufData[this->numData] = this->_calculateChecksum(this->numData, this->bufData);
427
428 // optionally enable RS485 transmitter
430
431 // send slave response (data+chk)
432 this->_serialWrite(this->bufData, this->numData+1);
433
434 // advance state to receiving echo
436
437 } // if slave response frame
438
439
440 // if master request ID is registered, get number of data bytes and advance state
441 else if ((this->callback[this->id].fct != nullptr) && (this->callback[this->id].type_numData & LIN_Slave_Base::MASTER_REQUEST))
442 {
443 // get type (high nibble) and number of response bytes (low nibble) from callback array
444 this->type = (LIN_Slave_Base::frame_t) (this->callback[this->id].type_numData & 0xF0);
445 this->numData = this->callback[this->id].type_numData & 0x0F;
446
447 // advance state to receiving data
449
450 } // if master request frame
451
452
453 // ID is not registered -> wait for next break
454 else
455 {
456 // print debug message
457 DEBUG_PRINT(2, "drop frame PID 0x%02X", this->pid);
458
459 // reset state machine
461
462 } // if frame not registered
463
464 break; // STATE_WAIT_FOR_PID
465
466
467 // receive master request data
469
470 // check received data
471 this->bufData[(this->idxData)++] = byteReceived;
472
473 // if data is finished, advance to checksum check
474 if (this->idxData >= this->numData)
476
477 break; // STATE_RECEIVING_DATA
478
479
480 // receive slave response echo
482
483 // compare received echo to sent data
484 if (this->bufData[(this->idxData)++] != byteReceived)
485 {
486 // set error and abort frame
487 this->error = (LIN_Slave_Base::error_t) ((int) this->error | (int) LIN_Slave_Base::ERROR_ECHO);
489
490 // flush receive buffer
491 while (this->available())
492 this->_serialRead();
493
494 // optionally disable RS485 transmitter
496
497 // print debug message
498 DEBUG_PRINT(1, "echo error, received 0x%02X, expected 0x%02X", byteReceived, this->bufData[(this->idxData)-1]);
499
500 // return immediately
501 return;
502
503 } // if echo error
504
505 // if data is finished, finish frame
506 else if (this->idxData >= this->numData+1)
507 {
509
510 // optionally disable RS485 transmitter
512 }
513
514 break; // STATE_RECEIVING_ECHO
515
516
517 // Data has been received for master request frame, waiting for checksum
519
520 // calculate checksum for master request frame
521 chk_calc = this->_calculateChecksum(this->numData, this->bufData);
522
523 // Checksum valid -> call user-defined callback function for this ID
524 if (byteReceived == chk_calc)
525 {
526 // call user-defined master request callback function. Only reachable if callback has been registered
527 this->callback[id].fct(numData, bufData);
528
529 // print debug message
530 DEBUG_PRINT(2, "handle master request PID 0x%02X", this->pid);
531
532 } // if checksum ok
533
534 // checksum error
535 else
536 {
537 // set error
538 this->error = (LIN_Slave_Base::error_t) ((int) this->error | (int) LIN_Slave_Base::ERROR_CHK);
540
541 // flush receive buffer
542 while (this->available())
543 this->_serialRead();
544
545 // optionally disable RS485 transmitter
547
548 // print debug message
549 DEBUG_PRINT(1, "CHK error, received 0x%02X, calculated 0x%02X", byteReceived, chk_calc);
550
551 // return immediately
552 return;
553
554 } // if checksum error
555
556 // frame is finished
558
559 // optionally disable RS485 transmitter
561
562 break; // STATE_WAIT_FOR_CHK
563
564
565 // this should never happen -> error
566 default:
567
568 // set error and abort frame
569 this->error = (LIN_Slave_Base::error_t) ((int) this->error | (int) LIN_Slave_Base::LIN_Slave_Base::ERROR_STATE);
571
572 // flush receive buffer
573 while (this->available())
574 this->_serialRead();
575
576 // optionally disable RS485 transmitter
578
579 // print debug message
580 DEBUG_PRINT(1, "illegal state %d, this should never happen...", this->state);
581
582 // return immediately
583 return;
584
585 } // switch(state)
586
587 } // if byte received
588
589} // LIN_Slave_Base::handler
590
591/*-----------------------------------------------------------------------------
592 END OF FILE
593-----------------------------------------------------------------------------*/
Base class for LIN slave emulation (non-functional)
uint32_t timeLastRx
time [us] of last received byte in frame
uint8_t idxData
current index in bufData
LIN_Slave_Base::error_t error
error state. Is latched until cleared
void registerSlaveResponseHandler(uint8_t ID, LIN_Slave_Base::LinMessageCallback Fct, uint8_t NumData)
Attach user callback function for slave response frame.
uint16_t baudrate
communication baudrate [Baud]
virtual void _serialWrite(uint8_t buf[], uint8_t num)
write bytes to Tx buffer. Here dummy
int8_t pinTxEN
optional Tx direction pin, e.g. for LIN via RS485
frame_t
LIN frame type. Use high nibble for type, low nibble for number of data bytes -> minimize callback[] ...
@ SLAVE_RESPONSE
LIN slave response frame.
@ MASTER_REQUEST
LIN master request frame.
uint8_t bufData[9]
buffer for data bytes (max. 8B) + checksum
uint32_t timeoutRx
timeout [us] for bytes in frame
virtual bool _getBreakFlag(void)
Get break detection flag. Is hardware dependent.
uint8_t numData
number of data bytes in frame
LIN_Slave_Base::frame_t type
frame type (master request or slave response)
@ STATE_DONE
frame is completed
@ STATE_WAIT_FOR_PID
SYNC received, wait for frame PID.
@ STATE_OFF
LIN interface closed.
@ STATE_WAIT_FOR_BREAK
no LIN transmission ongoing, wait for BRK
@ STATE_WAIT_FOR_SYNC
BRK received, wait for SYNC.
@ STATE_RECEIVING_ECHO
receiving slave response echo
@ STATE_RECEIVING_DATA
receiving master request data
@ STATE_WAIT_FOR_CHK
waiting for checksum
LIN_Slave_Base::version_t version
LIN protocol version.
uint8_t id
unprotected frame identifier (0..63)
void _disableTransmitter(void)
Disable RS485 transmitter (DE=low)
virtual void begin(uint16_t Baudrate=19200)
Open serial interface.
void registerMasterRequestHandler(uint8_t ID, LIN_Slave_Base::LinMessageCallback Fct, uint8_t NumData)
Attach user callback function for master request frame.
error_t
LIN error codes. Use bitmasks, as error is latched. Use same as LIN_master_portable.
@ ERROR_SYNC
error in SYNC (not 0x55)
@ ERROR_TIMEOUT
frame timeout error
@ ERROR_ECHO
error reading response echo
@ ERROR_PID
ID parity error.
@ ERROR_CHK
LIN checksum error.
virtual uint8_t _serialRead(void)
read next byte from Rx buffer. Here dummy
uint8_t _calculateChecksum(uint8_t NumData, uint8_t Data[])
Calculate LIN frame checksum.
char nameLIN[LIN_SLAVE_BUFLEN_NAME]
LIN node name, e.g. for debug.
void(* LinMessageCallback)(uint8_t numData, uint8_t *data)
Type for frame callback function.
uint8_t _calculatePID(uint8_t ID)
Calculate protected frame ID.
virtual void _resetBreakFlag(void)
Clear break detection flag. Is hardware dependent.
version_t
LIN protocol version.
@ LIN_V1
LIN protocol version 1.x.
LIN_Slave_Base(LIN_Slave_Base::version_t Version=LIN_Slave_Base::LIN_V2, const char NameLIN[]="Slave", uint32_t TimeoutRx=1500L, const int8_t PinTxEN=INT8_MIN)
LIN slave node constructor.
uint8_t pid
protected frame identifier (0..255)
LIN_Slave_Base::state_t state
status of LIN state machine
void _enableTransmitter(void)
Enable RS485 transmitter (DE=high)
virtual void end(void)
Close serial interface.
virtual bool available(void)
check if a byte is available in Rx buffer. Here dummy
LIN_Slave_Base::callback_t callback[64]
array of user callback functions for IDs 0x00..0x3F
virtual void handler(void)
Handle LIN protocol and call user-defined frame callbacks.
uint8_t type_numData
frame type (high nibble) and number of data bytes (low nibble)
LinMessageCallback fct
frame callback function