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