foxBMS  1.6.0
The foxBMS Battery Management System API Documentation
can_helper.c
Go to the documentation of this file.
1 /**
2  *
3  * @copyright © 2010 - 2023, 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 2023-10-12 (date of last update)
47  * @version v1.6.0
48  * @ingroup DRIVERS
49  * @prefix CAN
50  *
51  * @brief Helper functions for the CAN module
52  *
53  *
54  */
55 
56 /*========== Includes =======================================================*/
57 #include "can_helper.h"
58 
59 #include "database.h"
60 #include "foxmath.h"
61 
62 #include <math.h>
63 #include <stdint.h>
64 
65 /*========== Macros and Definitions =========================================*/
66 /** Plausibility checking can signals lengths */
67 #define CAN_SIGNAL_MAX_SIZE (64u)
68 
69 /** bitmask for extraction of one byte */
70 #define CAN_MESSAGE_BIT_MASK_ONE_BYTE (0xFFu)
71 
72 /** length of one CAN byte in bit */
73 #define CAN_BYTE_LENGTH (8u)
74 
75 /*========== Static Constant and Variable Definitions =======================*/
76 
77 /** To convert big endian startbit to usual little endian representation (from 0 as LSB to 63 as MSB) */
78 static const uint8_t can_bigEndianTable[CAN_SIGNAL_MAX_SIZE] = {
79  56u, 57u, 58u, 59u, 60u, 61u, 62u, 63u, 48u, 49u, 50u, 51u, 52u, 53u, 54u, 55u, 40u, 41u, 42u, 43u, 44u, 45u,
80  46u, 47u, 32u, 33u, 34u, 35u, 36u, 37u, 38u, 39u, 24u, 25u, 26u, 27u, 28u, 29u, 30u, 31u, 16u, 17u, 18u, 19u,
81  20u, 21u, 22u, 23u, 8u, 9u, 10u, 11u, 12u, 13u, 14u, 15u, 0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u};
82 
83 /*========== Extern Constant and Variable Definitions =======================*/
84 
85 /*========== Static Function Prototypes =====================================*/
86 
87 /**
88  * @brief Convert bit start in big endian case.
89  * In the big endian case for CAN, the bit start is set
90  * to the MSB and the bit numbering is not directly usable.
91  * This functions converts the bit start to set it to
92  * the LSB of the signal and with the usual bit numbering.
93  * @param bitStart bit start in big endian format
94  * @param bitLength signal length
95  * @return bit start position converted to little endian format
96  */
97 static uint64_t CAN_ConvertBitStartBigEndian(uint64_t bitStart, uint64_t bitLength);
98 
99 /*========== Static Function Implementations ================================*/
100 
101 static uint64_t CAN_ConvertBitStartBigEndian(uint64_t bitStart, uint64_t bitLength) {
102  /* A valid message has to start before the end of the message */
103  FAS_ASSERT(bitStart < CAN_SIGNAL_MAX_SIZE);
104  /* The longest message may be CAN_SIGNAL_MAX_SIZE long */
105  FAS_ASSERT(bitLength <= CAN_SIGNAL_MAX_SIZE);
106  /* A signal must contain at least one bit */
107  FAS_ASSERT(bitLength > 0u);
108 
109  /**
110  * Example: big endian, bitStart = 53, bitLength = 13
111  * For big endian, bitStart corresponds to MSB.
112  * First convert |07 06 05 04 03 02 01 00| to |63 62 61 60 59 58 57 56|
113  * |15 14 13 12 11 10 09 08| |55 54 53 52 51 50 49 48|
114  * |23 22 21 20 19 18 17 16| |47 46 45 44 43 42 41 40|
115  * |31 30 29 28 27 26 25 24| |39 38 37 36 35 34 33 32|
116  * |39 38 37 36 35 34 33 32| |31 30 29 28 27 26 25 24|
117  * |47 46 45 44 43 42 41 40| |23 22 21 20 19 18 17 16|
118  * |55 54 53 52 51 50 49 48| |15 14 13 12 11 10 09 08|
119  * |63 62 61 60 59 58 57 56| |07 06 05 04 03 02 01 00|
120  * to get MSB position in the usual bit representation (from 0 as LSB to 63 as MSB).
121  * In the example, 53 must be converted to 13.
122  */
123  uint64_t position = can_bigEndianTable[bitStart];
124 
125  /**
126  * Usual bit position of MSB of signal is now available.
127  * Now subtract signal length to get LSB position.
128  * In the example, bitStart is converted from 53 to 0 for the length of 13 bits.
129  * This corresponds to the usual data access in the little endian case, from bit 0 to bit 12,
130  * where bitStart corresponds to LSB.
131  */
132  position = position - (bitLength - 1u);
133 
134  /* a valid message has to start before the end of the message */
135  FAS_ASSERT(position < CAN_SIGNAL_MAX_SIZE);
136  /* Check for a plausible message length (sum of start bit and length shall
137  not be larger than 64, otherwise it will not fit into the message) */
138  FAS_ASSERT((position + bitLength) <= CAN_SIGNAL_MAX_SIZE);
139 
140  return position;
141 }
142 
143 /*========== Extern Function Implementations ================================*/
144 
145 extern void CAN_TxPrepareSignalData(float_t *pSignal, CAN_SIGNAL_TYPE_s signalProperties) {
146  FAS_ASSERT(pSignal != NULL_PTR);
147 
148  /* Check min/max limits */
149  if (*pSignal > signalProperties.max) {
150  *pSignal = signalProperties.max;
151  } else if (*pSignal < signalProperties.min) {
152  *pSignal = signalProperties.min;
153  } else {
154  ; /* no action on *pSignal required */
155  }
156 
157  /* Apply offset */
158  *pSignal = *pSignal + signalProperties.offset;
159 
160  /* Apply factor */
161  *pSignal = *pSignal / signalProperties.factor;
162 }
163 
165  float_t *pSignalConverted,
166  float_t signalRaw,
167  CAN_SIGNAL_TYPE_s signalProperties) {
168  FAS_ASSERT(pSignalConverted != NULL_PTR);
169  /* Apply offset and factor */
170  *pSignalConverted = (signalRaw * signalProperties.factor) - signalProperties.offset;
171 }
172 
174  uint64_t *pMessage,
175  uint64_t bitStart,
176  uint8_t bitLength,
177  uint64_t canSignal,
178  CAN_ENDIANNESS_e endianness) {
179  FAS_ASSERT(pMessage != NULL_PTR);
180  FAS_ASSERT((endianness == CAN_BIG_ENDIAN) || (endianness == CAN_LITTLE_ENDIAN));
181  /* The longest message may be CAN_SIGNAL_MAX_SIZE long */
182  FAS_ASSERT(bitLength <= CAN_SIGNAL_MAX_SIZE);
183  /* A signal must contain at least one bit */
184  FAS_ASSERT(bitLength > 0u);
185 
186  uint64_t position = bitStart;
187 
188  if (endianness == CAN_BIG_ENDIAN) {
189  position = CAN_ConvertBitStartBigEndian(bitStart, bitLength);
190  }
191 
192  /* A valid message has to start before the end of the message */
193  FAS_ASSERT(position < CAN_SIGNAL_MAX_SIZE);
194  /* Check for a plausible message length (sum of start bit and length shall
195  not be larger than 64, otherwise it will not fit into the message) */
196  FAS_ASSERT((position + bitLength) <= CAN_SIGNAL_MAX_SIZE);
197 
198  /* Prepare a mask and assemble message */
199  uint64_t mask = UINT64_MAX;
200 
201  if (bitLength == CAN_SIGNAL_MAX_SIZE) {
202  /* Since a bit-shift of 64 on a 64bit integer is undefined, set to desired 0 */
203  mask = 0u;
204  } else {
205  mask <<= bitLength;
206  }
207  mask = ~mask;
208 
209  *pMessage |= (canSignal & mask) << position;
210 }
211 
212 extern void CAN_TxSetCanDataWithMessageData(uint64_t message, uint8_t *pCanData, CAN_ENDIANNESS_e endianness) {
213  FAS_ASSERT(pCanData != NULL_PTR);
214  /* Swap byte order if necessary */
215  if (endianness == CAN_BIG_ENDIAN) {
216  pCanData[CAN_BYTE_0_POSITION] =
217  (uint8_t)(((message) >> (CAN_BYTE_7_POSITION * CAN_BYTE_LENGTH)) & CAN_MESSAGE_BIT_MASK_ONE_BYTE);
218  pCanData[CAN_BYTE_1_POSITION] =
219  (uint8_t)(((message) >> (CAN_BYTE_6_POSITION * CAN_BYTE_LENGTH)) & CAN_MESSAGE_BIT_MASK_ONE_BYTE);
220  pCanData[CAN_BYTE_2_POSITION] =
221  (uint8_t)(((message) >> (CAN_BYTE_5_POSITION * CAN_BYTE_LENGTH)) & CAN_MESSAGE_BIT_MASK_ONE_BYTE);
222  pCanData[CAN_BYTE_3_POSITION] =
223  (uint8_t)(((message) >> (CAN_BYTE_4_POSITION * CAN_BYTE_LENGTH)) & CAN_MESSAGE_BIT_MASK_ONE_BYTE);
224  pCanData[CAN_BYTE_4_POSITION] =
225  (uint8_t)(((message) >> (CAN_BYTE_3_POSITION * CAN_BYTE_LENGTH)) & CAN_MESSAGE_BIT_MASK_ONE_BYTE);
226  pCanData[CAN_BYTE_5_POSITION] =
227  (uint8_t)(((message) >> (CAN_BYTE_2_POSITION * CAN_BYTE_LENGTH)) & CAN_MESSAGE_BIT_MASK_ONE_BYTE);
228  pCanData[CAN_BYTE_6_POSITION] =
229  (uint8_t)(((message) >> (CAN_BYTE_1_POSITION * CAN_BYTE_LENGTH)) & CAN_MESSAGE_BIT_MASK_ONE_BYTE);
230  pCanData[CAN_BYTE_7_POSITION] =
231  (uint8_t)(((message) >> (CAN_BYTE_0_POSITION * CAN_BYTE_LENGTH)) & CAN_MESSAGE_BIT_MASK_ONE_BYTE);
232  } else if (endianness == CAN_LITTLE_ENDIAN) {
233  pCanData[CAN_BYTE_0_POSITION] =
234  (uint8_t)(((message) >> (CAN_BYTE_0_POSITION * CAN_BYTE_LENGTH)) & CAN_MESSAGE_BIT_MASK_ONE_BYTE);
235  pCanData[CAN_BYTE_1_POSITION] =
236  (uint8_t)(((message) >> (CAN_BYTE_1_POSITION * CAN_BYTE_LENGTH)) & CAN_MESSAGE_BIT_MASK_ONE_BYTE);
237  pCanData[CAN_BYTE_2_POSITION] =
238  (uint8_t)(((message) >> (CAN_BYTE_2_POSITION * CAN_BYTE_LENGTH)) & CAN_MESSAGE_BIT_MASK_ONE_BYTE);
239  pCanData[CAN_BYTE_3_POSITION] =
240  (uint8_t)(((message) >> (CAN_BYTE_3_POSITION * CAN_BYTE_LENGTH)) & CAN_MESSAGE_BIT_MASK_ONE_BYTE);
241  pCanData[CAN_BYTE_4_POSITION] =
242  (uint8_t)(((message) >> (CAN_BYTE_4_POSITION * CAN_BYTE_LENGTH)) & CAN_MESSAGE_BIT_MASK_ONE_BYTE);
243  pCanData[CAN_BYTE_5_POSITION] =
244  (uint8_t)(((message) >> (CAN_BYTE_5_POSITION * CAN_BYTE_LENGTH)) & CAN_MESSAGE_BIT_MASK_ONE_BYTE);
245  pCanData[CAN_BYTE_6_POSITION] =
246  (uint8_t)(((message) >> (CAN_BYTE_6_POSITION * CAN_BYTE_LENGTH)) & CAN_MESSAGE_BIT_MASK_ONE_BYTE);
247  pCanData[CAN_BYTE_7_POSITION] =
248  (uint8_t)(((message) >> (CAN_BYTE_7_POSITION * CAN_BYTE_LENGTH)) & CAN_MESSAGE_BIT_MASK_ONE_BYTE);
249  } else {
250  /* Endianness must be big or little */
252  }
253 }
254 
256  uint64_t message,
257  uint64_t bitStart,
258  uint8_t bitLength,
259  uint64_t *pCanSignal,
260  CAN_ENDIANNESS_e endianness) {
261  FAS_ASSERT(pCanSignal != NULL_PTR);
262  FAS_ASSERT((endianness == CAN_BIG_ENDIAN) || (endianness == CAN_LITTLE_ENDIAN));
263  /* The longest message may be CAN_SIGNAL_MAX_SIZE long */
264  FAS_ASSERT(bitLength <= CAN_SIGNAL_MAX_SIZE);
265  /* A signal must contain at least one bit */
266  FAS_ASSERT(bitLength > 0u);
267  /* Signal start can not be outside of message data */
268  FAS_ASSERT(bitStart < CAN_SIGNAL_MAX_SIZE);
269  uint64_t position = bitStart;
270 
271  if (endianness == CAN_BIG_ENDIAN) {
272  position = CAN_ConvertBitStartBigEndian(bitStart, bitLength);
273  }
274 
275  /* A valid message has to start before the end of the message */
276  FAS_ASSERT(position < CAN_SIGNAL_MAX_SIZE);
277  /* Check for a plausible message length (sum of start bit and length shall
278  not be larger than 64, otherwise it will not fit into the message) */
279  FAS_ASSERT((position + bitLength) <= CAN_SIGNAL_MAX_SIZE);
280 
281  /* Prepare a mask and assemble message */
282  uint64_t mask = UINT64_MAX;
283 
284  if (bitLength == CAN_SIGNAL_MAX_SIZE) {
285  /* Since a bit-shift of 64 on a 64bit integer is undefined, set to desired 0 */
286  mask = 0u;
287  } else {
288  mask <<= bitLength;
289  }
290  mask = ~mask;
291 
292  *pCanSignal = (message >> position) & mask;
293 }
294 
296  uint64_t *pMessage,
297  const uint8_t *const kpkCanData,
298  CAN_ENDIANNESS_e endianness) {
299  FAS_ASSERT(pMessage != NULL_PTR);
300  FAS_ASSERT(kpkCanData != NULL_PTR);
301  /* Swap byte order if necessary */
302  if (endianness == CAN_BIG_ENDIAN) {
303  *pMessage = ((((uint64_t)kpkCanData[CAN_BYTE_0_POSITION]) & CAN_MESSAGE_BIT_MASK_ONE_BYTE)
305  ((((uint64_t)kpkCanData[CAN_BYTE_1_POSITION]) & CAN_MESSAGE_BIT_MASK_ONE_BYTE)
307  ((((uint64_t)kpkCanData[CAN_BYTE_2_POSITION]) & CAN_MESSAGE_BIT_MASK_ONE_BYTE)
309  ((((uint64_t)kpkCanData[CAN_BYTE_3_POSITION]) & CAN_MESSAGE_BIT_MASK_ONE_BYTE)
311  ((((uint64_t)kpkCanData[CAN_BYTE_4_POSITION]) & CAN_MESSAGE_BIT_MASK_ONE_BYTE)
313  ((((uint64_t)kpkCanData[CAN_BYTE_5_POSITION]) & CAN_MESSAGE_BIT_MASK_ONE_BYTE)
315  ((((uint64_t)kpkCanData[CAN_BYTE_6_POSITION]) & CAN_MESSAGE_BIT_MASK_ONE_BYTE)
317  ((((uint64_t)kpkCanData[CAN_BYTE_7_POSITION]) & CAN_MESSAGE_BIT_MASK_ONE_BYTE)
319  } else if (endianness == CAN_LITTLE_ENDIAN) {
320  *pMessage = ((((uint64_t)kpkCanData[CAN_BYTE_0_POSITION]) & CAN_MESSAGE_BIT_MASK_ONE_BYTE)
322  ((((uint64_t)kpkCanData[CAN_BYTE_1_POSITION]) & CAN_MESSAGE_BIT_MASK_ONE_BYTE)
324  ((((uint64_t)kpkCanData[CAN_BYTE_2_POSITION]) & CAN_MESSAGE_BIT_MASK_ONE_BYTE)
326  ((((uint64_t)kpkCanData[CAN_BYTE_3_POSITION]) & CAN_MESSAGE_BIT_MASK_ONE_BYTE)
328  ((((uint64_t)kpkCanData[CAN_BYTE_4_POSITION]) & CAN_MESSAGE_BIT_MASK_ONE_BYTE)
330  ((((uint64_t)kpkCanData[CAN_BYTE_5_POSITION]) & CAN_MESSAGE_BIT_MASK_ONE_BYTE)
332  ((((uint64_t)kpkCanData[CAN_BYTE_6_POSITION]) & CAN_MESSAGE_BIT_MASK_ONE_BYTE)
334  ((((uint64_t)kpkCanData[CAN_BYTE_7_POSITION]) & CAN_MESSAGE_BIT_MASK_ONE_BYTE)
336  } else {
337  /* Endianness must be big or little */
339  }
340 }
341 
342 extern uint8_t CAN_ConvertBooleanToInteger(bool input) {
343  uint8_t returnValue = 0u;
344  if (input == true) {
345  returnValue = 1u;
346  }
347  return returnValue;
348 }
349 
350 /*========== Externalized Static Function Implementations (Unit Test) =======*/
351 #ifdef UNITY_UNIT_TEST
352 #endif
CAN_ENDIANNESS_e
Definition: can_cfg.h:156
@ CAN_LITTLE_ENDIAN
Definition: can_cfg.h:157
@ CAN_BIG_ENDIAN
Definition: can_cfg.h:158
#define CAN_BYTE_LENGTH
Definition: can_helper.c:73
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:101
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:173
static const uint8_t can_bigEndianTable[CAN_SIGNAL_MAX_SIZE]
Definition: can_helper.c:78
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:212
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:295
uint8_t CAN_ConvertBooleanToInteger(bool input)
Transform a bool to a bit (set if true)
Definition: can_helper.c:342
#define CAN_MESSAGE_BIT_MASK_ONE_BYTE
Definition: can_helper.c:70
void CAN_TxPrepareSignalData(float_t *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:145
#define CAN_SIGNAL_MAX_SIZE
Definition: can_helper.c:67
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:255
void CAN_RxConvertRawSignalData(float_t *pSignalConverted, float_t 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:164
Headers for the helper functions for the CAN module.
#define CAN_BYTE_2_POSITION
Definition: can_helper.h:73
#define CAN_BYTE_4_POSITION
Definition: can_helper.h:75
#define CAN_BYTE_0_POSITION
Definition: can_helper.h:71
#define CAN_BYTE_6_POSITION
Definition: can_helper.h:77
#define CAN_BYTE_7_POSITION
Definition: can_helper.h:78
#define CAN_BYTE_3_POSITION
Definition: can_helper.h:74
#define CAN_BYTE_5_POSITION
Definition: can_helper.h:76
#define CAN_BYTE_1_POSITION
Definition: can_helper.h:72
Database module header.
#define FAS_ASSERT(x)
Assertion macro that asserts that x is true.
Definition: fassert.h:255
#define FAS_TRAP
Define that evaluates to essential boolean false thus tripping an assert.
Definition: fassert.h:129
math library for often used math functions
#define NULL_PTR
Null pointer.
Definition: fstd_types.h:77