-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathLin_Interface.cpp
295 lines (265 loc) · 9.67 KB
/
Lin_Interface.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
// Lin-Interface.cpp
//
// Utilizes a UART to provide a Lin-Interface
// This class is inherited from the "HardwareSerial" call
//
// Copyright mestrode <ardlib@mestro.de>
// Original Source: "https://github.com/mestrode/Lin-Interface-Library"
#include "Lin_Interface.hpp"
#include <Arduino.h>
/// @brief reads data from a lin device by requesting a specific FrameID
/// @details Start frame and read answer from bus device
/// The received data will be passed to the Lin_Interface::LinMessage[] array
/// Receives as much as possible, but maximum 8 data byte + checksum
/// Verify Checksum according to LIN 2.0 rules
/// @param FrameID ID of frame (will be converted to protected ID)
/// @returns verification of checksum was succesful
bool Lin_Interface::readFrame(uint8_t FrameID)
{
uint8_t ProtectedID = getProtectedID(FrameID);
bool ChecksumValid = false;
// start transmission
HardwareSerial::begin(baud, SERIAL_8N1);
writeBreak(); // initiate Frame with a Break
HardwareSerial::write(0x55); // Sync
HardwareSerial::write(ProtectedID); // PID
HardwareSerial::flush();
// wait for available data
delay(100);
// Break, Sync and ProtectedID will be received --> discard them
int bytes_received = -4;
while (HardwareSerial::available())
{
if (bytes_received >= (8 + 1)) // max 8x Data + 1x Checksum
{
// receive max 9 Bytes: 8 Data + 1 Chksum
break;
}
switch (bytes_received)
{
case -4: //??
case -3: // break = 0x00
case -2: // sync = 0x55
case -1: // Protected ID
{
// discard Sync and PID (send by us)
uint8_t buffer = HardwareSerial::read();
// Sync and PID may to be verified here
if (buffer == 0x00) { // break
bytes_received = -3;
}
if (buffer == 0x55) { // sync
bytes_received = -2;
}
if (buffer == ProtectedID) { // PID
bytes_received = -1;
}
break;
}
default: // Data 0...7, Checksum
// Receive and save only Data Byte (send by slave)
LinMessage[bytes_received] = HardwareSerial::read();
}
bytes_received++;
}
uint8_t Checksum = LinMessage[bytes_received - 1];
bytes_received--;
// erase data in buffer, in case a 9th or 10th Byte was received
HardwareSerial::flush();
while (HardwareSerial::available()) {
HardwareSerial::read();
if (verboseMode > 0)
{
Serial.print("additional byte discarded\n");
}
}
HardwareSerial::end();
// verify Checksum
ChecksumValid = (0xFF == (uint8_t)(Checksum + ~getChecksum(ProtectedID, bytes_received)));
if (verboseMode > 0)
{
Serial.printf(" --->>>>>> FID %02Xh = 55|%02X|", FrameID, ProtectedID);
for (int i = 0; i < 8; ++i)
{
if (i >= bytes_received)
break;
Serial.printf("%02X.", LinMessage[i]);
}
Serial.printf("\b|%02X", Checksum);
if (!ChecksumValid)
{
Serial.printf(" Checksum failed ");
}
Serial.println();
}
return ChecksumValid;
} // bool readFrame()
/// @brief write a complete LIN2.0 frame without request of data to the lin-bus
/// @details write LIN Frame (Break, Synk, PID, Data, Checksum) to the Bus, and hope somebody will read this
/// Checksum Calculations regarding LIN 2.0
/// The data of this frame is 'dataLen' long and incuded in the Lin_Interface::LinMessage[] array
/// @param FrameID ID of frame (will be converted to protected ID)
/// @param dataLen count of data within the LinMessage array (containing only the data) should be transmitted
void Lin_Interface::writeFrame(uint8_t FrameID, uint8_t dataLen)
{
uint8_t ProtectedID = getProtectedID(FrameID);
uint8_t cksum = getChecksum(ProtectedID, dataLen);
// übertragung startet
HardwareSerial::begin(baud, SERIAL_8N1);
writeBreak(); // initiate Frame with a Break
HardwareSerial::write(0x55); // Sync
HardwareSerial::write(ProtectedID); // PID
for (int i = 0; i < dataLen; ++i)
{
HardwareSerial::write(LinMessage[i]); // Message (array from 1..8)
}
HardwareSerial::write(cksum);
// wait for available data
delay(10);
/// TODO: read back of the break needs to be verified
verboseMode = 1;
// Read Break and discard
if (HardwareSerial::available())
{
HardwareSerial::read();
}
// Read Sync
uint8_t RX_Sync = 0x00;
if (HardwareSerial::available())
{
RX_Sync = HardwareSerial::read();
}
//Read PID
uint8_t RX_ProtectedID = 0x00;
if (HardwareSerial::available())
{
RX_ProtectedID = HardwareSerial::read();
}
// read DATA + CHKSUM
bool moreData = false;
int bytes_received = 0;
while (HardwareSerial::available())
{
if (bytes_received >= 8 + 1 + 4)
{
// receive max 9 Bytes = 8 Data + 1 Chksum
moreData = true;
break;
}
// Receive Byte from Bus (Slave)
LinMessage[bytes_received] = HardwareSerial::read();
bytes_received++;
}
uint8_t Checksum_received = LinMessage[bytes_received - 1];
bytes_received--;
// erase data in buffer, in case a 9th or 10th Byte was received
HardwareSerial::flush();
HardwareSerial::end();
// use received PID for verification
uint8_t ChkSumCalc = getChecksum(RX_ProtectedID, bytes_received);
if (verboseMode > 0)
{
Serial.printf(" <<<<<<--- FID %02Xh (%02X) = %02X|%02X|", FrameID, ProtectedID, RX_Sync, RX_ProtectedID);
for (int i = 0; i < 8 + 1 + 4; ++i)
{
if (i >= bytes_received)
break;
Serial.printf("%02X ", LinMessage[i]);
}
Serial.printf("\b|%02X", Checksum_received);
if (Checksum_received != ChkSumCalc)
{
Serial.printf("\b != ChkSum calc %02Xh| TX %02Xh ", ChkSumCalc, cksum);
}
if (moreData)
{
Serial.print("more Bytes available");
}
Serial.println();
}
} // void writeFrame()
/// TODO: function needs to be verified
/// send Frame (Break, Synk, PID, Data, Classic-Checksum) to the Bus
/// Checksum Calculations regarding LIN 1.x
void Lin_Interface::writeFrameClassic(uint8_t FrameID, uint8_t dataLen)
{
uint8_t ProtectedID = getProtectedID(FrameID);
uint8_t cksum = getChecksum(0x00, dataLen);
HardwareSerial::begin(baud, SERIAL_8N1);
writeBreak(); // initiate Frame with a Break
HardwareSerial::write(0x55); // Sync
HardwareSerial::write(ProtectedID); // ID
for (int i = 0; i < dataLen; ++i)
{
HardwareSerial::write(LinMessage[i]); // Message (array from 1..8)
}
HardwareSerial::write(cksum);
HardwareSerial::flush();
/// TODO: verification of written data (see Lin_Interface::writeFrame)
HardwareSerial::end();
} // void Lin_Interface::writeFrameClassic
/// Send a Break for introduction of a Frame
/// This is done by sending a Byte (0x00) + Stop Bit by using half baud rate
/// @returns if the 0x00 has been send
size_t Lin_Interface::writeBreak()
{
HardwareSerial::flush();
// configure to half baudrate --> a t_bit will be doubled
HardwareSerial::updateBaudRate(baud >> 1);
// write 0x00, including Stop-Bit (=1),
// qualifies when writing in slow motion like a Break in normal speed
size_t ret = write(uint8_t(0x00));
// ensure this is send
HardwareSerial::flush();
// restore normal speed
HardwareSerial::updateBaudRate(baud);
return ret;
}
/// get Protected ID by calculating parity bits and combine with Frame ID
/// @param FrameID to be converted
/// @return Protected ID
uint8_t Lin_Interface::getProtectedID(uint8_t FrameID)
{
// calc Parity Bit 0
uint8_t p0 = bitRead(FrameID, 0) ^ bitRead(FrameID, 1) ^ bitRead(FrameID, 2) ^ bitRead(FrameID, 4);
// calc Parity Bit 1
uint8_t p1 = ~(bitRead(FrameID, 1) ^ bitRead(FrameID, 3) ^ bitRead(FrameID, 4) ^ bitRead(FrameID, 5));
// combine bits to protected ID
// 0..5 id is limited between 0x00..0x3F
// 6 parity bit 0
// 7 parity bit 1
return ((p1 << 7) | (p0 << 6) | (FrameID & 0x3F));
}
/// @brief Checksum calculation for LIN Frame
/// @details
/// EnhancedChecksum considers ProtectedID
/// LIN 2.0 only for FrameID between 0x00..0x3B
/// LIN 2.0 uses for 0x3C and above ClassicChecksum for legacy (auto detected)
/// ClassicChecksum
/// LIN 1.x in general (use 'ProtectedID' = 0x00 to ensure that)
/// see LIN Specification 2.2A (2021-12-31) for details
/// https://microchipdeveloper.com/local--files/lin:specification/LIN-Spec_2.2_Rev_A.PDF
/// 2.8.3 Example of Checksum Calculation
/// @param ProtectedID initial Byte, set to 0x00, when calc Checksum for classic LIN Frame
/// @param dataLen length of Frame (only Data Bytes)
/// @returns calculated checksum
uint8_t Lin_Interface::getChecksum(uint8_t ProtectedID, uint8_t dataLen)
{
uint16_t sum = ProtectedID;
// test FrameID bits for classicChecksum
if ((sum & 0x3F) >= 0x3C)
{
// LIN 1.x: legacy
// LIN 2.0: don't include PID for ChkSum calculation on configuration and reserved frames
sum = 0x00;
}
// sum up all bytes (including carryover to the high byte)
// ID allready considered
while (dataLen-- > 0)
sum += LinMessage[dataLen];
// add high byte (carry over) to the low byte
while (sum >> 8)
sum = (sum & 0xFF) + (sum >> 8);
// inverting result
return (~sum);
}