Compare commits
4 Commits
2c0d7350e3
...
aa85867083
Author | SHA1 | Date | |
---|---|---|---|
aa85867083 | |||
1edc8d3e1c | |||
d4dc7e4533 | |||
686e26b609 |
2
AMS_Master_Code/.clang-format
Normal file
2
AMS_Master_Code/.clang-format
Normal file
@ -0,0 +1,2 @@
|
||||
IndentWidth: 4
|
||||
ColumnLimit: 120
|
@ -6,15 +6,36 @@
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
extern uint16_t min_voltage;
|
||||
extern uint16_t max_voltage;
|
||||
extern struct {uint16_t min; uint16_t max;} module_voltages[N_BMS];
|
||||
extern int16_t max_temp;
|
||||
extern int16_t min_temp;
|
||||
extern struct {int16_t min; int16_t max;} module_temps[N_BMS];
|
||||
extern float module_std_deviation[N_BMS];
|
||||
#define ADBMS_NO_LOGGING_DEFS
|
||||
#include "ADBMS_Driver.h"
|
||||
|
||||
extern int16_t cellTemps[N_BMS][10];
|
||||
extern struct {
|
||||
struct {
|
||||
float soc;
|
||||
uint16_t min_voltage;
|
||||
uint16_t max_voltage;
|
||||
uint16_t min_temp;
|
||||
uint16_t max_temp;
|
||||
} pack;
|
||||
struct {
|
||||
BMS_Chip * chip;
|
||||
int16_t cellTemps[10];
|
||||
struct {
|
||||
uint8_t min_v_idx : 4; uint8_t max_v_idx : 4; // cell index: 0-15
|
||||
uint8_t min_t_idx : 4; uint8_t max_t_idx : 4; // used like this: battery->module[i].chip->cellVoltages[min_v_idx]
|
||||
};
|
||||
} module[N_BMS];
|
||||
} battery;
|
||||
|
||||
//Helper for getting min/max values
|
||||
static inline struct { uint16_t min_v; uint16_t max_v; int16_t min_t; int16_t max_t; } getModuleMinMax(int i) {
|
||||
return (typeof(getModuleMinMax(0))) {
|
||||
.min_v = battery.module[i].chip->cellVoltages[battery.module[i].min_v_idx],
|
||||
.max_v = battery.module[i].chip->cellVoltages[battery.module[i].max_v_idx],
|
||||
.min_t = battery.module[i].cellTemps[battery.module[i].min_t_idx],
|
||||
.max_t = battery.module[i].cellTemps[battery.module[i].max_t_idx]
|
||||
};
|
||||
}
|
||||
|
||||
HAL_StatusTypeDef battery_init(SPI_HandleTypeDef* hspi);
|
||||
HAL_StatusTypeDef battery_update();
|
||||
|
@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
#ifndef __CONFIG_ADBMS6830_H
|
||||
#define __CONFIG_ADBMS6830_H
|
||||
#include "main.h"
|
||||
|
||||
#define N_BMS 1
|
||||
#define N_CELLS 16
|
||||
@ -9,15 +8,6 @@
|
||||
#define ADBMS_SPI_TIMEOUT 50 // Timeout in ms
|
||||
#define DEFAULT_UV 3000 // mV
|
||||
#define DEFAULT_OV 4200 // mV
|
||||
|
||||
[[maybe_unused, gnu::always_inline]]
|
||||
static inline void mcuAdbmsCSLow() {
|
||||
HAL_GPIO_WritePin(AMS_CS_GPIO_Port, AMS_CS_Pin, GPIO_PIN_RESET);
|
||||
}
|
||||
|
||||
[[maybe_unused, gnu::always_inline]]
|
||||
static inline void mcuAdbmsCSHigh() {
|
||||
HAL_GPIO_WritePin(AMS_CS_GPIO_Port, AMS_CS_Pin, GPIO_PIN_SET);
|
||||
}
|
||||
#define ERROR_TIME_THRESH 150 // ms
|
||||
|
||||
#endif //__CONFIG_ADBMS6830_H
|
||||
|
@ -3,16 +3,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
extern float current_soc;
|
||||
|
||||
void soc_init();
|
||||
void soc_update();
|
||||
|
||||
typedef struct {
|
||||
uint16_t ocv;
|
||||
float soc;
|
||||
} ocv_soc_pair_t;
|
||||
extern ocv_soc_pair_t OCV_SOC_PAIRS[];
|
||||
float soc_for_ocv(uint16_t ocv);
|
||||
|
||||
#endif // INC_SOC_ESTIMATION_H
|
||||
|
@ -1,216 +0,0 @@
|
||||
---
|
||||
Language: Cpp
|
||||
# BasedOnStyle: LLVM
|
||||
AccessModifierOffset: -2
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignArrayOfStructures: None
|
||||
AlignConsecutiveAssignments:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCompound: false
|
||||
PadOperators: true
|
||||
AlignConsecutiveBitFields:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCompound: false
|
||||
PadOperators: false
|
||||
AlignConsecutiveDeclarations:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCompound: false
|
||||
PadOperators: false
|
||||
AlignConsecutiveMacros:
|
||||
Enabled: true
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: true
|
||||
AlignCompound: false
|
||||
PadOperators: false
|
||||
AlignEscapedNewlines: Right
|
||||
AlignOperands: Align
|
||||
AlignTrailingComments: true
|
||||
AllowAllArgumentsOnNextLine: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortEnumsOnASingleLine: true
|
||||
AllowShortBlocksOnASingleLine: Never
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: All
|
||||
AllowShortLambdasOnASingleLine: All
|
||||
AllowShortIfStatementsOnASingleLine: Never
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AlwaysBreakTemplateDeclarations: MultiLine
|
||||
AttributeMacros:
|
||||
- __capability
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: false
|
||||
AfterClass: false
|
||||
AfterControlStatement: Never
|
||||
AfterEnum: false
|
||||
AfterFunction: false
|
||||
AfterNamespace: false
|
||||
AfterObjCDeclaration: false
|
||||
AfterStruct: false
|
||||
AfterUnion: false
|
||||
AfterExternBlock: false
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
BeforeLambdaBody: false
|
||||
BeforeWhile: false
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: true
|
||||
SplitEmptyRecord: true
|
||||
SplitEmptyNamespace: true
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeConceptDeclarations: Always
|
||||
BreakBeforeBraces: Attach
|
||||
BreakBeforeInheritanceComma: false
|
||||
BreakInheritanceList: BeforeColon
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
BreakConstructorInitializers: BeforeColon
|
||||
BreakAfterJavaFieldAnnotations: false
|
||||
BreakStringLiterals: true
|
||||
ColumnLimit: 120
|
||||
CommentPragmas: "^ IWYU pragma:"
|
||||
QualifierAlignment: Leave
|
||||
CompactNamespaces: false
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
Cpp11BracedListStyle: true
|
||||
DeriveLineEnding: true
|
||||
DerivePointerAlignment: false
|
||||
DisableFormat: false
|
||||
EmptyLineAfterAccessModifier: Never
|
||||
EmptyLineBeforeAccessModifier: LogicalBlock
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
PackConstructorInitializers: BinPack
|
||||
BasedOnStyle: ""
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
AllowAllConstructorInitializersOnNextLine: true
|
||||
FixNamespaceComments: true
|
||||
ForEachMacros:
|
||||
- foreach
|
||||
- Q_FOREACH
|
||||
- BOOST_FOREACH
|
||||
IfMacros:
|
||||
- KJ_IF_MAYBE
|
||||
IncludeBlocks: Preserve
|
||||
IncludeCategories:
|
||||
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
|
||||
Priority: 2
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
|
||||
Priority: 3
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
- Regex: ".*"
|
||||
Priority: 1
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
IncludeIsMainRegex: "(Test)?$"
|
||||
IncludeIsMainSourceRegex: ""
|
||||
IndentAccessModifiers: false
|
||||
IndentCaseLabels: false
|
||||
IndentCaseBlocks: false
|
||||
IndentGotoLabels: true
|
||||
IndentPPDirectives: None
|
||||
IndentExternBlock: AfterExternBlock
|
||||
IndentRequiresClause: true
|
||||
IndentWidth: 4
|
||||
IndentWrappedFunctionNames: false
|
||||
InsertBraces: false
|
||||
InsertTrailingCommas: None
|
||||
JavaScriptQuotes: Leave
|
||||
JavaScriptWrapImports: true
|
||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||
LambdaBodyIndentation: Signature
|
||||
MacroBlockBegin: ""
|
||||
MacroBlockEnd: ""
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: None
|
||||
ObjCBinPackProtocolList: Auto
|
||||
ObjCBlockIndentWidth: 2
|
||||
ObjCBreakBeforeNestedBlockParam: true
|
||||
ObjCSpaceAfterProperty: false
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PenaltyBreakAssignment: 2
|
||||
PenaltyBreakBeforeFirstCallParameter: 19
|
||||
PenaltyBreakComment: 300
|
||||
PenaltyBreakFirstLessLess: 120
|
||||
PenaltyBreakOpenParenthesis: 0
|
||||
PenaltyBreakString: 1000
|
||||
PenaltyBreakTemplateDeclaration: 10
|
||||
PenaltyExcessCharacter: 1000000
|
||||
PenaltyReturnTypeOnItsOwnLine: 60
|
||||
PenaltyIndentedWhitespace: 0
|
||||
PointerAlignment: Left
|
||||
PPIndentWidth: -1
|
||||
ReferenceAlignment: Pointer
|
||||
ReflowComments: true
|
||||
RemoveBracesLLVM: false
|
||||
RequiresClausePosition: OwnLine
|
||||
SeparateDefinitionBlocks: Leave
|
||||
ShortNamespaceLines: 1
|
||||
SortIncludes: CaseSensitive
|
||||
SortJavaStaticImport: Before
|
||||
SortUsingDeclarations: true
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterLogicalNot: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCaseColon: false
|
||||
SpaceBeforeCpp11BracedList: false
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceBeforeParensOptions:
|
||||
AfterControlStatements: true
|
||||
AfterForeachMacros: true
|
||||
AfterFunctionDefinitionName: false
|
||||
AfterFunctionDeclarationName: false
|
||||
AfterIfMacros: true
|
||||
AfterOverloadedOperator: false
|
||||
AfterRequiresInClause: false
|
||||
AfterRequiresInExpression: false
|
||||
BeforeNonEmptyParentheses: false
|
||||
SpaceAroundPointerQualifiers: Default
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceInEmptyBlock: false
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesInAngles: Never
|
||||
SpacesInConditionalStatement: false
|
||||
SpacesInContainerLiterals: true
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInLineCommentPrefix:
|
||||
Minimum: 1
|
||||
Maximum: -1
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
SpaceBeforeSquareBrackets: false
|
||||
BitFieldColonSpacing: Both
|
||||
Standard: Latest
|
||||
StatementAttributeLikeMacros:
|
||||
- Q_EMIT
|
||||
StatementMacros:
|
||||
- Q_UNUSED
|
||||
- QT_REQUIRE_VERSION
|
||||
TabWidth: 8
|
||||
UseCRLF: false
|
||||
UseTab: Never
|
||||
WhitespaceSensitiveMacros:
|
||||
- STRINGIZE
|
||||
- PP_STRINGIZE
|
||||
- BOOST_PP_STRINGIZE
|
||||
- NS_SWIFT_NAME
|
||||
- CF_SWIFT_NAME
|
||||
---
|
||||
|
@ -1,9 +0,0 @@
|
||||
/.vscode/
|
||||
/build/
|
||||
/Debug
|
||||
/.cache/
|
||||
.clangd
|
||||
compile_commands.json
|
||||
STM32Make.make
|
||||
/.metadata/
|
||||
openocd.cfg
|
File diff suppressed because it is too large
Load Diff
@ -1,46 +0,0 @@
|
||||
/*
|
||||
* ADBMS_Abstraction.h
|
||||
*
|
||||
* Created on: 14.07.2022
|
||||
* Author: max
|
||||
*/
|
||||
|
||||
#ifndef INC_ADBMS_ABSTRACTION_H_
|
||||
#define INC_ADBMS_ABSTRACTION_H_
|
||||
|
||||
#include "ADBMS_Driver.h"
|
||||
|
||||
#define mV_from_ADBMS6830(x) (((((int16_t)(x))) * 0.150) + 1500)
|
||||
|
||||
// Macro to convert voltage in mV to the internal format for under/over voltage thresholds.
|
||||
// Calculation: internal = (mV - 1500) / (16 * 0.15) = (mV - 1500) / 2.4.
|
||||
// To perform integer arithmetic with rounding, we compute:
|
||||
// internal = ((mV - 1500) * 5 + 6) / 12
|
||||
#define mV_to_ADBMS6830(mV) ((((mV) - 1500) * 5 + 6) / 12)
|
||||
|
||||
HAL_StatusTypeDef amsReset();
|
||||
|
||||
HAL_StatusTypeDef initAMS(SPI_HandleTypeDef* hspi);
|
||||
HAL_StatusTypeDef amsWakeUp();
|
||||
|
||||
HAL_StatusTypeDef amsCellMeasurement(Cell_Module (*module)[N_BMS]);
|
||||
|
||||
HAL_StatusTypeDef amsAuxAndStatusMeasurement(Cell_Module (*module)[N_BMS]);
|
||||
|
||||
HAL_StatusTypeDef amsConfigBalancing(const uint32_t channels[static N_BMS], uint8_t dutyCycle);
|
||||
HAL_StatusTypeDef amsStartBalancing();
|
||||
HAL_StatusTypeDef amsStopBalancing();
|
||||
|
||||
HAL_StatusTypeDef amsSelfTest();
|
||||
|
||||
HAL_StatusTypeDef amsConfigOverUnderVoltage(uint16_t overVoltage, uint16_t underVoltage); //arguments in mV
|
||||
|
||||
HAL_StatusTypeDef amsCheckUnderOverVoltage(Cell_Module (*module)[N_BMS]);
|
||||
|
||||
HAL_StatusTypeDef amsClearFlag();
|
||||
HAL_StatusTypeDef amsClearAux();
|
||||
HAL_StatusTypeDef amsClearOVUV();
|
||||
|
||||
HAL_StatusTypeDef amsReadCellVoltages(Cell_Module (*module)[N_BMS]);
|
||||
|
||||
#endif /* INC_ADBMS_ABSTRACTION_H_ */
|
@ -0,0 +1,40 @@
|
||||
#ifndef INC_ADBMS_ABSTRACTION_H_
|
||||
#define INC_ADBMS_ABSTRACTION_H_
|
||||
|
||||
#include "ADBMS_Driver.h"
|
||||
#include "ADBMS_Intern.h"
|
||||
|
||||
#define mV_from_ADBMS6830(x) (((((int16_t)(x))) * 0.150) + 1500)
|
||||
|
||||
// Macro to convert voltage in mV to the internal format for under/over voltage thresholds.
|
||||
// Calculation: internal = (mV - 1500) / (16 * 0.15) = (mV - 1500) / 2.4.
|
||||
// To perform integer arithmetic with rounding, we compute:
|
||||
// internal = ((mV - 1500) * 5 + 6) / 12
|
||||
#define mV_to_ADBMS6830(mV) ((((mV) - 1500) * 5 + 6) / 12)
|
||||
|
||||
ADBMS_Internal_Status amsReset();
|
||||
|
||||
ADBMS_Internal_Status initAMS(USER_PTR_TYPE ptr);
|
||||
ADBMS_Internal_Status amsWakeUp();
|
||||
|
||||
ADBMS_Internal_Status amsCellMeasurement(BMS_Chip (*module)[N_BMS]);
|
||||
|
||||
ADBMS_Internal_Status amsAuxAndStatusMeasurement(BMS_Chip (*module)[N_BMS]);
|
||||
|
||||
ADBMS_Internal_Status amsConfigBalancing(const uint32_t channels[static N_BMS], uint8_t dutyCycle);
|
||||
ADBMS_Internal_Status amsStartBalancing();
|
||||
ADBMS_Internal_Status amsStopBalancing();
|
||||
|
||||
ADBMS_Internal_Status amsSelfTest();
|
||||
|
||||
ADBMS_Internal_Status amsConfigOverUnderVoltage(uint16_t overVoltage, uint16_t underVoltage); //arguments in mV
|
||||
|
||||
ADBMS_Internal_Status amsCheckUnderOverVoltage(BMS_Chip (*module)[N_BMS]);
|
||||
|
||||
ADBMS_Internal_Status amsClearFlag();
|
||||
ADBMS_Internal_Status amsClearAux();
|
||||
ADBMS_Internal_Status amsClearOVUV();
|
||||
|
||||
ADBMS_Internal_Status amsReadCellVoltages(BMS_Chip (*module)[N_BMS]);
|
||||
|
||||
#endif /* INC_ADBMS_ABSTRACTION_H_ */
|
@ -1,10 +1,3 @@
|
||||
/*
|
||||
* ADBMS_CMD_MAKROS.h
|
||||
*
|
||||
* Created on: 14.07.2022
|
||||
* Author: max
|
||||
*/
|
||||
|
||||
#ifndef INC_ADBMS_CMD_MAKROS_H_
|
||||
#define INC_ADBMS_CMD_MAKROS_H_
|
||||
|
@ -1,11 +1,10 @@
|
||||
#ifndef ADBMS_DRIVER_H
|
||||
#define ADBMS_DRIVER_H
|
||||
|
||||
#include "ADBMS_Intern.h"
|
||||
#include "config_ADBMS6830.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#define ERROR_TIME_THRESH 150 // ms
|
||||
|
||||
typedef enum : uint16_t {
|
||||
ADBMS_NO_ERROR = 0,
|
||||
ADBMS_OVERTEMP,
|
||||
@ -87,11 +86,11 @@ typedef struct {
|
||||
|
||||
int16_t cellVoltages[MAXIMUM_CELL_VOLTAGES];
|
||||
int16_t auxVoltages[MAXIMUM_AUX_VOLTAGES];
|
||||
} Cell_Module;
|
||||
} BMS_Chip;
|
||||
|
||||
extern Cell_Module modules[N_BMS];
|
||||
extern BMS_Chip bms_data[N_BMS];
|
||||
|
||||
[[gnu::nonnull]] ADBMS_DetailedStatus AMS_Init(SPI_HandleTypeDef* hspi);
|
||||
[[gnu::nonnull]] ADBMS_DetailedStatus AMS_Init(USER_PTR_TYPE ptr);
|
||||
|
||||
ADBMS_DetailedStatus AMS_Idle_Loop();
|
||||
|
@ -0,0 +1,83 @@
|
||||
#pragma once
|
||||
#ifndef __ADBMS_INTERN_H
|
||||
#define __ADBMS_INTERN_H
|
||||
#include <stdint.h>
|
||||
|
||||
#include "stm32h7xx_hal.h"
|
||||
#include "main.h"
|
||||
|
||||
typedef enum {
|
||||
ADBMS_OK = 0,
|
||||
ADBMS_ERROR,
|
||||
ADBMS_BUSY,
|
||||
ADBMS_TIMEOUT,
|
||||
} ADBMS_Internal_Status;
|
||||
|
||||
[[maybe_unused, gnu::always_inline]]
|
||||
static inline void mcuAdbmsCSLow() {
|
||||
HAL_GPIO_WritePin(AMS_CS_GPIO_Port, AMS_CS_Pin, GPIO_PIN_RESET);
|
||||
}
|
||||
|
||||
[[maybe_unused, gnu::always_inline]]
|
||||
static inline void mcuAdbmsCSHigh() {
|
||||
HAL_GPIO_WritePin(AMS_CS_GPIO_Port, AMS_CS_Pin, GPIO_PIN_SET);
|
||||
}
|
||||
|
||||
// user_ptr is passed to AMS_Init and could be used to pass the SPI handle
|
||||
// the BMS code does not modify it in any way
|
||||
#define USER_PTR_TYPE SPI_HandleTypeDef*
|
||||
|
||||
[[maybe_unused, gnu::always_inline, gnu::access(read_only, 2, 3), gnu::nonnull(1)]]
|
||||
static inline ADBMS_Internal_Status mcuSPITransmit(USER_PTR_TYPE user_ptr, const uint8_t* buffer, uint8_t buffersize, uint32_t timeout) {
|
||||
const HAL_StatusTypeDef status = HAL_SPI_Transmit(user_ptr, buffer, buffersize, timeout);
|
||||
return (status == HAL_OK) ? ADBMS_OK : ADBMS_ERROR;
|
||||
}
|
||||
|
||||
[[maybe_unused, gnu::always_inline, gnu::access(read_write, 2, 3), gnu::nonnull(1)]]
|
||||
static inline ADBMS_Internal_Status mcuSPIReceive(USER_PTR_TYPE user_ptr, uint8_t* buffer, uint8_t buffersize, uint32_t timeout) {
|
||||
const HAL_StatusTypeDef status = HAL_SPI_Receive(user_ptr, buffer, buffersize, timeout);
|
||||
return (status == HAL_OK) ? ADBMS_OK : ADBMS_ERROR;
|
||||
}
|
||||
|
||||
[[maybe_unused, gnu::always_inline, gnu::access(read_write, 2, 4), gnu::access(read_only, 3, 4), gnu::nonnull(1, 2)]]
|
||||
static inline ADBMS_Internal_Status mcuSPITransmitReceive(USER_PTR_TYPE user_ptr, uint8_t* rxbuffer, const uint8_t* txbuffer, uint8_t buffersize, uint32_t timeout) {
|
||||
const HAL_StatusTypeDef status = HAL_SPI_TransmitReceive(user_ptr, txbuffer, rxbuffer, buffersize, timeout);
|
||||
return (status == HAL_OK) ? ADBMS_OK : ADBMS_ERROR;
|
||||
}
|
||||
|
||||
// Delay by `delay` milliseconds
|
||||
[[maybe_unused, gnu::always_inline]]
|
||||
static inline void mcuDelay(uint32_t delay) {
|
||||
HAL_Delay(delay);
|
||||
}
|
||||
|
||||
// Optional logging, leave as is if not needed
|
||||
|
||||
#ifndef ADBMS_NO_LOGGING_DEFS
|
||||
|
||||
#define LOG_PREFIX "[ADBMS] "
|
||||
#include "log.h"
|
||||
|
||||
enum {
|
||||
ADBMS_LOG_LEVEL_FATAL = 0,
|
||||
ADBMS_LOG_LEVEL_ERROR,
|
||||
ADBMS_LOG_LEVEL_WARNING,
|
||||
ADBMS_LOG_LEVEL_INFO,
|
||||
ADBMS_LOG_LEVEL_DEBUG,
|
||||
ADBMS_LOG_LEVEL_NOISY,
|
||||
};
|
||||
|
||||
// Returns a timestamp in milliseconds
|
||||
// Only used for logging
|
||||
[[maybe_unused, gnu::always_inline]]
|
||||
static inline uint32_t mcuGetTime() {
|
||||
return HAL_GetTick();
|
||||
}
|
||||
|
||||
#define debug_log(level, ...) log_message((log_level_t)level, __VA_ARGS__)
|
||||
#define debug_log_cont(level, ...) log_message_cont((log_level_t)level, __VA_ARGS__)
|
||||
#define DEBUG_CHANNEL_ENABLED(level) log_is_level_enabled((log_level_t)level)
|
||||
|
||||
#endif // ADBMS_NO_LOGGING_DEFS
|
||||
|
||||
#endif // __ADBMS_INTERN_H
|
@ -1,19 +1,11 @@
|
||||
/*
|
||||
* ADBMS_LL_Driver.h
|
||||
*
|
||||
* Created on: 05.06.2022
|
||||
* Author: max
|
||||
*/
|
||||
|
||||
#ifndef ADBMS_LL_DRIVER_H_
|
||||
#define ADBMS_LL_DRIVER_H_
|
||||
|
||||
#include "config_ADBMS6830.h"
|
||||
#include "ADBMS_Intern.h"
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
uint8_t adbmsDriverInit(SPI_HandleTypeDef* hspi);
|
||||
|
||||
//2 command + 2 PEC + (data + 2 DPEC) per BMS
|
||||
#define CMD_BUFFER_SIZE(datalen) (4 + (N_BMS * ((datalen) + 2)))
|
||||
|
||||
@ -22,32 +14,31 @@ uint8_t adbmsDriverInit(SPI_HandleTypeDef* hspi);
|
||||
#define CMD_EMPTY_BUFFER ((uint8_t[CMD_BUFFER_SIZE(0)]){0})
|
||||
#define CMD_EMPTY_BUFFER_SIZE CMD_BUFFER_SIZE(0)
|
||||
|
||||
HAL_StatusTypeDef ___writeCMD(uint16_t command, uint8_t * args, size_t arglen);
|
||||
ADBMS_Internal_Status ___writeCMD(uint16_t command, uint8_t * args, size_t arglen);
|
||||
|
||||
[[gnu::access(read_write, 2, 4), gnu::nonnull(2), gnu::always_inline]] //add dummy size variable for bounds checking, should be optimized out
|
||||
static inline HAL_StatusTypeDef __writeCMD(uint16_t command, uint8_t * args, size_t arglen, size_t) {
|
||||
static inline ADBMS_Internal_Status __writeCMD(uint16_t command, uint8_t * args, size_t arglen, size_t) {
|
||||
return ___writeCMD(command, args, arglen);
|
||||
}
|
||||
|
||||
#define writeCMD(command, args, arglen) \
|
||||
__writeCMD(command, args, arglen, CMD_BUFFER_SIZE(arglen))
|
||||
|
||||
HAL_StatusTypeDef ___readCMD(uint16_t command, uint8_t * buffer, size_t arglen);
|
||||
ADBMS_Internal_Status ___readCMD(uint16_t command, uint8_t * buffer, size_t arglen);
|
||||
|
||||
[[gnu::access(read_write, 2, 4), gnu::nonnull(2), gnu::always_inline]] //add dummy size variable for bounds checking, should be optimized out
|
||||
static inline HAL_StatusTypeDef __readCMD(uint16_t command, uint8_t * buffer, size_t arglen, size_t) {
|
||||
static inline ADBMS_Internal_Status __readCMD(uint16_t command, uint8_t * buffer, size_t arglen, size_t) {
|
||||
return ___readCMD(command, buffer, arglen);
|
||||
}
|
||||
|
||||
#define readCMD(command, buffer, buflen) \
|
||||
__readCMD(command, buffer, buflen, CMD_BUFFER_SIZE(buflen))
|
||||
|
||||
HAL_StatusTypeDef __pollCMD(uint16_t command, uint8_t waitTime);
|
||||
ADBMS_Internal_Status __pollCMD(uint16_t command, uint8_t waitTime);
|
||||
|
||||
#define pollCMD(command) \
|
||||
__pollCMD(command, (N_BMS * 2) + 1) //poll is only valid after 2 * N_BMS clock cycles, +1 for safety, see datasheet page 55
|
||||
|
||||
uint8_t wakeUpCmd();
|
||||
static inline void mcuDelay(uint16_t delay) { HAL_Delay(delay); };
|
||||
void initSPI(USER_PTR_TYPE ptr);
|
||||
|
||||
#endif /* ADBMS_LL_DRIVER_H_ */
|
@ -1,40 +1,32 @@
|
||||
/*
|
||||
* ADBMS_Abstraction.c
|
||||
*
|
||||
* Created on: 14.07.2022
|
||||
* Author: max
|
||||
*/
|
||||
#include "ADBMS_Abstraction.h"
|
||||
#include "ADBMS_CMD_MAKROS.h"
|
||||
#include "ADBMS_CMD_Defines.h"
|
||||
#include "ADBMS_LL_Driver.h"
|
||||
#include "config_ADBMS6830.h"
|
||||
#include "ADBMS_Intern.h"
|
||||
#include <stddef.h>
|
||||
#include "NTC.h"
|
||||
|
||||
#define SWO_LOG_PREFIX "[ADBMS] "
|
||||
#include "swo_log.h"
|
||||
|
||||
static const char* const HAL_Statuses[] = {"HAL_OK", "HAL_ERROR", "HAL_BUSY", "HAL_TIMEOUT"};
|
||||
static const char* const ADBMS_Statuses[] = {"ADBMS_OK", "ADBMS_ERROR", "ADBMS_BUSY", "ADBMS_TIMEOUT"};
|
||||
|
||||
#define CHECK_RETURN(x) \
|
||||
do { \
|
||||
HAL_StatusTypeDef status = x; \
|
||||
ADBMS_Internal_Status status = x; \
|
||||
if (status != 0) { \
|
||||
debug_log(LOG_LEVEL_ERROR, "in %s:%d@%s: %s failed with status %d (%s)", __FILE_NAME__, __LINE__, __func__,\
|
||||
debug_log(ADBMS_LOG_LEVEL_ERROR, "in %s:%d@%s: %s failed with status %d (%s)", __FILE_NAME__, __LINE__, __func__,\
|
||||
#x, status, \
|
||||
(status < (sizeof(HAL_Statuses) / sizeof(HAL_Statuses[0]))) ? HAL_Statuses[status] : "Unknown"); \
|
||||
(status < (sizeof(ADBMS_Statuses) / sizeof(ADBMS_Statuses[0]))) ? ADBMS_Statuses[status] : "Unknown"); \
|
||||
return status; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
HAL_StatusTypeDef amsReset() {
|
||||
ADBMS_Internal_Status amsReset() {
|
||||
amsWakeUp();
|
||||
readCMD(SRST, CMD_EMPTY_BUFFER, 0);
|
||||
|
||||
amsWakeUp();
|
||||
|
||||
uint8_t sidbuffer[CMD_BUFFER_SIZE(SID_GROUP_SIZE)] = {};
|
||||
if (readCMD(RDSID, sidbuffer, SID_GROUP_SIZE) != HAL_OK) {
|
||||
if (readCMD(RDSID, sidbuffer, SID_GROUP_SIZE) != ADBMS_OK) {
|
||||
bool nonzero = false;
|
||||
for (size_t i = 0; i < N_BMS; i++) {
|
||||
if (sidbuffer[BUFFER_BMS_OFFSET(i, SID_GROUP_SIZE)] != 0) {
|
||||
@ -42,27 +34,27 @@ HAL_StatusTypeDef amsReset() {
|
||||
}
|
||||
}
|
||||
if (nonzero) {
|
||||
debug_log(LOG_LEVEL_ERROR,
|
||||
debug_log(ADBMS_LOG_LEVEL_ERROR,
|
||||
"CRC failure when reading BMS IDs, but some IDs are nonzero. --- Intermittent connection?");
|
||||
} else {
|
||||
debug_log(LOG_LEVEL_ERROR, "CRC failure when reading BMS IDs, all IDs are zero. --- No connection?");
|
||||
debug_log(ADBMS_LOG_LEVEL_ERROR, "CRC failure when reading BMS IDs, all IDs are zero. --- No connection?");
|
||||
}
|
||||
return HAL_ERROR;
|
||||
return ADBMS_ERROR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
debug_log(LOG_LEVEL_INFO, "BMS reset complete");
|
||||
debug_log(ADBMS_LOG_LEVEL_INFO, "BMS reset complete");
|
||||
|
||||
if (DEBUG_CHANNEL_ENABLED(LOG_LEVEL_INFO)) {
|
||||
debug_log(LOG_LEVEL_INFO, "Found IDs: ");
|
||||
if (DEBUG_CHANNEL_ENABLED(ADBMS_LOG_LEVEL_INFO)) {
|
||||
debug_log(ADBMS_LOG_LEVEL_INFO, "Found IDs: ");
|
||||
for (size_t i = 0; i < N_BMS; i++) {
|
||||
uint64_t id = 0;
|
||||
for (size_t j = 0; j < SID_GROUP_SIZE; j++) {
|
||||
id |= sidbuffer[BUFFER_BMS_OFFSET(i, SID_GROUP_SIZE) + j] << (j * 8);
|
||||
}
|
||||
modules[i].bmsID = id;
|
||||
debug_log_cont(LOG_LEVEL_INFO, "0x%08lx%08lx ", (uint32_t)(id >> 32), (uint32_t)(id & 0xFFFFFFFF)); //newlib does not support %llu
|
||||
bms_data[i].bmsID = id;
|
||||
debug_log_cont(ADBMS_LOG_LEVEL_INFO, "0x%08lx%08lx ", (uint32_t)(id >> 32), (uint32_t)(id & 0xFFFFFFFF)); //newlib does not support %llu
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,27 +71,25 @@ HAL_StatusTypeDef amsReset() {
|
||||
CHECK_RETURN(writeCMD(ADCV | ADCV_CONT | ADCV_RD, flagsbuffer, 0)); //start continuous cell voltage measurement with redundancy
|
||||
CHECK_RETURN(writeCMD(ADAX | ADAX_CONV_ALL, flagsbuffer, 0)); //start aux measurement
|
||||
|
||||
return HAL_OK;
|
||||
return ADBMS_OK;
|
||||
}
|
||||
|
||||
HAL_StatusTypeDef initAMS(SPI_HandleTypeDef* hspi) {
|
||||
// pull MSTR High for Microcontroller mode ADBMS6822
|
||||
HAL_GPIO_WritePin(MSTR1_GPIO_Port, MSTR1_Pin, GPIO_PIN_SET);
|
||||
adbmsDriverInit(hspi);
|
||||
ADBMS_Internal_Status initAMS(USER_PTR_TYPE ptr) {
|
||||
initSPI(ptr); // Initialize the SPI interface
|
||||
return amsReset();
|
||||
}
|
||||
|
||||
HAL_StatusTypeDef amsWakeUp() {
|
||||
ADBMS_Internal_Status amsWakeUp() {
|
||||
return __pollCMD(PLADC, 100); //wake up ADBMS6830, wait for T_wake = 200 us (100 cycles at 500 kHz)
|
||||
}
|
||||
|
||||
HAL_StatusTypeDef amsCellMeasurement(Cell_Module (*module)[N_BMS]) {
|
||||
ADBMS_Internal_Status amsCellMeasurement(BMS_Chip (*module)[N_BMS]) {
|
||||
#warning check conversion counter to ensure that continuous conversion has not been stopped
|
||||
#warning check for OW conditions: ADSV | ADSV_OW_0 / ADSV_OW_1
|
||||
return amsReadCellVoltages(module);
|
||||
}
|
||||
|
||||
HAL_StatusTypeDef amsAuxAndStatusMeasurement(Cell_Module (*module)[N_BMS]) {
|
||||
ADBMS_Internal_Status amsAuxAndStatusMeasurement(BMS_Chip (*module)[N_BMS]) {
|
||||
uint8_t rxbuf[CMD_BUFFER_SIZE(STATUS_GROUP_C_SIZE)] = {};
|
||||
CHECK_RETURN(readCMD(RDSTATC, rxbuf, STATUS_GROUP_C_SIZE));
|
||||
for (size_t i = 0; i < N_BMS; i++) {
|
||||
@ -124,8 +114,8 @@ HAL_StatusTypeDef amsAuxAndStatusMeasurement(Cell_Module (*module)[N_BMS]) {
|
||||
(*module)[i].status.OSCCHK = (rxbuf[offset + 5] >> 0) & 0x01;
|
||||
}
|
||||
|
||||
if (pollCMD(PLAUX) == HAL_BUSY) {
|
||||
// return HAL_BUSY;
|
||||
if (pollCMD(PLAUX) == ADBMS_BUSY) {
|
||||
// return ADBMS_BUSY;
|
||||
}
|
||||
|
||||
constexpr size_t auxGroupSizes[] = {AUX_GROUP_A_SIZE, AUX_GROUP_B_SIZE, AUX_GROUP_C_SIZE, AUX_GROUP_D_SIZE};
|
||||
@ -160,12 +150,12 @@ HAL_StatusTypeDef amsAuxAndStatusMeasurement(Cell_Module (*module)[N_BMS]) {
|
||||
|
||||
CHECK_RETURN(writeCMD(ADAX | ADAX_CONV_ALL, rxbuf, 0)); // start aux conversion for next iteration
|
||||
|
||||
return HAL_OK;
|
||||
return ADBMS_OK;
|
||||
}
|
||||
|
||||
HAL_StatusTypeDef amsConfigBalancing(const uint32_t channels[static N_BMS], uint8_t dutyCycle) {
|
||||
ADBMS_Internal_Status amsConfigBalancing(const uint32_t channels[static N_BMS], uint8_t dutyCycle) {
|
||||
if (dutyCycle > 0x0F) {
|
||||
return HAL_ERROR; // only 4 bits are allowed for dutyCycle
|
||||
return ADBMS_ERROR; // only 4 bits are allowed for dutyCycle
|
||||
}
|
||||
|
||||
uint8_t rxbufA[CMD_BUFFER_SIZE(PWM_GROUP_A_SIZE)] = {};
|
||||
@ -191,13 +181,13 @@ HAL_StatusTypeDef amsConfigBalancing(const uint32_t channels[static N_BMS], uint
|
||||
|
||||
// log the new PWM settings
|
||||
// output is: PWM - [BMS_ID]: [PWM0] ... [PWM16]
|
||||
if (DEBUG_CHANNEL_ENABLED(LOG_LEVEL_DEBUG)) {
|
||||
debug_log(LOG_LEVEL_DEBUG, "PWM - %d: ", i);
|
||||
if (DEBUG_CHANNEL_ENABLED(ADBMS_LOG_LEVEL_DEBUG)) {
|
||||
debug_log(ADBMS_LOG_LEVEL_DEBUG, "PWM - %d: ", i);
|
||||
for (size_t j = 0; j < 6; j++) {
|
||||
debug_log_cont(LOG_LEVEL_DEBUG, "%x %x ", rxbufA[offsetA + j] & 0x0F, rxbufA[offsetA + j] >> 4);
|
||||
debug_log_cont(ADBMS_LOG_LEVEL_DEBUG, "%x %x ", rxbufA[offsetA + j] & 0x0F, rxbufA[offsetA + j] >> 4);
|
||||
}
|
||||
for (size_t j = 0; j < 2; j++) {
|
||||
debug_log_cont(LOG_LEVEL_DEBUG, "%x %x ", rxbufB[offsetB + j] & 0x0F, rxbufB[offsetB + j] >> 4);
|
||||
debug_log_cont(ADBMS_LOG_LEVEL_DEBUG, "%x %x ", rxbufB[offsetB + j] & 0x0F, rxbufB[offsetB + j] >> 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -205,19 +195,19 @@ HAL_StatusTypeDef amsConfigBalancing(const uint32_t channels[static N_BMS], uint
|
||||
CHECK_RETURN(writeCMD(WRPWMA, rxbufA, PWM_GROUP_A_SIZE));
|
||||
CHECK_RETURN(writeCMD(WRPWMB, rxbufB, PWM_GROUP_B_SIZE));
|
||||
|
||||
return HAL_OK;
|
||||
return ADBMS_OK;
|
||||
}
|
||||
|
||||
HAL_StatusTypeDef amsStartBalancing() { return writeCMD(UNMUTE, CMD_EMPTY_BUFFER, 0); }
|
||||
ADBMS_Internal_Status amsStartBalancing() { return writeCMD(UNMUTE, CMD_EMPTY_BUFFER, 0); }
|
||||
|
||||
HAL_StatusTypeDef amsStopBalancing() { return writeCMD(MUTE, CMD_EMPTY_BUFFER, 0); }
|
||||
ADBMS_Internal_Status amsStopBalancing() { return writeCMD(MUTE, CMD_EMPTY_BUFFER, 0); }
|
||||
|
||||
HAL_StatusTypeDef amsSelfTest() { return 0; }
|
||||
ADBMS_Internal_Status amsSelfTest() { return 0; }
|
||||
|
||||
HAL_StatusTypeDef amsConfigOverUnderVoltage(uint16_t overVoltage, uint16_t underVoltage) {
|
||||
ADBMS_Internal_Status amsConfigOverUnderVoltage(uint16_t overVoltage, uint16_t underVoltage) {
|
||||
uint8_t buffer[CMD_BUFFER_SIZE(CFG_GROUP_B_SIZE)] = {};
|
||||
|
||||
debug_log(LOG_LEVEL_INFO, "Configuring OV/UV thresholds to %u mV (%u internal) / %u mV (%u internal)", overVoltage,
|
||||
debug_log(ADBMS_LOG_LEVEL_INFO, "Configuring OV/UV thresholds to %u mV (%u internal) / %u mV (%u internal)", overVoltage,
|
||||
mV_to_ADBMS6830(overVoltage), underVoltage, mV_to_ADBMS6830(underVoltage));
|
||||
|
||||
CHECK_RETURN(readCMD(RDCFGB, buffer, CFG_GROUP_B_SIZE));
|
||||
@ -226,8 +216,8 @@ HAL_StatusTypeDef amsConfigOverUnderVoltage(uint16_t overVoltage, uint16_t under
|
||||
underVoltage = mV_to_ADBMS6830(underVoltage);
|
||||
|
||||
if (underVoltage & 0xF000 || overVoltage & 0xF000) { // only 12 bits allowed
|
||||
debug_log(LOG_LEVEL_ERROR, "Invalid OV/UV thresholds: %u mV / %u mV", overVoltage, underVoltage);
|
||||
return HAL_ERROR;
|
||||
debug_log(ADBMS_LOG_LEVEL_ERROR, "Invalid OV/UV thresholds: %u mV / %u mV", overVoltage, underVoltage);
|
||||
return ADBMS_ERROR;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < N_BMS; i++) {
|
||||
@ -247,7 +237,7 @@ HAL_StatusTypeDef amsConfigOverUnderVoltage(uint16_t overVoltage, uint16_t under
|
||||
return writeCMD(WRCFGB, buffer, CFG_GROUP_B_SIZE);
|
||||
}
|
||||
|
||||
HAL_StatusTypeDef amsCheckUnderOverVoltage(Cell_Module (*module)[N_BMS]) {
|
||||
ADBMS_Internal_Status amsCheckUnderOverVoltage(BMS_Chip (*module)[N_BMS]) {
|
||||
uint8_t regbuffer[CMD_BUFFER_SIZE(STATUS_GROUP_D_SIZE)] = {};
|
||||
|
||||
CHECK_RETURN(readCMD(RDSTATD, regbuffer, STATUS_GROUP_D_SIZE));
|
||||
@ -272,19 +262,19 @@ HAL_StatusTypeDef amsCheckUnderOverVoltage(Cell_Module (*module)[N_BMS]) {
|
||||
return writeCMD(CLOVUV, buffer, STATUS_GROUP_D_SIZE); // flags are latched, so we need to clear them
|
||||
}
|
||||
|
||||
HAL_StatusTypeDef amsClearFlag() {
|
||||
ADBMS_Internal_Status amsClearFlag() {
|
||||
uint8_t buffer[CMD_BUFFER_SIZE(6)] = {[0 ... CMD_BUFFER_SIZE(6) - 1] = 0xFF};
|
||||
return writeCMD(CLRFLAG, buffer, 6);
|
||||
}
|
||||
|
||||
HAL_StatusTypeDef amsClearAux() { return writeCMD(CLRAUX, CMD_EMPTY_BUFFER, 0); }
|
||||
ADBMS_Internal_Status amsClearAux() { return writeCMD(CLRAUX, CMD_EMPTY_BUFFER, 0); }
|
||||
|
||||
HAL_StatusTypeDef amsClearOVUV() {
|
||||
ADBMS_Internal_Status amsClearOVUV() {
|
||||
uint8_t buffer[CMD_BUFFER_SIZE(6)] = {[0 ... CMD_BUFFER_SIZE(6) - 1] = 0xFF};
|
||||
return writeCMD(CLOVUV, buffer, 6);
|
||||
}
|
||||
|
||||
HAL_StatusTypeDef amsReadCellVoltages(Cell_Module (*module)[N_BMS]) {
|
||||
ADBMS_Internal_Status amsReadCellVoltages(BMS_Chip (*module)[N_BMS]) {
|
||||
uint8_t rxbuffer[CMD_BUFFER_SIZE(CV_GROUP_A_SIZE)] = {};
|
||||
|
||||
CHECK_RETURN(readCMD(RDCVA, rxbuffer, CV_GROUP_A_SIZE));
|
||||
@ -333,12 +323,12 @@ HAL_StatusTypeDef amsReadCellVoltages(Cell_Module (*module)[N_BMS]) {
|
||||
(*module)[i].cellVoltages[15] = mV_from_ADBMS6830(rxbuffer[offset + 0] | (rxbuffer[offset + 1] << 8));
|
||||
}
|
||||
|
||||
return HAL_OK;
|
||||
return ADBMS_OK;
|
||||
}
|
||||
|
||||
// Each selected BMS must have a corresponding address, and the data array for that BMS must be at least datalens[i]
|
||||
// bytes long
|
||||
HAL_StatusTypeDef amsSendI2C(const uint8_t addresses[static N_BMS], uint8_t* data[static N_BMS],
|
||||
ADBMS_Internal_Status amsSendI2C(const uint8_t addresses[static N_BMS], uint8_t* data[static N_BMS],
|
||||
const uint8_t datalens[static N_BMS], uint32_t bms_mask) {
|
||||
uint8_t buffer[CMD_BUFFER_SIZE(COMM_GROUP_SIZE)] = {};
|
||||
|
||||
@ -409,12 +399,12 @@ HAL_StatusTypeDef amsSendI2C(const uint8_t addresses[static N_BMS], uint8_t* dat
|
||||
CHECK_RETURN(writeCMD(WRCOMM, buffer, COMM_GROUP_SIZE));
|
||||
__pollCMD(STCOMM, 72);
|
||||
|
||||
return HAL_OK;
|
||||
return ADBMS_OK;
|
||||
}
|
||||
|
||||
// Each selected BMS must have a corresponding address, and the data array for that BMS must be at least datalens[i]
|
||||
// bytes long
|
||||
HAL_StatusTypeDef amsReadI2C(const uint8_t addresses[static N_BMS], uint8_t* data[static N_BMS],
|
||||
ADBMS_Internal_Status amsReadI2C(const uint8_t addresses[static N_BMS], uint8_t* data[static N_BMS],
|
||||
const uint8_t datalens[static N_BMS], uint32_t bms_mask) {
|
||||
uint8_t buffer[CMD_BUFFER_SIZE(COMM_GROUP_SIZE)] = {};
|
||||
|
||||
@ -518,5 +508,5 @@ HAL_StatusTypeDef amsReadI2C(const uint8_t addresses[static N_BMS], uint8_t* dat
|
||||
}
|
||||
}
|
||||
|
||||
return HAL_OK;
|
||||
return ADBMS_OK;
|
||||
}
|
@ -1,20 +1,12 @@
|
||||
/*
|
||||
* AMS_HighLevel.c
|
||||
*
|
||||
* Created on: 20.07.2022
|
||||
* Author: max
|
||||
*/
|
||||
#include "ADBMS_Abstraction.h"
|
||||
#include "ADBMS_Driver.h"
|
||||
#include "config_ADBMS6830.h"
|
||||
#include "stm32h7xx_hal.h"
|
||||
#include "ADBMS_Intern.h"
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#define SWO_LOG_PREFIX "[ADBMS] "
|
||||
#include "swo_log.h"
|
||||
|
||||
Cell_Module modules[N_BMS] = {};
|
||||
BMS_Chip bms_data[N_BMS] = {};
|
||||
|
||||
uint8_t packetChecksumFails = 0;
|
||||
#define MAX_PACKET_CHECKSUM_FAILS 5
|
||||
@ -31,15 +23,14 @@ struct pollingTimes pollingTimes = {0, 0};
|
||||
|
||||
static constexpr ADBMS_DetailedStatus NO_ERROR = {ADBMS_NO_ERROR};
|
||||
|
||||
ADBMS_DetailedStatus AMS_Init(SPI_HandleTypeDef* hspi) {
|
||||
debug_log(LOG_LEVEL_INFO, "ADBMS6830B HAL - configured for %d controllers and %d cells per controller...", N_BMS,
|
||||
ADBMS_DetailedStatus AMS_Init(USER_PTR_TYPE ptr) {
|
||||
debug_log(ADBMS_LOG_LEVEL_INFO, "ADBMS6830B HAL - configured for %d controllers and %d cells per controller...", N_BMS,
|
||||
N_CELLS);
|
||||
if (initAMS(hspi) != HAL_OK) {
|
||||
debug_log(LOG_LEVEL_ERROR, "ADBMS6830B HAL - initialization failed");
|
||||
if (initAMS(ptr) != ADBMS_OK) {
|
||||
debug_log(ADBMS_LOG_LEVEL_ERROR, "ADBMS6830B HAL - initialization failed");
|
||||
return (ADBMS_DetailedStatus){ADBMS_INTERNAL_BMS_FAULT, -1};
|
||||
}
|
||||
|
||||
pollingTimes = (struct pollingTimes){HAL_GetTick(), HAL_GetTick()};
|
||||
packetChecksumFails = 0;
|
||||
deviceSleeps = 0;
|
||||
return NO_ERROR;
|
||||
@ -49,7 +40,7 @@ ADBMS_DetailedStatus AMS_Init(SPI_HandleTypeDef* hspi) {
|
||||
({ \
|
||||
int first_match = -1; \
|
||||
for (size_t __any_intern_i = 0; __any_intern_i < N_BMS; __any_intern_i++) { \
|
||||
Cell_Module module = modules[__any_intern_i]; \
|
||||
BMS_Chip module = bms_data[__any_intern_i]; \
|
||||
if ((x)) { \
|
||||
first_match = __any_intern_i; \
|
||||
break; \
|
||||
@ -64,7 +55,7 @@ ADBMS_DetailedStatus AMS_Idle_Loop() {
|
||||
// set_error_source(ERROR_SOURCE_INTERNAL); //so we can't tell if we timed out
|
||||
}
|
||||
|
||||
packetChecksumFails += (amsAuxAndStatusMeasurement(&modules) == HAL_ERROR);
|
||||
packetChecksumFails += (amsAuxAndStatusMeasurement(&bms_data) == ADBMS_ERROR);
|
||||
|
||||
int match = 0;
|
||||
if ((match = any(module.status.SLEEP))) {
|
||||
@ -72,7 +63,7 @@ ADBMS_DetailedStatus AMS_Idle_Loop() {
|
||||
if (deviceSleeps > MAX_DEVICE_SLEEP) {
|
||||
return (ADBMS_DetailedStatus){ADBMS_INTERNAL_BMS_TIMEOUT, match - 1};
|
||||
} else {
|
||||
debug_log(LOG_LEVEL_WARNING, "BMS %d unexpectedly in sleep mode, resetting...", match - 1);
|
||||
debug_log(ADBMS_LOG_LEVEL_WARNING, "BMS %d unexpectedly in sleep mode, resetting...", match - 1);
|
||||
amsReset();
|
||||
}
|
||||
}
|
||||
@ -83,22 +74,22 @@ ADBMS_DetailedStatus AMS_Idle_Loop() {
|
||||
// iteration.
|
||||
amsClearFlag();
|
||||
// Log specific BMS fault details
|
||||
debug_log(LOG_LEVEL_ERROR, "Fault on BMS %d: ", match - 1);
|
||||
auto faultyModule = &modules[match - 1];
|
||||
if (faultyModule->status.CS_FLT) debug_log_cont(LOG_LEVEL_ERROR, "CS_FLT ");
|
||||
if (faultyModule->status.SPIFLT) debug_log_cont(LOG_LEVEL_ERROR, "SPIFLT ");
|
||||
if (faultyModule->status.CMED) debug_log_cont(LOG_LEVEL_ERROR, "CMED ");
|
||||
if (faultyModule->status.SMED) debug_log_cont(LOG_LEVEL_ERROR, "SMED ");
|
||||
if (faultyModule->status.VDE) debug_log_cont(LOG_LEVEL_ERROR, "VDE ");
|
||||
if (faultyModule->status.VDEL) debug_log_cont(LOG_LEVEL_ERROR, "VDEL ");
|
||||
if (faultyModule->status.OSCCHK) debug_log_cont(LOG_LEVEL_ERROR, "OSCCHK ");
|
||||
if (faultyModule->status.TMODCHK) debug_log_cont(LOG_LEVEL_ERROR, "TMODCHK ");
|
||||
debug_log(ADBMS_LOG_LEVEL_ERROR, "Fault on BMS %d: ", match - 1);
|
||||
auto faultyModule = &bms_data[match - 1];
|
||||
if (faultyModule->status.CS_FLT) debug_log_cont(ADBMS_LOG_LEVEL_ERROR, "CS_FLT ");
|
||||
if (faultyModule->status.SPIFLT) debug_log_cont(ADBMS_LOG_LEVEL_ERROR, "SPIFLT ");
|
||||
if (faultyModule->status.CMED) debug_log_cont(ADBMS_LOG_LEVEL_ERROR, "CMED ");
|
||||
if (faultyModule->status.SMED) debug_log_cont(ADBMS_LOG_LEVEL_ERROR, "SMED ");
|
||||
if (faultyModule->status.VDE) debug_log_cont(ADBMS_LOG_LEVEL_ERROR, "VDE ");
|
||||
if (faultyModule->status.VDEL) debug_log_cont(ADBMS_LOG_LEVEL_ERROR, "VDEL ");
|
||||
if (faultyModule->status.OSCCHK) debug_log_cont(ADBMS_LOG_LEVEL_ERROR, "OSCCHK ");
|
||||
if (faultyModule->status.TMODCHK) debug_log_cont(ADBMS_LOG_LEVEL_ERROR, "TMODCHK ");
|
||||
|
||||
return (ADBMS_DetailedStatus){ADBMS_INTERNAL_BMS_FAULT, match - 1};
|
||||
}
|
||||
|
||||
packetChecksumFails += (amsCellMeasurement(&modules) == HAL_ERROR);
|
||||
packetChecksumFails += (amsCheckUnderOverVoltage(&modules) == HAL_ERROR);
|
||||
packetChecksumFails += (amsCellMeasurement(&bms_data) == ADBMS_ERROR);
|
||||
packetChecksumFails += (amsCheckUnderOverVoltage(&bms_data) == ADBMS_ERROR);
|
||||
|
||||
if (packetChecksumFails > MAX_PACKET_CHECKSUM_FAILS) {
|
||||
return (ADBMS_DetailedStatus){ADBMS_INTERNAL_BMS_CHECKSUM_FAIL, -1};
|
@ -1,18 +1,19 @@
|
||||
/*
|
||||
* ADBMS_LL_Driver.c
|
||||
*
|
||||
* Created on: 05.06.2022
|
||||
* Author: max
|
||||
*/
|
||||
|
||||
#include "ADBMS_LL_Driver.h"
|
||||
#include "config_ADBMS6830.h"
|
||||
#include "stm32h7xx_hal.h"
|
||||
#include "ADBMS_Intern.h"
|
||||
#include <stdint.h>
|
||||
#include <strings.h>
|
||||
|
||||
#define SWO_LOG_PREFIX "[ADBMS] "
|
||||
#include "swo_log.h"
|
||||
#if __has_include("stm32h7xx_hal.h")
|
||||
#include "stm32h7xx_hal.h"
|
||||
#define STM32_HAL
|
||||
#endif
|
||||
|
||||
static USER_PTR_TYPE usr_ptr; // SPI_HandleTypeDef*
|
||||
|
||||
void initSPI(USER_PTR_TYPE ptr) {
|
||||
usr_ptr = ptr; // Store the user pointer for SPI communication
|
||||
}
|
||||
|
||||
#define INITIAL_COMMAND_PEC 0x0010
|
||||
#define INITIAL_DATA_PEC 0x0010
|
||||
@ -24,30 +25,6 @@
|
||||
#define CRC10_REMAINDER_MASK 0x200
|
||||
#define CRC10_RESULT_MASK 0x3FF
|
||||
|
||||
SPI_HandleTypeDef* adbmsspi;
|
||||
|
||||
uint8_t adbmsDriverInit(SPI_HandleTypeDef* hspi) {
|
||||
mcuAdbmsCSLow();
|
||||
HAL_Delay(1);
|
||||
mcuAdbmsCSHigh();
|
||||
adbmsspi = hspi;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static HAL_StatusTypeDef mcuSPITransmit(const uint8_t* buffer, uint8_t buffersize) {
|
||||
const HAL_StatusTypeDef status = HAL_SPI_Transmit(adbmsspi, buffer, buffersize, ADBMS_SPI_TIMEOUT);
|
||||
__HAL_SPI_CLEAR_OVRFLAG(adbmsspi);
|
||||
return status;
|
||||
}
|
||||
|
||||
static HAL_StatusTypeDef mcuSPIReceive(uint8_t* buffer, uint8_t buffersize) {
|
||||
return HAL_SPI_Receive(adbmsspi, buffer, buffersize, ADBMS_SPI_TIMEOUT);
|
||||
}
|
||||
|
||||
static HAL_StatusTypeDef mcuSPITransmitReceive(uint8_t* rxbuffer, const uint8_t* txbuffer, uint8_t buffersize) {
|
||||
return HAL_SPI_TransmitReceive(adbmsspi, txbuffer, rxbuffer, buffersize, ADBMS_SPI_TIMEOUT);
|
||||
}
|
||||
|
||||
//command PEC calculation
|
||||
//CRC-15
|
||||
//x^15 + x^14 + x^10 + x^8 + x^7 + x^4 + x^3 + 1
|
||||
@ -129,10 +106,12 @@ static uint8_t checkDataPEC(const uint8_t* data, uint8_t len) {
|
||||
return (computeCRC10(data, len, false) == 0) ? 0 : 1;
|
||||
}
|
||||
|
||||
static const char* const HAL_Statuses[] = {"HAL_OK", "HAL_ERROR", "HAL_BUSY", "HAL_TIMEOUT"};
|
||||
static const char* const ADBMS_Statuses[] = {"ADBMS_OK", "ADBMS_ERROR", "ADBMS_BUSY", "ADBMS_TIMEOUT"};
|
||||
|
||||
#ifdef STM32_HAL // feel free to replace this with your own SPI error handling
|
||||
static void print_spi_details() {
|
||||
const uint32_t spi_error = HAL_SPI_GetError(adbmsspi);
|
||||
extern SPI_HandleTypeDef * usr_ptr;
|
||||
const uint32_t spi_error = HAL_SPI_GetError(usr_ptr);
|
||||
|
||||
typedef struct {
|
||||
uint32_t mask;
|
||||
@ -161,24 +140,26 @@ static void print_spi_details() {
|
||||
constexpr size_t numErrors = sizeof(errors) / sizeof(errors[0]);
|
||||
for (size_t i = 0; i < numErrors; i++) {
|
||||
if (spi_error & errors[i].mask) {
|
||||
debug_log_cont(LOG_LEVEL_ERROR, "%s ", errors[i].label);
|
||||
debug_log_cont(ADBMS_LOG_LEVEL_ERROR, "%s ", errors[i].label);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
HAL_StatusTypeDef ___writeCMD(uint16_t command, uint8_t * args, size_t arglen) {
|
||||
HAL_StatusTypeDef ret;
|
||||
|
||||
ADBMS_Internal_Status ___writeCMD(uint16_t command, uint8_t * args, size_t arglen) {
|
||||
ADBMS_Internal_Status ret;
|
||||
if (arglen > 0) {
|
||||
args[0] = (command >> 8) & 0xFF;
|
||||
args[1] = (command) & 0xFF;
|
||||
|
||||
if (DEBUG_CHANNEL_ENABLED(LOG_LEVEL_NOISY)) {
|
||||
debug_log(LOG_LEVEL_NOISY, "%lu W | %02X %02X ", HAL_GetTick(), args[0], args[1]);
|
||||
if (DEBUG_CHANNEL_ENABLED(ADBMS_LOG_LEVEL_NOISY)) {
|
||||
debug_log(ADBMS_LOG_LEVEL_NOISY, "%lu W | %02X %02X ", mcuGetTime(), args[0], args[1]);
|
||||
|
||||
for (size_t i = 0; i < N_BMS; i++) {
|
||||
debug_log_cont(LOG_LEVEL_NOISY, "%d: ", i);
|
||||
debug_log_cont(ADBMS_LOG_LEVEL_NOISY, "%d: ", i);
|
||||
for (size_t j = 0; j < arglen; j++) {
|
||||
debug_log_cont(LOG_LEVEL_NOISY, "%02X ", args[BUFFER_BMS_OFFSET(i, arglen) + j]);
|
||||
debug_log_cont(ADBMS_LOG_LEVEL_NOISY, "%02X ", args[BUFFER_BMS_OFFSET(i, arglen) + j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -192,7 +173,7 @@ HAL_StatusTypeDef ___writeCMD(uint16_t command, uint8_t * args, size_t arglen) {
|
||||
}
|
||||
|
||||
mcuAdbmsCSLow();
|
||||
ret = mcuSPITransmit(args, CMD_BUFFER_SIZE(arglen));
|
||||
ret = mcuSPITransmit(usr_ptr, args, CMD_BUFFER_SIZE(arglen), ADBMS_SPI_TIMEOUT);
|
||||
mcuAdbmsCSHigh();
|
||||
} else {
|
||||
args[0] = (command >> 8) & 0xFF;
|
||||
@ -200,97 +181,103 @@ HAL_StatusTypeDef ___writeCMD(uint16_t command, uint8_t * args, size_t arglen) {
|
||||
calculateCommandPEC(args, 4);
|
||||
|
||||
mcuAdbmsCSLow();
|
||||
ret = mcuSPITransmit(args, 4);
|
||||
ret = mcuSPITransmit(usr_ptr, args, 4, ADBMS_SPI_TIMEOUT);
|
||||
mcuAdbmsCSHigh();
|
||||
}
|
||||
|
||||
if (ret != HAL_OK) {
|
||||
debug_log(LOG_LEVEL_ERROR, "STM32 SPI HAL returned error %s", HAL_Statuses[ret]);
|
||||
debug_log(LOG_LEVEL_ERROR, "SPI error bits: ");
|
||||
if (ret != ADBMS_OK) {
|
||||
debug_log(ADBMS_LOG_LEVEL_ERROR, "SPI HAL returned error %s", ADBMS_Statuses[ret]);
|
||||
#ifdef STM32_HAL
|
||||
debug_log(ADBMS_LOG_LEVEL_ERROR, "SPI error bits: ");
|
||||
print_spi_details();
|
||||
#endif
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
HAL_StatusTypeDef ___readCMD(uint16_t command, uint8_t * buffer, size_t arglen) {
|
||||
ADBMS_Internal_Status ___readCMD(uint16_t command, uint8_t * buffer, size_t arglen) {
|
||||
buffer[0] = (command >> 8) & 0xFF;
|
||||
buffer[1] = (command)&0xFF;
|
||||
calculateCommandPEC(buffer, 4);
|
||||
|
||||
mcuAdbmsCSLow();
|
||||
const HAL_StatusTypeDef status = mcuSPITransmitReceive(buffer, buffer, CMD_BUFFER_SIZE(arglen));
|
||||
const ADBMS_Internal_Status status = mcuSPITransmitReceive(usr_ptr, buffer, buffer, CMD_BUFFER_SIZE(arglen), ADBMS_SPI_TIMEOUT);
|
||||
mcuAdbmsCSHigh();
|
||||
|
||||
if (status != HAL_OK) {
|
||||
debug_log(LOG_LEVEL_ERROR, "STM32 SPI HAL returned error %s", HAL_Statuses[status]);
|
||||
debug_log(LOG_LEVEL_ERROR, "SPI error bits: ");
|
||||
if (status != ADBMS_OK) {
|
||||
debug_log(ADBMS_LOG_LEVEL_ERROR, "SPI HAL returned error %s", ADBMS_Statuses[status]);
|
||||
#ifdef STM32_HAL
|
||||
debug_log(ADBMS_LOG_LEVEL_ERROR, "SPI error bits: ");
|
||||
print_spi_details();
|
||||
#endif
|
||||
return status;
|
||||
}
|
||||
|
||||
//[[maybe_unused]] uint8_t commandCounter = buffer[sizeof(buffer) - 2] & 0xFC; //command counter is bits 7-2
|
||||
//TODO: check command counter?
|
||||
|
||||
if (DEBUG_CHANNEL_ENABLED(LOG_LEVEL_NOISY)) {
|
||||
debug_log(LOG_LEVEL_NOISY, "%lu R | %02X %02X ", HAL_GetTick(), command >> 8, command & 0xFF);
|
||||
if (DEBUG_CHANNEL_ENABLED(ADBMS_LOG_LEVEL_NOISY)) {
|
||||
debug_log(ADBMS_LOG_LEVEL_NOISY, "%lu R | %02X %02X ", mcuGetTime(), command >> 8, command & 0xFF);
|
||||
|
||||
//print out data bytes
|
||||
if (arglen > 0) {
|
||||
for (size_t i = 0; i < N_BMS; i++) {
|
||||
debug_log_cont(LOG_LEVEL_NOISY, "%d: ", i);
|
||||
debug_log_cont(ADBMS_LOG_LEVEL_NOISY, "%d: ", i);
|
||||
for (size_t j = 0; j < arglen; j++) {
|
||||
debug_log_cont(LOG_LEVEL_NOISY, "%02X ", buffer[BUFFER_BMS_OFFSET(i, arglen) + j]);
|
||||
debug_log_cont(ADBMS_LOG_LEVEL_NOISY, "%02X ", buffer[BUFFER_BMS_OFFSET(i, arglen) + j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (arglen == 0) {
|
||||
return HAL_OK; //no data to check
|
||||
return ADBMS_OK; //no data to check
|
||||
}
|
||||
|
||||
//check data PEC
|
||||
for (size_t i = 0; i < N_BMS; i++) {
|
||||
const size_t offset = BUFFER_BMS_OFFSET(i, arglen);
|
||||
if (checkDataPEC(&buffer[offset], arglen + 2) != 0) {
|
||||
debug_log(LOG_LEVEL_ERROR, "Invalid data PEC when reading BMS %d", i);
|
||||
debug_log(LOG_LEVEL_ERROR, "Received: ");
|
||||
debug_log(ADBMS_LOG_LEVEL_ERROR, "Invalid data PEC when reading BMS %d", i);
|
||||
debug_log(ADBMS_LOG_LEVEL_ERROR, "Received: ");
|
||||
for (size_t j = 0; j < arglen + 2; j++) {
|
||||
debug_log_cont(LOG_LEVEL_ERROR, "%02X ", buffer[offset + j]);
|
||||
debug_log_cont(ADBMS_LOG_LEVEL_ERROR, "%02X ", buffer[offset + j]);
|
||||
}
|
||||
debug_log_cont(LOG_LEVEL_ERROR, "| %02X %02X ", buffer[offset + arglen], buffer[offset + arglen + 1]); //print out the DPEC
|
||||
debug_log(LOG_LEVEL_ERROR, " DATA ^");
|
||||
debug_log_cont(ADBMS_LOG_LEVEL_ERROR, "| %02X %02X ", buffer[offset + arglen], buffer[offset + arglen + 1]); //print out the DPEC
|
||||
debug_log(ADBMS_LOG_LEVEL_ERROR, " DATA ^");
|
||||
//print out spaces until start of DPEC
|
||||
for (size_t j = 0; j < arglen - 1; j++) {
|
||||
debug_log_cont(LOG_LEVEL_ERROR, (arglen < 2) ? "" : "^^^");
|
||||
debug_log_cont(ADBMS_LOG_LEVEL_ERROR, (arglen < 2) ? "" : "^^^");
|
||||
}
|
||||
debug_log_cont(LOG_LEVEL_ERROR, "^^ ");
|
||||
debug_log_cont(LOG_LEVEL_ERROR, " PEC ^");
|
||||
return HAL_ERROR;
|
||||
debug_log_cont(ADBMS_LOG_LEVEL_ERROR, "^^ ");
|
||||
debug_log_cont(ADBMS_LOG_LEVEL_ERROR, " PEC ^");
|
||||
return ADBMS_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return HAL_OK;
|
||||
return ADBMS_OK;
|
||||
}
|
||||
|
||||
//check poll command - no data PEC sent back, waitTime is in SPI clock cycles
|
||||
HAL_StatusTypeDef __pollCMD(uint16_t command, uint8_t waitTime) {
|
||||
ADBMS_Internal_Status __pollCMD(uint16_t command, uint8_t waitTime) {
|
||||
uint8_t buffer[4 + (waitTime / 8) + 1] = {}; //8 cycles per byte, +1 as we round up
|
||||
buffer[0] = (command >> 8) & 0xFF;
|
||||
buffer[1] = (command) & 0xFF;
|
||||
calculateCommandPEC(buffer, 4);
|
||||
|
||||
mcuAdbmsCSLow();
|
||||
const HAL_StatusTypeDef status = mcuSPITransmitReceive(buffer, buffer, sizeof buffer);
|
||||
const ADBMS_Internal_Status status = mcuSPITransmitReceive(usr_ptr, buffer, buffer, sizeof buffer, ADBMS_SPI_TIMEOUT);
|
||||
mcuAdbmsCSHigh();
|
||||
|
||||
if (status != HAL_OK) {
|
||||
debug_log(LOG_LEVEL_ERROR, "STM32 SPI HAL returned error %s", HAL_Statuses[status]);
|
||||
debug_log(LOG_LEVEL_ERROR, "SPI error bits: ");
|
||||
if (status != ADBMS_OK) {
|
||||
debug_log(ADBMS_LOG_LEVEL_ERROR, "SPI HAL returned error %s", ADBMS_Statuses[status]);
|
||||
#ifdef STM32_HAL
|
||||
debug_log(ADBMS_LOG_LEVEL_ERROR, "SPI error bits: ");
|
||||
print_spi_details();
|
||||
#endif
|
||||
return status;
|
||||
}
|
||||
|
||||
return ((buffer[sizeof buffer] & 0x0F) == 0x0) ? HAL_BUSY : HAL_OK; //SDO goes high when data is ready
|
||||
return ((buffer[sizeof buffer] & 0x0F) == 0x0) ? ADBMS_BUSY : ADBMS_OK; //SDO goes high when data is ready
|
||||
}
|
@ -72,6 +72,8 @@ void log_message(log_level_t level, const char *msg, ...);
|
||||
|
||||
#ifdef LOG_PREFIX
|
||||
#define log_message(level, fmt, ...) \
|
||||
static_assert(__builtin_constant_p(fmt), "Format string must be a compile-time constant if using LOG_PREFIX"); \
|
||||
static_assert(__builtin_constant_p(LOG_PREFIX), "LOG_PREFIX must be a compile-time constant"); \
|
||||
log_message(level, LOG_PREFIX fmt, ##__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "battery.h"
|
||||
#define ADBMS_NO_LOGGING_DEFS // fix conflicting defines
|
||||
#include "ADBMS_Driver.h"
|
||||
#include "NTC.h"
|
||||
#include "can.h"
|
||||
@ -6,6 +7,7 @@
|
||||
#include "ts_state_machine.h"
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include "main.h"
|
||||
|
||||
#define SWO_LOG_PREFIX "[BATTERY] "
|
||||
#include "swo_log.h"
|
||||
@ -14,16 +16,8 @@
|
||||
#define MAX_ERRORS_WINDOW_SIZE 16 // size of the error window for error detection
|
||||
|
||||
#define MAX_TEMP 60 // max temperature in C
|
||||
|
||||
uint16_t min_voltage = 0xFFFF;
|
||||
uint16_t max_voltage = 0;
|
||||
typeof(module_voltages) module_voltages = {[0 ... N_BMS - 1] = {0xFFFF, 0}};
|
||||
int16_t min_temp = INT16_MAX;
|
||||
int16_t max_temp = INT16_MIN;
|
||||
typeof(module_temps) module_temps = {[0 ... N_BMS - 1] = {INT16_MAX, INT16_MIN}};
|
||||
float module_std_deviation[N_BMS] = {};
|
||||
|
||||
int16_t cellTemps[N_BMS][10] = {};
|
||||
typeof(battery) battery = {};
|
||||
|
||||
static bool error_window[MAX_ERRORS_WINDOW_SIZE] = {};
|
||||
static size_t error_window_index = 0;
|
||||
@ -45,8 +39,10 @@ static inline void update_error_window(bool error, int id) {
|
||||
error_window_index %= MAX_ERRORS_WINDOW_SIZE;
|
||||
}
|
||||
|
||||
HAL_StatusTypeDef battery_init(SPI_HandleTypeDef *hspi) {
|
||||
const auto ret = AMS_Init(hspi);
|
||||
HAL_StatusTypeDef battery_init(SPI_HandleTypeDef *hspi) {
|
||||
// pull MSTR High for Microcontroller mode ADBMS6822
|
||||
HAL_GPIO_WritePin(MSTR1_GPIO_Port, MSTR1_Pin, GPIO_PIN_SET);
|
||||
auto const ret = AMS_Init(hspi);
|
||||
if (ret.status != ADBMS_NO_ERROR) {
|
||||
debug_log(LOG_LEVEL_ERROR, "Failed to initialize BMS: %s",
|
||||
ADBMS_Status_ToString(ret.status));
|
||||
@ -55,13 +51,27 @@ HAL_StatusTypeDef battery_init(SPI_HandleTypeDef *hspi) {
|
||||
}
|
||||
return HAL_ERROR;
|
||||
}
|
||||
|
||||
// Initialize battery structure with default values
|
||||
for (size_t i = 0; i < N_BMS; i++) {
|
||||
battery.module[i].chip = &bms_data[i];
|
||||
for (size_t j = 0; j < 10; j++) {
|
||||
battery.module[i].cellTemps[j] = 0;
|
||||
}
|
||||
}
|
||||
battery.pack.soc = 0.0f;
|
||||
battery.pack.min_voltage = 0xFFFF;
|
||||
battery.pack.max_voltage = 0;
|
||||
battery.pack.min_temp = INT16_MAX;
|
||||
battery.pack.max_temp = INT16_MIN;
|
||||
|
||||
debug_log(LOG_LEVEL_INFO, "Battery initialized successfully");
|
||||
return HAL_OK;
|
||||
}
|
||||
|
||||
[[gnu::optimize("no-math-errno")]]
|
||||
HAL_StatusTypeDef battery_update() {
|
||||
const auto ret = AMS_Idle_Loop();
|
||||
auto const ret = AMS_Idle_Loop();
|
||||
if (ret.status != ADBMS_NO_ERROR) {
|
||||
debug_log(LOG_LEVEL_ERROR, "Error while updating battery data: %s",
|
||||
ADBMS_Status_ToString(ret.status));
|
||||
@ -73,15 +83,15 @@ HAL_StatusTypeDef battery_update() {
|
||||
if (ret.bms_id != -1 && ret.bms_id < N_BMS) {
|
||||
const char* error_type = (ret.status == ADBMS_OVERVOLT) ? "overvoltage" : "undervoltage";
|
||||
const uint32_t voltage_flags = (ret.status == ADBMS_OVERVOLT) ?
|
||||
modules[ret.bms_id].overVoltage :
|
||||
modules[ret.bms_id].underVoltage;
|
||||
bms_data[ret.bms_id].overVoltage :
|
||||
bms_data[ret.bms_id].underVoltage;
|
||||
|
||||
debug_log(LOG_LEVEL_ERROR, "Cell %s detected on module %d, affected cells: ",
|
||||
error_type, ret.bms_id);
|
||||
|
||||
for (size_t cell = 0; cell < N_CELLS; cell++) {
|
||||
if (voltage_flags & (1UL << cell)) {
|
||||
debug_log_cont(LOG_LEVEL_ERROR, "%u (%d mV) ", cell, modules[ret.bms_id].cellVoltages[cell]);
|
||||
debug_log_cont(LOG_LEVEL_ERROR, "%u (%d mV) ", cell, bms_data[ret.bms_id].cellVoltages[cell]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -96,70 +106,61 @@ HAL_StatusTypeDef battery_update() {
|
||||
}
|
||||
|
||||
update_error_window(false, ret.bms_id);
|
||||
|
||||
min_voltage = 0xFFFF;
|
||||
max_voltage = 0;
|
||||
min_temp = INT16_MAX;
|
||||
max_temp = INT16_MIN;
|
||||
|
||||
battery.pack.min_voltage = 0xFFFF;
|
||||
battery.pack.max_voltage = 0;
|
||||
battery.pack.min_temp = INT16_MAX;
|
||||
battery.pack.max_temp = INT16_MIN;
|
||||
|
||||
for (size_t i = 0; i < N_BMS; i++) {
|
||||
// Calculate standard deviation for each module
|
||||
if (DEBUG_CHANNEL_ENABLED(DEBUG_CHANNEL)) {
|
||||
float sum = 0;
|
||||
float mean = 0;
|
||||
float variance = 0;
|
||||
|
||||
// First pass: calculate mean
|
||||
for (size_t j = 0; j < N_CELLS; j++) {
|
||||
sum += modules[i].cellVoltages[j];
|
||||
}
|
||||
mean = sum / N_CELLS;
|
||||
|
||||
// Second pass: calculate variance
|
||||
for (size_t j = 0; j < N_CELLS; j++) {
|
||||
const float diff = modules[i].cellVoltages[j] - mean;
|
||||
variance += diff * diff;
|
||||
}
|
||||
variance /= N_CELLS;
|
||||
|
||||
// Calculate standard deviation
|
||||
module_std_deviation[i] = sqrtf(variance);
|
||||
}
|
||||
|
||||
// Initialize min/max indices for this module
|
||||
battery.module[i].min_v_idx = 0;
|
||||
battery.module[i].max_v_idx = 0;
|
||||
battery.module[i].min_t_idx = 0;
|
||||
battery.module[i].max_t_idx = 0;
|
||||
|
||||
// Track min/max voltages
|
||||
for (size_t j = 0; j < N_CELLS; j++) {
|
||||
if (modules[i].cellVoltages[j] > min_voltage) {
|
||||
min_voltage = modules[i].cellVoltages[j];
|
||||
if (bms_data[i].cellVoltages[j] < battery.pack.min_voltage) {
|
||||
battery.pack.min_voltage = bms_data[i].cellVoltages[j];
|
||||
}
|
||||
if (modules[i].cellVoltages[j] < max_voltage) {
|
||||
max_voltage = modules[i].cellVoltages[j];
|
||||
if (bms_data[i].cellVoltages[j] > battery.pack.max_voltage) {
|
||||
battery.pack.max_voltage = bms_data[i].cellVoltages[j];
|
||||
}
|
||||
if (modules[i].cellVoltages[j] > module_voltages[i].max) {
|
||||
module_voltages[i].max = modules[i].cellVoltages[j];
|
||||
|
||||
// Update min/max voltage indices for this module
|
||||
if (bms_data[i].cellVoltages[j] < bms_data[i].cellVoltages[battery.module[i].min_v_idx]) {
|
||||
battery.module[i].min_v_idx = j;
|
||||
}
|
||||
if (modules[i].cellVoltages[j] < module_voltages[i].min) {
|
||||
module_voltages[i].min = modules[i].cellVoltages[j];
|
||||
if (bms_data[i].cellVoltages[j] > bms_data[i].cellVoltages[battery.module[i].max_v_idx]) {
|
||||
battery.module[i].max_v_idx = j;
|
||||
}
|
||||
}
|
||||
|
||||
// Process temperature values
|
||||
for (size_t j = 0; j < 10; j++) { //10 GPIOs
|
||||
cellTemps[i][j] = ntc_mv_to_celsius(modules[i].auxVoltages[j]);
|
||||
battery.module[i].cellTemps[j] = ntc_mv_to_celsius(bms_data[i].auxVoltages[j]);
|
||||
|
||||
if (cellTemps[i][j] > max_temp) {
|
||||
max_temp = cellTemps[i][j];
|
||||
|
||||
// For new battery struct
|
||||
if (battery.module[i].cellTemps[j] > battery.pack.max_temp) {
|
||||
battery.pack.max_temp = battery.module[i].cellTemps[j];
|
||||
}
|
||||
if (cellTemps[i][j] < min_temp) {
|
||||
min_temp = cellTemps[i][j];
|
||||
if (battery.module[i].cellTemps[j] < battery.pack.min_temp) {
|
||||
battery.pack.min_temp = battery.module[i].cellTemps[j];
|
||||
}
|
||||
|
||||
// Update min/max temperature indices for this module
|
||||
if (battery.module[i].cellTemps[j] < battery.module[i].cellTemps[battery.module[i].min_t_idx]) {
|
||||
battery.module[i].min_t_idx = j;
|
||||
}
|
||||
if (battery.module[i].cellTemps[j] > battery.module[i].cellTemps[battery.module[i].max_t_idx]) {
|
||||
battery.module[i].max_t_idx = j;
|
||||
}
|
||||
|
||||
if (cellTemps[i][j] > module_temps[i].max) {
|
||||
module_temps[i].max = cellTemps[i][j];
|
||||
}
|
||||
if (cellTemps[i][j] < module_temps[i].min) {
|
||||
module_temps[i].min = cellTemps[i][j];
|
||||
}
|
||||
|
||||
if (cellTemps[i][j] > (MAX_TEMP * (uint16_t)(TEMP_CONV))) {
|
||||
debug_log(LOG_LEVEL_ERROR, "Cell %u on BMS %u overtemp: %d0 mC", j, i, cellTemps[i][j]);
|
||||
// Check for overtemperature condition
|
||||
if (battery.module[i].cellTemps[j] > (MAX_TEMP * (uint16_t)(TEMP_CONV))) {
|
||||
debug_log(LOG_LEVEL_ERROR, "Cell %u on BMS %u overtemp: %d0 mC", j, i, battery.module[i].cellTemps[j]);
|
||||
can_send_error(TS_ERRORKIND_CELL_OVERTEMP, i);
|
||||
ts_sm_set_error_source(TS_ERROR_SOURCE_SLAVES, TS_ERRORKIND_CELL_OVERTEMP, true);
|
||||
} else {
|
||||
|
@ -35,7 +35,7 @@ void can_init(FDCAN_HandleTypeDef *handle) {
|
||||
ftcan_add_filter(CAN_ID_AMS_DETAILS_FC, 0xFFF);
|
||||
ftcan_add_filter(ISOTP_LOG_FC_CAN_ID, 0xFFF);
|
||||
|
||||
const auto status = isotp_add_connection(CAN_ID_AMS_DETAILS_FC, CAN_ID_AMS_DETAILS, ISOTP_FLAGS_NONE);
|
||||
auto const status = isotp_add_connection(CAN_ID_AMS_DETAILS_FC, CAN_ID_AMS_DETAILS, ISOTP_FLAGS_NONE);
|
||||
if (status < 0) {
|
||||
log_warning("Failed to add ISO-TP connection: %s", isotp_status_to_string((isotp_status_t)status));
|
||||
}
|
||||
@ -49,9 +49,9 @@ void can_init(FDCAN_HandleTypeDef *handle) {
|
||||
HAL_StatusTypeDef can_send_status() {
|
||||
uint8_t data[8];
|
||||
data[0] = ts_state.current_state | (sdc_closed << 7);
|
||||
data[1] = roundf(current_soc);
|
||||
ftcan_marshal_unsigned(&data[2], min_voltage, 2);
|
||||
ftcan_marshal_signed(&data[4], max_temp, 2);
|
||||
data[1] = roundf(battery.pack.soc); // Use the battery struct now
|
||||
ftcan_marshal_unsigned(&data[2], battery.pack.min_voltage, 2); // Use the battery struct now
|
||||
ftcan_marshal_signed(&data[4], battery.pack.max_temp, 2); // Use the battery struct now
|
||||
data[6] = imd_data.state | (imd_data.ok << 7);
|
||||
if (imd_data.r_iso < 0xFFF) {
|
||||
data[7] = imd_data.r_iso >> 4;
|
||||
@ -70,8 +70,8 @@ HAL_StatusTypeDef can_send_status() {
|
||||
|
||||
HAL_StatusTypeDef can_send_details() {
|
||||
static uint8_t module_index = 0;
|
||||
static uint8_t data[103] = {}; //sizeof(Cell_Module) + 10 + 1
|
||||
const auto module = &modules[module_index];
|
||||
static uint8_t data[103] = {}; //sizeof(BMS_Chip) + 10 + 1
|
||||
auto const module = &bms_data[module_index];
|
||||
auto data_ptr = &data[1];
|
||||
|
||||
isotp_status_t status = isotp_try_add_message(isotp_connection_id, data, sizeof(data));
|
||||
@ -136,7 +136,7 @@ HAL_StatusTypeDef can_send_details() {
|
||||
|
||||
// Marshal temperature data
|
||||
for (int i = 0; i < 10; i++) {
|
||||
data_ptr = ftcan_marshal_signed(data_ptr, cellTemps[module_index][i], 2);
|
||||
data_ptr = ftcan_marshal_signed(data_ptr, battery.module[module_index].cellTemps[i], 2);
|
||||
}
|
||||
|
||||
if ((status = isotp_add_message(isotp_connection_id, data, sizeof(data))) != ISOTP_OK) {
|
||||
@ -164,7 +164,7 @@ void ftcan_msg_received_cb(uint16_t id, size_t len, const uint8_t *data) {
|
||||
switch (id) {
|
||||
case ISOTP_LOG_FC_CAN_ID:
|
||||
case CAN_ID_AMS_DETAILS_FC:
|
||||
const auto status = isotp_handle_incoming(id, data, len);
|
||||
auto const status = isotp_handle_incoming(id, data, len);
|
||||
if (status != ISOTP_OK) {
|
||||
log_debug("Error when handling flow control: %s", isotp_status_to_string(status));
|
||||
}
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include "swo_log_backend.h"
|
||||
|
||||
void print_master_status() {
|
||||
const auto backend = swo_log_get_backend();
|
||||
auto const backend = swo_log_get_backend();
|
||||
|
||||
if (!backend->is_enabled(LOG_LEVEL_INFO)) {
|
||||
return; // No need to print if the backend is not enabled for this log level
|
||||
@ -61,32 +61,25 @@ void print_master_status() {
|
||||
swo_write(" Time delta: %lu ms", HAL_GetTick() - shunt_data.last_message);
|
||||
|
||||
swo_write("\nBattery data:");
|
||||
swo_write(" Min/Max voltage: %d mV / %d mV", min_voltage, max_voltage);
|
||||
swo_write(" Min/Max temp: %d °C / %d °C", min_temp, max_temp);
|
||||
swo_write(" SoC: %.2f %%", current_soc);
|
||||
swo_write(" Module data: Min V | Max V | Std Dev | Min T | Max T");
|
||||
auto max_std_dev = 0.0f;
|
||||
for (size_t i = 0; i < N_BMS; i++) {
|
||||
if (module_std_deviation[i] > max_std_dev) {
|
||||
max_std_dev = module_std_deviation[i];
|
||||
}
|
||||
}
|
||||
swo_write(" Min/Max voltage: %d mV / %d mV", battery.pack.min_voltage, battery.pack.max_voltage);
|
||||
swo_write(" Min/Max temp: %d °C / %d °C", battery.pack.min_temp, battery.pack.max_temp);
|
||||
swo_write(" SoC: %.2f %%", battery.pack.soc);
|
||||
swo_write(" Module data: Min V | Max V | Min T | Max T");
|
||||
for (size_t i = 0; i < N_BMS; i++) {
|
||||
auto module_data = getModuleMinMax(i);
|
||||
#if USE_ANSI_ESCAPE_CODES
|
||||
#define COLOR_MIN "\033[38;5;75m" //blue for min
|
||||
#define COLOR_MAX "\033[38;5;203m" //red for max
|
||||
#define COLOR_RESET "\033[0m"
|
||||
swo_write(" %2zu: %s%5d mV%s | %s%5d mV%s | %s%.2f mV%s | %s%3d °C%s | %s%3d °C%s", i,
|
||||
(module_voltages[i].min == min_voltage) ? COLOR_MIN : "", (module_voltages[i].min), COLOR_RESET,
|
||||
(module_voltages[i].max == max_voltage) ? COLOR_MAX : "", (module_voltages[i].max), COLOR_RESET,
|
||||
(module_std_deviation[i] > max_std_dev) ? COLOR_MAX : "", module_std_deviation[i], COLOR_RESET,
|
||||
(module_temps[i].min == min_temp) ? COLOR_MIN : "", (module_temps[i].min), COLOR_RESET,
|
||||
(module_temps[i].max == max_temp) ? COLOR_MAX : "", (module_temps[i].max), COLOR_RESET);
|
||||
swo_write(" %2zu: %s%5d mV%s | %s%5d mV%s | %s%3d °C%s | %s%3d °C%s", i,
|
||||
(module_data.min_v == battery.pack.min_voltage) ? COLOR_MIN : "", (module_data.min_v), COLOR_RESET,
|
||||
(module_data.max_v == battery.pack.max_voltage) ? COLOR_MAX : "", (module_data.max_v), COLOR_RESET,
|
||||
(module_data.min_t == battery.pack.min_temp) ? COLOR_MIN : "", (module_data.min_t), COLOR_RESET,
|
||||
(module_data.max_t == battery.pack.max_temp) ? COLOR_MAX : "", (module_data.max_t), COLOR_RESET);
|
||||
#else
|
||||
swo_write(" %2zu: %5d mV | %5d mV | %.2f mV | %3d °C | %3d °C", i,
|
||||
module_voltages[i].min, module_voltages[i].max,
|
||||
module_std_deviation[i],
|
||||
module_temps[i].min, module_temps[i].max);
|
||||
swo_write(" %2zu: %5d mV | %5d mV | %3d °C | %3d °C", i,
|
||||
module_data.min_v, module_data.max_v,
|
||||
module_data.min_t, module_data.max_t);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
#include "swo_log_backend.h"
|
||||
|
||||
void print_battery_info() {
|
||||
const auto backend = swo_log_get_backend();
|
||||
auto const backend = swo_log_get_backend();
|
||||
|
||||
if (!backend->is_enabled(LOG_LEVEL_INFO)) {
|
||||
return; // No need to print if the backend is not enabled for this log level
|
||||
@ -28,131 +28,131 @@ void print_battery_info() {
|
||||
|
||||
for (size_t i = 0; i < N_BMS; i++) {
|
||||
swo_write("Module %d status:", i);
|
||||
swo_write(" BMS ID: 0x%08lx%08lx", (uint32_t)(modules[i].bmsID >> 32), (uint32_t)(modules[i].bmsID & 0xFFFFFFFF));
|
||||
|
||||
swo_write(" BMS ID: 0x%08lx%08lx", (uint32_t)(bms_data[i].bmsID >> 32), (uint32_t)(bms_data[i].bmsID & 0xFFFFFFFF));
|
||||
|
||||
// Print cell voltages in 4x4 format
|
||||
swo_write(" Cell voltages (mV):");
|
||||
swo_write(" C0: %4d C1: %4d C2: %4d C3: %4d",
|
||||
modules[i].cellVoltages[0], modules[i].cellVoltages[1],
|
||||
modules[i].cellVoltages[2], modules[i].cellVoltages[3]);
|
||||
bms_data[i].cellVoltages[0], bms_data[i].cellVoltages[1],
|
||||
bms_data[i].cellVoltages[2], bms_data[i].cellVoltages[3]);
|
||||
swo_write(" C4: %4d C5: %4d C6: %4d C7: %4d",
|
||||
modules[i].cellVoltages[4], modules[i].cellVoltages[5],
|
||||
modules[i].cellVoltages[6], modules[i].cellVoltages[7]);
|
||||
bms_data[i].cellVoltages[4], bms_data[i].cellVoltages[5],
|
||||
bms_data[i].cellVoltages[6], bms_data[i].cellVoltages[7]);
|
||||
swo_write(" C8: %4d C9: %4d C10: %4d C11: %4d",
|
||||
modules[i].cellVoltages[8], modules[i].cellVoltages[9],
|
||||
modules[i].cellVoltages[10], modules[i].cellVoltages[11]);
|
||||
bms_data[i].cellVoltages[8], bms_data[i].cellVoltages[9],
|
||||
bms_data[i].cellVoltages[10], bms_data[i].cellVoltages[11]);
|
||||
swo_write(" C12: %4d C13: %4d C14: %4d C15: %4d",
|
||||
modules[i].cellVoltages[12], modules[i].cellVoltages[13],
|
||||
modules[i].cellVoltages[14], modules[i].cellVoltages[15]);
|
||||
|
||||
bms_data[i].cellVoltages[12], bms_data[i].cellVoltages[13],
|
||||
bms_data[i].cellVoltages[14], bms_data[i].cellVoltages[15]);
|
||||
|
||||
// Print GPIO values
|
||||
swo_write(" GPIO voltages (mV):");
|
||||
swo_write(
|
||||
" G0: %4d G1: %4d G2: %4d G3: %4d G4: %4d",
|
||||
modules[i].auxVoltages[0], modules[i].auxVoltages[1],
|
||||
modules[i].auxVoltages[2], modules[i].auxVoltages[3],
|
||||
modules[i].auxVoltages[4]);
|
||||
bms_data[i].auxVoltages[0], bms_data[i].auxVoltages[1],
|
||||
bms_data[i].auxVoltages[2], bms_data[i].auxVoltages[3],
|
||||
bms_data[i].auxVoltages[4]);
|
||||
swo_write(
|
||||
" G5: %4d G6: %4d G7: %4d G8: %4d G9: %4d",
|
||||
modules[i].auxVoltages[5], modules[i].auxVoltages[6],
|
||||
modules[i].auxVoltages[7], modules[i].auxVoltages[8],
|
||||
modules[i].auxVoltages[9]);
|
||||
|
||||
bms_data[i].auxVoltages[5], bms_data[i].auxVoltages[6],
|
||||
bms_data[i].auxVoltages[7], bms_data[i].auxVoltages[8],
|
||||
bms_data[i].auxVoltages[9]);
|
||||
|
||||
// Print temperatures
|
||||
swo_write(" GPIO as temperatures (°C):");
|
||||
swo_write(
|
||||
" G0: %4d G1: %4d G2: %4d G3: %4d G4: %4d",
|
||||
cellTemps[i][0], cellTemps[i][1], cellTemps[i][2],
|
||||
cellTemps[i][3], cellTemps[i][4]);
|
||||
battery.module[i].cellTemps[0], battery.module[i].cellTemps[1], battery.module[i].cellTemps[2],
|
||||
battery.module[i].cellTemps[3], battery.module[i].cellTemps[4]);
|
||||
swo_write(
|
||||
" G5: %4d G6: %4d G7: %4d G8: %4d G9: %4d",
|
||||
cellTemps[i][5], cellTemps[i][6], cellTemps[i][7],
|
||||
cellTemps[i][8], cellTemps[i][9]);
|
||||
|
||||
battery.module[i].cellTemps[5], battery.module[i].cellTemps[6], battery.module[i].cellTemps[7],
|
||||
battery.module[i].cellTemps[8], battery.module[i].cellTemps[9]);
|
||||
|
||||
swo_write(
|
||||
" Internal temp: %d, VAnalog: %d, VDigital: %d, VRef: %d",
|
||||
modules[i].internalDieTemp, modules[i].analogSupplyVoltage,
|
||||
modules[i].digitalSupplyVoltage, modules[i].refVoltage);
|
||||
|
||||
bms_data[i].internalDieTemp, bms_data[i].analogSupplyVoltage,
|
||||
bms_data[i].digitalSupplyVoltage, bms_data[i].refVoltage);
|
||||
|
||||
// Print error flags if any are set
|
||||
bool hasFlags = false;
|
||||
char flagBuffer[128] = "";
|
||||
char *bufPos = flagBuffer;
|
||||
|
||||
if (modules[i].status.CS_FLT) {
|
||||
|
||||
if (bms_data[i].status.CS_FLT) {
|
||||
bufPos = stpcpy(bufPos, "CS_FLT ");
|
||||
hasFlags = true;
|
||||
}
|
||||
if (modules[i].status.SMED) {
|
||||
if (bms_data[i].status.SMED) {
|
||||
bufPos = stpcpy(bufPos, "SMED ");
|
||||
hasFlags = true;
|
||||
}
|
||||
if (modules[i].status.SED) {
|
||||
if (bms_data[i].status.SED) {
|
||||
bufPos = stpcpy(bufPos, "SED ");
|
||||
hasFlags = true;
|
||||
}
|
||||
if (modules[i].status.CMED) {
|
||||
if (bms_data[i].status.CMED) {
|
||||
bufPos = stpcpy(bufPos, "CMED ");
|
||||
hasFlags = true;
|
||||
}
|
||||
if (modules[i].status.CED) {
|
||||
if (bms_data[i].status.CED) {
|
||||
bufPos = stpcpy(bufPos, "CED ");
|
||||
hasFlags = true;
|
||||
}
|
||||
if (modules[i].status.VD_UV) {
|
||||
if (bms_data[i].status.VD_UV) {
|
||||
bufPos = stpcpy(bufPos, "VD_UV ");
|
||||
hasFlags = true;
|
||||
}
|
||||
if (modules[i].status.VD_OV) {
|
||||
if (bms_data[i].status.VD_OV) {
|
||||
bufPos = stpcpy(bufPos, "VD_OV ");
|
||||
hasFlags = true;
|
||||
}
|
||||
if (modules[i].status.VA_UV) {
|
||||
if (bms_data[i].status.VA_UV) {
|
||||
bufPos = stpcpy(bufPos, "VA_UV ");
|
||||
hasFlags = true;
|
||||
}
|
||||
if (modules[i].status.VA_OV) {
|
||||
if (bms_data[i].status.VA_OV) {
|
||||
bufPos = stpcpy(bufPos, "VA_OV ");
|
||||
hasFlags = true;
|
||||
}
|
||||
if (modules[i].status.THSD) {
|
||||
if (bms_data[i].status.THSD) {
|
||||
bufPos = stpcpy(bufPos, "THSD ");
|
||||
hasFlags = true;
|
||||
}
|
||||
if (modules[i].status.SLEEP) {
|
||||
if (bms_data[i].status.SLEEP) {
|
||||
bufPos = stpcpy(bufPos, "SLEEP ");
|
||||
hasFlags = true;
|
||||
}
|
||||
if (modules[i].status.SPIFLT) {
|
||||
if (bms_data[i].status.SPIFLT) {
|
||||
bufPos = stpcpy(bufPos, "SPIFLT ");
|
||||
hasFlags = true;
|
||||
}
|
||||
if (modules[i].status.COMPARE) {
|
||||
if (bms_data[i].status.COMPARE) {
|
||||
bufPos = stpcpy(bufPos, "COMPARE ");
|
||||
hasFlags = true;
|
||||
}
|
||||
if (modules[i].status.VDE) {
|
||||
if (bms_data[i].status.VDE) {
|
||||
bufPos = stpcpy(bufPos, "VDE ");
|
||||
hasFlags = true;
|
||||
}
|
||||
if (modules[i].status.VDEL) {
|
||||
if (bms_data[i].status.VDEL) {
|
||||
bufPos = stpcpy(bufPos, "VDEL ");
|
||||
hasFlags = true;
|
||||
}
|
||||
|
||||
swo_write(" Status flags: %s", hasFlags ? flagBuffer : "[none]");
|
||||
|
||||
if (modules[i].status.CS_FLT) { // Print out which ADCs are faulting
|
||||
if (bms_data[i].status.CS_FLT) { // Print out which ADCs are faulting
|
||||
swo_write("Comparison fault on ADC/Cell(s): ");
|
||||
for (ssize_t j = 0; j < 16; j++) {
|
||||
if (modules[i].status.CS_FLT & (1u << j)) {
|
||||
if (bms_data[i].status.CS_FLT & (1u << j)) {
|
||||
swo_write("%d ", j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
swo_write(" Conversion counter: %d",
|
||||
modules[i].status.CCTS);
|
||||
bms_data[i].status.CCTS);
|
||||
}
|
||||
swo_write("\n------ Updated at %lu ------", HAL_GetTick());
|
||||
}
|
@ -12,52 +12,29 @@
|
||||
#define SOC_ESTIMATION_NO_CURRENT_THRESH 200 // mA
|
||||
#define SOC_ESTIMATION_NO_CURRENT_TIME 100000 // ms
|
||||
#define SOC_ESTIMATION_BATTERY_CAPACITY 64800000 // mAs
|
||||
|
||||
typedef struct {
|
||||
uint16_t ocv;
|
||||
float soc;
|
||||
} ocv_soc_pair_t;
|
||||
|
||||
ocv_soc_pair_t OCV_SOC_PAIRS[] = {
|
||||
{2500, 0.00f}, {2990, 3.97f}, {3230, 9.36f}, {3320, 12.60f},
|
||||
{3350, 13.68f}, {3410, 20.15f}, {3530, 32.01f}, {3840, 66.53f},
|
||||
{4010, 83.79f}, {4020, 90.26f}, {4040, 94.58f}, {4100, 98.89f},
|
||||
{4200, 100.00f}};
|
||||
|
||||
float current_soc;
|
||||
|
||||
int current_was_flowing;
|
||||
uint32_t last_current_time;
|
||||
float soc_before_current;
|
||||
float mAs_before_current;
|
||||
|
||||
void soc_init() {
|
||||
current_soc = 0;
|
||||
last_current_time = 0;
|
||||
current_was_flowing = 1;
|
||||
}
|
||||
|
||||
void soc_update() {
|
||||
const uint32_t now = HAL_GetTick();
|
||||
if (abs(shunt_data.current) >= SOC_ESTIMATION_NO_CURRENT_THRESH) {
|
||||
last_current_time = now;
|
||||
if (!current_was_flowing) {
|
||||
soc_before_current = current_soc;
|
||||
mAs_before_current = shunt_data.current_counter;
|
||||
}
|
||||
current_was_flowing = 1;
|
||||
} else {
|
||||
current_was_flowing = 0;
|
||||
}
|
||||
|
||||
if (now - last_current_time >= SOC_ESTIMATION_NO_CURRENT_TIME ||
|
||||
last_current_time == 0) {
|
||||
// Assume we're measuring OCV if there's been no current for a while (or
|
||||
// we've just turned on the battery).
|
||||
current_soc = soc_for_ocv(min_voltage);
|
||||
} else {
|
||||
// Otherwise, use the current counter to update SoC
|
||||
const float as_delta = shunt_data.current_counter - mAs_before_current;
|
||||
const float soc_delta = as_delta / SOC_ESTIMATION_BATTERY_CAPACITY * 100;
|
||||
current_soc = soc_before_current - soc_delta;
|
||||
}
|
||||
}
|
||||
|
||||
float soc_for_ocv(uint16_t ocv) {
|
||||
static float soc_for_ocv(uint16_t ocv) {
|
||||
size_t i = 0;
|
||||
const size_t array_length = sizeof(OCV_SOC_PAIRS) / sizeof(*OCV_SOC_PAIRS);
|
||||
// Find the index of the first element with OCV greater than the target OCV
|
||||
@ -88,3 +65,29 @@ float soc_for_ocv(uint16_t ocv) {
|
||||
|
||||
return interpolated_soc;
|
||||
}
|
||||
|
||||
void soc_update() {
|
||||
const uint32_t now = HAL_GetTick();
|
||||
if (abs(shunt_data.current) >= SOC_ESTIMATION_NO_CURRENT_THRESH) {
|
||||
last_current_time = now;
|
||||
if (!current_was_flowing) {
|
||||
soc_before_current = battery.pack.soc;
|
||||
mAs_before_current = shunt_data.current_counter;
|
||||
}
|
||||
current_was_flowing = 1;
|
||||
} else {
|
||||
current_was_flowing = 0;
|
||||
}
|
||||
|
||||
if (now - last_current_time >= SOC_ESTIMATION_NO_CURRENT_TIME ||
|
||||
last_current_time == 0) {
|
||||
// Assume we're measuring OCV if there's been no current for a while (or
|
||||
// we've just turned on the battery).
|
||||
battery.pack.soc = soc_for_ocv(battery.pack.min_voltage);
|
||||
} else {
|
||||
// Otherwise, use the current counter to update SoC
|
||||
const float as_delta = shunt_data.current_counter - mAs_before_current;
|
||||
const float soc_delta = as_delta / SOC_ESTIMATION_BATTERY_CAPACITY * 100;
|
||||
battery.pack.soc = soc_before_current - soc_delta;
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ void ts_sm_update() {
|
||||
ts_state.current_state = TS_ERROR;
|
||||
}
|
||||
|
||||
const auto old_state = ts_state.current_state;
|
||||
auto const old_state = ts_state.current_state;
|
||||
|
||||
switch (ts_state.current_state) {
|
||||
case TS_INACTIVE:
|
||||
|
Loading…
x
Reference in New Issue
Block a user