foxBMS  1.1.2
The foxBMS Battery Management System API Documentation
can_helper.c
Go to the documentation of this file.
1 /**
2  *
3  * @copyright © 2010 - 2021, Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V.
4  * All rights reserved.
5  *
6  * SPDX-License-Identifier: BSD-3-Clause
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright notice, this
12  * list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright notice,
15  * this list of conditions and the following disclaimer in the documentation
16  * and/or other materials provided with the distribution.
17  *
18  * 3. Neither the name of the copyright holder nor the names of its
19  * contributors may be used to endorse or promote products derived from
20  * this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
28  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  *
33  * We kindly request you to use one or more of the following phrases to refer to
34  * foxBMS in your hardware, software, documentation or advertising materials:
35  *
36  * - ″This product uses parts of foxBMS®″
37  * - ″This product includes parts of foxBMS®″
38  * - ″This product is derived from foxBMS®″
39  *
40  */
41 
42 /**
43  * @file can_helper.c
44  * @author foxBMS Team
45  * @date 2021-04-22 (date of creation)
46  * @updated 2021-07-29 (date of last update)
47  * @ingroup DRIVERS
48  * @prefix CAN
49  *
50  * @brief Helper functions for the CAN module
51  *
52  *
53  */
54 
55 /*========== Includes =======================================================*/
56 #include "can_helper.h"
57 
58 #include "database.h"
59 #include "foxmath.h"
60 
61 /*========== Macros and Definitions =========================================*/
62 /** Plausibility checking can signals lengths */
63 #define CAN_SIGNAL_MAX_SIZE (64u)
64 
65 /** bitmask for extraction of one byte */
66 #define CAN_MESSAGE_BIT_MASK_ONE_BYTE (0xFFu)
67 
68 /** length of one CAN byte in bit */
69 #define CAN_BYTE_LENGTH (8u)
70 
71 /*========== Static Constant and Variable Definitions =======================*/
72 
73 /** To convert big endian startbit to usual little endian representation (from 0 as LSB to 63 as MSB) */
74 static const uint8_t can_bigEndianTable[CAN_SIGNAL_MAX_SIZE] = {
75  56u, 57u, 58u, 59u, 60u, 61u, 62u, 63u, 48u, 49u, 50u, 51u, 52u, 53u, 54u, 55u, 40u, 41u, 42u, 43u, 44u, 45u,
76  46u, 47u, 32u, 33u, 34u, 35u, 36u, 37u, 38u, 39u, 24u, 25u, 26u, 27u, 28u, 29u, 30u, 31u, 16u, 17u, 18u, 19u,
77  20u, 21u, 22u, 23u, 8u, 9u, 10u, 11u, 12u, 13u, 14u, 15u, 0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u};
78 
79 /*========== Extern Constant and Variable Definitions =======================*/
80 
81 /*========== Static Function Prototypes =====================================*/
82 
83 /**
84  * @brief Convert bit start in big endian case.
85  * In the big endian case for CAN, the bit start is set
86  * to the MSB and the bit numbering is not directly usable.
87  * This functions converts the bit start to set it to
88  * the LSB of the signal and with the usual bit numbering.
89  * @param bitStart bit start in big endian format
90  * @param bitLength signal length
91  * @return bit start position converted to little endian format
92  */
93 static uint64_t CAN_ConvertBitStartBigEndian(uint64_t bitStart, uint64_t bitLength);
94 
95 /*========== Static Function Implementations ================================*/
96 
97 static uint64_t CAN_ConvertBitStartBigEndian(uint64_t bitStart, uint64_t bitLength) {
98  /* A valid message has to start before the end of the message */
99  FAS_ASSERT(bitStart < CAN_SIGNAL_MAX_SIZE);
100  /* The longest message may be CAN_SIGNAL_MAX_SIZE long */
101  FAS_ASSERT(bitLength <= CAN_SIGNAL_MAX_SIZE);
102  /* A signal must contain at least one bit */
103  FAS_ASSERT(bitLength > 0u);
104 
105  /**
106  * Example: big endian, bitStart = 53, bitLength = 13
107  * For big endian, bitStart corresponds to MSB.
108  * First convert |07 06 05 04 03 02 01 00| to |63 62 61 60 59 58 57 56|
109  * |15 14 13 12 11 10 09 08| |55 54 53 52 51 50 49 48|
110  * |23 22 21 20 19 18 17 16| |47 46 45 44 43 42 41 40|
111  * |31 30 29 28 27 26 25 24| |39 38 37 36 35 34 33 32|
112  * |39 38 37 36 35 34 33 32| |31 30 29 28 27 26 25 24|
113  * |47 46 45 44 43 42 41 40| |23 22 21 20 19 18 17 16|
114  * |55 54 53 52 51 50 49 48| |15 14 13 12 11 10 09 08|
115  * |63 62 61 60 59 58 57 56| |07 06 05 04 03 02 01 00|
116  * to get MSB position in the usual bit representation (from 0 as LSB to 63 as MSB).
117  * In the example, 53 must be converted to 13.
118  */
119  uint64_t position = can_bigEndianTable[bitStart];
120 
121  /**
122  * Usual bit position of MSB of signal is now available.
123  * Now subtract signal length to get LSB position.
124  * In the example, bitStart is converted from 53 to 0 for the length of 13 bits.
125  * This corresponds to the usual data access in the little endian case, from bit 0 to bit 12,
126  * where bitStart corresponds to LSB.
127  */
128  position = position - (bitLength - 1u);
129 
130  /* a valid message has to start before the end of the message */
131  FAS_ASSERT(position < CAN_SIGNAL_MAX_SIZE);
132  /* Check for a plausible message length (sum of start bit and length shall
133  not be larger than 64, otherwise it will not fit into the message) */
134  FAS_ASSERT((position + bitLength) <= CAN_SIGNAL_MAX_SIZE);
135 
136  return position;
137 }
138 
139 /*========== Extern Function Implementations ================================*/
140 
141 extern void CAN_TxPrepareSignalData(float *pSignal, CAN_SIGNAL_TYPE_s signalProperties) {
142  FAS_ASSERT(pSignal != NULL_PTR);
143 
144  /* Check min/max limits */
145  if (*pSignal > signalProperties.max) {
146  *pSignal = signalProperties.max;
147  } else if (*pSignal < signalProperties.min) {
148  *pSignal = signalProperties.min;
149  } else {
150  ; /* no action on *pSignal required */
151  }
152 
153  /* Apply offset */
154  *pSignal = *pSignal + signalProperties.offset;
155 
156  /* Apply factor */
157  *pSignal = *pSignal / signalProperties.factor;
158 }
159 
160 extern void CAN_RxConvertRawSignalData(float *pSignalConverted, float signalRaw, CAN_SIGNAL_TYPE_s signalProperties) {
161  FAS_ASSERT(pSignalConverted != NULL_PTR);
162  /* Apply offset and factor */
163  *pSignalConverted = (signalRaw * signalProperties.factor) - signalProperties.offset;
164 }
165 
167  uint64_t *pMessage,
168  uint64_t bitStart,
169  uint8_t bitLength,
170  uint64_t canSignal,
171  CAN_ENDIANNESS_e endianness) {
172  FAS_ASSERT(pMessage != NULL_PTR);
173  FAS_ASSERT((endianness == CAN_BIG_ENDIAN) || (endianness == CAN_LITTLE_ENDIAN));
174  /* The longest message may be CAN_SIGNAL_MAX_SIZE long */
175  FAS_ASSERT(bitLength <= CAN_SIGNAL_MAX_SIZE);
176  /* A signal must contain at least one bit */
177  FAS_ASSERT(bitLength > 0u);
178 
179  uint64_t position = bitStart;
180 
181  if (endianness == CAN_BIG_ENDIAN) {
182  position = CAN_ConvertBitStartBigEndian(bitStart, bitLength);
183  }
184 
185  /* A valid message has to start before the end of the message */
186  FAS_ASSERT(position < CAN_SIGNAL_MAX_SIZE);
187  /* Check for a plausible message length (sum of start bit and length shall
188  not be larger than 64, otherwise it will not fit into the message) */
189  FAS_ASSERT((position + bitLength) <= CAN_SIGNAL_MAX_SIZE);
190 
191  /* Prepare a mask and assemble message */
192  uint64_t mask = UINT64_MAX;
193 
194  if (bitLength == CAN_SIGNAL_MAX_SIZE) {
195  /* Since a bit-shift of 64 on a 64bit integer is undefined, set to desired 0 */
196  mask = 0u;
197  } else {
198  mask <<= bitLength;
199  }
200  mask = ~mask;
201 
202  *pMessage |= (canSignal & mask) << position;
203 }
204 
205 extern void CAN_TxSetCanDataWithMessageData(uint64_t message, uint8_t *pCanData, CAN_ENDIANNESS_e endianness) {
206  FAS_ASSERT(pCanData != NULL_PTR);
207  /* Swap byte order if necessary */
208  if (endianness == CAN_BIG_ENDIAN) {
209  pCanData[CAN_BYTE_0_POSITION] =
210  (uint8_t)(((message) >> (CAN_BYTE_7_POSITION * CAN_BYTE_LENGTH)) & CAN_MESSAGE_BIT_MASK_ONE_BYTE);
211  pCanData[CAN_BYTE_1_POSITION] =
212  (uint8_t)(((message) >> (CAN_BYTE_6_POSITION * CAN_BYTE_LENGTH)) & CAN_MESSAGE_BIT_MASK_ONE_BYTE);
213  pCanData[CAN_BYTE_2_POSITION] =
214  (uint8_t)(((message) >> (CAN_BYTE_5_POSITION * CAN_BYTE_LENGTH)) & CAN_MESSAGE_BIT_MASK_ONE_BYTE);
215  pCanData[CAN_BYTE_3_POSITION] =
216  (uint8_t)(((message) >> (CAN_BYTE_4_POSITION * CAN_BYTE_LENGTH)) & CAN_MESSAGE_BIT_MASK_ONE_BYTE);
217  pCanData[CAN_BYTE_4_POSITION] =
218  (uint8_t)(((message) >> (CAN_BYTE_3_POSITION * CAN_BYTE_LENGTH)) & CAN_MESSAGE_BIT_MASK_ONE_BYTE);
219  pCanData[CAN_BYTE_5_POSITION] =
220  (uint8_t)(((message) >> (CAN_BYTE_2_POSITION * CAN_BYTE_LENGTH)) & CAN_MESSAGE_BIT_MASK_ONE_BYTE);
221  pCanData[CAN_BYTE_6_POSITION] =
222  (uint8_t)(((message) >> (CAN_BYTE_1_POSITION * CAN_BYTE_LENGTH)) & CAN_MESSAGE_BIT_MASK_ONE_BYTE);
223  pCanData[CAN_BYTE_7_POSITION] =
224  (uint8_t)(((message) >> (CAN_BYTE_0_POSITION * CAN_BYTE_LENGTH)) & CAN_MESSAGE_BIT_MASK_ONE_BYTE);
225  } else if (endianness == CAN_LITTLE_ENDIAN) {
226  pCanData[CAN_BYTE_0_POSITION] =
227  (uint8_t)(((message) >> (CAN_BYTE_0_POSITION * CAN_BYTE_LENGTH)) & CAN_MESSAGE_BIT_MASK_ONE_BYTE);
228  pCanData[CAN_BYTE_1_POSITION] =
229  (uint8_t)(((message) >> (CAN_BYTE_1_POSITION * CAN_BYTE_LENGTH)) & CAN_MESSAGE_BIT_MASK_ONE_BYTE);
230  pCanData[CAN_BYTE_2_POSITION] =
231  (uint8_t)(((message) >> (CAN_BYTE_2_POSITION * CAN_BYTE_LENGTH)) & CAN_MESSAGE_BIT_MASK_ONE_BYTE);
232  pCanData[CAN_BYTE_3_POSITION] =
233  (uint8_t)(((message) >> (CAN_BYTE_3_POSITION * CAN_BYTE_LENGTH)) & CAN_MESSAGE_BIT_MASK_ONE_BYTE);
234  pCanData[CAN_BYTE_4_POSITION] =
235  (uint8_t)(((message) >> (CAN_BYTE_4_POSITION * CAN_BYTE_LENGTH)) & CAN_MESSAGE_BIT_MASK_ONE_BYTE);
236  pCanData[CAN_BYTE_5_POSITION] =
237  (uint8_t)(((message) >> (CAN_BYTE_5_POSITION * CAN_BYTE_LENGTH)) & CAN_MESSAGE_BIT_MASK_ONE_BYTE);
238  pCanData[CAN_BYTE_6_POSITION] =
239  (uint8_t)(((message) >> (CAN_BYTE_6_POSITION * CAN_BYTE_LENGTH)) & CAN_MESSAGE_BIT_MASK_ONE_BYTE);
240  pCanData[CAN_BYTE_7_POSITION] =
241  (uint8_t)(((message) >> (CAN_BYTE_7_POSITION * CAN_BYTE_LENGTH)) & CAN_MESSAGE_BIT_MASK_ONE_BYTE);
242  } else {
243  /* Endianness must be big or little */
245  }
246 }
247 
249  uint64_t message,
250  uint64_t bitStart,
251  uint8_t bitLength,
252  uint64_t *pCanSignal,
253  CAN_ENDIANNESS_e endianness) {
254  FAS_ASSERT(pCanSignal != NULL_PTR);
255  FAS_ASSERT((endianness == CAN_BIG_ENDIAN) || (endianness == CAN_LITTLE_ENDIAN));
256  /* The longest message may be CAN_SIGNAL_MAX_SIZE long */
257  FAS_ASSERT(bitLength <= CAN_SIGNAL_MAX_SIZE);
258  /* A signal must contain at least one bit */
259  FAS_ASSERT(bitLength > 0u);
260  /* Signal start can not be outside of message data */
261  FAS_ASSERT(bitStart < CAN_SIGNAL_MAX_SIZE);
262  uint64_t position = bitStart;
263 
264  if (endianness == CAN_BIG_ENDIAN) {
265  position = CAN_ConvertBitStartBigEndian(bitStart, bitLength);
266  }
267 
268  /* A valid message has to start before the end of the message */
269  FAS_ASSERT(position < CAN_SIGNAL_MAX_SIZE);
270  /* Check for a plausible message length (sum of start bit and length shall
271  not be larger than 64, otherwise it will not fit into the message) */
272  FAS_ASSERT((position + bitLength) <= CAN_SIGNAL_MAX_SIZE);
273 
274  /* Prepare a mask and assemble message */
275  uint64_t mask = UINT64_MAX;
276 
277  if (bitLength == CAN_SIGNAL_MAX_SIZE) {
278  /* Since a bit-shift of 64 on a 64bit integer is undefined, set to desired 0 */
279  mask = 0u;
280  } else {
281  mask <<= bitLength;
282  }
283  mask = ~mask;
284 
285  *pCanSignal = (message >> position) & mask;
286 }
287 
289  uint64_t *pMessage,
290  const uint8_t *const kpkCanData,
291  CAN_ENDIANNESS_e endianness) {
292  FAS_ASSERT(pMessage != NULL_PTR);
293  FAS_ASSERT(kpkCanData != NULL_PTR);
294  /* Swap byte order if necessary */
295  if (endianness == CAN_BIG_ENDIAN) {
296  *pMessage = ((((uint64_t)kpkCanData[CAN_BYTE_0_POSITION]) & CAN_MESSAGE_BIT_MASK_ONE_BYTE)
298  ((((uint64_t)kpkCanData[CAN_BYTE_1_POSITION]) & CAN_MESSAGE_BIT_MASK_ONE_BYTE)
300  ((((uint64_t)kpkCanData[CAN_BYTE_2_POSITION]) & CAN_MESSAGE_BIT_MASK_ONE_BYTE)
302  ((((uint64_t)kpkCanData[CAN_BYTE_3_POSITION]) & CAN_MESSAGE_BIT_MASK_ONE_BYTE)
304  ((((uint64_t)kpkCanData[CAN_BYTE_4_POSITION]) & CAN_MESSAGE_BIT_MASK_ONE_BYTE)
306  ((((uint64_t)kpkCanData[CAN_BYTE_5_POSITION]) & CAN_MESSAGE_BIT_MASK_ONE_BYTE)
308  ((((uint64_t)kpkCanData[CAN_BYTE_6_POSITION]) & CAN_MESSAGE_BIT_MASK_ONE_BYTE)
310  ((((uint64_t)kpkCanData[CAN_BYTE_7_POSITION]) & CAN_MESSAGE_BIT_MASK_ONE_BYTE)
312  } else if (endianness == CAN_LITTLE_ENDIAN) {
313  *pMessage = ((((uint64_t)kpkCanData[CAN_BYTE_0_POSITION]) & CAN_MESSAGE_BIT_MASK_ONE_BYTE)
315  ((((uint64_t)kpkCanData[CAN_BYTE_1_POSITION]) & CAN_MESSAGE_BIT_MASK_ONE_BYTE)
317  ((((uint64_t)kpkCanData[CAN_BYTE_2_POSITION]) & CAN_MESSAGE_BIT_MASK_ONE_BYTE)
319  ((((uint64_t)kpkCanData[CAN_BYTE_3_POSITION]) & CAN_MESSAGE_BIT_MASK_ONE_BYTE)
321  ((((uint64_t)kpkCanData[CAN_BYTE_4_POSITION]) & CAN_MESSAGE_BIT_MASK_ONE_BYTE)
323  ((((uint64_t)kpkCanData[CAN_BYTE_5_POSITION]) & CAN_MESSAGE_BIT_MASK_ONE_BYTE)
325  ((((uint64_t)kpkCanData[CAN_BYTE_6_POSITION]) & CAN_MESSAGE_BIT_MASK_ONE_BYTE)
327  ((((uint64_t)kpkCanData[CAN_BYTE_7_POSITION]) & CAN_MESSAGE_BIT_MASK_ONE_BYTE)
329  } else {
330  /* Endianness must be big or little */
332  }
333 }
334 
335 /*========== Externalized Static Function Implementations (Unit Test) =======*/
@ CAN_LITTLE_ENDIAN
Definition: can_cfg.h:289
@ CAN_BIG_ENDIAN
Definition: can_cfg.h:290
enum CAN_ENDIANNESS CAN_ENDIANNESS_e
#define CAN_BYTE_LENGTH
Definition: can_helper.c:69
static uint64_t CAN_ConvertBitStartBigEndian(uint64_t bitStart, uint64_t bitLength)
Convert bit start in big endian case. In the big endian case for CAN, the bit start is set to the MSB...
Definition: can_helper.c:97
void CAN_TxSetMessageDataWithSignalData(uint64_t *pMessage, uint64_t bitStart, uint8_t bitLength, uint64_t canSignal, CAN_ENDIANNESS_e endianness)
Puts CAN signal data in a 64-bit variable. This function is used to compose a 64-bit CAN message....
Definition: can_helper.c:166
static const uint8_t can_bigEndianTable[CAN_SIGNAL_MAX_SIZE]
Definition: can_helper.c:74
void CAN_TxSetCanDataWithMessageData(uint64_t message, uint8_t *pCanData, CAN_ENDIANNESS_e endianness)
Copy CAN data from a 64-bit variable to 8 bytes. This function is used to copy a 64-bit CAN message t...
Definition: can_helper.c:205
void CAN_RxGetMessageDataFromCanData(uint64_t *pMessage, const uint8_t *const kpkCanData, CAN_ENDIANNESS_e endianness)
Copy CAN data from 8 bytes to a 64-bit variable.
Definition: can_helper.c:288
void CAN_TxPrepareSignalData(float *pSignal, CAN_SIGNAL_TYPE_s signalProperties)
Prepare signal data. This function takes the signal data and applies factor, applies offset and compa...
Definition: can_helper.c:141
#define CAN_MESSAGE_BIT_MASK_ONE_BYTE
Definition: can_helper.c:66
#define CAN_SIGNAL_MAX_SIZE
Definition: can_helper.c:63
void CAN_RxConvertRawSignalData(float *pSignalConverted, float signalRaw, CAN_SIGNAL_TYPE_s signalProperties)
Convert raw signal data. This function takes the raw signal data and applies offset and factor to con...
Definition: can_helper.c:160
void CAN_RxGetSignalDataFromMessageData(uint64_t message, uint64_t bitStart, uint8_t bitLength, uint64_t *pCanSignal, CAN_ENDIANNESS_e endianness)
Gets CAN signal data from a 64-bit variable. This function is used to get signal data from a 64-bit C...
Definition: can_helper.c:248
Headers for the helper functions for the CAN module.
#define CAN_BYTE_2_POSITION
Definition: can_helper.h:70
#define CAN_BYTE_4_POSITION
Definition: can_helper.h:72
#define CAN_BYTE_0_POSITION
Definition: can_helper.h:68
#define CAN_BYTE_6_POSITION
Definition: can_helper.h:74
#define CAN_BYTE_7_POSITION
Definition: can_helper.h:75
#define CAN_BYTE_3_POSITION
Definition: can_helper.h:71
#define CAN_BYTE_5_POSITION
Definition: can_helper.h:73
#define CAN_BYTE_1_POSITION
Definition: can_helper.h:69
Database module header.
#define FAS_ASSERT(x)
Assertion macro that asserts that x is true.
Definition: fassert.h:237
#define FAS_TRAP
Define that evaluates to essential boolean false thus tripping an assert.
Definition: fassert.h:108
math library for often used math functions
#define NULL_PTR
Null pointer.
Definition: fstd_types.h:66